Commit 36e1c1425f124170d6af907312bdc9c744805654

Authored by m-holger
1 parent 847f0246

Integrate `QPDFPageDocumentHelper` with `QPDF` for improved page management. Add…

… shared helper retrieval, validation methods, and update usages across the codebase. Remove unused test coverage entries.
examples/examples.testcov
1   -pdf-bookmarks lines 0
2   -pdf-bookmarks numbers 0
3   -pdf-bookmarks none 0
4   -pdf-bookmarks has count 0
5   -pdf-bookmarks no count 0
6   -pdf-bookmarks open 0
7   -pdf-bookmarks closed 0
8   -pdf-bookmarks dest 0
9   -pdf-bookmarks targets 0
10 1 pdf-mod-info --dump 0
11 2 pdf-mod-info no in file 0
12 3 pdf-mod-info in-place 0
... ...
examples/pdf-bookmarks.cc
... ... @@ -47,7 +47,7 @@ print_lines(std::vector<int>& numbers)
47 47 void
48 48 generate_page_map(QPDF& qpdf)
49 49 {
50   - QPDFPageDocumentHelper dh(qpdf);
  50 + auto& dh = QPDFPageDocumentHelper::get(qpdf);
51 51 int n = 0;
52 52 for (auto const& page: dh.getAllPages()) {
53 53 page_map[page] = ++n;
... ... @@ -60,11 +60,9 @@ show_bookmark_details(QPDFOutlineObjectHelper outline, std::vector<int> numbers)
60 60 // No default so gcc will warn on missing tag
61 61 switch (style) {
62 62 case st_none:
63   - QTC::TC("examples", "pdf-bookmarks none");
64 63 break;
65 64  
66 65 case st_numbers:
67   - QTC::TC("examples", "pdf-bookmarks numbers");
68 66 for (auto const& number: numbers) {
69 67 std::cout << number << ".";
70 68 }
... ... @@ -72,7 +70,6 @@ show_bookmark_details(QPDFOutlineObjectHelper outline, std::vector&lt;int&gt; numbers)
72 70 break;
73 71  
74 72 case st_lines:
75   - QTC::TC("examples", "pdf-bookmarks lines");
76 73 print_lines(numbers);
77 74 std::cout << "|\n";
78 75 print_lines(numbers);
... ... @@ -83,27 +80,21 @@ show_bookmark_details(QPDFOutlineObjectHelper outline, std::vector&lt;int&gt; numbers)
83 80 if (show_open) {
84 81 int count = outline.getCount();
85 82 if (count) {
86   - QTC::TC("examples", "pdf-bookmarks has count");
87 83 if (count > 0) {
88 84 // hierarchy is open at this point
89   - QTC::TC("examples", "pdf-bookmarks open");
90 85 std::cout << "(v) ";
91 86 } else {
92   - QTC::TC("examples", "pdf-bookmarks closed");
93 87 std::cout << "(>) ";
94 88 }
95 89 } else {
96   - QTC::TC("examples", "pdf-bookmarks no count");
97 90 std::cout << "( ) ";
98 91 }
99 92 }
100 93  
101 94 if (show_targets) {
102   - QTC::TC("examples", "pdf-bookmarks targets");
103 95 std::string target = "unknown";
104 96 QPDFObjectHandle dest_page = outline.getDestPage();
105 97 if (!dest_page.isNull()) {
106   - QTC::TC("examples", "pdf-bookmarks dest");
107 98 if (page_map.contains(dest_page)) {
108 99 target = std::to_string(page_map[dest_page]);
109 100 }
... ...
examples/pdf-count-strings.cc
... ... @@ -76,7 +76,7 @@ main(int argc, char* argv[])
76 76 QPDF pdf;
77 77 pdf.processFile(infilename);
78 78 int pageno = 0;
79   - for (auto& page: QPDFPageDocumentHelper(pdf).getAllPages()) {
  79 + for (auto& page: QPDFPageDocumentHelper::get(pdf).getAllPages()) {
80 80 ++pageno;
81 81 // Pass the contents of a page through our string counter. If it's an even page, capture
82 82 // the output. This illustrates that you may capture any output generated by the filter,
... ...
examples/pdf-create.cc
... ... @@ -229,7 +229,7 @@ check(
229 229  
230 230 QPDF pdf;
231 231 pdf.processFile(filename);
232   - auto pages = QPDFPageDocumentHelper(pdf).getAllPages();
  232 + auto pages = QPDFPageDocumentHelper::get(pdf).getAllPages();
233 233 if (n_color_spaces * n_filters != pages.size()) {
234 234 throw std::logic_error("incorrect number of pages");
235 235 }
... ...
examples/pdf-overlay-page.cc
... ... @@ -29,14 +29,14 @@ stamp_page(char const* infile, char const* stampfile, char const* outfile)
29 29 stamppdf.processFile(stampfile);
30 30  
31 31 // Get first page from other file
32   - QPDFPageObjectHelper stamp_page_1 = QPDFPageDocumentHelper(stamppdf).getAllPages().at(0);
  32 + QPDFPageObjectHelper stamp_page_1 = QPDFPageDocumentHelper::get(stamppdf).getAllPages().at(0);
33 33 // Convert page to a form XObject
34 34 QPDFObjectHandle foreign_fo = stamp_page_1.getFormXObjectForPage();
35 35 // Copy form XObject to the input file
36 36 QPDFObjectHandle stamp_fo = inpdf.copyForeignObject(foreign_fo);
37 37  
38 38 // For each page...
39   - for (auto& ph: QPDFPageDocumentHelper(inpdf).getAllPages()) {
  39 + for (auto& ph: QPDFPageDocumentHelper::get(inpdf).getAllPages()) {
40 40 // Find a unique resource name for the new form XObject
41 41 QPDFObjectHandle resources = ph.getAttribute("/Resources", true);
42 42 int min_suffix = 1;
... ...
include/qpdf/QPDF.hh
... ... @@ -65,6 +65,7 @@ class QPDFParser;
65 65 class QPDFAcroFormDocumentHelper;
66 66 class QPDFEmbeddedFileDocumentHelper;
67 67 class QPDFOutlineDocumentHelper;
  68 +class QPDFPageDocumentHelper;
68 69 class QPDFPageLabelDocumentHelper;
69 70  
70 71 class QPDF
... ... @@ -804,6 +805,7 @@ class QPDF
804 805 inline QPDFAcroFormDocumentHelper& acroform();
805 806 inline QPDFEmbeddedFileDocumentHelper& embedded_files();
806 807 inline QPDFOutlineDocumentHelper& outlines();
  808 + inline QPDFPageDocumentHelper& pages();
807 809 inline QPDFPageLabelDocumentHelper& page_labels();
808 810  
809 811 // For testing only -- do not add to DLL
... ...
include/qpdf/QPDFPageDocumentHelper.hh
... ... @@ -35,6 +35,21 @@ class QPDFAcroFormDocumentHelper;
35 35 class QPDFPageDocumentHelper: public QPDFDocumentHelper
36 36 {
37 37 public:
  38 + // Get a shared document helper for a given QPDF object.
  39 + //
  40 + // Retrieving a document helper for a QPDF object rather than creating a new one avoids repeated
  41 + // validation of the Acroform structure, which can be expensive.
  42 + QPDF_DLL
  43 + static QPDFPageDocumentHelper& get(QPDF& qpdf);
  44 +
  45 + // Re-validate the Pages structure. This is useful if you have modified the Pages structure in
  46 + // a way that would invalidate the cache.
  47 + //
  48 + // If repair is true, the document will be repaired if possible if the validation encounters
  49 + // errors.
  50 + QPDF_DLL
  51 + void validate(bool repair = true);
  52 +
38 53 QPDF_DLL
39 54 QPDFPageDocumentHelper(QPDF&);
40 55  
... ... @@ -112,17 +127,7 @@ class QPDFPageDocumentHelper: public QPDFDocumentHelper
112 127 int required_flags,
113 128 int forbidden_flags);
114 129  
115   - class Members
116   - {
117   - friend class QPDFPageDocumentHelper;
118   -
119   - public:
120   - ~Members() = default;
121   -
122   - private:
123   - Members() = default;
124   - Members(Members const&) = delete;
125   - };
  130 + class Members;
126 131  
127 132 std::shared_ptr<Members> m;
128 133 };
... ...
libqpdf/QPDFJob.cc
... ... @@ -17,7 +17,6 @@
17 17 #include <qpdf/QPDFExc.hh>
18 18 #include <qpdf/QPDFLogger.hh>
19 19 #include <qpdf/QPDFObjectHandle_private.hh>
20   -#include <qpdf/QPDFPageDocumentHelper.hh>
21 20 #include <qpdf/QPDFPageObjectHelper.hh>
22 21 #include <qpdf/QPDFSystemError.hh>
23 22 #include <qpdf/QPDFUsage.hh>
... ... @@ -790,7 +789,7 @@ QPDFJob::doCheck(QPDF&amp; pdf)
790 789  
791 790 // Parse all content streams
792 791 int pageno = 0;
793   - for (auto& page: QPDFPageDocumentHelper(pdf).getAllPages()) {
  792 + for (auto& page: pdf.pages().getAllPages()) {
794 793 ++pageno;
795 794 try {
796 795 page.parseContents(nullptr);
... ... @@ -858,7 +857,7 @@ QPDFJob::doShowPages(QPDF&amp; pdf)
858 857 {
859 858 int pageno = 0;
860 859 auto& cout = *m->log->getInfo();
861   - for (auto& ph: QPDFPageDocumentHelper(pdf).getAllPages()) {
  860 + for (auto& ph: pdf.pages().getAllPages()) {
862 861 QPDFObjectHandle page = ph.getObjectHandle();
863 862 ++pageno;
864 863  
... ... @@ -1052,7 +1051,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1052 1051 auto& pldh = pdf.page_labels();
1053 1052 auto& odh = pdf.outlines();
1054 1053 int pageno = -1;
1055   - for (auto& ph: QPDFPageDocumentHelper(pdf).getAllPages()) {
  1054 + for (auto& ph: pdf.pages().getAllPages()) {
1056 1055 ++pageno;
1057 1056 JSON j_page = JSON::makeDictionary();
1058 1057 QPDFObjectHandle page = ph.getObjectHandle();
... ... @@ -1113,7 +1112,7 @@ QPDFJob::doJSONPageLabels(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1113 1112 {
1114 1113 JSON j_labels = JSON::makeArray();
1115 1114 auto& pldh = pdf.page_labels();
1116   - long long npages = QIntC::to_longlong(QPDFPageDocumentHelper(pdf).getAllPages().size());
  1115 + long long npages = QIntC::to_longlong(pdf.pages().getAllPages().size());
1117 1116 if (pldh.hasPageLabels()) {
1118 1117 std::vector<QPDFObjectHandle> labels;
1119 1118 pldh.getLabelsForPageRange(0, npages - 1, 0, labels);
... ... @@ -1161,7 +1160,7 @@ QPDFJob::doJSONOutlines(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1161 1160 {
1162 1161 std::map<QPDFObjGen, int> page_numbers;
1163 1162 int n = 0;
1164   - for (auto const& ph: QPDFPageDocumentHelper(pdf).getAllPages()) {
  1163 + for (auto const& ph: pdf.pages().getAllPages()) {
1165 1164 QPDFObjectHandle oh = ph.getObjectHandle();
1166 1165 page_numbers[oh.getObjGen()] = ++n;
1167 1166 }
... ... @@ -1180,7 +1179,7 @@ QPDFJob::doJSONAcroform(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1180 1179 j_acroform.addDictionaryMember("needappearances", JSON::makeBool(afdh.getNeedAppearances()));
1181 1180 JSON j_fields = j_acroform.addDictionaryMember("fields", JSON::makeArray());
1182 1181 int pagepos1 = 0;
1183   - for (auto const& page: QPDFPageDocumentHelper(pdf).getAllPages()) {
  1182 + for (auto const& page: pdf.pages().getAllPages()) {
1184 1183 ++pagepos1;
1185 1184 for (auto& aoh: afdh.getWidgetAnnotationsForPage(page)) {
1186 1185 QPDFFormFieldObjectHelper ffh = afdh.getFieldForAnnotation(aoh);
... ... @@ -1857,7 +1856,7 @@ QPDFJob::processInputSource(
1857 1856 void
1858 1857 QPDFJob::validateUnderOverlay(QPDF& pdf, UnderOverlay* uo)
1859 1858 {
1860   - QPDFPageDocumentHelper main_pdh(pdf);
  1859 + auto& main_pdh = pdf.pages();
1861 1860 int main_npages = QIntC::to_int(main_pdh.getAllPages().size());
1862 1861 processFile(uo->pdf, uo->filename.data(), uo->password.data(), true, false);
1863 1862 QPDFPageDocumentHelper uo_pdh(*(uo->pdf));
... ...
libqpdf/QPDFPageDocumentHelper.cc
... ... @@ -6,11 +6,26 @@
6 6 #include <qpdf/QTC.hh>
7 7 #include <qpdf/QUtil.hh>
8 8  
  9 +class QPDFPageDocumentHelper::Members
  10 +{
  11 +};
  12 +
9 13 QPDFPageDocumentHelper::QPDFPageDocumentHelper(QPDF& qpdf) :
10 14 QPDFDocumentHelper(qpdf)
11 15 {
12 16 }
13 17  
  18 +QPDFPageDocumentHelper&
  19 +QPDFPageDocumentHelper::get(QPDF& qpdf)
  20 +{
  21 + return qpdf.pages();
  22 +}
  23 +
  24 +void
  25 +QPDFPageDocumentHelper::validate(bool repair)
  26 +{
  27 +}
  28 +
14 29 std::vector<QPDFPageObjectHelper>
15 30 QPDFPageDocumentHelper::getAllPages()
16 31 {
... ...
libqpdf/qpdf/QPDF_private.hh
... ... @@ -7,6 +7,7 @@
7 7 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
8 8 #include <qpdf/QPDFObject_private.hh>
9 9 #include <qpdf/QPDFOutlineDocumentHelper.hh>
  10 +#include <qpdf/QPDFPageDocumentHelper.hh>
10 11 #include <qpdf/QPDFPageLabelDocumentHelper.hh>
11 12 #include <qpdf/QPDFTokenizer_private.hh>
12 13  
... ... @@ -558,6 +559,7 @@ class QPDF::Members
558 559 std::unique_ptr<QPDFAcroFormDocumentHelper> acroform;
559 560 std::unique_ptr<QPDFEmbeddedFileDocumentHelper> embedded_files;
560 561 std::unique_ptr<QPDFOutlineDocumentHelper> outlines;
  562 + std::unique_ptr<QPDFPageDocumentHelper> pages;
561 563 std::unique_ptr<QPDFPageLabelDocumentHelper> page_labels;
562 564 };
563 565  
... ... @@ -608,6 +610,15 @@ QPDF::outlines()
608 610 return *m->outlines;
609 611 }
610 612  
  613 +inline QPDFPageDocumentHelper&
  614 +QPDF::pages()
  615 +{
  616 + if (!m->pages) {
  617 + m->pages = std::make_unique<QPDFPageDocumentHelper>(*this);
  618 + }
  619 + return *m->pages;
  620 +}
  621 +
611 622 inline QPDFPageLabelDocumentHelper&
612 623 QPDF::page_labels()
613 624 {
... ...
manual/release-notes.rst
... ... @@ -26,20 +26,27 @@ more detail.
26 26 - Library Enhancements
27 27  
28 28 - Add ``QPDFNameTreeObjectHelper`` and ``QPDFNumberTreeObjectHelper``
29   - constructor overloads that allow a function to ne passed to
  29 + constructor overloads that allow a function to be passed to
30 30 validate the values in the tree.
31 31  
32 32 - Add new ``QPDFNameTreeObjectHelper`` and ``QPDFNumberTreeObjectHelper``
33 33 ``validate`` method to validate and optionally repair the name/number
34 34 tree.
35 35  
  36 + - Add new ``get`` and ``validate`` methods to all DocumentHelper classes.
  37 + The ``get`` method retrieves a shared DocumentHelper, avoiding the the
  38 + overhead of repeatedly validating the underlying document structure
  39 + and/or building internal caches. If the underlying document structure
  40 + is directly modified (without the use of DocumentHelpers), the
  41 + ``validate`` methods revalidates the structure and resynchronizes any
  42 + internal caches.
  43 +
36 44 - CLI Enhancements
37 45  
38 46 - Disallow option :qpdf:ref:`--deterministic-id` to be used together
39 47 with the incompatible options :qpdf:ref:`--encrypt` or
40 48 :qpdf:ref:`--copy-encryption`.
41 49  
42   -
43 50 - Other enhancements
44 51  
45 52 - ``QPDFWriter`` will no longer add filters when writing empty streams.
... ...