Commit bc28f7dbb48ca429904c57870df2f4433af7ea81

Authored by m-holger
1 parent 00cf4ce5

Introduce `FormNode::field()` and `FormNode::widget()` methods; refactor `QPDFAc…

…roFormDocumentHelper` to use them instead of manual key checks
libqpdf/QPDFAcroFormDocumentHelper.cc
... ... @@ -412,10 +412,9 @@ AcroForm::traverseField(QPDFObjectHandle field, QPDFObjectHandle const& parent,
412 412 // fields can be merged with terminal field dictionaries. Otherwise, the annotation fields might
413 413 // be there to be inherited by annotations below it.
414 414  
415   - Array Kids = field["/Kids"];
416   - const bool is_field = depth == 0 || Kids || field.hasKey("/Parent");
417   - const bool is_annotation =
418   - !Kids && (field.hasKey("/Subtype") || field.hasKey("/Rect") || field.hasKey("/AP"));
  415 + FormNode node = field;
  416 + const bool is_field = depth == 0 || node.field();
  417 + const bool is_annotation = node.widget();
419 418  
420 419 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper field found", (depth == 0) ? 0 : 1);
421 420 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper annotation found", (is_field ? 0 : 1));
... ... @@ -428,8 +427,8 @@ AcroForm::traverseField(QPDFObjectHandle field, QPDFObjectHandle const& parent,
428 427 }
429 428  
430 429 if (is_annotation) {
431   - QPDFObjectHandle our_field = (is_field ? field : parent);
432   - fields_[our_field.getObjGen()].annotations.emplace_back(field);
  430 + auto our_field = (is_field ? field : parent);
  431 + fields_[our_field].annotations.emplace_back(field);
433 432 annotation_to_field_[og] = QPDFFormFieldObjectHelper(our_field);
434 433 }
435 434  
... ... @@ -462,7 +461,7 @@ AcroForm::traverseField(QPDFObjectHandle field, QPDFObjectHandle const& parent,
462 461 name_to_fields_[name].insert(og);
463 462 }
464 463  
465   - for (auto const& kid: Kids) {
  464 + for (auto const& kid: node.Kids()) {
466 465 if (bad_fields_.contains(kid)) {
467 466 continue;
468 467 }
... ...
libqpdf/qpdf/AcroForm.hh
... ... @@ -644,6 +644,41 @@ namespace qpdf::impl
644 644 /// no value is found.
645 645 std::string default_appearance() const;
646 646  
  647 + /// @brief Determines whether the form node represents a field (does not apply to root
  648 + /// fields).
  649 + ///
  650 + /// This method checks if the current node represents a field by examining two conditions:
  651 + /// 1. The presence of children nodes (via the `/Kids` array).
  652 + /// 2. Whether it contains the `/Parent` key.
  653 + ///
  654 + /// @return `true` if the node represents a field (either has children or contains a
  655 + /// `/Parent` key), otherwise `false`.
  656 + bool
  657 + field() const
  658 + {
  659 + return Kids() || contains("/Parent");
  660 + }
  661 +
  662 + /// @brief Determines if the form node represents a widget annotation.
  663 + ///
  664 + /// This method checks whether the current form node is a widget annotation
  665 + /// by verifying the following conditions:
  666 + ///
  667 + /// - The node does not have any /Kids entries (i.e., it is not a parent node with
  668 + /// descendants).
  669 + /// - The node contains any of the following attributes commonly associated with widget
  670 + /// annotations:
  671 + /// - `/Subtype`
  672 + /// - `/Rect`
  673 + /// - `/AP`
  674 + ///
  675 + /// @return `true` if the form node is a widget annotation; otherwise, `false`.
  676 + bool
  677 + widget() const
  678 + {
  679 + return !Kids() && (contains("/Subtype") || contains("/Rect") || contains("/AP"));
  680 + }
  681 +
647 682 // Return the default resource dictionary for the field. This comes not from the field but
648 683 // from the document-level /AcroForm dictionary. While several PDF generators put a /DR key
649 684 // in the form field's dictionary, experimentation suggests that many popular readers,
... ...