mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 19:48:14 -06:00
fix(redact): remove AcroForm on Hidden::FormFields even when field count is zero (#4571)
* fix(redact): remove AcroForm on Hidden::FormFields even when field count is zero * fix(redact): report AcroForm presence in scan() and format
This commit is contained in:
@@ -474,7 +474,16 @@ pub fn scan(doc: &Document) -> Vec<(Hidden, usize)> {
|
||||
+ owned(Hidden::Attachments)
|
||||
}
|
||||
Hidden::Comments => annots.iter().filter(|a| is_comment(a.name(b"Subtype").unwrap_or(b""))).count(),
|
||||
Hidden::FormFields => pdfcraft_forms::fields(doc).len(),
|
||||
Hidden::FormFields => {
|
||||
let n = pdfcraft_forms::fields(doc).len();
|
||||
if n > 0 {
|
||||
n
|
||||
} else if cat.contains(b"AcroForm") {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
Hidden::HiddenText => content_pass(&mut doc2, true, false, false).map(|(n, _)| n).unwrap_or(0),
|
||||
Hidden::HiddenLayers => {
|
||||
let off = hidden_layers(doc).len();
|
||||
@@ -510,7 +519,8 @@ pub fn remove_hidden(doc: &mut Document, which: &[Hidden]) -> Result<Vec<(Hidden
|
||||
// Every annotation the pages list now: whatever is no longer listed at the end (flattened
|
||||
// widgets included) leaves the structure tree too.
|
||||
let listed_before = listed_annotations(doc);
|
||||
let fields = which.contains(&Hidden::FormFields) && count(Hidden::FormFields) > 0;
|
||||
let has_acroform = doc.get(root).as_dict().is_some_and(|d| d.contains(b"AcroForm"));
|
||||
let fields = which.contains(&Hidden::FormFields) && (count(Hidden::FormFields) > 0 || has_acroform);
|
||||
if fields {
|
||||
// Fields without appearances get one first, so their values stay visible.
|
||||
for f in pdfcraft_forms::fields(doc) {
|
||||
@@ -631,9 +641,12 @@ pub fn remove_hidden(doc: &mut Document, which: &[Hidden]) -> Result<Vec<(Hidden
|
||||
cat.set(b"OCProperties".to_vec(), Object::Dict(oc));
|
||||
}
|
||||
}
|
||||
Hidden::FormFields => {
|
||||
cat.remove(b"AcroForm");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let n = count(h);
|
||||
let n = if h == Hidden::FormFields && fields { count(h).max(1) } else { count(h) };
|
||||
if n > 0 {
|
||||
done.push((h, n));
|
||||
}
|
||||
|
||||
@@ -2708,6 +2708,27 @@ fn removing_form_fields_takes_hidden_widgets_and_their_values_out_of_the_file()
|
||||
assert!(annots_of(&doc, &pdfcraft_model::pages(&doc).swap_remove(0).dict).is_empty());
|
||||
}
|
||||
|
||||
/// F-021: removing form fields on a document with an AcroForm dictionary (even one with no
|
||||
/// enumerable terminal fields) removes the AcroForm dictionary, processes widgets, and requires full save.
|
||||
#[test]
|
||||
fn removing_form_fields_removes_acroform_even_without_terminal_fields() {
|
||||
use crate::sanitize::{Hidden, remove_hidden, scan};
|
||||
let mut doc =
|
||||
one_page(b"BT /F1 10 Tf 10 100 Td (Visible) Tj ET", "", vec![b"<< /Type /Annot /Subtype /Widget /Rect [10 200 150 220] >>".to_vec()]);
|
||||
doc.update_dict(doc.root().unwrap(), |d| d.set(b"AcroForm".to_vec(), fields_dict(&[]))).unwrap();
|
||||
doc.update_dict(ObjRef::new(3, 0), |d| d.set(b"Annots".to_vec(), ref_array(&[7]))).unwrap();
|
||||
let counts = scan(&doc);
|
||||
assert_eq!(counts.iter().find(|c| c.0 == Hidden::FormFields).map(|c| c.1), Some(1));
|
||||
assert!(doc.get(doc.root().unwrap()).as_dict().is_some_and(|d| d.contains(b"AcroForm")));
|
||||
assert!(!doc.full_save_required());
|
||||
|
||||
let done = remove_hidden(&mut doc, &[Hidden::FormFields]).unwrap();
|
||||
assert_eq!(done, [(Hidden::FormFields, 1)]);
|
||||
assert!(!doc.get(doc.root().unwrap()).as_dict().is_some_and(|d| d.contains(b"AcroForm")));
|
||||
assert!(annots_of(&doc, &pdfcraft_model::pages(&doc).swap_remove(0).dict).is_empty());
|
||||
assert!(doc.full_save_required());
|
||||
}
|
||||
|
||||
/// Removing hidden text cleans the structure tree as redaction does: the elements describing the
|
||||
/// removed text lose their descriptions, and a reference into a rewritten Form points at the copy.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user