From d35c34d89c06ff3d00a3678660bf4586f6ff1b4a Mon Sep 17 00:00:00 2001 From: m-holger Date: Mon, 1 Dec 2025 20:01:21 +0000 Subject: [PATCH] Refactor `AcroForm` to replace pointer-based method arguments with references for improved clarity and safety. Update related methods and references accordingly. --- include/qpdf/QPDFAcroFormDocumentHelper.hh | 1 + libqpdf/QPDF.cc | 16 ++++++++++++++++ libqpdf/QPDFAcroFormDocumentHelper.cc | 11 ++--------- libqpdf/QPDFJob.cc | 11 ++++++----- libqpdf/QPDF_pages.cc | 4 ++-- libqpdf/qpdf/AcroForm.hh | 21 +++++++++++++++------ libqpdf/qpdf/QPDF_private.hh | 34 ++++++++++++++++++++++++++++++---- 7 files changed, 72 insertions(+), 26 deletions(-) diff --git a/include/qpdf/QPDFAcroFormDocumentHelper.hh b/include/qpdf/QPDFAcroFormDocumentHelper.hh index 9ef2a7a..ef5c6b1 100644 --- a/include/qpdf/QPDFAcroFormDocumentHelper.hh +++ b/include/qpdf/QPDFAcroFormDocumentHelper.hh @@ -225,6 +225,7 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper std::set* new_fields = nullptr); private: + friend class QPDF::Doc; class Members; std::shared_ptr m; diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index d86f604..1b3e034 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -147,6 +148,21 @@ QPDF::QPDF() : m->unique_id = unique_id.fetch_add(1ULL); } +/// @brief Initializes the AcroForm functionality for the document. +/// @par +/// This method creates a unique instance of QPDFAcroFormDocumentHelper and associates it +/// with the document. It also updates the `acroform_` pointer to reference the AcroForm +/// instance managed by the helper. +/// +/// The method has been separated out from `acroform` to avoid it being inlined +/// unnecessarily. +void +QPDF::Doc::init_acroform() +{ + acroform_dh_ = std::make_unique(qpdf); + acroform_ = acroform_dh_->m.get(); +} + // Provide access to disconnect(). Disconnect will in due course be merged into the current ObjCache // (future Objects::Entry) to centralize all QPDF access to QPDFObject. class Disconnect: BaseHandle diff --git a/libqpdf/QPDFAcroFormDocumentHelper.cc b/libqpdf/QPDFAcroFormDocumentHelper.cc index e1911c2..c463b91 100644 --- a/libqpdf/QPDFAcroFormDocumentHelper.cc +++ b/libqpdf/QPDFAcroFormDocumentHelper.cc @@ -18,14 +18,7 @@ using namespace qpdf; using namespace qpdf::impl; using namespace std::literals; -class QPDFAcroFormDocumentHelper::Members: public AcroForm -{ - public: - Members(QPDF& qpdf) : - AcroForm(qpdf.doc()) - { - } -}; +using AcroForm = impl::AcroForm; QPDFAcroFormDocumentHelper::QPDFAcroFormDocumentHelper(QPDF& qpdf) : QPDFDocumentHelper(qpdf), @@ -36,7 +29,7 @@ QPDFAcroFormDocumentHelper::QPDFAcroFormDocumentHelper(QPDF& qpdf) : QPDFAcroFormDocumentHelper& QPDFAcroFormDocumentHelper::get(QPDF& qpdf) { - return qpdf.doc().acroform(); + return qpdf.doc().acroform_dh(); } void diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index 8820423..e1141a3 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -1866,7 +1867,7 @@ QPDFJob::doUnderOverlayForPage( if (!(uo.pdf && pagenos[pageno.idx].contains(uo_idx))) { return ""; } - auto& dest_afdh = dest_page.qpdf()->doc().acroform(); + auto& dest_afdh = dest_page.qpdf()->doc().acroform_dh(); auto const& pages = uo.pdf->doc().pages().all(); std::string content; @@ -1887,7 +1888,8 @@ QPDFJob::doUnderOverlayForPage( QPDFMatrix cm; std::string new_content = dest_page.placeFormXObject( fo[from_no.no][uo_idx], name, dest_page.getTrimBox().getArrayAsRectangle(), cm); - dest_page.copyAnnotations(from_page, cm, &dest_afdh, &from_page.qpdf()->doc().acroform()); + dest_page.copyAnnotations( + from_page, cm, &dest_afdh, &from_page.qpdf()->doc().acroform_dh()); if (!new_content.empty()) { resources.mergeResources(Dictionary({{"/XObject", Dictionary::empty()}})); auto xobject = resources.getKey("/XObject"); @@ -2107,7 +2109,7 @@ QPDFJob::handleTransformations(QPDF& pdf) QPDFAcroFormDocumentHelper* afdh_ptr = nullptr; auto afdh = [&]() -> QPDFAcroFormDocumentHelper& { if (!afdh_ptr) { - afdh_ptr = &pdf.doc().acroform(); + afdh_ptr = &pdf.doc().acroform_dh(); } return *afdh_ptr; }; @@ -2978,8 +2980,7 @@ QPDFJob::doSplitPages(QPDF& pdf) QPDF outpdf; outpdf.doc().config(m->d_cfg); outpdf.emptyPDF(); - QPDFAcroFormDocumentHelper* out_afdh = - afdh.hasAcroForm() ? &outpdf.doc().acroform() : nullptr; + impl::AcroForm* out_afdh = afdh.hasAcroForm() ? &outpdf.doc().acroform() : nullptr; for (size_t pageno = first; pageno <= last; ++pageno) { QPDFObjectHandle page = pages.at(pageno - 1); outpdf.addPage(page, false); diff --git a/libqpdf/QPDF_pages.cc b/libqpdf/QPDF_pages.cc index 021c42b..fdf4b68 100644 --- a/libqpdf/QPDF_pages.cc +++ b/libqpdf/QPDF_pages.cc @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include #include @@ -660,7 +660,7 @@ void Pages::flatten_annotations_for_page( QPDFPageObjectHelper& page, QPDFObjectHandle& resources, - QPDFAcroFormDocumentHelper& afdh, + impl::AcroForm& afdh, int required_flags, int forbidden_flags) { diff --git a/libqpdf/qpdf/AcroForm.hh b/libqpdf/qpdf/AcroForm.hh index 8f65d8c..955e125 100644 --- a/libqpdf/qpdf/AcroForm.hh +++ b/libqpdf/qpdf/AcroForm.hh @@ -546,7 +546,7 @@ namespace qpdf::impl /// @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. + /// object handle. QPDFObjectHandle const& V(bool inherit = true) const { @@ -557,7 +557,7 @@ namespace qpdf::impl } /// @brief Retrieves the field value `/V` attribute of the form field, considering - /// inheritance, if the value is a String. + /// 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 @@ -567,7 +567,7 @@ namespace qpdf::impl /// 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, + /// @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. /// @@ -592,14 +592,14 @@ namespace qpdf::impl } /// @brief Retrieves the default value `/DV` attribute of the form field, considering - /// inheritance, if the default value is a String. + /// 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. + /// @return A string containing the actual or inherited `/DV` 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 @@ -711,4 +711,13 @@ namespace qpdf::impl }; // class FormNode } // namespace qpdf::impl +class QPDFAcroFormDocumentHelper::Members: public qpdf::impl::AcroForm +{ + public: + Members(QPDF& qpdf) : + AcroForm(qpdf.doc()) + { + } +}; + #endif // ACRO_FORM_HH diff --git a/libqpdf/qpdf/QPDF_private.hh b/libqpdf/qpdf/QPDF_private.hh index f522be7..2eb58da 100644 --- a/libqpdf/qpdf/QPDF_private.hh +++ b/libqpdf/qpdf/QPDF_private.hh @@ -28,8 +28,9 @@ namespace qpdf namespace impl { + class AcroForm; using Doc = QPDF::Doc; - } + } // namespace impl class Doc: public QPDF { @@ -374,11 +375,33 @@ class QPDF::Doc bool reconstructed_xref() const; QPDFAcroFormDocumentHelper& + acroform_dh() + { + if (!acroform_) { + no_inspection(); + init_acroform(); + } + return *acroform_dh_; + } + + /// @brief Retrieves the shared impl::AcroForm instance associated with the document. + /// + /// @note The AcroForm class caches the form field structure for efficiency. If any part + /// of the form field structure is modified directly the `validate` method MUST be + /// called before calling any other AcroForm methods in order to refresh the cache. + /// + /// If the AcroForm instance has not already been initialized, the `init_acroform()` + /// function is called to initialize it. + /// + /// @return A reference to the shared AcroForm object of the document. + /// + /// @since 12.3 + impl::AcroForm& acroform() { if (!acroform_) { no_inspection(); - acroform_ = std::make_unique(qpdf); + init_acroform(); } return *acroform_; } @@ -438,8 +461,11 @@ class QPDF::Doc qpdf::Doc::Config cf; private: + void init_acroform(); + // Document Helpers; - std::unique_ptr acroform_; + std::unique_ptr acroform_dh_; + impl::AcroForm* acroform_{nullptr}; std::unique_ptr embedded_files_; std::unique_ptr outlines_; std::unique_ptr page_dh_; @@ -1173,7 +1199,7 @@ class QPDF::Doc::Pages: Common void flatten_annotations_for_page( QPDFPageObjectHelper& page, QPDFObjectHandle& resources, - QPDFAcroFormDocumentHelper& afdh, + impl::AcroForm& afdh, int required_flags, int forbidden_flags); -- libgit2 0.21.4