From 10eb4866d966b872c54fe6dc2a4dced3b66d89ff Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Wed, 14 Oct 2026 15:53:57 -0400 Subject: [PATCH] Fix up merged PRs: the media walker counts #209's kept object parts SmartArt and OLE objects now keep their parts as media items (ShapeKind::Opaque::parts). media_refs visits them, so slide.reuse copies them and picture.compress never deletes them; the Opaque arm names every field so a new one can't be missed. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/media_refs.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/media_refs.rs b/crates/engine/src/media_refs.rs index 33bc6a37..a4e17453 100644 --- a/crates/engine/src/media_refs.rs +++ b/crates/engine/src/media_refs.rs @@ -4,7 +4,7 @@ //! rewrites them in place. It covers pictures (and their artistic-effect originals), picture fills //! wherever a fill can sit (shapes, lines, text runs, table cells and styles, chart series and //! points, backgrounds, theme format schemes), media clips and their posters, 3-D models, OLE and -//! SmartArt previews, SmartArt node pictures, picture bullets, hyperlink, transition and +//! SmartArt previews and kept parts, SmartArt node pictures, picture bullets, hyperlink, transition and //! animation sounds, and embedded font faces. Adding a `MediaId` field to the model means adding //! it here; the shape kinds are matched exhaustively so a new kind can't be missed. @@ -164,10 +164,15 @@ macro_rules! walkers { f(p); } } - ShapeKind::Opaque { preview, .. } => { + // Every field named, so a new one can't slip past. + ShapeKind::Opaque { xml: _, preview, label: _, parts } => { if let Some(p) = preview { f(p); } + // SmartArt data, OLE embeddings… kept as media items (#209). + for (_, _, id) in parts { + f(id); + } } } opt_fill(&$($m)? s.fill, f); @@ -387,4 +392,19 @@ mod tests { got.sort(); assert_eq!(got, (9101..=9108).map(MediaId).collect::>()); } + + /// SmartArt and OLE objects keep their parts as media items (#209); those count as used. + #[test] + fn kept_parts_of_unmodelled_objects_are_references() { + let opaque = Shape { + kind: ShapeKind::Opaque { + xml: String::new(), + preview: Some(MediaId(9101)), + label: "object".into(), + parts: vec![("rId2".into(), "oleObject".into(), MediaId(9102))], + }, + ..Default::default() + }; + assert_eq!(shapes_media(std::slice::from_ref(&opaque)), vec![MediaId(9101), MediaId(9102)]); + } }