collective

Browse / github.com/storytold/designcraft / patch

An item a parent overrode and deleted is gone from the pages that use it (#1469)

IDML import shows a parent item that a child parent overrode and deleted **Problem:** in InDesign, removing an inherited item from a parent page works by overriding it and deleting the override. In IDML that is a `Page` whose `OverrideList` names the item while no page item with `OverriddenMasterPageItem` pointing to it exists. DesignCraft still draws the base parent's item. In our document a signature block from parent B overlaps the notice text on every page using parent E (E is based on B and removed the block). **Expectation:** an item listed in `OverrideList` without an overriding copy is not drawn on that parent or on pages using it. **Repro:** `designcraft

Status
open
Score
0.00
Adopters
0 other operators
Author
Hamza0101 (imported from pull request #1558, open)
Base
3ce3a24f08d03935bc8cf7cb23755aca8065558c
Size
+62 −2 in 1 file
Review
pending
Shared
2026-10-28
Id
a25bd81deba7c80d60f8c768

How it went

No other operator has reported using it yet.

Diff

raw · crates/render/src/lib.rs

diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs--- a/crates/render/src/lib.rs+++ b/crates/render/src/lib.rs@@ -496,12 +496,28 @@ impl Renderer {                 if !page.show_parent_items {                     continue;                 }-                for (ppi, ppage) in doc.shown_parent_pages(sr, pi) {+                let chain = doc.shown_parent_pages(sr, pi);+                // This page's overrides, and those of every parent page behind it: a parent that+                // overrode an item of the parent it is based on and then deleted the override+                // removed that item from itself and from the pages that use it (#1469). An+                // override always names an item of a parent further back, so one set does.+                let overridden: Vec<designcraft_doc::ItemId> = page+                    .overridden+                    .iter()+                    .copied()+                    .chain(+                        chain+                            .iter()+                            .filter_map(|(ppi, ppage)| doc.parents.get(*ppi)?.pages.get(*ppage))+                            .flat_map(|ppg| ppg.overridden.iter().copied()),+                    )+                    .collect();+                for (ppi, ppage) in chain {                     let Some(parent) = doc.parents.get(ppi) else { continue };                     let Some(ppg) = parent.pages.get(ppage) else { continue };                     let dx = page.x - ppg.x;                     for it in &parent.items {-                        if it.layer != layer.id || page.overridden.contains(&it.id) {+                        if it.layer != layer.id || overridden.contains(&it.id) {                             continue;                         }                         // Parent items belong to the parent page they sit on.@@ -1717,6 +1733,50 @@ mod tests {         assert!(paper(103, 103) && ink(108, 108) && paper(114, 114));     } +    /// In InDesign, removing an inherited item from a parent page means overriding it there and+    /// deleting the override: the parent's page keeps the item in its override list with no copy+    /// of its own. The pages that use that parent still drew the base's item — a signature block+    /// from parent B over the notice text of every page using parent E (#1469).+    #[test]+    fn an_item_a_parent_overrode_and_deleted_is_gone_from_the_pages_that_use_it() {+        let mut d = Document::new(&NewDocument { pages: 1, facing_pages: false, ..Default::default() });+        let lid = d.default_layer();+        // Parent A: a black box. Parent B: based on A, nothing of its own.+        let a = d.parents[0].id;+        let margins = d.parents[0].pages[0].margins;+        let b = d.add_parent("B", "Parent", 1, 0.0, margins);+        let box_id = designcraft_doc::ItemId(d.alloc());+        let rect = Rect::new(100.0, 100.0, 200.0, 160.0);+        let mut it = Item::new(box_id, lid, designcraft_doc::Shape::Rectangle, designcraft_geom::shapes::rectangle(rect));+        it.fill = designcraft_doc::Fill::swatch(designcraft_color::swatch::BLACK);+        d.insert_item(SpreadRef::Parent(0), it, None).unwrap();+        {+            let bp = std::sync::Arc::make_mut(&mut d.parents[1]);+            bp.parent.as_mut().unwrap().based_on = Some(a);+        }+        std::sync::Arc::make_mut(&mut d.spreads[0]).pages[0].parent = Some(b);+        let ink = |d: &Document, r: &mut Renderer| {+            let img = r.render_page(d, &Cache::new(), 0, 1.0, false, &RenderOptions::default()).unwrap();+            img.pixel(rect.center().x as u32, rect.center().y as u32)[0] < 64+        };+        let mut r = Renderer::new();+        r.threads = 0;+        assert!(ink(&d, &mut r), "the page shows parent A's box through parent B");++        // B overrides the box and the override is deleted: B's page keeps it in its override list.+        std::sync::Arc::make_mut(&mut d.parents[1]).pages[0].overridden.push(box_id);+        let mut r = Renderer::new();+        r.threads = 0;+        assert!(!ink(&d, &mut r), "the box is removed on B, so the page using B does not show it");++        // Removing the override on B brings it back, which is how InDesign's Remove All Local+        // Overrides behaves.+        std::sync::Arc::make_mut(&mut d.parents[1]).pages[0].overridden.clear();+        let mut r = Renderer::new();+        r.threads = 0;+        assert!(ink(&d, &mut r), "the box shows again");+    }+     #[test]     fn arrowheads_draw_at_line_ends() {         let mut d = Document::new(&NewDocument::default());