Refuse strict .atn export of an in-place layer duplicate

Photoshop's Duplicate Layer has no in-place flag, so `layer.duplicate
{"inPlace": true}` was exported as a plain Duplicate and replayed with
the artboard copy beside the original. Strict export now fails on it and
skipUnsupported reports it, as for a Close with a document target.

Fixes #7200

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kushagra Saxena
2026-10-19 20:40:13 +05:30
co-authored by Claude Opus 5.5
parent d95fdf3181
commit 443e6a3fc8
2 changed files with 23 additions and 0 deletions
+5
View File
@@ -1956,6 +1956,11 @@ fn duplicate_to(id: &str, p: &Value) -> Option<Descriptor> {
if id == "layer.duplicate" && jstr(p, "document") == Some("new") {
return None;
}
// Photoshop's Duplicate has no in-place flag: a plain one would place the artboard copy beside
// the original on playback.
if id == "layer.duplicate" && jbool(p, "inPlace") == Some(true) {
return None;
}
let mut d = match id {
"layer.duplicate" => desc("null").with("null", layer_ref(p)),
"image.duplicate" => {
+18
View File
@@ -1089,3 +1089,21 @@ fn add_noise_with_mask_target_is_not_exported_as_pixel_filter() {
assert_eq!(imported.actions[0].steps, vec![("filter.noise.addNoise".to_string(), json!({"amount": 75, "seed": 123}))]);
}
}
/// Photoshop's Duplicate Layer has no in-place flag: an in-place duplicate (the artboard copy
/// that stays on the original) must not be exported as a plain duplicate, which places the
/// copy beside the original on playback.
#[test]
fn duplicate_in_place_is_not_exported_as_plain_duplicate() {
let in_place = Action { name: "Probe".into(), steps: vec![("layer.duplicate".into(), json!({"inPlace": true}))], ..Default::default() };
assert!(export("Audit5", &[&in_place], &[], false).is_err());
let (_, skipped) = export("Audit5", &[&in_place], &[], true).unwrap();
assert_eq!(skipped, vec![Skipped { action: "Probe".into(), step: 0, id: "layer.duplicate".into() }]);
for params in [json!({}), json!({"inPlace": false})] {
let plain = Action { name: "Plain".into(), steps: vec![("layer.duplicate".into(), params)], ..Default::default() };
let (bytes, skipped) = export("Audit5", &[&plain], &[], false).unwrap();
assert!(skipped.is_empty());
let imported = import(&bytes).unwrap();
assert_eq!(imported.actions[0].steps, vec![("layer.duplicate".to_string(), json!({}))]);
}
}