Commit 8d7621457328f722be19f0884cfd30d90bd733ac
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.
Showing
6 changed files
with
55 additions
and
15 deletions
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& first, QPDF& 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& pdf, std::vector<std::unique_ptr<QPDF>>& 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& 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& 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); | ... | ... |