From 91ae14948fa1f3e3b6ec895e83756e51a4d70be0 Mon Sep 17 00:00:00 2001 From: m-holger Date: Mon, 10 Nov 2025 12:49:02 +0000 Subject: [PATCH] Refactor: replace `getInheritableFieldValue` with templated `inheritable_value` for flexibility and consistency across FormField methods. Adjust related method implementations accordingly. --- libqpdf/QPDFFormFieldObjectHelper.cc | 51 ++++++++++++++++++++++++--------------------------- libqpdf/qpdf/FormField.hh | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/libqpdf/QPDFFormFieldObjectHelper.cc b/libqpdf/QPDFFormFieldObjectHelper.cc index 704e368..aea8cbc 100644 --- a/libqpdf/QPDFFormFieldObjectHelper.cc +++ b/libqpdf/QPDFFormFieldObjectHelper.cc @@ -83,28 +83,25 @@ FormField::root_field(bool* is_different) 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(); - } - 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; - } + if (!obj) { + return null_oh; + } + 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 @@ -116,7 +113,7 @@ QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(std::string const& n std::string FormField::getInheritableFieldValueAsString(std::string const& name) { - auto fv = getInheritableFieldValue(name); + auto fv = inheritable_value(name); if (fv.isString()) { return fv.getUTF8Value(); } @@ -132,7 +129,7 @@ QPDFFormFieldObjectHelper::getInheritableFieldValueAsName(std::string const& nam std::string FormField::getInheritableFieldValueAsName(std::string const& name) { - if (Name fv = getInheritableFieldValue(name)) { + if (auto fv = inheritable_value(name)) { return fv; } return {}; @@ -227,13 +224,13 @@ FormField::getMappingName() QPDFObjectHandle QPDFFormFieldObjectHelper::getValue() { - return m->getValue(); + return Null::if_null(m->getValue()); } QPDFObjectHandle FormField::getValue() { - return getInheritableFieldValue("/V"); + return inheritable_value("/V"); } std::string @@ -251,13 +248,13 @@ FormField::getValueAsString() QPDFObjectHandle QPDFFormFieldObjectHelper::getDefaultValue() { - return m->getDefaultValue(); + return Null::if_null(m->getDefaultValue()); } QPDFObjectHandle FormField::getDefaultValue() { - return getInheritableFieldValue("/DV"); + return inheritable_value("/DV"); } std::string @@ -293,7 +290,7 @@ QPDFFormFieldObjectHelper::getDefaultAppearance() std::string FormField::getDefaultAppearance() { - auto value = getInheritableFieldValue("/DA"); + auto value = inheritable_value("/DA"); bool looked_in_acroform = false; if (!value.isString()) { value = from_AcroForm("/DA"); @@ -315,7 +312,7 @@ QPDFFormFieldObjectHelper::getQuadding() int FormField::getQuadding() { - QPDFObjectHandle fv = getInheritableFieldValue("/Q"); + auto fv = inheritable_value("/Q"); bool looked_in_acroform = false; if (!fv.isInteger()) { fv = from_AcroForm("/Q"); @@ -337,7 +334,7 @@ QPDFFormFieldObjectHelper::getFlags() int FormField::getFlags() { - QPDFObjectHandle f = getInheritableFieldValue("/Ff"); + auto f = inheritable_value("/Ff"); return f.isInteger() ? f.getIntValueAsInt() : 0; } @@ -426,7 +423,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) { diff --git a/libqpdf/qpdf/FormField.hh b/libqpdf/qpdf/FormField.hh index 13ea524..d85e692 100644 --- a/libqpdf/qpdf/FormField.hh +++ b/libqpdf/qpdf/FormField.hh @@ -60,8 +60,42 @@ namespace qpdf::impl /// @return The top-level field in the form field hierarchy. FormField root_field(bool* is_different = nullptr); - // Get a field value, possibly inheriting the value from an ancestor node. - QPDFObjectHandle getInheritableFieldValue(std::string const& name); + /// @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}; + } // Get an inherited field value as a string. If it is not a string, silently return the // empty string. -- libgit2 0.21.4