From 926b1bf9f7ebe014e25a7c0d6b4dd866c0f97025 Mon Sep 17 00:00:00 2001 From: justinjohn0306 <34035011+justinjohn0306@users.noreply.github.com> Date: Thu, 22 Oct 2026 15:40:13 +0530 Subject: [PATCH 1/2] fix(actions): accept Photoshop blend mode stringIDs --- crates/engine/src/atn_cmds.rs | 18 ++++++++++++++++++ crates/engine/src/atn_cmds/tests.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/crates/engine/src/atn_cmds.rs b/crates/engine/src/atn_cmds.rs index afba793027..45aa76368c 100644 --- a/crates/engine/src/atn_cmds.rs +++ b/crates/engine/src/atn_cmds.rs @@ -303,33 +303,51 @@ fn path_value(text: &str) -> D { /// Photoshop's `BlnM` enumeration ↔ PhotoCraft's blend mode labels. const BLEND_MODES: &[(&str, &str)] = &[ ("Nrml", "Normal"), + ("normal", "Normal"), ("Dslv", "Dissolve"), + ("dissolve", "Dissolve"), ("Drkn", "Darken"), + ("darken", "Darken"), ("Mltp", "Multiply"), + ("multiply", "Multiply"), ("CBrn", "Color Burn"), + ("colorBurn", "Color Burn"), ("linearBurn", "Linear Burn"), ("darkerColor", "Darker Color"), ("Lghn", "Lighten"), + ("lighten", "Lighten"), ("Scrn", "Screen"), + ("screen", "Screen"), ("CDdg", "Color Dodge"), + ("colorDodge", "Color Dodge"), ("linearDodge", "Linear Dodge (Add)"), ("lighterColor", "Lighter Color"), ("Ovrl", "Overlay"), + ("overlay", "Overlay"), ("SftL", "Soft Light"), + ("softLight", "Soft Light"), ("HrdL", "Hard Light"), + ("hardLight", "Hard Light"), ("vividLight", "Vivid Light"), ("linearLight", "Linear Light"), ("pinLight", "Pin Light"), ("hardMix", "Hard Mix"), ("Dfrn", "Difference"), + ("difference", "Difference"), ("Xclu", "Exclusion"), + ("exclusion", "Exclusion"), ("blendSubtraction", "Subtract"), ("blendDivide", "Divide"), ("H", "Hue"), + ("hue", "Hue"), ("Strt", "Saturation"), + ("saturation", "Saturation"), ("Clr", "Color"), + ("color", "Color"), ("Lmns", "Luminosity"), + ("luminosity", "Luminosity"), ("PsTh", "Pass Through"), + ("passThrough", "Pass Through"), ]; fn blend_label(ps: &str) -> Option<&'static str> { diff --git a/crates/engine/src/atn_cmds/tests.rs b/crates/engine/src/atn_cmds/tests.rs index bbec1def43..9954f85fee 100644 --- a/crates/engine/src/atn_cmds/tests.rs +++ b/crates/engine/src/atn_cmds/tests.rs @@ -3,6 +3,33 @@ use photocraft_psd::atn::{AtnAction, AtnEvent, AtnFile, AtnItem}; use photocraft_psd::descriptor::{Descriptor, Id, ReferenceItem, UnicodeString, Value as D}; use serde_json::json; +#[test] +fn blend_modes_accept_photoshop_string_ids_and_export_canonical_char_ids() { + for (string_id, char_id, label) in [ + ("normal", "Nrml", "Normal"), + ("dissolve", "Dslv", "Dissolve"), + ("darken", "Drkn", "Darken"), + ("multiply", "Mltp", "Multiply"), + ("colorBurn", "CBrn", "Color Burn"), + ("lighten", "Lghn", "Lighten"), + ("screen", "Scrn", "Screen"), + ("colorDodge", "CDdg", "Color Dodge"), + ("overlay", "Ovrl", "Overlay"), + ("softLight", "SftL", "Soft Light"), + ("hardLight", "HrdL", "Hard Light"), + ("difference", "Dfrn", "Difference"), + ("exclusion", "Xclu", "Exclusion"), + ("hue", "H", "Hue"), + ("saturation", "Strt", "Saturation"), + ("color", "Clr", "Color"), + ("luminosity", "Lmns", "Luminosity"), + ("passThrough", "PsTh", "Pass Through"), + ] { + assert_eq!(blend_label(string_id), Some(label), "{string_id}"); + assert_eq!(blend_ps(label), Some(char_id), "{label} remains serialized canonically"); + } +} + fn item(event: &str, label: &str, d: Option) -> AtnItem { let event = match event.as_bytes() { [a, b, c, dd] => AtnEvent::Code([*a, *b, *c, *dd]), From 1bfae77c16ee1760aed725fefcba73ab2a100e2d Mon Sep 17 00:00:00 2001 From: David Mann Date: Thu, 22 Oct 2026 21:39:28 +0200 Subject: [PATCH 2/2] Integration: test stringID blend modes through .atn import (#8502) The PR's test only called blend_label/blend_ps. This adds one through the import entry point: a Set Lyr step with Md enm("BlnM","softLight") and a Make layer step with Usng Md "luminosity", written with atn::write and imported, must give layer.setProps blend "Soft Light" and layer.new.layer mode "Luminosity" with no unsupported steps. Without the PR's table rows the Set step is unsupported (and the Make falls back to Normal). Co-authored-by: justinjohn0306 <34035011+justinjohn0306@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) --- crates/engine/src/atn_cmds/tests.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/crates/engine/src/atn_cmds/tests.rs b/crates/engine/src/atn_cmds/tests.rs index 9954f85fee..a3ddcde026 100644 --- a/crates/engine/src/atn_cmds/tests.rs +++ b/crates/engine/src/atn_cmds/tests.rs @@ -30,6 +30,32 @@ fn blend_modes_accept_photoshop_string_ids_and_export_canonical_char_ids() { } } +/// #8454: a Set and a Make layer step whose blend mode is stored as a Photoshop stringID import +/// with that mode (a Set was unsupported, a Make silently fell back to Normal). +#[test] +fn layer_steps_with_string_id_blend_modes_import_with_their_mode() { + let set = desc("null").with("null", target_ref("Lyr ")).with("T ", D::Descriptor(desc("Lyr ").with("Md ", enm("BlnM", "softLight")))); + let make = desc("null").with("null", class_ref("Lyr ")).with("Usng", D::Descriptor(desc("Lyr ").with("Md ", enm("BlnM", "luminosity")))); + let file = AtnFile { + name: UnicodeString::new_nul("Blend"), + expanded: false, + actions: vec![AtnAction { + name: UnicodeString::new_nul("A"), + items: vec![item("setd", "Set", Some(set)), item("Mk ", "Make", Some(make))], + ..Default::default() + }], + warnings: vec![], + }; + let imp = import(&atn::write(&file).unwrap()).unwrap(); + assert!(imp.unsupported.is_empty(), "{:?}", imp.unsupported.iter().map(|u| &u.event).collect::>()); + let steps = &imp.actions[0].steps; + assert_eq!(steps.len(), 2); + assert_eq!(steps[0].0, "layer.setProps"); + assert_eq!(steps[0].1["blend"], json!("Soft Light")); + assert_eq!(steps[1].0, "layer.new.layer"); + assert_eq!(steps[1].1["mode"], json!("Luminosity")); +} + fn item(event: &str, label: &str, d: Option) -> AtnItem { let event = match event.as_bytes() { [a, b, c, dd] => AtnEvent::Code([*a, *b, *c, *dd]),