From c86b33985d44dfd14ec4e6e8516a95390b9171de Mon Sep 17 00:00:00 2001 From: Eric Kaden <608341+EricKaden@users.noreply.github.com> Date: Sat, 24 Oct 2026 15:21:11 -0400 Subject: [PATCH] M12: XFA fields and exclusion groups that must be filled in are Required Co-authored-by: EULA <90616501+EULArule@users.noreply.github.com> --- crates/xfa/README.md | 6 +++-- crates/xfa/src/layout.rs | 12 ++++++++++ crates/xfa/src/model.rs | 4 ++++ crates/xfa/src/parse.rs | 8 +++++++ crates/xfa/src/pdf.rs | 8 ++++++- crates/xfa/src/tests.rs | 43 ++++++++++++++++++++++++++++++++++++ parity/acrobat-features.toml | 4 ++-- 7 files changed, 80 insertions(+), 5 deletions(-) diff --git a/crates/xfa/README.md b/crates/xfa/README.md index 23a6ef0d0e..0180d0f473 100644 --- a/crates/xfa/README.md +++ b/crates/xfa/README.md @@ -25,7 +25,7 @@ that warning and keeps the existing layout and accepted widget values. margins, borders (edge order rules), captions with reserves, check-button items, pictures, rich text (`exData` XHTML: paragraphs, `br`, bold/italic/underline/size spans, embedded fields), text-field limits (`text/maxChars` and `exData/maxLength`, carried into AcroForm `/MaxLen`), - inline JPEG, PNG and GIF images, lines, rectangles, occurrences, `breakBefore`/`breakAfter`, overflow + required fields and exclusion groups (``), inline JPEG, PNG and GIF images, lines, rectangles, occurrences, `breakBefore`/`breakAfter`, overflow leaders, `columnWidths` and `colSpan`. - **Layout** (`layout`): positioned, `tb`, `lr-tb`/`rl-tb`, `table` and `row` layouts; a container without a width is as wide as its content; flow across content areas and pages with @@ -38,7 +38,9 @@ that warning and keeps the existing layout and accepted widget values. XObjects, and widgets: text (multi-line, max length, alignment), date (Acrobat's `AFDate` format and keystroke actions from the picture clause), check boxes and radio groups (from `exclGroup`), push buttons with Reset / Print / Save As / URI actions recognised from their - scripts. Appearance streams are left empty for the forms layer to generate. Each field keeps + scripts. A field or exclusion group that must be filled in gets the `Required` flag (a radio + group on its group field), which viewers outline when they highlight fields. Appearance + streams are left empty for the forms layer to generate. Each field keeps its SOM path in `/PCSom`; the AcroForm gets `/PCXfaLayout` so a saved form is not laid out twice. The XFA packets stay, so Adobe's viewers keep rendering the form from them. diff --git a/crates/xfa/src/layout.rs b/crates/xfa/src/layout.rs index 670d43791a..a5831bcc31 100644 --- a/crates/xfa/src/layout.rs +++ b/crates/xfa/src/layout.rs @@ -168,6 +168,8 @@ pub struct Widget { pub items: Vec, /// A text field that doesn't scroll: it takes no more text than it shows (`DoNotScroll`). pub no_scroll: bool, + /// The field must be filled in (`Required`); a radio button, when its group must be. + pub required: bool, } #[derive(Clone, Debug, Default, PartialEq)] @@ -1799,6 +1801,15 @@ impl Layouter<'_> { let on = f.items.first().cloned().filter(|s| !s.trim().is_empty()).unwrap_or_else(|| (i + 1).to_string()); let tip = f.tooltip.clone().or_else(|| g.tooltip.clone()); let chosen = selected.as_deref() == Some(on.as_str()); + // A group that must be answered makes each of its buttons required, as the group + // field they make is. + let required; + let f = if g.required && !f.required { + required = Field { required: true, ..f.clone() }; + &required + } else { + f + }; self.draw_field(f, Rect::new(inner.x + r.x, inner.y + r.y, r.w, r.h), &ctx, Some((&group, &on, tip, chosen)), sibs.at(i))?; } } @@ -2087,6 +2098,7 @@ impl Layouter<'_> { action, items, no_scroll: f.no_scroll, + required: f.required, }))) } } diff --git a/crates/xfa/src/model.rs b/crates/xfa/src/model.rs index 0fac8db18e..98cca9ddcf 100644 --- a/crates/xfa/src/model.rs +++ b/crates/xfa/src/model.rs @@ -449,6 +449,8 @@ pub struct ExclGroup { pub fields: Vec, pub tooltip: Option, pub scripts: Vec