Add button field value extraction distinguishing pushbutton, checkbox, and radio button types via /Ff flags. Extracts selected state and appearance state name (/Yes, /Off, custom). - New module: forms/value_button.rs with ButtonKind enum and ButtonValue - Updated FormFieldValue::Button variant with kind and state_name fields - 15 unit tests covering all button types and edge cases - Fixed CCITTFaxDecoder test syntax blocking test execution Closes: pdftract-66pgk Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5.7 KiB
pdftract-66pgk: AcroForm Btn (button) value extraction
Bead ID
pdftract-66pgk
Title
AcroForm Btn (button) value extraction (Pushbutton + Checkbox + Radio variants)
Implementation Summary
Implemented button field value extraction in a new module crates/pdftract-core/src/forms/value_button.rs that distinguishes between pushbutton, checkbox, and radio button types via /Ff flags.
Changes Made
-
New module:
forms/value_button.rsButtonKindenum:Pushbutton,Checkbox,RadioButtonValuestruct with fields:kind: ButtonKindselected: boolstate_name: Option<String>- the appearance state name (/Yes, /Off, or custom)pushbutton: bool- raw flag from /Ff bit 26radio: bool- raw flag from /Ff bit 25
extract_button_value()function that parses /V (value) and /Ff (flags)- Helper constructors:
ButtonValue::pushbutton(),checkbox(),radio() - 15 comprehensive unit tests
-
Updated
forms/mod.rs- Added
pub mod value_button; - Re-exported
ButtonKind,ButtonValue,extract_button_value
- Added
-
Updated
forms/combiner.rs- Enhanced
FormFieldValue::Buttonvariant with:kind: ButtonKindstate_name: Option<String>
- Updated
merge_xfa_value_with_acro_type()to handle new fields - Updated test helper
make_button_value()with new structure
- Enhanced
-
Fixed pre-existing CCITTFaxDecoder test syntax errors
- Changed
CCITTFaxDecoder.parse_params()toCCITTFaxDecoder::parse_params()in 4 test locations - This was blocking test execution but unrelated to the bead's scope
- Changed
Bit Flag Implementation
Per PDF 1.7 spec and existing code:
/Ffbit 26 (1 << 25 = 0x2000000) → Pushbutton/Ffbit 25 (1 << 24 = 0x1000000) → Radio button- Neither bit set → Checkbox (default)
State Name Extraction
/Vabsent →selected: false, state_name: None/V == /Off→selected: false, state_name: Some("Off")/V == /Yesor any other name →selected: true, state_name: Some(name)
Test Results
Unit Tests (15 new tests in value_button.rs)
- ✅
test_button_kind_display- Display formatting - ✅
test_extract_pushbutton- Pushbutton extraction - ✅
test_extract_checkbox_selected_yes- Selected checkbox - ✅
test_extract_checkbox_unselected_off- Unselected checkbox - ✅
test_extract_checkbox_custom_state- Custom state name - ✅
test_extract_checkbox_no_value- Checkbox without /V - ✅
test_extract_radio_selected- Selected radio button - ✅
test_extract_radio_unselected- Unselected radio button - ✅
test_extract_radio_no_value- Radio button without /V - ✅
test_button_value_constructors- Helper constructors - ✅
test_extract_with_other_flags_set- Other /Ff flags don't interfere - ✅
test_extract_state_from_value_malformed- Graceful handling of malformed /V - ✅
test_button_kind_equality- PartialEq for ButtonKind - ✅
test_button_value_equality- PartialEq for ButtonValue - ✅
test_pushbutton_takes_precedence- Pushbutton flag wins over Radio if both set
Integration Tests
- ✅ All 41 forms module tests pass
- ✅ Combiner tests pass (8 tests)
- ✅ Existing
mod.rstests pass (18 tests)
Acceptance Criteria Status
| Criterion | Status | Notes |
|---|---|---|
| Pushbutton field → ButtonValue { kind: Pushbutton, selected: false, ... } | ✅ PASS | Implemented in extract_button_value() |
| Selected checkbox (/V == /Yes) → { kind: Checkbox, selected: true, state_name: Some("Yes") } | ✅ PASS | Test test_extract_checkbox_selected_yes |
| Unselected checkbox (/V == /Off) → { kind: Checkbox, selected: false, state_name: Some("Off") } | ✅ PASS | Test test_extract_checkbox_unselected_off |
| Radio button group with /V == "OptionA" → button with /AS == OptionA reports selected: true | ✅ PASS | Test test_extract_radio_selected |
| Custom state name (/V == /Selected) → state_name: Some("Selected"), selected: true | ✅ PASS | Test test_extract_checkbox_custom_state |
Code Quality
- ✅
cargo check --all-targets- passes for lib - ✅
cargo clippy --lib -p pdftract-core- no warnings in forms module - ✅
cargo fmt- all files formatted - ✅
cargo test --lib 'forms'- 41 tests pass - ✅ No
unwrap()orexpect()in non-test code - ✅ Exhaustive match arms on enums
- ✅ Public functions return
Result<T>where applicable
Files Modified
crates/pdftract-core/src/forms/value_button.rs(new) - 389 linescrates/pdftract-core/src/forms/mod.rs- added module and re-exportscrates/pdftract-core/src/forms/combiner.rs- updated Button variant with kind and state_namecrates/pdftract-core/src/parser/stream.rs- fixed CCITTFaxDecoder test syntax (unrelated but blocking)
Related Beads
- Coordinator:
pdftract-5t92(7.4.2: AcroForm value extraction for Tx / Btn / Ch types) - Sibling beads: Tx variant, Ch variant
- Downstream: 7.4.4 combiner consumes these values
Next Steps
This bead completes the Btn variant extraction. The remaining work for the coordinator bead pdftract-5t92 includes:
- Tx (text) value extraction
- Ch (choice) value extraction
- Integration tests for all three types together
Commit Message
feat(pdftract-66pgk): implement AcroForm Btn value extraction
Add button field value extraction distinguishing pushbutton, checkbox, and radio button types via /Ff flags. Extracts selected state and appearance state name (/Yes, /Off, custom).
- New module: forms/value_button.rs with ButtonKind enum and ButtonValue
- Updated FormFieldValue::Button variant with kind and state_name fields
- 15 unit tests covering all button types and edge cases
- Fixed CCITTFaxDecoder test syntax blocking test execution
Closes: pdftract-66pgk