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,6 +63,7 @@ class BufferInputSource;
63 class QPDFLogger; 63 class QPDFLogger;
64 class QPDFParser; 64 class QPDFParser;
65 class QPDFAcroFormDocumentHelper; 65 class QPDFAcroFormDocumentHelper;
  66 +class QPDFPageLabelDocumentHelper;
66 67
67 class QPDF 68 class QPDF
68 { 69 {
@@ -799,6 +800,7 @@ class QPDF @@ -799,6 +800,7 @@ class QPDF
799 800
800 inline bool reconstructed_xref() const; 801 inline bool reconstructed_xref() const;
801 inline QPDFAcroFormDocumentHelper& acroform(); 802 inline QPDFAcroFormDocumentHelper& acroform();
  803 + inline QPDFPageLabelDocumentHelper& page_labels();
802 804
803 // For testing only -- do not add to DLL 805 // For testing only -- do not add to DLL
804 static bool test_json_validators(); 806 static bool test_json_validators();
include/qpdf/QPDFPageLabelDocumentHelper.hh
@@ -22,11 +22,10 @@ @@ -22,11 +22,10 @@
22 22
23 #include <qpdf/QPDFDocumentHelper.hh> 23 #include <qpdf/QPDFDocumentHelper.hh>
24 24
  25 +#include <qpdf/DLL.h>
25 #include <qpdf/QPDF.hh> 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 // Page labels are discussed in the PDF spec (ISO-32000) in section 12.4.2. 30 // Page labels are discussed in the PDF spec (ISO-32000) in section 12.4.2.
32 // 31 //
@@ -42,6 +41,21 @@ @@ -42,6 +41,21 @@
42 class QPDFPageLabelDocumentHelper: public QPDFDocumentHelper 41 class QPDFPageLabelDocumentHelper: public QPDFDocumentHelper
43 { 42 {
44 public: 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 QPDF_DLL 59 QPDF_DLL
46 QPDFPageLabelDocumentHelper(QPDF&); 60 QPDFPageLabelDocumentHelper(QPDF&);
47 61
libqpdf/QPDFJob.cc
@@ -1053,7 +1053,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool&amp; first, QPDF&amp; pdf) @@ -1053,7 +1053,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1053 JSON::writeDictionaryKey(p, first, "pages", 1); 1053 JSON::writeDictionaryKey(p, first, "pages", 1);
1054 bool first_page = true; 1054 bool first_page = true;
1055 JSON::writeArrayOpen(p, first_page, 2); 1055 JSON::writeArrayOpen(p, first_page, 2);
1056 - QPDFPageLabelDocumentHelper pldh(pdf); 1056 + auto& pldh = pdf.page_labels();
1057 QPDFOutlineDocumentHelper odh(pdf); 1057 QPDFOutlineDocumentHelper odh(pdf);
1058 int pageno = -1; 1058 int pageno = -1;
1059 for (auto& ph: QPDFPageDocumentHelper(pdf).getAllPages()) { 1059 for (auto& ph: QPDFPageDocumentHelper(pdf).getAllPages()) {
@@ -1116,7 +1116,7 @@ void @@ -1116,7 +1116,7 @@ void
1116 QPDFJob::doJSONPageLabels(Pipeline* p, bool& first, QPDF& pdf) 1116 QPDFJob::doJSONPageLabels(Pipeline* p, bool& first, QPDF& pdf)
1117 { 1117 {
1118 JSON j_labels = JSON::makeArray(); 1118 JSON j_labels = JSON::makeArray();
1119 - QPDFPageLabelDocumentHelper pldh(pdf); 1119 + auto& pldh = pdf.page_labels();
1120 long long npages = QIntC::to_longlong(QPDFPageDocumentHelper(pdf).getAllPages().size()); 1120 long long npages = QIntC::to_longlong(QPDFPageDocumentHelper(pdf).getAllPages().size());
1121 if (pldh.hasPageLabels()) { 1121 if (pldh.hasPageLabels()) {
1122 std::vector<QPDFObjectHandle> labels; 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,7 +2548,7 @@ QPDFJob::handlePageSpecs(QPDF&amp; pdf, std::vector&lt;std::unique_ptr&lt;QPDF&gt;&gt;&amp; page_hea
2548 cis = page_spec_cfis[page_data.filename]; 2548 cis = page_spec_cfis[page_data.filename];
2549 cis->stayOpen(true); 2549 cis->stayOpen(true);
2550 } 2550 }
2551 - QPDFPageLabelDocumentHelper pldh(*page_data.qpdf); 2551 + auto& pldh = page_data.qpdf->page_labels();
2552 auto& other_afdh = page_data.qpdf->acroform(); 2552 auto& other_afdh = page_data.qpdf->acroform();
2553 if (pldh.hasPageLabels()) { 2553 if (pldh.hasPageLabels()) {
2554 any_page_labels = true; 2554 any_page_labels = true;
@@ -2992,7 +2992,7 @@ QPDFJob::doSplitPages(QPDF&amp; pdf) @@ -2992,7 +2992,7 @@ QPDFJob::doSplitPages(QPDF&amp; pdf)
2992 QPDFPageDocumentHelper dh(pdf); 2992 QPDFPageDocumentHelper dh(pdf);
2993 dh.removeUnreferencedResources(); 2993 dh.removeUnreferencedResources();
2994 } 2994 }
2995 - QPDFPageLabelDocumentHelper pldh(pdf); 2995 + auto& pldh = pdf.page_labels();
2996 auto& afdh = pdf.acroform(); 2996 auto& afdh = pdf.acroform();
2997 std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); 2997 std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
2998 size_t pageno_len = std::to_string(pages.size()).length(); 2998 size_t pageno_len = std::to_string(pages.size()).length();
libqpdf/QPDFPageLabelDocumentHelper.cc
1 #include <qpdf/QPDFPageLabelDocumentHelper.hh> 1 #include <qpdf/QPDFPageLabelDocumentHelper.hh>
2 2
  3 +#include <qpdf/QPDFNumberTreeObjectHelper.hh>
3 #include <qpdf/QPDFObjectHandle_private.hh> 4 #include <qpdf/QPDFObjectHandle_private.hh>
  5 +#include <qpdf/QPDF_private.hh>
  6 +
  7 +using namespace qpdf;
4 8
5 class QPDFPageLabelDocumentHelper::Members 9 class QPDFPageLabelDocumentHelper::Members
6 { 10 {
@@ -16,14 +20,23 @@ QPDFPageLabelDocumentHelper::QPDFPageLabelDocumentHelper(QPDF&amp; qpdf) : @@ -16,14 +20,23 @@ QPDFPageLabelDocumentHelper::QPDFPageLabelDocumentHelper(QPDF&amp; qpdf) :
16 QPDFDocumentHelper(qpdf), 20 QPDFDocumentHelper(qpdf),
17 m(std::make_shared<Members>()) 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 m->labels = std::make_unique<QPDFNumberTreeObjectHelper>( 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,6 +5,7 @@
5 5
6 #include <qpdf/QPDFAcroFormDocumentHelper.hh> 6 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
7 #include <qpdf/QPDFObject_private.hh> 7 #include <qpdf/QPDFObject_private.hh>
  8 +#include <qpdf/QPDFPageLabelDocumentHelper.hh>
8 #include <qpdf/QPDFTokenizer_private.hh> 9 #include <qpdf/QPDFTokenizer_private.hh>
9 10
10 using namespace qpdf; 11 using namespace qpdf;
@@ -553,6 +554,7 @@ class QPDF::Members @@ -553,6 +554,7 @@ class QPDF::Members
553 554
554 // Document Helpers; 555 // Document Helpers;
555 std::unique_ptr<QPDFAcroFormDocumentHelper> acroform; 556 std::unique_ptr<QPDFAcroFormDocumentHelper> acroform;
  557 + std::unique_ptr<QPDFPageLabelDocumentHelper> page_labels;
556 }; 558 };
557 559
558 // JobSetter class is restricted to QPDFJob. 560 // JobSetter class is restricted to QPDFJob.
@@ -584,4 +586,13 @@ QPDF::acroform() @@ -584,4 +586,13 @@ QPDF::acroform()
584 return *m->acroform; 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 #endif // QPDF_PRIVATE_HH 598 #endif // QPDF_PRIVATE_HH
qpdf/test_driver.cc
@@ -1802,7 +1802,7 @@ static void @@ -1802,7 +1802,7 @@ static void
1802 test_47(QPDF& pdf, char const* arg2) 1802 test_47(QPDF& pdf, char const* arg2)
1803 { 1803 {
1804 // Test page labels. 1804 // Test page labels.
1805 - QPDFPageLabelDocumentHelper pldh(pdf); 1805 + auto& pldh = QPDFPageLabelDocumentHelper::get(pdf);
1806 long long npages = pdf.getRoot().getKey("/Pages").getKey("/Count").getIntValue(); 1806 long long npages = pdf.getRoot().getKey("/Pages").getKey("/Count").getIntValue();
1807 std::vector<QPDFObjectHandle> labels; 1807 std::vector<QPDFObjectHandle> labels;
1808 pldh.getLabelsForPageRange(0, npages - 1, 1, labels); 1808 pldh.getLabelsForPageRange(0, npages - 1, 1, labels);