Commit 0ea709d427760b8007372eeefead66e373d94e93

Authored by m-holger
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
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