diff --git a/crates/pptx/src/read.rs b/crates/pptx/src/read.rs index 9c8756f87..886201a30 100644 --- a/crates/pptx/src/read.rs +++ b/crates/pptx/src/read.rs @@ -76,6 +76,15 @@ impl Imp<'_> { self.media_by_part.insert(key, id); Some(id) } + /// Drop the media copied since the list was `len` long, with their part-cache entries: used when + /// the content that read them is discarded, so they are neither kept nor reused uncharged. + pub fn forget_media_from(&mut self, len: usize) { + if self.media.len() > len { + let gone: std::collections::HashSet = self.media[len..].iter().map(|m| m.id).collect(); + self.media.truncate(len); + self.media_by_part.retain(|_, id| !gone.contains(id)); + } + } /// Media item for an external link. pub fn media_link(&mut self, url: &str) -> MediaId { if let Some(m) = self.media.iter().find(|m| m.link.as_deref() == Some(url)) { diff --git a/crates/pptx/src/read/shapes.rs b/crates/pptx/src/read/shapes.rs index 4e3b54398..98ec3a155 100644 --- a/crates/pptx/src/read/shapes.rs +++ b/crates/pptx/src/read/shapes.rs @@ -471,7 +471,7 @@ fn frame(imp: &mut Imp, part: &Part, ctx: &mut IdCtx, e: &El, depth: usize) -> S }; // Keep the parts it points to so a save writes it back whole (#203). A part with relationships // of its own, or an external link, can't be carried: the save then falls back to the preview. - let xml_before = imp.diagram_xml_bytes; + let (xml_before, media_before) = (imp.diagram_xml_bytes, imp.media.len()); let parts = super::rel_refs(e) .into_iter() .map(|id| { @@ -496,6 +496,7 @@ fn frame(imp: &mut Imp, part: &Part, ctx: &mut IdCtx, e: &El, depth: usize) -> S // A fallback that drops its parts does not keep their bytes charged. if parts.is_none() { imp.diagram_xml_bytes = xml_before; + imp.forget_media_from(media_before); } let parts = parts.unwrap_or_default(); s.kind = ShapeKind::Opaque { xml: part.keep(e), preview, label, parts }; @@ -601,7 +602,7 @@ fn smartart(imp: &mut Imp, part: &Part, gd: &El, frame: &Shape, depth: usize) -> let tree = d.root.child("spTree")?; let mut sub = IdCtx { fresh: true, ..Default::default() }; let before = imp.smartart_shapes; - let xml_before = imp.diagram_xml_bytes; + let (xml_before, media_before) = (imp.diagram_xml_bytes, imp.media.len()); let children = sp_tree(imp, &dpart, &mut sub, tree, depth + 1); if children.is_empty() { return None; @@ -612,6 +613,7 @@ fn smartart(imp: &mut Imp, part: &Part, gd: &El, frame: &Shape, depth: usize) -> // Over the presentation's remaining budget: a placeholder, and nothing charged, nested SmartArt included. imp.smartart_shapes = before; imp.diagram_xml_bytes = xml_before; + imp.forget_media_from(media_before); return None; } imp.smartart_shapes = total;