diff --git a/crates/metafile/src/import/picture.rs b/crates/metafile/src/import/picture.rs index 8373253ae4..4689572a62 100644 --- a/crates/metafile/src/import/picture.rs +++ b/crates/metafile/src/import/picture.rs @@ -36,6 +36,13 @@ use crate::{Imported, Kind}; const PATTERNS: &str = "hatched and pattern brushes are filled with their average color"; const ENDS_EARLY: &str = "the file ends early: what it held was imported"; +const TOO_MANY: &str = "objects past the most one file may hold were left out"; +const AT_LIMIT: &str = "the file reached the most objects one file may hold: any after them were not read"; + +/// The most items `craft_metafile` keeps from one file (its `MAX_ITEMS`). It stops reading at the +/// limit and does not say whether more items followed, so a picture holding this many may have +/// been cut short. +const READER_ITEMS: usize = 200_000; /// Points a unit of a WMF without a placeable header (1440 units an inch). const WMF_UNIT: f64 = 72.0 / 1440.0; @@ -243,7 +250,7 @@ impl Mapper { fn push(&mut self, clips: Vec>, mut node: Node) { if self.out.len() >= MAX_NODES { - return; + return self.warn(TOO_MANY); } if !node.visual_bounds().is_none_or(sane) { return self.warn(FAR_AWAY); @@ -526,6 +533,9 @@ pub(crate) fn import(bytes: &[u8]) -> Result { if ends_early(bytes, kind) { m.warn(ENDS_EARLY); } + if pic.items.len() >= READER_ITEMS { + m.warn(AT_LIMIT); + } for item in pic.items { m.item(item); } diff --git a/crates/metafile/src/tests/import.rs b/crates/metafile/src/tests/import.rs index 4dcb3020c4..a1ddcc335e 100644 --- a/crates/metafile/src/tests/import.rs +++ b/crates/metafile/src/tests/import.rs @@ -370,6 +370,18 @@ fn damaged_files_fail_or_import_partly() { assert!(import(&huge).is_ok()); } +#[test] +fn a_file_with_more_objects_than_are_read_says_so() { + // RECTANGLE records: 10 more than the reader keeps, then a few under that. + let rect = (43, le(&[0, 0, 100, 100])); + let many = hand_emf(5000, 3000, &vec![rect.clone(); 200_010]); + let back = import(&many).unwrap(); + assert_eq!(paths(&back.document).len(), 200_000); + assert!(back.warnings.iter().any(|w| w.contains("reached the most objects")), "{:?}", back.warnings); + let few = import(&hand_emf(5000, 3000, &vec![rect; 3])).unwrap(); + assert!(few.warnings.is_empty(), "{:?}", few.warnings); +} + /// An EMR_STRETCHDIBITS copying `png` (a BI_PNG bitmap) to a 10 × 10 box. fn png_stretch_dibits(png: &[u8]) -> Vec { // Bounds; destination, source and its size; the BITMAPINFO and the bits (from the record start);