Commit 847f02462e2f949573c0a83962d5766e90d86baa
1 parent
e6555a36
Integrate `QPDFOutlineDocumentHelper` with `QPDF` for improved outline managemen…
…t. Add shared helper retrieval, validation methods, and update usages across the codebase.
Showing
7 changed files
with
48 additions
and
6 deletions
examples/pdf-bookmarks.cc
| @@ -177,7 +177,7 @@ main(int argc, char* argv[]) | @@ -177,7 +177,7 @@ main(int argc, char* argv[]) | ||
| 177 | QPDF qpdf; | 177 | QPDF qpdf; |
| 178 | qpdf.processFile(filename, password); | 178 | qpdf.processFile(filename, password); |
| 179 | 179 | ||
| 180 | - QPDFOutlineDocumentHelper odh(qpdf); | 180 | + auto& odh = QPDFOutlineDocumentHelper::get(qpdf); |
| 181 | if (odh.hasOutlines()) { | 181 | if (odh.hasOutlines()) { |
| 182 | std::vector<int> numbers; | 182 | std::vector<int> numbers; |
| 183 | if (show_targets) { | 183 | if (show_targets) { |
fuzz/qpdf_outlines_fuzzer.cc
| @@ -49,7 +49,7 @@ FuzzHelper::testOutlines() | @@ -49,7 +49,7 @@ FuzzHelper::testOutlines() | ||
| 49 | { | 49 | { |
| 50 | std::shared_ptr<QPDF> q = getQpdf(); | 50 | std::shared_ptr<QPDF> q = getQpdf(); |
| 51 | std::list<std::vector<QPDFOutlineObjectHelper>> queue; | 51 | std::list<std::vector<QPDFOutlineObjectHelper>> queue; |
| 52 | - QPDFOutlineDocumentHelper odh(*q); | 52 | + auto& odh = QPDFOutlineDocumentHelper::get(*q); |
| 53 | queue.push_back(odh.getTopLevelOutlines()); | 53 | queue.push_back(odh.getTopLevelOutlines()); |
| 54 | while (!queue.empty()) { | 54 | while (!queue.empty()) { |
| 55 | for (auto& ol: *(queue.begin())) { | 55 | for (auto& ol: *(queue.begin())) { |
include/qpdf/QPDF.hh
| @@ -64,6 +64,7 @@ class QPDFLogger; | @@ -64,6 +64,7 @@ class QPDFLogger; | ||
| 64 | class QPDFParser; | 64 | class QPDFParser; |
| 65 | class QPDFAcroFormDocumentHelper; | 65 | class QPDFAcroFormDocumentHelper; |
| 66 | class QPDFEmbeddedFileDocumentHelper; | 66 | class QPDFEmbeddedFileDocumentHelper; |
| 67 | +class QPDFOutlineDocumentHelper; | ||
| 67 | class QPDFPageLabelDocumentHelper; | 68 | class QPDFPageLabelDocumentHelper; |
| 68 | 69 | ||
| 69 | class QPDF | 70 | class QPDF |
| @@ -802,6 +803,7 @@ class QPDF | @@ -802,6 +803,7 @@ class QPDF | ||
| 802 | inline bool reconstructed_xref() const; | 803 | inline bool reconstructed_xref() const; |
| 803 | inline QPDFAcroFormDocumentHelper& acroform(); | 804 | inline QPDFAcroFormDocumentHelper& acroform(); |
| 804 | inline QPDFEmbeddedFileDocumentHelper& embedded_files(); | 805 | inline QPDFEmbeddedFileDocumentHelper& embedded_files(); |
| 806 | + inline QPDFOutlineDocumentHelper& outlines(); | ||
| 805 | inline QPDFPageLabelDocumentHelper& page_labels(); | 807 | inline QPDFPageLabelDocumentHelper& page_labels(); |
| 806 | 808 | ||
| 807 | // For testing only -- do not add to DLL | 809 | // For testing only -- do not add to DLL |
include/qpdf/QPDFOutlineDocumentHelper.hh
| @@ -38,6 +38,21 @@ | @@ -38,6 +38,21 @@ | ||
| 38 | class QPDFOutlineDocumentHelper: public QPDFDocumentHelper | 38 | class QPDFOutlineDocumentHelper: public QPDFDocumentHelper |
| 39 | { | 39 | { |
| 40 | public: | 40 | public: |
| 41 | + // Get a shared document helper for a given QPDF object. | ||
| 42 | + // | ||
| 43 | + // Retrieving a document helper for a QPDF object rather than creating a new one avoids repeated | ||
| 44 | + // validation of the Acroform structure, which can be expensive. | ||
| 45 | + QPDF_DLL | ||
| 46 | + static QPDFOutlineDocumentHelper& get(QPDF& qpdf); | ||
| 47 | + | ||
| 48 | + // Re-validate the Outlines structure. This is useful if you have modified the structure of the | ||
| 49 | + // Outlines dictionary in a way that would invalidate the cache. | ||
| 50 | + // | ||
| 51 | + // If repair is true, the document will be repaired if possible if the validation encounters | ||
| 52 | + // errors. | ||
| 53 | + QPDF_DLL | ||
| 54 | + void validate(bool repair = true); | ||
| 55 | + | ||
| 41 | QPDF_DLL | 56 | QPDF_DLL |
| 42 | QPDFOutlineDocumentHelper(QPDF&); | 57 | QPDFOutlineDocumentHelper(QPDF&); |
| 43 | 58 |
libqpdf/QPDFJob.cc
| @@ -17,7 +17,6 @@ | @@ -17,7 +17,6 @@ | ||
| 17 | #include <qpdf/QPDFExc.hh> | 17 | #include <qpdf/QPDFExc.hh> |
| 18 | #include <qpdf/QPDFLogger.hh> | 18 | #include <qpdf/QPDFLogger.hh> |
| 19 | #include <qpdf/QPDFObjectHandle_private.hh> | 19 | #include <qpdf/QPDFObjectHandle_private.hh> |
| 20 | -#include <qpdf/QPDFOutlineDocumentHelper.hh> | ||
| 21 | #include <qpdf/QPDFPageDocumentHelper.hh> | 20 | #include <qpdf/QPDFPageDocumentHelper.hh> |
| 22 | #include <qpdf/QPDFPageObjectHelper.hh> | 21 | #include <qpdf/QPDFPageObjectHelper.hh> |
| 23 | #include <qpdf/QPDFSystemError.hh> | 22 | #include <qpdf/QPDFSystemError.hh> |
| @@ -1051,7 +1050,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool& first, QPDF& pdf) | @@ -1051,7 +1050,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool& first, QPDF& pdf) | ||
| 1051 | bool first_page = true; | 1050 | bool first_page = true; |
| 1052 | JSON::writeArrayOpen(p, first_page, 2); | 1051 | JSON::writeArrayOpen(p, first_page, 2); |
| 1053 | auto& pldh = pdf.page_labels(); | 1052 | auto& pldh = pdf.page_labels(); |
| 1054 | - QPDFOutlineDocumentHelper odh(pdf); | 1053 | + auto& odh = pdf.outlines(); |
| 1055 | int pageno = -1; | 1054 | int pageno = -1; |
| 1056 | for (auto& ph: QPDFPageDocumentHelper(pdf).getAllPages()) { | 1055 | for (auto& ph: QPDFPageDocumentHelper(pdf).getAllPages()) { |
| 1057 | ++pageno; | 1056 | ++pageno; |
| @@ -1168,8 +1167,7 @@ QPDFJob::doJSONOutlines(Pipeline* p, bool& first, QPDF& pdf) | @@ -1168,8 +1167,7 @@ QPDFJob::doJSONOutlines(Pipeline* p, bool& first, QPDF& pdf) | ||
| 1168 | } | 1167 | } |
| 1169 | 1168 | ||
| 1170 | JSON j_outlines = JSON::makeArray(); | 1169 | JSON j_outlines = JSON::makeArray(); |
| 1171 | - QPDFOutlineDocumentHelper odh(pdf); | ||
| 1172 | - addOutlinesToJson(odh.getTopLevelOutlines(), j_outlines, page_numbers); | 1170 | + addOutlinesToJson(pdf.outlines().getTopLevelOutlines(), j_outlines, page_numbers); |
| 1173 | JSON::writeDictionaryItem(p, first, "outlines", j_outlines, 1); | 1171 | JSON::writeDictionaryItem(p, first, "outlines", j_outlines, 1); |
| 1174 | } | 1172 | } |
| 1175 | 1173 |
libqpdf/QPDFOutlineDocumentHelper.cc
| 1 | #include <qpdf/QPDFOutlineDocumentHelper.hh> | 1 | #include <qpdf/QPDFOutlineDocumentHelper.hh> |
| 2 | 2 | ||
| 3 | #include <qpdf/QPDFObjectHandle_private.hh> | 3 | #include <qpdf/QPDFObjectHandle_private.hh> |
| 4 | +#include <qpdf/QPDF_private.hh> | ||
| 4 | #include <qpdf/QTC.hh> | 5 | #include <qpdf/QTC.hh> |
| 5 | 6 | ||
| 6 | class QPDFOutlineDocumentHelper::Members | 7 | class QPDFOutlineDocumentHelper::Members |
| @@ -27,6 +28,21 @@ QPDFOutlineDocumentHelper::QPDFOutlineDocumentHelper(QPDF& qpdf) : | @@ -27,6 +28,21 @@ QPDFOutlineDocumentHelper::QPDFOutlineDocumentHelper(QPDF& qpdf) : | ||
| 27 | QPDFDocumentHelper(qpdf), | 28 | QPDFDocumentHelper(qpdf), |
| 28 | m(std::make_shared<Members>()) | 29 | m(std::make_shared<Members>()) |
| 29 | { | 30 | { |
| 31 | + validate(); | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +QPDFOutlineDocumentHelper& | ||
| 35 | +QPDFOutlineDocumentHelper::get(QPDF& qpdf) | ||
| 36 | +{ | ||
| 37 | + return qpdf.outlines(); | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +void | ||
| 41 | +QPDFOutlineDocumentHelper::validate(bool repair) | ||
| 42 | +{ | ||
| 43 | + m->outlines.clear(); | ||
| 44 | + m->names_dest = nullptr; | ||
| 45 | + | ||
| 30 | QPDFObjectHandle root = qpdf.getRoot(); | 46 | QPDFObjectHandle root = qpdf.getRoot(); |
| 31 | if (!root.hasKey("/Outlines")) { | 47 | if (!root.hasKey("/Outlines")) { |
| 32 | return; | 48 | return; |
libqpdf/qpdf/QPDF_private.hh
| @@ -6,6 +6,7 @@ | @@ -6,6 +6,7 @@ | ||
| 6 | #include <qpdf/QPDFAcroFormDocumentHelper.hh> | 6 | #include <qpdf/QPDFAcroFormDocumentHelper.hh> |
| 7 | #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> | 7 | #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> |
| 8 | #include <qpdf/QPDFObject_private.hh> | 8 | #include <qpdf/QPDFObject_private.hh> |
| 9 | +#include <qpdf/QPDFOutlineDocumentHelper.hh> | ||
| 9 | #include <qpdf/QPDFPageLabelDocumentHelper.hh> | 10 | #include <qpdf/QPDFPageLabelDocumentHelper.hh> |
| 10 | #include <qpdf/QPDFTokenizer_private.hh> | 11 | #include <qpdf/QPDFTokenizer_private.hh> |
| 11 | 12 | ||
| @@ -556,6 +557,7 @@ class QPDF::Members | @@ -556,6 +557,7 @@ class QPDF::Members | ||
| 556 | // Document Helpers; | 557 | // Document Helpers; |
| 557 | std::unique_ptr<QPDFAcroFormDocumentHelper> acroform; | 558 | std::unique_ptr<QPDFAcroFormDocumentHelper> acroform; |
| 558 | std::unique_ptr<QPDFEmbeddedFileDocumentHelper> embedded_files; | 559 | std::unique_ptr<QPDFEmbeddedFileDocumentHelper> embedded_files; |
| 560 | + std::unique_ptr<QPDFOutlineDocumentHelper> outlines; | ||
| 559 | std::unique_ptr<QPDFPageLabelDocumentHelper> page_labels; | 561 | std::unique_ptr<QPDFPageLabelDocumentHelper> page_labels; |
| 560 | }; | 562 | }; |
| 561 | 563 | ||
| @@ -597,6 +599,15 @@ QPDF::embedded_files() | @@ -597,6 +599,15 @@ QPDF::embedded_files() | ||
| 597 | return *m->embedded_files; | 599 | return *m->embedded_files; |
| 598 | } | 600 | } |
| 599 | 601 | ||
| 602 | +inline QPDFOutlineDocumentHelper& | ||
| 603 | +QPDF::outlines() | ||
| 604 | +{ | ||
| 605 | + if (!m->outlines) { | ||
| 606 | + m->outlines = std::make_unique<QPDFOutlineDocumentHelper>(*this); | ||
| 607 | + } | ||
| 608 | + return *m->outlines; | ||
| 609 | +} | ||
| 610 | + | ||
| 600 | inline QPDFPageLabelDocumentHelper& | 611 | inline QPDFPageLabelDocumentHelper& |
| 601 | QPDF::page_labels() | 612 | QPDF::page_labels() |
| 602 | { | 613 | { |