mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 10:03:51 +00:00
* craft-psd: raise the 64-bit .abr sample budget to 2 GiB Photoshop writes brush packs up to its 2 GB .abr limit; a 1 GiB budget refused valid packs over it. 32-bit targets keep 512 MiB. Ported from storytold/photocraft#13282 (photocraft#5949). * craft-psd: FELS is checked and transcoded as filter effects FELS holds the smart-filter masks of linked smart objects in the FEid layout. check_structure now re-parses it, and a layered TIFF transcodes it field by field: a little-endian save dropped the block as unknown. Ported from storytold/photocraft#8275 (photocraft#8260); the test is new. * craft-psd: a layered TIFF keeps external smart-object links An external linked file (liFE) holds the descriptor of its link, and from version 3 its bytes. The TIFF transcoder skipped both, so the record failed to convert and a little-endian save dropped the lnkE block. Ported from storytold/photocraft#6072; the test is new. * craft-psd: the example and the save test write into fresh private directories The big_psb example and the save_and_open test used a predictable temp directory name, which a planted directory or symlink could take over. They now create a new owner-only directory, and the example removes a half-written fixture from its own default folder. Ported from storytold/photocraft#13266 (its crates/psd part). * craft-psd: the pixel preflight sees the merged image's compression and size from_bytes_and_merged_preflight_info and from_source_and_merged_preflight_info hand the preflight the merged image's compression and encoded length before the image is read, so a caller can estimate the memory a decode will need (an opaque RGB image stored raw needs no inflate buffer). A missing compression field is None, and a refusal takes precedence over it. Ported from storytold/photocraft#11288 (its crates/psd part); the test is new. * craft-psd: typed layer selection ids (resource 1069) ResourceData::LayerSelectionIds: the selected layers' ids in stored order, duplicates and zero kept. The count is checked against the whole payload before anything is allocated; truncated or trailing data is malformed, not a partial selection. Ported from storytold/photocraft#10268 (its crates/psd part). * craft-psd: README matches the code The typed blocks tsly, lmgm and vmgm, vector masks and slices typed by path and slices, the resources ImageResource::parsed types, the ATN round-trip caveats and version-12 reading, and the corpus test's known-bad file. Ported from storytold/photocraft#11600 (its crates/psd part), with the resource list brought up to date. * craft-psd: atn_dump escapes control characters from the file A set's names, warnings, paths and errors are printed with control characters escaped, so a file cannot drive the terminal. Ported from storytold/photocraft#11218 (merged into PhotoCraft's copy after craft-psd was moved). * craft-psd: the .abr budget test checks the 64-bit bound at compile time clippy refuses an assertion whose value is constant. --------- Co-authored-by: rgoza1996 <97354241+rgoza1996@users.noreply.github.com> Co-authored-by: Korg1ch <57859048+Korg1ch@users.noreply.github.com> Co-authored-by: PJaYOfficial <1597623+PJaYOfficial@users.noreply.github.com> Co-authored-by: acilione <antoninocilione96@gmail.com> Co-authored-by: kirubhakaran12 <27127513+kirubhakaran12@users.noreply.github.com> Co-authored-by: Nicolas Falesy <112726756+nicolasfalesy@users.noreply.github.com>
94 lines
4.2 KiB
Rust
94 lines
4.2 KiB
Rust
//! Print the actions and steps of `.atn` files, one event per line, with the top-level keys of
|
|
//! each step's descriptor: `cargo run -p craft-psd --example atn_dump -- set.atn…`
|
|
//!
|
|
//! Handy when adding a Photoshop event to the engine's `.atn` import table: it shows the event
|
|
//! id, the panel name and the parameter keys Photoshop recorded.
|
|
|
|
use craft_psd::atn;
|
|
use craft_psd::descriptor::{Descriptor, ReferenceItem, Value};
|
|
|
|
/// Escapes control characters so a file's names cannot drive the terminal.
|
|
fn clean(s: &str) -> String {
|
|
s.chars().flat_map(char::escape_debug).collect()
|
|
}
|
|
|
|
fn summary(v: &Value) -> String {
|
|
match v {
|
|
Value::Integer(i) => i.to_string(),
|
|
Value::Double(d) => format!("{d}"),
|
|
Value::UnitFloat { unit, value } => format!("{value}{}", String::from_utf8_lossy(unit)),
|
|
Value::Boolean(b) => b.to_string(),
|
|
Value::Text(t) => format!("{:?}", t.to_string_lossy()),
|
|
Value::Enumerated { type_id, value } => {
|
|
format!("{}.{}", String::from_utf8_lossy(type_id.as_bytes()), String::from_utf8_lossy(value.as_bytes()))
|
|
}
|
|
Value::Descriptor(d) | Value::GlobalObject(d) => format!("{{{}}}", keys(d)),
|
|
Value::List(l) => format!("[{}]", l.iter().map(summary).collect::<Vec<_>>().join(", ")),
|
|
Value::Reference(r) => format!("ref[{}]", r.iter().map(reference).collect::<Vec<_>>().join(" ")),
|
|
other => format!("{other:?}").chars().take(40).collect(),
|
|
}
|
|
}
|
|
|
|
fn id(i: &craft_psd::descriptor::Id) -> String {
|
|
String::from_utf8_lossy(i.as_bytes()).trim().to_string()
|
|
}
|
|
|
|
fn reference(item: &ReferenceItem) -> String {
|
|
match item {
|
|
ReferenceItem::Property { class, key } => format!("prop({},{})", id(&class.class_id), id(key)),
|
|
ReferenceItem::Class(c) => format!("Clss({})", id(&c.class_id)),
|
|
ReferenceItem::Enumerated { class, type_id, value } => format!("Enmr({},{}.{})", id(&class.class_id), id(type_id), id(value)),
|
|
ReferenceItem::Offset { class, offset } => format!("rele({},{offset})", id(&class.class_id)),
|
|
ReferenceItem::Identifier { class, id: v } => format!("Idnt({},{v})", id(&class.class_id)),
|
|
ReferenceItem::Index { class, index } => format!("indx({},{index})", id(&class.class_id)),
|
|
ReferenceItem::Name { class, name } => format!("name({},{:?})", id(&class.class_id), name.to_string_lossy()),
|
|
}
|
|
}
|
|
|
|
fn keys(d: &Descriptor) -> String {
|
|
d.items.iter().map(|(k, v)| format!("{}={}", String::from_utf8_lossy(k.as_bytes()), summary(v))).collect::<Vec<_>>().join(" ")
|
|
}
|
|
|
|
fn main() {
|
|
let mut failed = false;
|
|
for path in std::env::args().skip(1) {
|
|
let bytes = match std::fs::read(&path) {
|
|
Ok(b) => b,
|
|
Err(e) => {
|
|
eprintln!("{}: {e}", clean(&path));
|
|
failed = true;
|
|
continue;
|
|
}
|
|
};
|
|
match atn::parse(&bytes) {
|
|
Ok(f) => {
|
|
println!(
|
|
"== {}: set {:?}, {} actions{}{}",
|
|
clean(&path),
|
|
f.name.to_string_lossy(),
|
|
f.actions.len(),
|
|
f.encoding.as_deref().map(|e| format!(", names in {}", clean(e))).unwrap_or_default(),
|
|
if f.warnings.is_empty() { String::new() } else { format!(" ({})", clean(&f.warnings.join("; "))) }
|
|
);
|
|
for a in &f.actions {
|
|
println!(" action {:?} ({} steps)", a.name.to_string_lossy(), a.items.len());
|
|
for item in &a.items {
|
|
let flags = format!("{}{}", if item.enabled { "" } else { " off" }, if item.with_dialog { " dialog" } else { "" });
|
|
match &item.descriptor {
|
|
Some(d) => println!(" {:<22} {:<28}{flags} {}", clean(&item.event.id()), clean(&f.item_label(item)), clean(&keys(d))),
|
|
None => println!(" {:<22} {:<28}{flags}", clean(&item.event.id()), clean(&f.item_label(item))),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("== {}: {}", clean(&path), clean(&e.to_string()));
|
|
failed = true;
|
|
}
|
|
}
|
|
}
|
|
if failed {
|
|
std::process::exit(1);
|
|
}
|
|
}
|