mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-06 02:21:45 +00:00
fix(redact): rewritten form XObjects drop /PCAdded (#5868)
Page content streams rewritten by redaction or hidden-content removal already dropped /PCAdded, the source text of an added item, but a form XObject whose content was rewritten kept its original dictionary, /PCAdded included, so the removed text stayed readable in the saved file.
This commit is contained in:
1 parent
661ded72bb
commit
69ddcd89bf
2 files changed
+31
No files matched your search
@@ -861,6 +861,11 @@ fn xobject(
|
||||
}
|
||||
let mut dict = s.dict.clone();
|
||||
dict.remove(b"Length");
|
||||
if new_data.is_some() {
|
||||
// As for page content streams: the source text of an added item (Edit ▸ Add
|
||||
// content) would keep what the rewrite removed.
|
||||
dict.remove(b"PCAdded");
|
||||
}
|
||||
let mut cleaned_res = res.clone();
|
||||
// Install rewritten nested forms before finding live property names: a new form may
|
||||
// still inherit its caller's resources, and its new Do name must resolve here.
|
||||
|
||||
@@ -3871,3 +3871,29 @@ fn a_reply_on_another_page_to_a_removed_popup_goes_too() {
|
||||
assert!(!bytes.windows(gone.len()).any(|w| w == gone), "{} survived", String::from_utf8_lossy(gone));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_rewritten_form_xobject_drops_the_source_text_of_added_content() {
|
||||
// A form drawing invisible text whose dictionary repeats it in /PCAdded.
|
||||
let form = stream(
|
||||
"/Type /XObject /Subtype /Form /BBox [0 0 300 300] /Resources << /Font << /F1 5 0 R >> >> /PCMark /Added /PCAdded << /Kind /Text /Text (LEAK_FORM_SOURCE) >>",
|
||||
b"BT /F1 10 Tf 3 Tr 10 100 Td (HIDDEN) Tj ET BT /F1 10 Tf 10 200 Td (Shown) Tj ET",
|
||||
);
|
||||
let mut doc = one_page(b"/X Do", "/XObject << /X 7 0 R >>", vec![form]);
|
||||
sanitize::remove_hidden(&mut doc, &[sanitize::Hidden::HiddenText]).unwrap();
|
||||
let bytes = write_full(&doc, &SaveOptions { object_streams: false, ..SaveOptions::default() }).unwrap();
|
||||
assert!(!bytes.windows(16).any(|w| w == b"LEAK_FORM_SOURCE"), "the form's added-content source text survived");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_redacted_form_xobject_drops_the_source_text_of_added_content() {
|
||||
let form = stream(
|
||||
"/Type /XObject /Subtype /Form /BBox [0 0 300 300] /Resources << /Font << /F1 5 0 R >> >> /PCMark /Added /PCAdded << /Kind /Text /Text (AB1234CD) >>",
|
||||
b"BT /F1 10 Tf 10 100 Td (AB1234CD) Tj ET",
|
||||
);
|
||||
let mut doc = one_page(b"/X Do", "/XObject << /X 7 0 R >>", vec![form]);
|
||||
mark(&mut doc, 0, &[[20.0, 95.0, 40.0, 110.0]], "");
|
||||
apply(&mut doc, None).unwrap();
|
||||
let bytes = write_full(&doc, &SaveOptions { object_streams: false, ..SaveOptions::default() }).unwrap();
|
||||
assert!(!bytes.windows(4).any(|w| w == b"1234"), "redacted text left in the saved file");
|
||||
}
|
||||
Reference in new issue
Block a user