Commit e6555a36f0fdf4236acb76a33ba92be98615512c

Authored by m-holger
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.
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&amp; 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&amp; 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&amp; 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&amp; 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&amp; first, QPDF&amp; 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&amp; 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&amp; 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&amp; 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());
... ...