M2.6: tests pin damage in an earlier filter stage, on another page, and in the saved file

- cos: a filter chain whose first stage stops at damage and whose
  second reads all it was given is damaged.
- optimize: a page damaged partway stops resampling for the whole
  document, so another page drawing the same image whole doesn't get
  it resampled.
- redact: after redacting content with /DecodeParms by reference, no
  stream in the saved file holds the text, read with its parameters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
This commit is contained in:
Grzegorz MartensandClaude Opus 5.5 committed 2026-11-06 01:01:24 +01:00
1 parent 22eb55225f
commit 11bf10613b
3 files changed
+37 -1

No files matched your search

+11
View File
@@ -850,6 +850,17 @@ mod tests {
dict.set(b"Filter".to_vec(), Object::Array(vec![Object::name("FlateDecode"), Object::name("ASCIIHexDecode")]));
let hex = Stream::from_raw(dict, Stream::flate(Dict::new(), b"4142zz4344>").raw.to_vec());
assert_eq!(budget.decode_with_damage(&doc, &hex, usize::MAX).ok(), Some((b"AB".to_vec(), true)));
// So is an earlier stage's, though the stage after it reads all it was given: a stored
// block of whole hex, then a block of the reserved type (RFC 1951 §3.2.3).
let readable = b"4142>";
let len = u16::try_from(readable.len()).unwrap_or_default();
let mut raw = vec![0x78, 0x01, 0x00];
raw.extend_from_slice(&len.to_le_bytes());
raw.extend_from_slice(&(!len).to_le_bytes());
raw.extend_from_slice(readable);
raw.push(0x07);
let flate_damaged = Stream::from_raw(hex.dict.clone(), raw);
assert_eq!(budget.decode_with_damage(&doc, &flate_damaged, usize::MAX).ok(), Some((b"AB".to_vec(), true)));
}
/// `/DecodeParms` given by reference, or a parameter given by reference in them, is read: a
+18
View File
@@ -3220,6 +3220,24 @@ fn content_damaged_partway_leaves_every_image_at_its_resolution() {
}
}
/// One page damaged partway stops resampling for the whole document: another page drawing the same
/// image at its full resolution, whole, doesn't get it resampled either.
#[test]
fn content_damaged_partway_on_one_page_leaves_the_image_another_page_draws() {
let mut doc = Document::new_empty();
let img = image(&mut doc, 720, 720, 1, false, None);
let damaged = page(&mut doc, &[("Im", img)], "");
let bad = doc.add(Object::Stream(damaged_flate(b"0 g\n", b"q 720 0 0 720 0 0 cm /Im Do Q")));
doc.update_dict(damaged, |d| d.set(b"Contents".to_vec(), Object::Ref(bad))).unwrap();
let whole = page(&mut doc, &[("Im", img)], "q 72 0 0 72 0 0 cm /Im Do Q");
assert_eq!(effective_resolutions(&doc, &[whole])[&img], 720.0);
let ppi = effective_resolutions(&doc, &[damaged, whole]).get(&img).copied().unwrap_or(f64::INFINITY);
assert_eq!(ppi, f64::INFINITY);
let r = optimize(&mut doc, &Settings::default()).unwrap();
assert_eq!(r.images_resampled, 0, "{r:?}");
assert_eq!(doc.get(img).as_dict().and_then(|d| d.int(b"Width")), Some(720));
}
/// `levels` nested forms with their own resources, the innermost drawing `img` over the unit square.
fn nested_forms(doc: &mut Document, img: ObjRef, levels: usize) -> ObjRef {
let mut inner = form(doc, Some(Object::Dict(resources(xobjects(&[("Im", img)])))), "/Im Do");
+8 -1
View File
@@ -130,7 +130,14 @@ fn content_with_parameters_given_by_reference_is_redacted() {
mark(&mut doc, 0, &marked, "");
let r = apply(&mut doc, None).unwrap();
assert_eq!((r.marks, r.glyphs), (1, 6), "{what}");
assert_eq!(under(&mut reopen(&doc), 0, &marked), 0, "{what}");
let mut saved = reopen(&doc);
assert_eq!(under(&mut saved, 0, &marked), 0, "{what}");
// Nor is the text left in a stream the page no longer draws, read with its parameters.
for (r, o) in saved.scan_objects() {
let Object::Stream(s) = &*o else { continue };
let data = pdfcraft_cos::DecodeBudget::new(usize::MAX, 0).decode_with_damage(&saved, s, usize::MAX).map(|(d, _)| d).unwrap_or_default();
assert!(!data.windows(6).any(|w| w == b"SECRET"), "{what}: {r:?}");
}
}
}