Repository navigation
PPF-2112: Radio buttons are read as checkboxes when the group's default value is Off - #2115
Conversation
The checkbox rule that matches a parent /DV of /Yes or /Off ignored the radio flag, so off buttons in such a group became checkboxes. That either crashed widget discovery or silently read the group as a checkbox. Add a Radio rule ahead of it that checks the parent's radio flag, and let pattern values be predicates so the rule can test a flag bit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
[P2] Preserve unselected radio groups with The new radio classification rule exposes an existing problem in For a group with Reproduction using this PR's helper: from io import BytesIO
from runpy import run_path
from pikepdf import Name, Pdf
from PyPDFForm import PdfWrapper
build = run_path("tests/test_functional.py")["build_button_group"]
with Pdf.open(BytesIO(build(49152, Name.Off, None))) as pdf:
pdf.Root.AcroForm.Fields[0].V = Name.Off
stream = BytesIO()
pdf.save(stream)
form = PdfWrapper(stream.getvalue())
print(form.data) # Actual: {'group': 1}; expected: {'group': None}
with Pdf.open(BytesIO(form.fill({}).read())) as pdf:
field = pdf.Root.AcroForm.Fields[0]
print(field.V) # /b; expected: /Off
print([str(kid.AS) for kid in field.Kids])
# Actual: ['/Off', '/b']; expected: ['/Off', '/Off']Please exclude Validation on |
get_radio_value compared the parent /V against every normal appearance, including /Off, so a group with /V /Off reported its last option as selected and an empty fill turned that option on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves #2112.
Changes
The checkbox rule in
WIDGET_TYPE_PATTERNSthat matches a parent/DVof/Yesor/Offignored the radio flag, so off buttons in such a group became checkboxes.PdfWrapper(...)then either raisedAttributeErroror silently read the group as a checkbox.Radiorule sits just before that checkbox rule and matches when the parent field has the radio flag (/Ffbit 16)._is_value_matchnow accepts a callable pattern value, so a rule can test a flag bit._is_widgetandconstruct_widgetstill share the one table.get_radio_valueno longer treats/Offas a selected option. Without this, the new rule would make a group with/DV /Offand/V /Offreport its last option as selected, and an empty fill would turn that option on. Radio groups with/V /Offand no/DValready had this bug, and it is fixed for them too.Why not the patch proposed in the issue
It skips every
Checkboxmatch for a radio-flagged widget with a parent. That also covers the rule for widgets with their own/FT, so a radio widget under a container field with no/FTmatches nothing afterwards and disappears fromwidgets. On master it is aCheckbox, and it stays one here.Reading the flag from the parent also removes the limit noted there: a button in the group with a stray
/Ffof 0 is still aRadio.Validation
/DVof/Offand/Yeswith the first, second, or no button on, including.pagesand a fill round trip. Others confirm that a group with/V /Offreads as unselected and stays off through an empty fill, and that a button without the radio flag stays aCheckbox. The radio cases fail without their fixes.pdf_samples/are unchanged by the fix. None of them has a radio group with/V /Off. No expected PDFs needed regenerating.coverage run -m pytest(495 passed, 100% coverage),make linting, andruff format --checkon the changed files passed, using the local./venvwith standard zlib.#2114 reached
masterduring this work. It only changes workflow files, so these results still apply.🤖 Generated with Claude Code