From fae356fd50077e1e4831f1765c4ca5a64d2e62f5 Mon Sep 17 00:00:00 2001 From: Nicek Date: Wed, 28 Oct 2026 23:55:05 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=B2=98=E8=B4=B4=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E7=9A=84=E5=86=85=E5=AE=B9=E5=93=88=E5=B8=8C=E9=94=AE=E8=A2=AB?= =?UTF-8?q?=E5=8D=A0=E7=94=A8=E6=97=B6=E6=94=B9=E7=94=A8=E7=A9=BA=E9=97=B2?= =?UTF-8?q?=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/engine/src/cmd/edit/props.rs | 15 +++++++++++++-- crates/engine/src/property_copy_tests.rs | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/cmd/edit/props.rs b/crates/engine/src/cmd/edit/props.rs index 845679de..8724f267 100644 --- a/crates/engine/src/cmd/edit/props.rs +++ b/crates/engine/src/cmd/edit/props.rs @@ -391,8 +391,19 @@ pub(super) fn paint_paste(s: &mut Session, p: &Value, fills: bool) -> Result 999 { + return Err(bad(cmd, "no free image key for the copied image")); + } + key = format!("{base}-{n}"); + } doc.images.entry(key.clone()).or_insert(d); img.image = key; } diff --git a/crates/engine/src/property_copy_tests.rs b/crates/engine/src/property_copy_tests.rs index 676158f2..0d85901f 100644 --- a/crates/engine/src/property_copy_tests.rs +++ b/crates/engine/src/property_copy_tests.rs @@ -143,6 +143,30 @@ fn a_copied_image_key_taken_in_another_file_gets_the_copied_data() { assert_eq!(doc.images.get("img-key"), Some(&taken), "the destination's image stays"); } +#[test] +fn a_copied_image_whose_hash_key_is_also_taken_gets_a_free_key() { + let mut s = Session::new(); + let a = rect(&mut s); + let bytes = vec![1, 2, 3]; + let copied = figcraft_doc::ImageData { bytes: std::sync::Arc::new(bytes.clone()), width: 2, height: 2 }; + let taken = figcraft_doc::ImageData { bytes: std::sync::Arc::new(vec![9, 9, 9]), width: 3, height: 3 }; + // Images are usually keyed by their content hash: here the copied key is that hash, and the + // other file already uses it for other data, so the replacement key is taken as well. + let hash = figcraft_format::image_hash(&bytes); + ex(&mut s, "fill.add", json!({"ids": [a], "paint": {"type": "image", "image": hash}})); + s.doc_mut().unwrap().images.insert(hash.clone(), copied.clone()); + ex(&mut s, "fill.copy", json!({"id": a, "index": 1})); + ex(&mut s, "file.new", json!({})); + let b = rect(&mut s); + s.doc_mut().unwrap().images.insert(hash.clone(), taken.clone()); + ex(&mut s, "fill.paste", json!({"ids": [b], "index": 0})); + let doc = s.doc().unwrap(); + let figcraft_color::PaintKind::Image(img) = &doc.node(b).unwrap().fills[0].kind else { panic!("the pasted fill is an image") }; + assert_ne!(img.image, hash, "the taken hash is not reused"); + assert_eq!(doc.images.get(&img.image), Some(&copied), "the pasted paint points at the copied data"); + assert_eq!(doc.images.get(&hash), Some(&taken), "the destination's image stays"); +} + #[test] fn a_copied_pattern_does_not_paste_into_another_file() { let mut s = Session::new();