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,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& first, QPDF& pdf) | @@ -1053,7 +1053,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool& first, QPDF& 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& pdf, std::vector<std::unique_ptr<QPDF>>& page_hea | @@ -2548,7 +2548,7 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& 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& pdf) | @@ -2992,7 +2992,7 @@ QPDFJob::doSplitPages(QPDF& 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& qpdf) : | @@ -16,14 +20,23 @@ QPDFPageLabelDocumentHelper::QPDFPageLabelDocumentHelper(QPDF& 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); |