Commit 8d7621457328f722be19f0884cfd30d90bd733ac

Authored by m-holger
1 parent 551a9534

Add `QPDFPageLabelDocumentHelper` integration

Integrate `QPDFPageLabelDocumentHelper` with `QPDF` for improved page label handling. Add methods for retrieving shared helper instances, validating page label structures, and streamline usage throughout the codebase.
include/qpdf/QPDF.hh
... ... @@ -63,6 +63,7 @@ class BufferInputSource;
63 63 class QPDFLogger;
64 64 class QPDFParser;
65 65 class QPDFAcroFormDocumentHelper;
  66 +class QPDFPageLabelDocumentHelper;
66 67  
67 68 class QPDF
68 69 {
... ... @@ -799,6 +800,7 @@ class QPDF
799 800  
800 801 inline bool reconstructed_xref() const;
801 802 inline QPDFAcroFormDocumentHelper& acroform();
  803 + inline QPDFPageLabelDocumentHelper& page_labels();
802 804  
803 805 // For testing only -- do not add to DLL
804 806 static bool test_json_validators();
... ...
include/qpdf/QPDFPageLabelDocumentHelper.hh
... ... @@ -22,11 +22,10 @@
22 22  
23 23 #include <qpdf/QPDFDocumentHelper.hh>
24 24  
  25 +#include <qpdf/DLL.h>
25 26 #include <qpdf/QPDF.hh>
26   -#include <qpdf/QPDFNumberTreeObjectHelper.hh>
27   -#include <vector>
28 27  
29   -#include <qpdf/DLL.h>
  28 +#include <vector>
30 29  
31 30 // Page labels are discussed in the PDF spec (ISO-32000) in section 12.4.2.
32 31 //
... ... @@ -42,6 +41,21 @@
42 41 class QPDFPageLabelDocumentHelper: public QPDFDocumentHelper
43 42 {
44 43 public:
  44 + // Get a shared document helper for a given QPDF object.
  45 + //
  46 + // Retrieving a document helper for a QPDF object rather than creating a new one avoids repeated
  47 + // validation of the PageLabels structure, which can be expensive.
  48 + QPDF_DLL
  49 + static QPDFPageLabelDocumentHelper& get(QPDF& qpdf);
  50 +
  51 + // Re-validate the PageLabels structure. This is useful if you have modified the structure of
  52 + // the PageLabels dictionary in a way that could have invalidated the structure.
  53 + //
  54 + // If repair is true, the document will be repaired if possible if the validation encounters
  55 + // errors.
  56 + QPDF_DLL
  57 + void validate(bool repair = true);
  58 +
45 59 QPDF_DLL
46 60 QPDFPageLabelDocumentHelper(QPDF&);
47 61  
... ...
libqpdf/QPDFJob.cc
... ... @@ -1053,7 +1053,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1053 1053 JSON::writeDictionaryKey(p, first, "pages", 1);
1054 1054 bool first_page = true;
1055 1055 JSON::writeArrayOpen(p, first_page, 2);
1056   - QPDFPageLabelDocumentHelper pldh(pdf);
  1056 + auto& pldh = pdf.page_labels();
1057 1057 QPDFOutlineDocumentHelper odh(pdf);
1058 1058 int pageno = -1;
1059 1059 for (auto& ph: QPDFPageDocumentHelper(pdf).getAllPages()) {
... ... @@ -1116,7 +1116,7 @@ void
1116 1116 QPDFJob::doJSONPageLabels(Pipeline* p, bool& first, QPDF& pdf)
1117 1117 {
1118 1118 JSON j_labels = JSON::makeArray();
1119   - QPDFPageLabelDocumentHelper pldh(pdf);
  1119 + auto& pldh = pdf.page_labels();
1120 1120 long long npages = QIntC::to_longlong(QPDFPageDocumentHelper(pdf).getAllPages().size());
1121 1121 if (pldh.hasPageLabels()) {
1122 1122 std::vector<QPDFObjectHandle> labels;
... ... @@ -2548,7 +2548,7 @@ QPDFJob::handlePageSpecs(QPDF&amp; pdf, std::vector&lt;std::unique_ptr&lt;QPDF&gt;&gt;&amp; page_hea
2548 2548 cis = page_spec_cfis[page_data.filename];
2549 2549 cis->stayOpen(true);
2550 2550 }
2551   - QPDFPageLabelDocumentHelper pldh(*page_data.qpdf);
  2551 + auto& pldh = page_data.qpdf->page_labels();
2552 2552 auto& other_afdh = page_data.qpdf->acroform();
2553 2553 if (pldh.hasPageLabels()) {
2554 2554 any_page_labels = true;
... ... @@ -2992,7 +2992,7 @@ QPDFJob::doSplitPages(QPDF&amp; pdf)
2992 2992 QPDFPageDocumentHelper dh(pdf);
2993 2993 dh.removeUnreferencedResources();
2994 2994 }
2995   - QPDFPageLabelDocumentHelper pldh(pdf);
  2995 + auto& pldh = pdf.page_labels();
2996 2996 auto& afdh = pdf.acroform();
2997 2997 std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
2998 2998 size_t pageno_len = std::to_string(pages.size()).length();
... ...
libqpdf/QPDFPageLabelDocumentHelper.cc
1 1 #include <qpdf/QPDFPageLabelDocumentHelper.hh>
2 2  
  3 +#include <qpdf/QPDFNumberTreeObjectHelper.hh>
3 4 #include <qpdf/QPDFObjectHandle_private.hh>
  5 +#include <qpdf/QPDF_private.hh>
  6 +
  7 +using namespace qpdf;
4 8  
5 9 class QPDFPageLabelDocumentHelper::Members
6 10 {
... ... @@ -16,14 +20,23 @@ QPDFPageLabelDocumentHelper::QPDFPageLabelDocumentHelper(QPDF&amp; qpdf) :
16 20 QPDFDocumentHelper(qpdf),
17 21 m(std::make_shared<Members>())
18 22 {
19   - QPDFObjectHandle root = qpdf.getRoot();
20   - if (root.hasKey("/PageLabels")) {
  23 + validate();
  24 +}
  25 +
  26 +QPDFPageLabelDocumentHelper&
  27 +QPDFPageLabelDocumentHelper::get(QPDF& qpdf)
  28 +{
  29 + return qpdf.page_labels();
  30 +}
  31 +
  32 +void
  33 +QPDFPageLabelDocumentHelper::validate(bool repair)
  34 +{
  35 + m->labels = nullptr;
  36 + if (Dictionary labels = qpdf.getRoot()["/PageLabels"]) {
21 37 m->labels = std::make_unique<QPDFNumberTreeObjectHelper>(
22   - root.getKey("/PageLabels"),
23   - this->qpdf,
24   - [](QPDFObjectHandle const& o) -> bool { return o.isDictionary(); },
25   - true);
26   - m->labels->validate();
  38 + labels, qpdf, [](QPDFObjectHandle const& o) -> bool { return o.isDictionary(); }, true);
  39 + m->labels->validate(repair);
27 40 }
28 41 }
29 42  
... ...
libqpdf/qpdf/QPDF_private.hh
... ... @@ -5,6 +5,7 @@
5 5  
6 6 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
7 7 #include <qpdf/QPDFObject_private.hh>
  8 +#include <qpdf/QPDFPageLabelDocumentHelper.hh>
8 9 #include <qpdf/QPDFTokenizer_private.hh>
9 10  
10 11 using namespace qpdf;
... ... @@ -553,6 +554,7 @@ class QPDF::Members
553 554  
554 555 // Document Helpers;
555 556 std::unique_ptr<QPDFAcroFormDocumentHelper> acroform;
  557 + std::unique_ptr<QPDFPageLabelDocumentHelper> page_labels;
556 558 };
557 559  
558 560 // JobSetter class is restricted to QPDFJob.
... ... @@ -584,4 +586,13 @@ QPDF::acroform()
584 586 return *m->acroform;
585 587 }
586 588  
  589 +inline QPDFPageLabelDocumentHelper&
  590 +QPDF::page_labels()
  591 +{
  592 + if (!m->page_labels) {
  593 + m->page_labels = std::make_unique<QPDFPageLabelDocumentHelper>(*this);
  594 + }
  595 + return *m->page_labels;
  596 +}
  597 +
587 598 #endif // QPDF_PRIVATE_HH
... ...
qpdf/test_driver.cc
... ... @@ -1802,7 +1802,7 @@ static void
1802 1802 test_47(QPDF& pdf, char const* arg2)
1803 1803 {
1804 1804 // Test page labels.
1805   - QPDFPageLabelDocumentHelper pldh(pdf);
  1805 + auto& pldh = QPDFPageLabelDocumentHelper::get(pdf);
1806 1806 long long npages = pdf.getRoot().getKey("/Pages").getKey("/Count").getIntValue();
1807 1807 std::vector<QPDFObjectHandle> labels;
1808 1808 pldh.getLabelsForPageRange(0, npages - 1, 1, labels);
... ...