From 5917b24aaaef965de4de1eb90acb5f2d6e7cbf82 Mon Sep 17 00:00:00 2001 From: aiongg Date: Tue, 13 Oct 2026 19:06:46 +0000 Subject: [PATCH] Integration: #819 a pasted missing graphic always gets a fresh asset id from the target --- crates/engine/src/cmd/links.rs | 34 +++++++++++++++++++++++++++++++++ crates/engine/src/cmd/object.rs | 12 ++++-------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/crates/engine/src/cmd/links.rs b/crates/engine/src/cmd/links.rs index c48b3651f..09e004659 100644 --- a/crates/engine/src/cmd/links.rs +++ b/crates/engine/src/cmd/links.rs @@ -615,6 +615,40 @@ mod shared_asset_tests { assert_ne!(g.asset, missing); assert!(!dst.assets.contains_key(&g.asset)); } + + #[test] + fn pasted_missing_graphic_never_takes_an_asset_placed_later() { + use designcraft_doc::build::NewDocument; + use designcraft_doc::{Graphic, Item, ItemId, Shape, SpreadRef}; + let mut src = Document::new(&NewDocument::default()); + let mut dst = Document::new(&NewDocument::default()); + // The source's missing id is ahead of the target's counter: the target would allocate it to its next image. + for _ in 0..50 { + src.alloc(); + } + let missing = AssetId(src.alloc()); + let iid = ItemId(src.alloc()); + let mut it = Item::new( + iid, + src.default_layer(), + Shape::Rectangle, + designcraft_geom::shapes::rectangle(designcraft_geom::Rect::new(0.0, 0.0, 9.0, 9.0)), + ); + it.content = Content::Graphic(Graphic { + asset: missing, + size: (4.0, 4.0), + xf: designcraft_geom::Affine::IDENTITY, + clip: None, + auto_fit: Default::default(), + fit_align: 4, + crop: [1.0; 4], + wrap: Default::default(), + }); + src.insert_item(SpreadRef::Doc(0), it, None).unwrap(); + let ids = super::super::object::duplicate_from(&mut dst, &src, &[iid], SpreadRef::Doc(0), designcraft_geom::Vec2::ZERO).unwrap(); + let g = dst.item(ids[0]).and_then(|i| i.graphic()).unwrap().asset; + assert!((0..100).all(|_| AssetId(dst.alloc()) != g), "a later image in the target takes the pasted graphic's id"); + } } #[cfg(test)] diff --git a/crates/engine/src/cmd/object.rs b/crates/engine/src/cmd/object.rs index e5d259804..28614a3ec 100644 --- a/crates/engine/src/cmd/object.rs +++ b/crates/engine/src/cmd/object.rs @@ -948,15 +948,11 @@ pub fn duplicate_from(dst: &mut Document, src: &Document, ids: &[ItemId], to: Sp Ok(out) } -/// The id to reference in `dst` for asset `id` of `src`. When `src` lacks the asset (a missing graphic), an id that `dst` -/// already uses for a different asset is swapped for a fresh unused one so the reference stays missing instead of showing -/// an unrelated image. +/// The id to reference in `dst` for asset `id` of `src`. When `src` lacks the asset (a missing graphic), the reference +/// gets a fresh id from `dst`, so it stays missing: `src`'s id may already name, or later be allocated to, another +/// image in `dst`. fn adopt_or_dangle(dst: &mut Document, src: &Document, id: AssetId) -> AssetId { - match dst.adopt_asset(src, id) { - Some(a) => a, - None if dst.assets.contains_key(&id) => AssetId(dst.alloc()), - None => id, - } + dst.adopt_asset(src, id).unwrap_or_else(|| AssetId(dst.alloc())) } fn renumber(dst: &mut Document, src: &Document, it: &mut Item, stories: &mut HashMap) {