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,7 +79,7 @@ process( | ||
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | // Add the embedded file at the document level as an attachment. | 81 | // Add the embedded file at the document level as an attachment. |
| 82 | - auto efdh = QPDFEmbeddedFileDocumentHelper(q); | 82 | + auto& efdh = QPDFEmbeddedFileDocumentHelper::get(q); |
| 83 | efdh.replaceEmbeddedFile(key, fs); | 83 | efdh.replaceEmbeddedFile(key, fs); |
| 84 | 84 | ||
| 85 | // Create a file attachment annotation. | 85 | // Create a file attachment annotation. |
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 QPDFEmbeddedFileDocumentHelper; | ||
| 66 | class QPDFPageLabelDocumentHelper; | 67 | class QPDFPageLabelDocumentHelper; |
| 67 | 68 | ||
| 68 | class QPDF | 69 | class QPDF |
| @@ -800,6 +801,7 @@ class QPDF | @@ -800,6 +801,7 @@ class QPDF | ||
| 800 | 801 | ||
| 801 | inline bool reconstructed_xref() const; | 802 | inline bool reconstructed_xref() const; |
| 802 | inline QPDFAcroFormDocumentHelper& acroform(); | 803 | inline QPDFAcroFormDocumentHelper& acroform(); |
| 804 | + inline QPDFEmbeddedFileDocumentHelper& embedded_files(); | ||
| 803 | inline QPDFPageLabelDocumentHelper& page_labels(); | 805 | inline QPDFPageLabelDocumentHelper& page_labels(); |
| 804 | 806 | ||
| 805 | // For testing only -- do not add to DLL | 807 | // For testing only -- do not add to DLL |
include/qpdf/QPDFEmbeddedFileDocumentHelper.hh
| @@ -36,6 +36,21 @@ | @@ -36,6 +36,21 @@ | ||
| 36 | class QPDFEmbeddedFileDocumentHelper: public QPDFDocumentHelper | 36 | class QPDFEmbeddedFileDocumentHelper: public QPDFDocumentHelper |
| 37 | { | 37 | { |
| 38 | public: | 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 | QPDF_DLL | 54 | QPDF_DLL |
| 40 | QPDFEmbeddedFileDocumentHelper(QPDF&); | 55 | QPDFEmbeddedFileDocumentHelper(QPDF&); |
| 41 | 56 |
libqpdf/QPDFEmbeddedFileDocumentHelper.cc
| 1 | #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> | 1 | #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/QPDFNameTreeObjectHelper.hh> | ||
| 4 | +#include <qpdf/QPDF_private.hh> | ||
| 5 | + | ||
| 3 | // File attachments are stored in the /EmbeddedFiles (name tree) key of the /Names dictionary from | 6 | // File attachments are stored in the /EmbeddedFiles (name tree) key of the /Names dictionary from |
| 4 | // the document catalog. Each entry points to a /FileSpec, which in turn points to one more Embedded | 7 | // the document catalog. Each entry points to a /FileSpec, which in turn points to one more Embedded |
| 5 | // File Streams. Note that file specs can appear in other places as well, such as file attachment | 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,6 +47,19 @@ QPDFEmbeddedFileDocumentHelper::QPDFEmbeddedFileDocumentHelper(QPDF& qpdf) : | ||
| 44 | QPDFDocumentHelper(qpdf), | 47 | QPDFDocumentHelper(qpdf), |
| 45 | m(std::make_shared<Members>()) | 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 | auto names = qpdf.getRoot().getKey("/Names"); | 63 | auto names = qpdf.getRoot().getKey("/Names"); |
| 48 | if (names.isDictionary()) { | 64 | if (names.isDictionary()) { |
| 49 | auto embedded_files = names.getKey("/EmbeddedFiles"); | 65 | auto embedded_files = names.getKey("/EmbeddedFiles"); |
| @@ -53,7 +69,7 @@ QPDFEmbeddedFileDocumentHelper::QPDFEmbeddedFileDocumentHelper(QPDF& qpdf) : | @@ -53,7 +69,7 @@ QPDFEmbeddedFileDocumentHelper::QPDFEmbeddedFileDocumentHelper(QPDF& qpdf) : | ||
| 53 | qpdf, | 69 | qpdf, |
| 54 | [](QPDFObjectHandle const& o) -> bool { return o.isDictionary(); }, | 70 | [](QPDFObjectHandle const& o) -> bool { return o.isDictionary(); }, |
| 55 | true); | 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,15 +13,12 @@ | ||
| 13 | #include <qpdf/Pl_StdioFile.hh> | 13 | #include <qpdf/Pl_StdioFile.hh> |
| 14 | #include <qpdf/Pl_String.hh> | 14 | #include <qpdf/Pl_String.hh> |
| 15 | #include <qpdf/QIntC.hh> | 15 | #include <qpdf/QIntC.hh> |
| 16 | -#include <qpdf/QPDFAcroFormDocumentHelper.hh> | ||
| 17 | #include <qpdf/QPDFCryptoProvider.hh> | 16 | #include <qpdf/QPDFCryptoProvider.hh> |
| 18 | -#include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> | ||
| 19 | #include <qpdf/QPDFExc.hh> | 17 | #include <qpdf/QPDFExc.hh> |
| 20 | #include <qpdf/QPDFLogger.hh> | 18 | #include <qpdf/QPDFLogger.hh> |
| 21 | #include <qpdf/QPDFObjectHandle_private.hh> | 19 | #include <qpdf/QPDFObjectHandle_private.hh> |
| 22 | #include <qpdf/QPDFOutlineDocumentHelper.hh> | 20 | #include <qpdf/QPDFOutlineDocumentHelper.hh> |
| 23 | #include <qpdf/QPDFPageDocumentHelper.hh> | 21 | #include <qpdf/QPDFPageDocumentHelper.hh> |
| 24 | -#include <qpdf/QPDFPageLabelDocumentHelper.hh> | ||
| 25 | #include <qpdf/QPDFPageObjectHelper.hh> | 22 | #include <qpdf/QPDFPageObjectHelper.hh> |
| 26 | #include <qpdf/QPDFSystemError.hh> | 23 | #include <qpdf/QPDFSystemError.hh> |
| 27 | #include <qpdf/QPDFUsage.hh> | 24 | #include <qpdf/QPDFUsage.hh> |
| @@ -894,7 +891,7 @@ QPDFJob::doShowPages(QPDF& pdf) | @@ -894,7 +891,7 @@ QPDFJob::doShowPages(QPDF& pdf) | ||
| 894 | void | 891 | void |
| 895 | QPDFJob::doListAttachments(QPDF& pdf) | 892 | QPDFJob::doListAttachments(QPDF& pdf) |
| 896 | { | 893 | { |
| 897 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | 894 | + auto& efdh = pdf.embedded_files(); |
| 898 | if (efdh.hasEmbeddedFiles()) { | 895 | if (efdh.hasEmbeddedFiles()) { |
| 899 | for (auto const& i: efdh.getEmbeddedFiles()) { | 896 | for (auto const& i: efdh.getEmbeddedFiles()) { |
| 900 | std::string const& key = i.first; | 897 | std::string const& key = i.first; |
| @@ -934,7 +931,7 @@ QPDFJob::doListAttachments(QPDF& pdf) | @@ -934,7 +931,7 @@ QPDFJob::doListAttachments(QPDF& pdf) | ||
| 934 | void | 931 | void |
| 935 | QPDFJob::doShowAttachment(QPDF& pdf) | 932 | QPDFJob::doShowAttachment(QPDF& pdf) |
| 936 | { | 933 | { |
| 937 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | 934 | + auto& efdh = pdf.embedded_files(); |
| 938 | auto fs = efdh.getEmbeddedFile(m->attachment_to_show); | 935 | auto fs = efdh.getEmbeddedFile(m->attachment_to_show); |
| 939 | if (!fs) { | 936 | if (!fs) { |
| 940 | throw std::runtime_error("attachment " + m->attachment_to_show + " not found"); | 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,7 +1318,7 @@ QPDFJob::doJSONAttachments(Pipeline* p, bool& first, QPDF& pdf) | ||
| 1321 | }; | 1318 | }; |
| 1322 | 1319 | ||
| 1323 | JSON j_attachments = JSON::makeDictionary(); | 1320 | JSON j_attachments = JSON::makeDictionary(); |
| 1324 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | 1321 | + auto& efdh = pdf.embedded_files(); |
| 1325 | for (auto const& iter: efdh.getEmbeddedFiles()) { | 1322 | for (auto const& iter: efdh.getEmbeddedFiles()) { |
| 1326 | std::string const& key = iter.first; | 1323 | std::string const& key = iter.first; |
| 1327 | auto fsoh = iter.second; | 1324 | auto fsoh = iter.second; |
| @@ -2073,7 +2070,7 @@ void | @@ -2073,7 +2070,7 @@ void | ||
| 2073 | QPDFJob::addAttachments(QPDF& pdf) | 2070 | QPDFJob::addAttachments(QPDF& pdf) |
| 2074 | { | 2071 | { |
| 2075 | maybe_set_pagemode(pdf, "/UseAttachments"); | 2072 | maybe_set_pagemode(pdf, "/UseAttachments"); |
| 2076 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | 2073 | + auto& efdh = pdf.embedded_files(); |
| 2077 | std::vector<std::string> duplicated_keys; | 2074 | std::vector<std::string> duplicated_keys; |
| 2078 | for (auto const& to_add: m->attachments_to_add) { | 2075 | for (auto const& to_add: m->attachments_to_add) { |
| 2079 | if ((!to_add.replace) && efdh.getEmbeddedFile(to_add.key)) { | 2076 | if ((!to_add.replace) && efdh.getEmbeddedFile(to_add.key)) { |
| @@ -2117,7 +2114,7 @@ void | @@ -2117,7 +2114,7 @@ void | ||
| 2117 | QPDFJob::copyAttachments(QPDF& pdf) | 2114 | QPDFJob::copyAttachments(QPDF& pdf) |
| 2118 | { | 2115 | { |
| 2119 | maybe_set_pagemode(pdf, "/UseAttachments"); | 2116 | maybe_set_pagemode(pdf, "/UseAttachments"); |
| 2120 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | 2117 | + auto& efdh = pdf.embedded_files(); |
| 2121 | std::vector<std::string> duplicates; | 2118 | std::vector<std::string> duplicates; |
| 2122 | for (auto const& to_copy: m->attachments_to_copy) { | 2119 | for (auto const& to_copy: m->attachments_to_copy) { |
| 2123 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { | 2120 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { |
| @@ -2125,7 +2122,7 @@ QPDFJob::copyAttachments(QPDF& pdf) | @@ -2125,7 +2122,7 @@ QPDFJob::copyAttachments(QPDF& pdf) | ||
| 2125 | }); | 2122 | }); |
| 2126 | std::unique_ptr<QPDF> other; | 2123 | std::unique_ptr<QPDF> other; |
| 2127 | processFile(other, to_copy.path.c_str(), to_copy.password.c_str(), false, false); | 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 | auto other_attachments = other_efdh.getEmbeddedFiles(); | 2126 | auto other_attachments = other_efdh.getEmbeddedFiles(); |
| 2130 | for (auto const& iter: other_attachments) { | 2127 | for (auto const& iter: other_attachments) { |
| 2131 | std::string new_key = to_copy.prefix + iter.first; | 2128 | std::string new_key = to_copy.prefix + iter.first; |
| @@ -2259,7 +2256,7 @@ QPDFJob::handleTransformations(QPDF& pdf) | @@ -2259,7 +2256,7 @@ QPDFJob::handleTransformations(QPDF& pdf) | ||
| 2259 | pdf.getRoot().replaceKey("/PageLabels", page_labels); | 2256 | pdf.getRoot().replaceKey("/PageLabels", page_labels); |
| 2260 | } | 2257 | } |
| 2261 | if (!m->attachments_to_remove.empty()) { | 2258 | if (!m->attachments_to_remove.empty()) { |
| 2262 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | 2259 | + auto& efdh = pdf.embedded_files(); |
| 2263 | for (auto const& key: m->attachments_to_remove) { | 2260 | for (auto const& key: m->attachments_to_remove) { |
| 2264 | if (efdh.removeEmbeddedFile(key)) { | 2261 | if (efdh.removeEmbeddedFile(key)) { |
| 2265 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { | 2262 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { |
libqpdf/qpdf/QPDF_private.hh
| @@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
| 4 | #include <qpdf/QPDF.hh> | 4 | #include <qpdf/QPDF.hh> |
| 5 | 5 | ||
| 6 | #include <qpdf/QPDFAcroFormDocumentHelper.hh> | 6 | #include <qpdf/QPDFAcroFormDocumentHelper.hh> |
| 7 | +#include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> | ||
| 7 | #include <qpdf/QPDFObject_private.hh> | 8 | #include <qpdf/QPDFObject_private.hh> |
| 8 | #include <qpdf/QPDFPageLabelDocumentHelper.hh> | 9 | #include <qpdf/QPDFPageLabelDocumentHelper.hh> |
| 9 | #include <qpdf/QPDFTokenizer_private.hh> | 10 | #include <qpdf/QPDFTokenizer_private.hh> |
| @@ -554,6 +555,7 @@ class QPDF::Members | @@ -554,6 +555,7 @@ class QPDF::Members | ||
| 554 | 555 | ||
| 555 | // Document Helpers; | 556 | // Document Helpers; |
| 556 | std::unique_ptr<QPDFAcroFormDocumentHelper> acroform; | 557 | std::unique_ptr<QPDFAcroFormDocumentHelper> acroform; |
| 558 | + std::unique_ptr<QPDFEmbeddedFileDocumentHelper> embedded_files; | ||
| 557 | std::unique_ptr<QPDFPageLabelDocumentHelper> page_labels; | 559 | std::unique_ptr<QPDFPageLabelDocumentHelper> page_labels; |
| 558 | }; | 560 | }; |
| 559 | 561 | ||
| @@ -586,6 +588,15 @@ QPDF::acroform() | @@ -586,6 +588,15 @@ QPDF::acroform() | ||
| 586 | return *m->acroform; | 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 | inline QPDFPageLabelDocumentHelper& | 600 | inline QPDFPageLabelDocumentHelper& |
| 590 | QPDF::page_labels() | 601 | QPDF::page_labels() |
| 591 | { | 602 | { |
qpdf/test_driver.cc
| @@ -2624,7 +2624,7 @@ test_76(QPDF& pdf, char const* arg2) | @@ -2624,7 +2624,7 @@ test_76(QPDF& pdf, char const* arg2) | ||
| 2624 | { | 2624 | { |
| 2625 | // Embedded files. arg2 is a file to attach. Hard-code the | 2625 | // Embedded files. arg2 is a file to attach. Hard-code the |
| 2626 | // mime type and file name for test purposes. | 2626 | // mime type and file name for test purposes. |
| 2627 | - QPDFEmbeddedFileDocumentHelper efdh(pdf); | 2627 | + auto &efdh = QPDFEmbeddedFileDocumentHelper::get(pdf); |
| 2628 | auto fs1 = QPDFFileSpecObjectHelper::createFileSpec(pdf, "att1.txt", arg2); | 2628 | auto fs1 = QPDFFileSpecObjectHelper::createFileSpec(pdf, "att1.txt", arg2); |
| 2629 | fs1.setDescription("some text"); | 2629 | fs1.setDescription("some text"); |
| 2630 | auto efs1 = QPDFEFStreamObjectHelper(fs1.getEmbeddedFileStream()); | 2630 | auto efs1 = QPDFEFStreamObjectHelper(fs1.getEmbeddedFileStream()); |