Commit 0ea709d427760b8007372eeefead66e373d94e93
1 parent
6923f0d4
Refactor `FormNode::setRadioButtonValue`
Fix identification of radio button widgets by relying on the absence of `Kids` arrays. Fix the exclusion of RadioButtonFields with parents. Fixes #1449
Showing
2 changed files
with
22 additions
and
16 deletions
libqpdf/QPDFFormFieldObjectHelper.cc
| @@ -446,8 +446,8 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) | @@ -446,8 +446,8 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) | ||
| 446 | void | 446 | void |
| 447 | FormNode::setV(QPDFObjectHandle value, bool need_appearances) | 447 | FormNode::setV(QPDFObjectHandle value, bool need_appearances) |
| 448 | { | 448 | { |
| 449 | - Name name = value; | ||
| 450 | if (FT() == "/Btn") { | 449 | if (FT() == "/Btn") { |
| 450 | + Name name = value; | ||
| 451 | if (isCheckbox()) { | 451 | if (isCheckbox()) { |
| 452 | if (!name) { | 452 | if (!name) { |
| 453 | warn("ignoring attempt to set a checkbox field to a value whose type is not name"); | 453 | warn("ignoring attempt to set a checkbox field to a value whose type is not name"); |
| @@ -498,31 +498,30 @@ FormNode::setV(std::string const& utf8_value, bool need_appearances) | @@ -498,31 +498,30 @@ FormNode::setV(std::string const& utf8_value, bool need_appearances) | ||
| 498 | } | 498 | } |
| 499 | 499 | ||
| 500 | void | 500 | void |
| 501 | -FormNode::setRadioButtonValue(QPDFObjectHandle name) | 501 | +FormNode::setRadioButtonValue(Name const& name) |
| 502 | { | 502 | { |
| 503 | + qpdf_expect(name); | ||
| 503 | // Set the value of a radio button field. This has the following specific behavior: | 504 | // Set the value of a radio button field. This has the following specific behavior: |
| 504 | - // * If this is a radio button field that has a parent that is also a radio button field and has | ||
| 505 | - // no explicit /V, call itself on the parent | 505 | + // * If this is a node without /Kids, assume this is a individual radio button widget and call |
| 506 | + // itself on the parent | ||
| 506 | // * If this is a radio button field with children, set /V to the given value. Then, for each | 507 | // * If this is a radio button field with children, set /V to the given value. Then, for each |
| 507 | // child, if the child has the specified value as one of its keys in the /N subdictionary of | 508 | // child, if the child has the specified value as one of its keys in the /N subdictionary of |
| 508 | // its /AP (i.e. its normal appearance stream dictionary), set /AS to name; otherwise, if /Off | 509 | // its /AP (i.e. its normal appearance stream dictionary), set /AS to name; otherwise, if /Off |
| 509 | // is a member, set /AS to /Off. | 510 | // is a member, set /AS to /Off. |
| 510 | - // Note that we never turn on /NeedAppearances when setting a radio button field. | ||
| 511 | - QPDFObjectHandle parent = oh().getKey("/Parent"); | ||
| 512 | - if (parent.isDictionary() && parent.getKey("/Parent").null()) { | ||
| 513 | - FormNode ph(parent); | ||
| 514 | - if (ph.isRadioButton()) { | ||
| 515 | - // This is most likely one of the individual buttons. Try calling on the parent. | ||
| 516 | - ph.setRadioButtonValue(name); | 511 | + auto kids = Kids(); |
| 512 | + if (!kids) { | ||
| 513 | + // This is most likely one of the individual buttons. Try calling on the parent. | ||
| 514 | + auto parent = Parent(); | ||
| 515 | + if (parent.Kids()) { | ||
| 516 | + parent.setRadioButtonValue(name); | ||
| 517 | return; | 517 | return; |
| 518 | } | 518 | } |
| 519 | } | 519 | } |
| 520 | - auto kids = Kids(); | ||
| 521 | - if (!(isRadioButton() && parent.null() && kids)) { | 520 | + if (!isRadioButton() || !kids) { |
| 522 | warn("don't know how to set the value of this field as a radio button"); | 521 | warn("don't know how to set the value of this field as a radio button"); |
| 523 | return; | 522 | return; |
| 524 | } | 523 | } |
| 525 | - setFieldAttribute("/V", name); | 524 | + replace("/V", name); |
| 526 | for (FormNode kid: kids) { | 525 | for (FormNode kid: kids) { |
| 527 | auto ap = kid.AP(); | 526 | auto ap = kid.AP(); |
| 528 | QPDFObjectHandle annot; | 527 | QPDFObjectHandle annot; |
| @@ -542,7 +541,7 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) | @@ -542,7 +541,7 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) | ||
| 542 | warn("unable to set the value of this radio button"); | 541 | warn("unable to set the value of this radio button"); |
| 543 | continue; | 542 | continue; |
| 544 | } | 543 | } |
| 545 | - if (ap["/N"].contains(name.getName())) { | 544 | + if (ap["/N"].contains(name.value())) { |
| 546 | annot.replace("/AS", name); | 545 | annot.replace("/AS", name); |
| 547 | } else { | 546 | } else { |
| 548 | annot.replace("/AS", Name("/Off")); | 547 | annot.replace("/AS", Name("/Off")); |
libqpdf/qpdf/AcroForm.hh
| @@ -767,7 +767,14 @@ namespace qpdf::impl | @@ -767,7 +767,14 @@ namespace qpdf::impl | ||
| 767 | return {qpdf() ? qpdf()->getRoot()["/AcroForm"][name] : null_oh}; | 767 | return {qpdf() ? qpdf()->getRoot()["/AcroForm"][name] : null_oh}; |
| 768 | } | 768 | } |
| 769 | 769 | ||
| 770 | - void setRadioButtonValue(QPDFObjectHandle name); | 770 | + /// @brief Sets the value of a radio button field. |
| 771 | + /// | ||
| 772 | + /// This method updates the value of a radio button field to the specified name. | ||
| 773 | + /// It ensures that the radio button's state is consistent with the provided value. | ||
| 774 | + /// | ||
| 775 | + /// @param name The name of the radio button value to set. This corresponds to the | ||
| 776 | + /// appearance state name of the selected radio button. | ||
| 777 | + void setRadioButtonValue(Name const& name); | ||
| 771 | void setCheckBoxValue(bool value); | 778 | void setCheckBoxValue(bool value); |
| 772 | void generateTextAppearance(QPDFAnnotationObjectHelper&); | 779 | void generateTextAppearance(QPDFAnnotationObjectHelper&); |
| 773 | 780 |