From 4c4929028999ef1cbde2dcfd4ddbf2b614cee18f Mon Sep 17 00:00:00 2001 From: FredApps Date: Sun, 8 Nov 2026 14:40:19 +0100 Subject: [PATCH] Refuse ruled-line extraction after incomplete page content --- crates/edit/src/rules.rs | 4 ++++ crates/edit/src/tests.rs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/crates/edit/src/rules.rs b/crates/edit/src/rules.rs index a5bcfebbf1..ef3f78f772 100644 --- a/crates/edit/src/rules.rs +++ b/crates/edit/src/rules.rs @@ -26,6 +26,10 @@ pub fn page_rules(doc: &Document, page: usize) -> Result, EditErro let pages = pdfcraft_model::pages(doc); let p = pages.get(page).ok_or(EditError::NoSuchPage(page))?; let streams = crate::text::content_streams(doc, &p.dict)?; + // Unread content can change the graphics state used by every later stream. + if streams.iter().any(|stream| stream.read != crate::text::Read::Whole) { + return Err(EditError::Invalid("page content could not be completely decoded; cannot extract its ruled lines safely".into())); + } let pieces = Pieces::join(&streams.iter().map(|stream| stream.data.as_slice()).collect::>()); let ops = pieces.parse().ops; diff --git a/crates/edit/src/tests.rs b/crates/edit/src/tests.rs index 7c8f65dbab..8bc7e9e437 100644 --- a/crates/edit/src/tests.rs +++ b/crates/edit/src/tests.rs @@ -11202,3 +11202,21 @@ fn review_inside_scope_group_move_matches_direct_transform_under_outer_scale() { } } } + +#[test] +fn page_rules_refuse_unread_graphics_state() { + let mut dict = Dict::new(); + dict.set(b"Filter".to_vec(), Object::name("UnsupportedFilter")); + let unread = Stream::from_raw(dict, b"2 0 0 2 0 0 cm".to_vec()); + let damaged = damaged_flate(b"0 g\n", b"2 0 0 2 0 0 cm"); + for stream in [unread, damaged] { + let mut doc = image_page_with("10 10 m 100 10 l S"); + let incomplete = doc.add(Object::Stream(stream)); + doc.update_dict(ObjRef::new(3, 0), |page| { + page.set(b"Contents".to_vec(), Object::Array(vec![Object::Ref(incomplete), Object::Ref(ObjRef::new(4, 0))])); + }) + .unwrap(); + assert!(page_rules(&doc, 0).is_err(), "later geometry cannot be trusted after unread graphics state"); + } + assert_eq!(page_rules(&image_page_with("10 10 m 100 10 l S"), 0).unwrap(), [[10.0, 10.0, 100.0, 10.0]]); +}