diff --git a/libqpdf/QPDFFormFieldObjectHelper.cc b/libqpdf/QPDFFormFieldObjectHelper.cc index 54f854e..e1662e0 100644 --- a/libqpdf/QPDFFormFieldObjectHelper.cc +++ b/libqpdf/QPDFFormFieldObjectHelper.cc @@ -18,6 +18,8 @@ using namespace qpdf; using FormField = qpdf::impl::FormField; +const QPDFObjectHandle FormField::null_oh; + class QPDFFormFieldObjectHelper::Members: public FormField { public: @@ -48,93 +50,71 @@ QPDFFormFieldObjectHelper::isNull() QPDFFormFieldObjectHelper QPDFFormFieldObjectHelper::getParent() { - return {Null::if_null(m->getParent().oh())}; -} - -FormField -FormField::getParent() -{ - return {oh()["/Parent"]}; // maybe null + return {Null::if_null(m->Parent().oh())}; } QPDFFormFieldObjectHelper QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different) { - return Null::if_null(m->getTopLevelField(is_different).oh()); + return Null::if_null(m->root_field(is_different).oh()); } FormField -FormField::getTopLevelField(bool* is_different) +FormField::root_field(bool* is_different) { + if (is_different) { + *is_different = false; + } if (!obj) { return {}; } - auto top_field = oh(); + auto rf = *this; + size_t depth = 0; // Don't bother with loop detection until depth becomes suspicious QPDFObjGen::set seen; - while (seen.add(top_field) && !top_field.getKeyIfDict("/Parent").null()) { - top_field = top_field.getKey("/Parent"); + while (rf.Parent() && (++depth < 10 || seen.add(rf))) { + rf = rf.Parent(); if (is_different) { *is_different = true; } } - return {top_field}; -} - -QPDFObjectHandle -FormField::getFieldFromAcroForm(std::string const& name) -{ - QPDFObjectHandle result = QPDFObjectHandle::newNull(); - // Fields are supposed to be indirect, so this should work. - QPDF* q = oh().getOwningQPDF(); - if (!q) { - return result; - } - auto acroform = q->getRoot().getKey("/AcroForm"); - if (!acroform.isDictionary()) { - return result; - } - return acroform.getKey(name); + return rf; } QPDFObjectHandle QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name) { - return m->getInheritableFieldValue(name); + return Null::if_null(m->inheritable_value(name)); } -QPDFObjectHandle -FormField::getInheritableFieldValue(std::string const& name) +QPDFObjectHandle const& +FormField::inherited(std::string const& name, bool acroform) const { - QPDFObjectHandle node = oh(); - if (!node.isDictionary()) { - return QPDFObjectHandle::newNull(); + if (!obj) { + return null_oh; } - QPDFObjectHandle result(node.getKey(name)); - if (result.null()) { - QPDFObjGen::set seen; - while (seen.add(node) && node.hasKey("/Parent")) { - node = node.getKey("/Parent"); - result = node.getKey(name); - if (!result.null()) { - return result; - } + auto node = *this; + QPDFObjGen::set seen; + size_t depth = 0; // Don't bother with loop detection until depth becomes suspicious + while (node.Parent() && (++depth < 10 || seen.add(node))) { + node = node.Parent(); + if (auto const& result = node[name]) { + return {result}; } } - return result; + return acroform ? from_AcroForm(name) : null_oh; } std::string QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(std::string const& name) { - return m->getInheritableFieldValueAsString(name); + return m->inheritable_string(name); } std::string -FormField::getInheritableFieldValueAsString(std::string const& name) +FormField::inheritable_string(std::string const& name) const { - auto fv = getInheritableFieldValue(name); - if (fv.isString()) { - return fv.getUTF8Value(); + if (auto fv = inheritable_value(name)) { + return fv.utf8_value(); } return {}; } @@ -142,13 +122,7 @@ FormField::getInheritableFieldValueAsString(std::string const& name) std::string QPDFFormFieldObjectHelper::getInheritableFieldValueAsName(std::string const& name) { - return m->getInheritableFieldValueAsName(name); -} - -std::string -FormField::getInheritableFieldValueAsName(std::string const& name) -{ - if (Name fv = getInheritableFieldValue(name)) { + if (auto fv = m->inheritable_value(name)) { return fv; } return {}; @@ -157,35 +131,33 @@ FormField::getInheritableFieldValueAsName(std::string const& name) std::string QPDFFormFieldObjectHelper::getFieldType() { - return m->getFieldType(); -} - -std::string -FormField::getFieldType() -{ - return getInheritableFieldValueAsName("/FT"); + if (auto ft = m->FT()) { + return ft; + } + return {}; } std::string QPDFFormFieldObjectHelper::getFullyQualifiedName() { - return m->getFullyQualifiedName(); + return m->fully_qualified_name(); } std::string -FormField::getFullyQualifiedName() +FormField::fully_qualified_name() const { std::string result; - QPDFObjectHandle node = oh(); + auto node = *this; QPDFObjGen::set seen; - while (!node.null() && seen.add(node)) { - if (node.getKey("/T").isString()) { + size_t depth = 0; // Don't bother with loop detection until depth becomes suspicious + while (node && (++depth < 10 || seen.add(node))) { + if (auto T = node.T()) { if (!result.empty()) { - result = "." + result; + result.insert(0, 1, '.'); } - result = node.getKey("/T").getUTF8Value() + result; + result.insert(0, T.utf8_value()); } - node = node.getKey("/Parent"); + node = node.Parent(); } return result; } @@ -193,131 +165,110 @@ FormField::getFullyQualifiedName() std::string QPDFFormFieldObjectHelper::getPartialName() { - return m->getPartialName(); + return m->partial_name(); } std::string -FormField::getPartialName() +FormField::partial_name() const { - std::string result; - if (oh().getKey("/T").isString()) { - result = oh().getKey("/T").getUTF8Value(); + if (auto pn = T()) { + return pn.utf8_value(); } - return result; + return {}; } std::string QPDFFormFieldObjectHelper::getAlternativeName() { - return m->getAlternativeName(); + return m->alternative_name(); } std::string -FormField::getAlternativeName() +FormField::alternative_name() const { - if (oh().getKey("/TU").isString()) { - QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU present"); - return oh().getKey("/TU").getUTF8Value(); + if (auto an = TU()) { + return an.utf8_value(); } - QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU absent"); - return getFullyQualifiedName(); + return fully_qualified_name(); } std::string QPDFFormFieldObjectHelper::getMappingName() { - return m->getMappingName(); + return m->mapping_name(); } std::string -FormField::getMappingName() +FormField::mapping_name() const { - if (oh().getKey("/TM").isString()) { - QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM present"); - return oh().getKey("/TM").getUTF8Value(); + if (auto mn = TM()) { + return mn.utf8_value(); } - QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM absent"); - return getAlternativeName(); + return alternative_name(); } QPDFObjectHandle QPDFFormFieldObjectHelper::getValue() { - return m->getValue(); -} - -QPDFObjectHandle -FormField::getValue() -{ - return getInheritableFieldValue("/V"); + return Null::if_null(m->V()); } std::string QPDFFormFieldObjectHelper::getValueAsString() { - return getInheritableFieldValueAsString("/V"); + return m->value(); } std::string -FormField::getValueAsString() +FormField::value() const { - return getInheritableFieldValueAsString("/V"); + return inheritable_string("/V"); } QPDFObjectHandle QPDFFormFieldObjectHelper::getDefaultValue() { - return m->getDefaultValue(); -} - -QPDFObjectHandle -FormField::getDefaultValue() -{ - return getInheritableFieldValue("/DV"); + return Null::if_null(m->DV()); } std::string QPDFFormFieldObjectHelper::getDefaultValueAsString() { - return m->getDefaultValueAsString(); + return m->default_value(); } std::string -FormField::getDefaultValueAsString() +FormField::default_value() const { - return getInheritableFieldValueAsString("/DV"); + return inheritable_string("/DV"); } QPDFObjectHandle QPDFFormFieldObjectHelper::getDefaultResources() { - return m->getDefaultResources(); + return Null::if_null(m->getDefaultResources()); } QPDFObjectHandle FormField::getDefaultResources() { - return getFieldFromAcroForm("/DR"); + return from_AcroForm("/DR"); } std::string QPDFFormFieldObjectHelper::getDefaultAppearance() { - return m->getDefaultAppearance(); + return m->default_appearance(); } std::string -FormField::getDefaultAppearance() +FormField::default_appearance() const { - auto value = getInheritableFieldValue("/DA"); - bool looked_in_acroform = false; - if (!value.isString()) { - value = getFieldFromAcroForm("/DA"); - looked_in_acroform = true; + if (auto DA = inheritable_value("/DA")) { + return DA.utf8_value(); } - if (value.isString()) { - QTC::TC("qpdf", "QPDFFormFieldObjectHelper DA present", looked_in_acroform ? 0 : 1); - return value.getUTF8Value(); + if (String DA = from_AcroForm("/DA")) { + return DA.utf8_value(); } return {}; } @@ -331,10 +282,10 @@ QPDFFormFieldObjectHelper::getQuadding() int FormField::getQuadding() { - QPDFObjectHandle fv = getInheritableFieldValue("/Q"); + auto fv = inheritable_value("/Q"); bool looked_in_acroform = false; if (!fv.isInteger()) { - fv = getFieldFromAcroForm("/Q"); + fv = from_AcroForm("/Q"); looked_in_acroform = true; } if (fv.isInteger()) { @@ -353,7 +304,7 @@ QPDFFormFieldObjectHelper::getFlags() int FormField::getFlags() { - QPDFObjectHandle f = getInheritableFieldValue("/Ff"); + auto f = inheritable_value("/Ff"); return f.isInteger() ? f.getIntValueAsInt() : 0; } @@ -366,7 +317,7 @@ QPDFFormFieldObjectHelper::isText() bool FormField::isText() { - return getFieldType() == "/Tx"; + return FT() == "/Tx"; } bool @@ -378,7 +329,7 @@ QPDFFormFieldObjectHelper::isCheckbox() bool FormField::isCheckbox() { - return getFieldType() == "/Btn" && (getFlags() & (ff_btn_radio | ff_btn_pushbutton)) == 0; + return FT() == "/Btn" && (getFlags() & (ff_btn_radio | ff_btn_pushbutton)) == 0; } bool @@ -390,7 +341,7 @@ QPDFFormFieldObjectHelper::isChecked() bool FormField::isChecked() { - return isCheckbox() && Name(getValue()) != "/Off"; + return isCheckbox() && V() != "/Off"; } bool @@ -402,7 +353,7 @@ QPDFFormFieldObjectHelper::isRadioButton() bool FormField::isRadioButton() { - return getFieldType() == "/Btn" && (getFlags() & ff_btn_radio) == ff_btn_radio; + return FT() == "/Btn" && (getFlags() & ff_btn_radio) == ff_btn_radio; } bool @@ -414,7 +365,7 @@ QPDFFormFieldObjectHelper::isPushbutton() bool FormField::isPushbutton() { - return getFieldType() == "/Btn" && (getFlags() & ff_btn_pushbutton) == ff_btn_pushbutton; + return FT() == "/Btn" && (getFlags() & ff_btn_pushbutton) == ff_btn_pushbutton; } bool @@ -426,7 +377,7 @@ QPDFFormFieldObjectHelper::isChoice() bool FormField::isChoice() { - return getFieldType() == "/Ch"; + return FT() == "/Ch"; } std::vector @@ -442,7 +393,7 @@ FormField::getChoices() return {}; } std::vector result; - for (auto const& item: getInheritableFieldValue("/Opt").as_array()) { + for (auto const& item: inheritable_value("/Opt")) { if (item.isString()) { result.emplace_back(item.getUTF8Value()); } else if (item.size() == 2) { @@ -489,7 +440,7 @@ void FormField::setV(QPDFObjectHandle value, bool need_appearances) { Name name = value; - if (getFieldType() == "/Btn") { + if (FT() == "/Btn") { if (isCheckbox()) { if (!name) { warn("ignoring attempt to set a checkbox field to a value whose type is not name"); @@ -652,10 +603,9 @@ QPDFFormFieldObjectHelper::generateAppearance(QPDFAnnotationObjectHelper& aoh) void FormField::generateAppearance(QPDFAnnotationObjectHelper& aoh) { - std::string ft = getFieldType(); // Ignore field types we don't know how to generate appearances for. Button fields don't really // need them -- see code in QPDFAcroFormDocumentHelper::generateAppearancesIfNeeded. - if ((ft == "/Tx") || (ft == "/Ch")) { + if (FT() == "/Tx" || FT() == "/Ch") { generateTextAppearance(aoh); } } @@ -973,8 +923,8 @@ FormField::generateTextAppearance(QPDFAnnotationObjectHelper& aoh) return; } QPDFObjectHandle::Rectangle bbox = bbox_obj.getArrayAsRectangle(); - std::string DA = getDefaultAppearance(); - std::string V = getValueAsString(); + std::string DA = default_appearance(); + std::string V = value(); std::vector opt; if (isChoice() && (getFlags() & ff_ch_combo) == 0) { opt = getChoices(); diff --git a/libqpdf/qpdf/FormField.hh b/libqpdf/qpdf/FormField.hh index 3c7c243..dbeeffa 100644 --- a/libqpdf/qpdf/FormField.hh +++ b/libqpdf/qpdf/FormField.hh @@ -32,57 +32,264 @@ namespace qpdf::impl { } - // Return the field's parent. A form field object helper whose underlying object is null is - // returned if there is no parent. This condition may be tested by calling isNull(). - FormField getParent(); - - // Return the top-level field for this field. Typically this will be the field itself or its - // parent. If is_different is provided, it is set to true if the top-level field is - // different from the field itself; otherwise it is set to false. - FormField getTopLevelField(bool* is_different = nullptr); - - // Get a field value, possibly inheriting the value from an ancestor node. - QPDFObjectHandle getInheritableFieldValue(std::string const& name); - - // Get an inherited field value as a string. If it is not a string, silently return the - // empty string. - std::string getInheritableFieldValueAsString(std::string const& name); - - // Get an inherited field value of type name as a string representing the name. If it is not - // a name, silently return the empty string. - std::string getInheritableFieldValueAsName(std::string const& name); - - // Returns the value of /FT if present, otherwise returns the empty string. - std::string getFieldType(); + /// Retrieves the /Parent form field of the current field. + /// + /// This function accesses the parent field in the hierarchical structure of form fields, if + /// it exists. The parent is determined based on the /Parent attribute in the field + /// dictionary. + /// + /// @return A FormField object representing the parent field. If the current field has no + /// parent, an empty FormField object is returned. + FormField + Parent() + { + return {get("/Parent")}; + } - std::string getFullyQualifiedName(); + /// @brief Returns the top-level field associated with the current field. + /// + /// The function traverses the hierarchy of parent fields to identify the highest-level + /// field in the tree. Typically, this will be the current field itself unless it has a + /// parent field. Optionally, it can indicate whether the top-level field is different from + /// the current field. + /// + /// @param is_different A pointer to a boolean that, if provided, will be set to true if the + /// top-level field differs from the current field; otherwise, it will be set to + /// false. + /// + /// @return The top-level field in the form field hierarchy. + FormField root_field(bool* is_different = nullptr); + + /// @brief Retrieves the inherited value of the specified attribute. + /// + /// @param name The name of the attribute to retrieve. + /// @param acroform If true, checks the document's /AcroForm dictionary for the attribute + /// if it is not found in the field hierarchy. + /// + /// @return A constant reference to the QPDFObjectHandle representing the value of the + /// specified attribute, if found. If the attribute is not found in the field + /// hierarchy or the /AcroForm dictionary (when `acroform` is true), returns a + /// reference to a static null object handle. + QPDFObjectHandle const& inherited(std::string const& name, bool acroform = false) const; + + /// @brief Retrieves the value of a specified field, accounting for inheritance through the + /// hierarchy of ancestor nodes in the form field tree. + /// + /// This function attempts to retrieve the value of the specified field. If the `inherit` + /// parameter is set to `true` and the field value is not found at the current level, the + /// method traverses up the parent hierarchy to find the value. The traversal stops when a + /// value is found, when the root node is reached, or when a loop detection mechanism + /// prevents further traversal. + /// + /// @tparam T The return type of the field value. + /// @param name The name of the field to retrieve the value for. + /// @param inherit If set to `true`, the function will attempt to retrieve the value by + /// inheritance from the parent hierarchy of the form field. Defaults to `true`. + /// @return Returns the field's value if found; otherwise, returns a default-constructed + /// value of type `T`. + template + T + inheritable_value(std::string const& name, bool inherit = true, bool acroform = false) const + { + if (auto& v = get(name)) { + return {v}; + } + return {inherit ? inherited(name, acroform) : null_oh}; + } - std::string getPartialName(); + /// @brief Retrieves an inherited field string attribute as a string. + /// + /// @param name The name of the field for which the value is to be retrieved. + /// @return The inherited field value as a UTF-8 encoded string, or an empty string if the + /// value does not exist or is not of String type. + std::string inheritable_string(std::string const& name) const; + + /// @brief Retrieves the field type (/FT attribute). + /// + /// @param inherit If set to `true`, the function will attempt to retrieve the value by + /// inheritance from the parent hierarchy of the form field. Defaults to `true`. + /// @return Returns the field type if found; otherwise, returns a default-constructed + /// `Name`. + Name + FT(bool inherit = true) const + { + return inheritable_value("/FT"); + } - // Return the alternative field name (/TU), which is the field name intended to be presented - // to users. If not present, fall back to the fully qualified name. - std::string getAlternativeName(); + /// @brief Retrieves the partial field name (/T attribute). + /// + /// @return Returns the partial field name if found; otherwise, returns a + /// default-constructed `String`. + String + T() const + { + return {get("/T")}; + } - // Return the mapping field name (/TM). If not present, fall back to the alternative name, - // then to the partial name. - std::string getMappingName(); + /// @brief Retrieves the alternative name (/TU attribute). + /// + /// @return Returns the alternative name if found; otherwise, returns a default-constructed + /// `String`. + String + TU() const + { + return {get("/TU")}; + } - QPDFObjectHandle getValue(); + /// @brief Retrieves the mapping name (/TM attribute). + /// + /// @return Returns the mapping name if found; otherwise, returns a default-constructed + /// `String`. + String + TM() const + { + return {get("/TM")}; + } - // Return the field's value as a string. If this is called with a field whose value is not a - std::string getValueAsString(); + /// @brief Retrieves the fully qualified name of the form field. + /// + /// This method constructs the fully qualified name of the form field by traversing through + /// its parent hierarchy. The fully qualified name is constructed by concatenating the /T + /// (field name) attribute of each parent node with periods as separators, starting from the + /// root of the hierarchy. + /// + /// If the field has no parent hierarchy, the result will simply be the /T attribute of the + /// current field. In cases of potential circular references, loop detection is applied. + /// + /// @return A string representing the fully qualified name of the field. + std::string fully_qualified_name() const; + + /// @brief Retrieves the partial name (/T attribute) of the form field. + /// + /// This method returns the value of the field's /T attribute, which is the partial name + /// used to identify the field within its parent hierarchy. If the attribute is not set, an + /// empty string is returned. + /// + /// @return A string representing the partial name of the field in UTF-8 encoding, or an + /// empty string if the /T attribute is not present. + std::string partial_name() const; + + /// @brief Retrieves the alternative name for the form field. + /// + /// This method attempts to return the alternative name (/TU) of the form field, which is + /// the field name intended to be presented, to users as a UTF-8 string, if it exists. If + /// the alternative name is not present, the method falls back to the fully qualified name + /// of the form field. + /// + /// @return The alternative name of the form field as a string, or the + /// fully qualified name if the alternative name is unavailable. + std::string alternative_name() const; + + /// @brief Retrieves the mapping field name (/TM) for the form field. + /// + /// If the mapping name (/TM) is present, it is returned as a UTF-8 string. If not, it falls + /// back to the 'alternative name', which is obtained using the `alternative_name()` method. + /// + /// @return The mapping field name (/TM) as a UTF-8 string or the alternative name if the + /// mapping name is absent. + std::string mapping_name() const; + + /// @brief Retrieves the field value (`/V` attribute) of a specified field, accounting for + /// inheritance through thehierarchy of ancestor nodes in the form field tree. + /// + /// This function attempts to retrieve the `/V` attribute. If the `inherit` + /// parameter is set to `true` and the `/V` is not found at the current level, the + /// method traverses up the parent hierarchy to find the value. The traversal stops when + /// `/V` is found, when the root node is reached, or when a loop detection mechanism + /// prevents further traversal. + /// + /// @tparam T The return type. + /// @param inherit If set to `true`, the function will attempt to retrieve `/V` by + /// inheritance from the parent hierarchy of the form field. Defaults to `true`. + /// @return Returns the field's value if found; otherwise, returns a default-constructed + /// value of type `T`. + template + T + V(bool inherit = true) const + { + return inheritable_value("/V", inherit); + } - QPDFObjectHandle getDefaultValue(); + /// @brief Retrieves the field value (`/V` attribute) of a specified field, accounting for + /// inheritance through the hierarchy of ancestor nodes in the form field tree. + /// + /// This function attempts to retrieve the `/V` attribute. If the `inherit` + /// parameter is set to `true` and the `/V` is not found at the current level, the + /// method traverses up the parent hierarchy to find the value. The traversal stops when + /// `/V` is found, when the root node is reached, or when a loop detection mechanism + /// prevents further traversal. + /// + /// @param inherit If set to `true`, the function will attempt to retrieve `/V` by + /// inheritance from the parent hierarchy of the form field. Defaults to `true`. + /// @return Returns the field's value if found; otherwise, returns a default-constructed + /// object handle. + QPDFObjectHandle const& + V(bool inherit = true) const + { + if (auto& v = get("/V")) { + return v; + } + return {inherit ? inherited("/V") : null_oh}; + } - // Return the field's default value as a string. If this is called with a field whose value - // is not a string, the empty string will be silently returned. - std::string getDefaultValueAsString(); + /// @brief Retrieves the field value `/V` attribute of the form field, considering + /// inheritance, if the value is a String. + /// + /// This function extracts the value of the form field, accounting for potential inheritance + /// through the form hierarchy. It returns the value if it is a String, and an empty string + /// otherwise. + /// + /// @return A string containing the actual or inherited `/V` attribute of the form field, or + /// an empty string if the value is not present or not a String. + std::string value() const; + + /// @brief Retrieves the field default value (`/DV` attribute) of a specified field, + /// accounting for inheritance through the hierarchy of ancestor nodes in the form + /// field tree. + /// + /// This function attempts to retrieve the `/DV` attribute. If the `inherit` parameter is + /// set to `true` and the `/DV` is not found at the current level, the method traverses up + /// the parent hierarchy to find the value. The traversal stops when + /// `/DV` is found, when the root node is reached, or when a loop detection mechanism + /// prevents further traversal. + /// + /// @tparam T The return type. + /// @param inherit If set to `true`, the function will attempt to retrieve `/DV` by + /// inheritance from the parent hierarchy of the form field. Defaults to `true`. + /// @return Returns the field's default value if found; otherwise, returns a + /// default-constructed value of type `T`. + QPDFObjectHandle const& + DV(bool inherit = true) const + { + if (auto& v = get("/DV")) { + return v; + } + return {inherit ? inherited("/DV") : null_oh}; + } - // Return the default appearance string, taking inheritance from the field tree into - // account. Returns the empty string if the default appearance string is not available - // (because it's erroneously absent or because this is not a variable text field). If not - // found in the field hierarchy, look in /AcroForm. - std::string getDefaultAppearance(); + /// @brief Retrieves the default value `/DV` attribute of the form field, considering + /// inheritance, if the default value is a String. + /// + /// This function extracts the default value of the form field, accounting for potential + /// inheritance through the form hierarchy. It returns the value if it is a String, and an + /// empty string otherwise. + /// + /// @return A string containing the actual or inherited `/V` attribute of the form field, or + /// an empty string if the value is not present or not a String. + std::string default_value() const; + + /// @brief Returns the default appearance string for the form field, considering inheritance + /// from the field tree hierarchy and the document's /AcroForm dictionary. + /// + /// This method retrieves the field's /DA (default appearance) attribute. If the attribute + /// is not directly available, it checks the parent fields in the hierarchy for an inherited + /// value. If no value is found in the field hierarchy, it attempts to retrieve the /DA + /// attribute from the document's /AcroForm dictionary. The method returns an empty string + /// if no default appearance string is available or applicable. + /// + /// @return A string representing the default appearance, or an empty string if + /// no value is found. + std::string default_appearance() const; // Return the default resource dictionary for the field. This comes not from the field but // from the document-level /AcroForm dictionary. While several PDF generates put a /DR key @@ -154,12 +361,29 @@ namespace qpdf::impl void generateAppearance(QPDFAnnotationObjectHelper&); private: - QPDFObjectHandle getFieldFromAcroForm(std::string const& name); + /// @brief Retrieves an entry from the document's /AcroForm dictionary using the specified + /// name. + /// + /// The method accesses the AcroForm dictionary within the root object of the PDF document. + /// If the the AcroForm dictionary contains the given field name, it retrieves the + /// corresponding entry. Otherwise, it returns a default-constructed object handle. + /// + /// @param name The name of the form field to retrieve. + /// @return A object handle corresponding to the specified name within the AcroForm + /// dictionary. + QPDFObjectHandle const& + from_AcroForm(std::string const& name) const + { + return {qpdf() ? qpdf()->getRoot()["/AcroForm"][name] : null_oh}; + } + void setRadioButtonValue(QPDFObjectHandle name); void setCheckBoxValue(bool value); void generateTextAppearance(QPDFAnnotationObjectHelper&); QPDFObjectHandle getFontFromResource(QPDFObjectHandle resources, std::string const& font_name); + + static const QPDFObjectHandle null_oh; }; } // namespace qpdf::impl diff --git a/libtests/objects.cc b/libtests/objects.cc index 6f20aee..52da62c 100644 --- a/libtests/objects.cc +++ b/libtests/objects.cc @@ -90,7 +90,6 @@ test_0(QPDF& pdf, char const* arg2) assert_compare_numbers(UINT_MAX, t.getKey("/Q3").getUIntValueAsUInt()); } - static void test_1(QPDF& pdf, char const* arg2) { @@ -121,7 +120,7 @@ test_1(QPDF& pdf, char const* arg2) bool thrown = false; try { - i.at("/A"); + i.at("/A"); } catch (std::runtime_error const&) { thrown = true; } @@ -161,7 +160,9 @@ runtest(int n, char const* filename1, char const* arg2) // the test suite to see how the test is invoked to find the file // that the test is supposed to operate on. - std::set ignore_filename = {1,}; + std::set ignore_filename = { + 1, + }; QPDF pdf; std::shared_ptr file_buf; @@ -175,7 +176,8 @@ runtest(int n, char const* filename1, char const* arg2) } std::map test_functions = { - {0, test_0}, {1, test_1}, + {0, test_0}, + {1, test_1}, }; auto fn = test_functions.find(n); diff --git a/manual/release-notes.rst b/manual/release-notes.rst index db330b8..9ddd1f7 100644 --- a/manual/release-notes.rst +++ b/manual/release-notes.rst @@ -23,6 +23,12 @@ more detail. not work on some older Linux distributions. If you need support for an older distribution, please use version 12.2.0 or below. + - Bug fixes + + - Set `is_different` flag in `QPDFFormFieldObjectHelper::getTopLevelField` to + false if the field is a top-level field. Previously the flag was only set + if the field is a top-level field. + - Library Enhancements - Add ``QPDFNameTreeObjectHelper`` and ``QPDFNumberTreeObjectHelper`` diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index 157e38d..6c58745 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -174,12 +174,7 @@ QPDFObjectHandle dictionary empty map for asMap 0 QPDFObjectHandle numeric non-numeric 0 QPDFObjectHandle erase array bounds 0 qpdf-c called qpdf_check_pdf 0 -QPDFFormFieldObjectHelper TU present 0 -QPDFFormFieldObjectHelper TM present 0 -QPDFFormFieldObjectHelper TU absent 0 -QPDFFormFieldObjectHelper TM absent 0 QPDFFormFieldObjectHelper Q present 1 -QPDFFormFieldObjectHelper DA present 1 QPDFAcroFormDocumentHelper field found 1 QPDFAcroFormDocumentHelper annotation found 1 QPDFJob automatically set keep files open 1