Commit e6555a36f0fdf4236acb76a33ba92be98615512c
1 parent
8d762145
Integrate `QPDFEmbeddedFileDocumentHelper` with `QPDF` to streamline embedded fi…
…le handling. Add shared helper retrieval, validation methods, and update usages across the codebase.
Showing
7 changed files
with
54 additions
and
13 deletions
examples/pdf-attach-file.cc
| ... | ... | @@ -79,7 +79,7 @@ process( |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | 81 | // Add the embedded file at the document level as an attachment. |
| 82 | - auto efdh = QPDFEmbeddedFileDocumentHelper(q); | |
| 82 | + auto& efdh = QPDFEmbeddedFileDocumentHelper::get(q); | |
| 83 | 83 | efdh.replaceEmbeddedFile(key, fs); |
| 84 | 84 | |
| 85 | 85 | // Create a file attachment annotation. | ... | ... |
include/qpdf/QPDF.hh
| ... | ... | @@ -63,6 +63,7 @@ class BufferInputSource; |
| 63 | 63 | class QPDFLogger; |
| 64 | 64 | class QPDFParser; |
| 65 | 65 | class QPDFAcroFormDocumentHelper; |
| 66 | +class QPDFEmbeddedFileDocumentHelper; | |
| 66 | 67 | class QPDFPageLabelDocumentHelper; |
| 67 | 68 | |
| 68 | 69 | class QPDF |
| ... | ... | @@ -800,6 +801,7 @@ class QPDF |
| 800 | 801 | |
| 801 | 802 | inline bool reconstructed_xref() const; |
| 802 | 803 | inline QPDFAcroFormDocumentHelper& acroform(); |
| 804 | + inline QPDFEmbeddedFileDocumentHelper& embedded_files(); | |
| 803 | 805 | inline QPDFPageLabelDocumentHelper& page_labels(); |
| 804 | 806 | |
| 805 | 807 | // For testing only -- do not add to DLL | ... | ... |
include/qpdf/QPDFEmbeddedFileDocumentHelper.hh
| ... | ... | @@ -36,6 +36,21 @@ |
| 36 | 36 | class QPDFEmbeddedFileDocumentHelper: public QPDFDocumentHelper |
| 37 | 37 | { |
| 38 | 38 | public: |
| 39 | + // Get a shared document helper for a given QPDF object. | |
| 40 | + // | |
| 41 | + // Retrieving a document helper for a QPDF object rather than creating a new one avoids repeated | |
| 42 | + // validation of the EmbeddedFiles structure, which can be expensive. | |
| 43 | + QPDF_DLL | |
| 44 | + static QPDFEmbeddedFileDocumentHelper& get(QPDF& qpdf); | |
| 45 | + | |
| 46 | + // Re-validate the EmbeddedFiles structure. This is useful if you have modified the structure of | |
| 47 | + // the EmbeddedFiles dictionary in a way that would invalidate the cache. | |
| 48 | + // | |
| 49 | + // If repair is true, the document will be repaired if possible if the validation encounters | |
| 50 | + // errors. | |
| 51 | + QPDF_DLL | |
| 52 | + void validate(bool repair = true); | |
| 53 | + | |
| 39 | 54 | QPDF_DLL |
| 40 | 55 | QPDFEmbeddedFileDocumentHelper(QPDF&); |
| 41 | 56 | ... | ... |
libqpdf/QPDFEmbeddedFileDocumentHelper.cc
| 1 | 1 | #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/QPDFNameTreeObjectHelper.hh> | |
| 4 | +#include <qpdf/QPDF_private.hh> | |
| 5 | + | |
| 3 | 6 | // File attachments are stored in the /EmbeddedFiles (name tree) key of the /Names dictionary from |
| 4 | 7 | // the document catalog. Each entry points to a /FileSpec, which in turn points to one more Embedded |
| 5 | 8 | // File Streams. Note that file specs can appear in other places as well, such as file attachment |
| ... | ... | @@ -44,6 +47,19 @@ QPDFEmbeddedFileDocumentHelper::QPDFEmbeddedFileDocumentHelper(QPDF& qpdf) : |
| 44 | 47 | QPDFDocumentHelper(qpdf), |
| 45 | 48 | m(std::make_shared<Members>()) |
| 46 | 49 | { |
| 50 | + validate(); | |
| 51 | +} | |
| 52 | + | |
| 53 | +QPDFEmbeddedFileDocumentHelper& | |
| 54 | +QPDFEmbeddedFileDocumentHelper::get(QPDF& qpdf) | |
| 55 | +{ | |
| 56 | + return qpdf.embedded_files(); | |
| 57 | +} | |
| 58 | + | |
| 59 | +void | |
| 60 | +QPDFEmbeddedFileDocumentHelper::validate(bool repair) | |
| 61 | +{ | |
| 62 | + m->embedded_files.reset(); | |
| 47 | 63 | auto names = qpdf.getRoot().getKey("/Names"); |
| 48 | 64 | if (names.isDictionary()) { |
| 49 | 65 | auto embedded_files = names.getKey("/EmbeddedFiles"); |
| ... | ... | @@ -53,7 +69,7 @@ QPDFEmbeddedFileDocumentHelper::QPDFEmbeddedFileDocumentHelper(QPDF& qpdf) : |
| 53 | 69 | qpdf, |
| 54 | 70 | [](QPDFObjectHandle const& o) -> bool { return o.isDictionary(); }, |
| 55 | 71 | true); |
| 56 | - m->embedded_files->validate(); | |
| 72 | + m->embedded_files->validate(repair); | |
| 57 | 73 | } |
| 58 | 74 | } |
| 59 | 75 | } | ... | ... |
libqpdf/QPDFJob.cc
| ... | ... | @@ -13,15 +13,12 @@ |
| 13 | 13 | #include <qpdf/Pl_StdioFile.hh> |
| 14 | 14 | #include <qpdf/Pl_String.hh> |
| 15 | 15 | #include <qpdf/QIntC.hh> |
| 16 | -#include <qpdf/QPDFAcroFormDocumentHelper.hh> | |
| 17 | 16 | #include <qpdf/QPDFCryptoProvider.hh> |
| 18 | -#include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> | |
| 19 | 17 | #include <qpdf/QPDFExc.hh> |
| 20 | 18 | #include <qpdf/QPDFLogger.hh> |
| 21 | 19 | #include <qpdf/QPDFObjectHandle_private.hh> |
| 22 | 20 | #include <qpdf/QPDFOutlineDocumentHelper.hh> |
| 23 | 21 | #include <qpdf/QPDFPageDocumentHelper.hh> |
| 24 | -#include <qpdf/QPDFPageLabelDocumentHelper.hh> | |
| 25 | 22 | #include <qpdf/QPDFPageObjectHelper.hh> |
| 26 | 23 | #include <qpdf/QPDFSystemError.hh> |
| 27 | 24 | #include <qpdf/QPDFUsage.hh> |
| ... | ... | @@ -894,7 +891,7 @@ QPDFJob::doShowPages(QPDF& pdf) |
| 894 | 891 | void |
| 895 | 892 | QPDFJob::doListAttachments(QPDF& pdf) |
| 896 | 893 | { |
| 897 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 894 | + auto& efdh = pdf.embedded_files(); | |
| 898 | 895 | if (efdh.hasEmbeddedFiles()) { |
| 899 | 896 | for (auto const& i: efdh.getEmbeddedFiles()) { |
| 900 | 897 | std::string const& key = i.first; |
| ... | ... | @@ -934,7 +931,7 @@ QPDFJob::doListAttachments(QPDF& pdf) |
| 934 | 931 | void |
| 935 | 932 | QPDFJob::doShowAttachment(QPDF& pdf) |
| 936 | 933 | { |
| 937 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 934 | + auto& efdh = pdf.embedded_files(); | |
| 938 | 935 | auto fs = efdh.getEmbeddedFile(m->attachment_to_show); |
| 939 | 936 | if (!fs) { |
| 940 | 937 | throw std::runtime_error("attachment " + m->attachment_to_show + " not found"); |
| ... | ... | @@ -1321,7 +1318,7 @@ QPDFJob::doJSONAttachments(Pipeline* p, bool& first, QPDF& pdf) |
| 1321 | 1318 | }; |
| 1322 | 1319 | |
| 1323 | 1320 | JSON j_attachments = JSON::makeDictionary(); |
| 1324 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 1321 | + auto& efdh = pdf.embedded_files(); | |
| 1325 | 1322 | for (auto const& iter: efdh.getEmbeddedFiles()) { |
| 1326 | 1323 | std::string const& key = iter.first; |
| 1327 | 1324 | auto fsoh = iter.second; |
| ... | ... | @@ -2073,7 +2070,7 @@ void |
| 2073 | 2070 | QPDFJob::addAttachments(QPDF& pdf) |
| 2074 | 2071 | { |
| 2075 | 2072 | maybe_set_pagemode(pdf, "/UseAttachments"); |
| 2076 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 2073 | + auto& efdh = pdf.embedded_files(); | |
| 2077 | 2074 | std::vector<std::string> duplicated_keys; |
| 2078 | 2075 | for (auto const& to_add: m->attachments_to_add) { |
| 2079 | 2076 | if ((!to_add.replace) && efdh.getEmbeddedFile(to_add.key)) { |
| ... | ... | @@ -2117,7 +2114,7 @@ void |
| 2117 | 2114 | QPDFJob::copyAttachments(QPDF& pdf) |
| 2118 | 2115 | { |
| 2119 | 2116 | maybe_set_pagemode(pdf, "/UseAttachments"); |
| 2120 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 2117 | + auto& efdh = pdf.embedded_files(); | |
| 2121 | 2118 | std::vector<std::string> duplicates; |
| 2122 | 2119 | for (auto const& to_copy: m->attachments_to_copy) { |
| 2123 | 2120 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { |
| ... | ... | @@ -2125,7 +2122,7 @@ QPDFJob::copyAttachments(QPDF& pdf) |
| 2125 | 2122 | }); |
| 2126 | 2123 | std::unique_ptr<QPDF> other; |
| 2127 | 2124 | processFile(other, to_copy.path.c_str(), to_copy.password.c_str(), false, false); |
| 2128 | - QPDFEmbeddedFileDocumentHelper other_efdh(*other); | |
| 2125 | + auto& other_efdh = other->embedded_files(); | |
| 2129 | 2126 | auto other_attachments = other_efdh.getEmbeddedFiles(); |
| 2130 | 2127 | for (auto const& iter: other_attachments) { |
| 2131 | 2128 | std::string new_key = to_copy.prefix + iter.first; |
| ... | ... | @@ -2259,7 +2256,7 @@ QPDFJob::handleTransformations(QPDF& pdf) |
| 2259 | 2256 | pdf.getRoot().replaceKey("/PageLabels", page_labels); |
| 2260 | 2257 | } |
| 2261 | 2258 | if (!m->attachments_to_remove.empty()) { |
| 2262 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 2259 | + auto& efdh = pdf.embedded_files(); | |
| 2263 | 2260 | for (auto const& key: m->attachments_to_remove) { |
| 2264 | 2261 | if (efdh.removeEmbeddedFile(key)) { |
| 2265 | 2262 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { | ... | ... |
libqpdf/qpdf/QPDF_private.hh
| ... | ... | @@ -4,6 +4,7 @@ |
| 4 | 4 | #include <qpdf/QPDF.hh> |
| 5 | 5 | |
| 6 | 6 | #include <qpdf/QPDFAcroFormDocumentHelper.hh> |
| 7 | +#include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> | |
| 7 | 8 | #include <qpdf/QPDFObject_private.hh> |
| 8 | 9 | #include <qpdf/QPDFPageLabelDocumentHelper.hh> |
| 9 | 10 | #include <qpdf/QPDFTokenizer_private.hh> |
| ... | ... | @@ -554,6 +555,7 @@ class QPDF::Members |
| 554 | 555 | |
| 555 | 556 | // Document Helpers; |
| 556 | 557 | std::unique_ptr<QPDFAcroFormDocumentHelper> acroform; |
| 558 | + std::unique_ptr<QPDFEmbeddedFileDocumentHelper> embedded_files; | |
| 557 | 559 | std::unique_ptr<QPDFPageLabelDocumentHelper> page_labels; |
| 558 | 560 | }; |
| 559 | 561 | |
| ... | ... | @@ -586,6 +588,15 @@ QPDF::acroform() |
| 586 | 588 | return *m->acroform; |
| 587 | 589 | } |
| 588 | 590 | |
| 591 | +inline QPDFEmbeddedFileDocumentHelper& | |
| 592 | +QPDF::embedded_files() | |
| 593 | +{ | |
| 594 | + if (!m->embedded_files) { | |
| 595 | + m->embedded_files = std::make_unique<QPDFEmbeddedFileDocumentHelper>(*this); | |
| 596 | + } | |
| 597 | + return *m->embedded_files; | |
| 598 | +} | |
| 599 | + | |
| 589 | 600 | inline QPDFPageLabelDocumentHelper& |
| 590 | 601 | QPDF::page_labels() |
| 591 | 602 | { | ... | ... |
qpdf/test_driver.cc
| ... | ... | @@ -2624,7 +2624,7 @@ test_76(QPDF& pdf, char const* arg2) |
| 2624 | 2624 | { |
| 2625 | 2625 | // Embedded files. arg2 is a file to attach. Hard-code the |
| 2626 | 2626 | // mime type and file name for test purposes. |
| 2627 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | |
| 2627 | + auto &efdh = QPDFEmbeddedFileDocumentHelper::get(pdf); | |
| 2628 | 2628 | auto fs1 = QPDFFileSpecObjectHelper::createFileSpec(pdf, "att1.txt", arg2); |
| 2629 | 2629 | fs1.setDescription("some text"); |
| 2630 | 2630 | auto efs1 = QPDFEFStreamObjectHelper(fs1.getEmbeddedFileStream()); | ... | ... |