fix(forms): skip a field of an unread kind before it claims shared widgets

This commit is contained in:
aiongg
2026-10-22 23:36:31 +00:00
parent 54152aed87
commit 5473d354a9
2 changed files with 24 additions and 6 deletions
+8 -6
View File
@@ -826,12 +826,8 @@ fn walk(
}
return;
}
let widget_refs: Vec<ObjRef> = if d.contains(b"Rect") { vec![r] } else { kids };
let widget_refs: Vec<ObjRef> = widget_refs.into_iter().filter(|w| seen.widgets.insert(*w)).collect();
let actions = actions_of(doc, d, &widget_refs, &mut seen.scripts);
let button = (inh.ft.as_deref() == Some(b"Btn") && inh.ff.unwrap_or(0) & flags::PUSH_BUTTON != 0)
.then(|| button_action(doc, d, &widget_refs, &mut seen.scripts))
.flatten();
// A field of no kind this reads is skipped before it claims widgets or scripts, which a later
// field listing them then reads.
let ff = inh.ff.unwrap_or(0);
let kind = match inh.ft.as_deref() {
Some(b"Tx") => FieldKind::Text,
@@ -843,6 +839,12 @@ fn walk(
Some(b"Sig") => FieldKind::Signature,
_ => return,
};
let widget_refs: Vec<ObjRef> = if d.contains(b"Rect") { vec![r] } else { kids };
let widget_refs: Vec<ObjRef> = widget_refs.into_iter().filter(|w| seen.widgets.insert(*w)).collect();
let actions = actions_of(doc, d, &widget_refs, &mut seen.scripts);
let button = (inh.ft.as_deref() == Some(b"Btn") && inh.ff.unwrap_or(0) & flags::PUSH_BUTTON != 0)
.then(|| button_action(doc, d, &widget_refs, &mut seen.scripts))
.flatten();
let widgets = widget_refs
.into_iter()
.filter_map(|w| {
+16
View File
@@ -3040,3 +3040,19 @@ fn a_widget_listed_repeatedly_is_read_once() {
let widgets: Vec<usize> = all.iter().map(|f| f.widgets.len()).collect();
assert_eq!(widgets, [1, 0], "{:?}", all.iter().map(|f| &f.name).collect::<Vec<_>>());
}
/// A field of a kind that isn't read doesn't claim the widget it shares with a field that is.
#[test]
fn a_skipped_field_leaves_its_widget_to_the_next() {
let doc = document(&[
"<< /Type /Catalog /Pages 4 0 R /AcroForm << /Fields [2 0 R 3 0 R] >> >>".into(),
"<< /FT /Zz /T (odd) /Kids [6 0 R] >>".into(),
"<< /FT /Tx /T (b) /Kids [6 0 R] >>".into(),
"<< /Type /Pages /Kids [5 0 R] /Count 1 >>".into(),
"<< /Type /Page /Parent 4 0 R /MediaBox [0 0 100 100] /Annots [6 0 R] >>".into(),
"<< /Type /Annot /Subtype /Widget /Parent 3 0 R /Rect [0 0 10 10] >>".into(),
]);
let all = fields(&doc);
let widgets: Vec<(&str, usize)> = all.iter().map(|f| (f.name.as_str(), f.widgets.len())).collect();
assert_eq!(widgets, [("b", 1)]);
}