pdftract/notes/pdftract-66pgk.md
jedarden 77f7c6a1ed 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

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 11:33:23 -04:00

132 lines
5.7 KiB
Markdown

# 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
1. **New module: `forms/value_button.rs`**
- `ButtonKind` enum: `Pushbutton`, `Checkbox`, `Radio`
- `ButtonValue` struct with fields:
- `kind: ButtonKind`
- `selected: bool`
- `state_name: Option<String>` - the appearance state name (/Yes, /Off, or custom)
- `pushbutton: bool` - raw flag from /Ff bit 26
- `radio: 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
2. **Updated `forms/mod.rs`**
- Added `pub mod value_button;`
- Re-exported `ButtonKind`, `ButtonValue`, `extract_button_value`
3. **Updated `forms/combiner.rs`**
- Enhanced `FormFieldValue::Button` variant with:
- `kind: ButtonKind`
- `state_name: Option<String>`
- Updated `merge_xfa_value_with_acro_type()` to handle new fields
- Updated test helper `make_button_value()` with new structure
4. **Fixed pre-existing CCITTFaxDecoder test syntax errors**
- Changed `CCITTFaxDecoder.parse_params()` to `CCITTFaxDecoder::parse_params()` in 4 test locations
- This was blocking test execution but unrelated to the bead's scope
### Bit Flag Implementation
Per PDF 1.7 spec and existing code:
- `/Ff` bit 26 (1 << 25 = 0x2000000) Pushbutton
- `/Ff` bit 25 (1 << 24 = 0x1000000) Radio button
- Neither bit set Checkbox (default)
### State Name Extraction
- `/V` absent `selected: false, state_name: None`
- `/V == /Off` `selected: false, state_name: Some("Off")`
- `/V == /Yes` or 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.rs` tests 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()` or `expect()` in non-test code
- Exhaustive match arms on enums
- Public functions return `Result<T>` where applicable
## Files Modified
1. `crates/pdftract-core/src/forms/value_button.rs` (new) - 389 lines
2. `crates/pdftract-core/src/forms/mod.rs` - added module and re-exports
3. `crates/pdftract-core/src/forms/combiner.rs` - updated Button variant with kind and state_name
4. `crates/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