Resolves test-file conflicts where main and this branch both appended tests:
main's file plus this branch's appended tests, unchanged.
fmt, clippy -p pdfcraft-forms -p pdfcraft-automation, and both crates' tests pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Adopt upstream's split set_props: rename_field returns the group a renamed
radio button joins, and redraw_or_join keeps set_props under the new
cognitive_complexity limit. join.rs imports explicitly (wildcard_imports).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Deleting one widget took it out of the field tree through its /Parent,
which a damaged file can leave out or point at another field; the group
kept the widget in its /Kids with no page showing it, and the delete
reported success. The field that owns the widget drops it from its own
/Kids as well (CodeRabbit on #4631).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbuSKAS2k5248LGFEapMUR
When the source radio field is also its widget, the join removed the
field-only keys but kept its whole /AA, so field triggers (K, F, V, C)
stayed on a dictionary that is now only a widget. Split it as
split_merged does for the target: keep the widget triggers, drop the
field's, and remove /AA when nothing is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Review follow-ups on #4977:
- ui-egui: after a rename, the Properties dialog re-selects the edited
widget by object, not by its old index. A button that joins a group is
appended after the group's own buttons, so the old index selected the
group's first button and a nudge moved the wrong one.
- split_merged (join and Duplicate): the new widget takes /A and the
widget triggers of /AA (E X D U Fo Bl PO PC PV PI); the field keeps
K F V C.
- join: a clashing export value searches at most taken + 1 ChoiceN
names, so one is always free, and the names are chosen before
anything is written.
Tests: a_joined_merged_groups_button_keeps_its_actions,
a_joining_button_gets_a_free_export_value_when_choice_names_run_long,
a_radio_button_joining_a_group_by_name_stays_selected (ui-egui). Each
fails with its fix reverted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Renaming a radio button to an existing radio group's name was refused as
a duplicate, so buttons placed separately could never become one group.
set_props now moves the renamed field's buttons into the named group
(ISO 32000-2 §12.7.5.2.4: a group is one field whose widgets are its
buttons). A clashing export value is renamed to the first free ChoiceN
in /AP /N, /D and /R; a moved button is on only if its value is the
group's value; merged field+widgets are handled on both sides (the
target split via split_merged, now shared with duplicate_field).
Refused when either group has /Opt, has no button, or the target is
locked.
Adapted from pdfcer (MIT, same author) 79870b56 and 116f48d3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9