diff --git a/libqpdf/QPDFFormFieldObjectHelper.cc b/libqpdf/QPDFFormFieldObjectHelper.cc index 1ca0e63..96ad0b7 100644 --- a/libqpdf/QPDFFormFieldObjectHelper.cc +++ b/libqpdf/QPDFFormFieldObjectHelper.cc @@ -415,7 +415,13 @@ QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, QPDFObjectH void FormNode::setFieldAttribute(std::string const& key, QPDFObjectHandle value) { - oh().replaceKey(key, value); + replace(key, value); +} + +void +FormNode::setFieldAttribute(std::string const& key, Name const& value) +{ + replace(key, value); } void @@ -427,7 +433,7 @@ QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, std::string void FormNode::setFieldAttribute(std::string const& key, std::string const& utf8_value) { - oh().replaceKey(key, QPDFObjectHandle::newUnicodeString(utf8_value)); + replace(key, String::utf16(utf8_value)); } void @@ -517,13 +523,13 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) } setFieldAttribute("/V", name); for (FormNode kid: kids) { - QPDFObjectHandle AP = kid["/AP"]; + auto ap = kid.AP(); QPDFObjectHandle annot; - if (AP.null()) { + if (!ap) { // The widget may be below. If there is more than one, just find the first one. - for (auto const& grandkid: kid.Kids()) { - AP = grandkid.getKey("/AP"); - if (!AP.null()) { + for (FormNode grandkid: kid.Kids()) { + ap = grandkid.AP(); + if (ap) { annot = grandkid; break; } @@ -535,11 +541,10 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) warn("unable to set the value of this radio button"); continue; } - if (AP.isDictionary() && AP.getKey("/N").isDictionary() && - AP.getKey("/N").hasKey(name.getName())) { - annot.replaceKey("/AS", name); + if (ap["/N"].contains(name.getName())) { + annot.replace("/AS", name); } else { - annot.replaceKey("/AS", QPDFObjectHandle::newName("/Off")); + annot.replace("/AS", Name("/Off")); } } } @@ -547,27 +552,26 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) void FormNode::setCheckBoxValue(bool value) { - QPDFObjectHandle AP = oh().getKey("/AP"); + auto ap = AP(); QPDFObjectHandle annot; - if (AP.null()) { - // The widget may be below. If there is more than one, just - // find the first one. - for (auto const& kid: Kids()) { - AP = kid.getKey("/AP"); - if (!AP.null()) { + if (ap) { + annot = oh(); + } else { + // The widget may be below. If there is more than one, just find the first one. + for (FormNode kid: Kids()) { + ap = kid.AP(); + if (ap) { annot = kid; break; } } - } else { - annot = oh(); } std::string on_value; if (value) { // Set the "on" value to the first value in the appearance stream's normal state dictionary // that isn't /Off. If not found, fall back to /Yes. - if (AP.isDictionary()) { - for (auto const& item: AP.getKey("/N").as_dictionary()) { + if (ap) { + for (auto const& item: Dictionary(ap["/N"])) { if (item.first != "/Off") { on_value = item.first; break; @@ -580,15 +584,13 @@ FormNode::setCheckBoxValue(bool value) } // Set /AS to the on value or /Off in addition to setting /V. - QPDFObjectHandle name = QPDFObjectHandle::newName(value ? on_value : "/Off"); + auto name = Name(value ? on_value : "/Off"); setFieldAttribute("/V", name); if (!annot) { - QTC::TC("qpdf", "QPDFObjectHandle broken checkbox"); warn("unable to set the value of this checkbox"); return; } - QTC::TC("qpdf", "QPDFFormFieldObjectHelper set checkbox AS"); - annot.replaceKey("/AS", name); + annot.replace("/AS", name); } void @@ -899,12 +901,11 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh) {"/Subtype", Name("/Form")}}); AS = QPDFObjectHandle::newStream(oh().getOwningQPDF(), "/Tx BMC\nEMC\n"); AS.replaceDict(dict); - Dictionary AP = aoh.getAppearanceDictionary(); - if (!AP) { - aoh.getObjectHandle().replaceKey("/AP", Dictionary::empty()); - AP = aoh.getAppearanceDictionary(); + if (auto ap = AP()) { + ap.replace("/N", AS); + } else { + aoh.replace("/AP", Dictionary({{"/N", AS}})); } - AP.replace("/N", AS); } if (!AS.isStream()) { aoh.warn("unable to get normal appearance stream for update"); diff --git a/libqpdf/qpdf/AcroForm.hh b/libqpdf/qpdf/AcroForm.hh index 468965f..48559a6 100644 --- a/libqpdf/qpdf/AcroForm.hh +++ b/libqpdf/qpdf/AcroForm.hh @@ -356,18 +356,19 @@ namespace qpdf::impl { } - /// @brief Retrieves the /Kids array. + // Widget and annotation attributes + + /// @brief Retrieves the /AP attribute of the form node as a Dictionary. /// - /// This method returns the /Kids entry, which is an array of the immediate descendants of - /// this node. It is only present if the node is a form field rather than a pure widget - /// annotation. + /// The /AP attribute, short for "appearance dictionary," defines how an annotation is + /// presented visually on a page. See section 12.5.5 of the PDF specification for more + /// details. /// - /// @return An `Array` object containing the /Kids elements. If the /Kids entry - /// does not exist or is not a valid array, the returned `Array` will be invalid. - Array - Kids() const + /// @return A Dictionary containing the /AP attribute of the form node. + Dictionary + AP() const { - return {get("/Kids")}; + return {get("/AP")}; } /// Retrieves the /Parent form field of the current field. @@ -384,6 +385,20 @@ namespace qpdf::impl return {get("/Parent")}; } + /// @brief Retrieves the /Kids array. + /// + /// This method returns the /Kids entry, which is an array of the immediate descendants of + /// this node. It is only present if the node is a form field rather than a pure widget + /// annotation. + /// + /// @return An `Array` object containing the /Kids elements. If the /Kids entry + /// does not exist or is not a valid array, the returned `Array` will be invalid. + Array + Kids() const + { + return {get("/Kids")}; + } + /// @brief Returns the top-level field associated with the current field. /// /// The function traverses the hierarchy of parent fields to identify the highest-level @@ -670,6 +685,7 @@ namespace qpdf::impl // want to set the name of a field, use QPDFAcroFormDocumentHelper::setFormFieldName // instead. void setFieldAttribute(std::string const& key, QPDFObjectHandle value); + void setFieldAttribute(std::string const& key, Name const& value); // Set an attribute to the given value as a Unicode string (UTF-16 BE encoded). The input // string should be UTF-8 encoded. If you have a QPDFAcroFormDocumentHelper and you want to diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index d39cb08..02b41e3 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -193,8 +193,6 @@ QPDFPageDocumentHelper non-widget annotation 0 QPDFObjectHandle replace with copy 0 QPDFAnnotationObjectHelper forbidden flags 0 QPDFAnnotationObjectHelper missing required flags 0 -QPDFFormFieldObjectHelper set checkbox AS 0 -QPDFObjectHandle broken checkbox 0 QPDFFormFieldObjectHelper list not found 0 QPDFFormFieldObjectHelper list found 0 QPDFFormFieldObjectHelper list first too low 0