Commit 2336590ff19ed1fab25901fe0f14694c46818b6a

Authored by m-holger
Committed by GitHub
2 parents 628cf873 9cbc87e1

Merge pull request #1578 from m-holger/members

Refactor QPDFFormFieldObjectHelper and QPDFEFStreamObjectHelper
include/qpdf/QPDFEFStreamObjectHelper.hh
... ... @@ -92,17 +92,7 @@ class QPDFEFStreamObjectHelper: public QPDFObjectHelper
92 92 void setParam(std::string const& pkey, QPDFObjectHandle const&);
93 93 static QPDFEFStreamObjectHelper newFromStream(QPDFObjectHandle stream);
94 94  
95   - class Members
96   - {
97   - friend class QPDFEFStreamObjectHelper;
98   -
99   - public:
100   - ~Members() = default;
101   -
102   - private:
103   - Members() = default;
104   - Members(Members const&) = delete;
105   - };
  95 + class Members;
106 96  
107 97 std::shared_ptr<Members> m;
108 98 };
... ...
include/qpdf/QPDFFileSpecObjectHelper.hh
... ... @@ -87,18 +87,7 @@ class QPDFFileSpecObjectHelper: public QPDFObjectHelper
87 87 setFilename(std::string const& unicode_name, std::string const& compat_name = "");
88 88  
89 89 private:
90   - class Members
91   - {
92   - friend class QPDFFileSpecObjectHelper;
93   -
94   - public:
95   - ~Members() = default;
96   -
97   - private:
98   - Members() = default;
99   - Members(Members const&) = delete;
100   - };
101   -
  90 + class Members;
102 91 std::shared_ptr<Members> m;
103 92 };
104 93  
... ...
libqpdf/CMakeLists.txt
... ... @@ -44,7 +44,6 @@ set(libqpdf_SOURCES
44 44 Pl_Flate.cc
45 45 Pl_Function.cc
46 46 Pl_LZWDecoder.cc
47   - Pl_MD5.cc
48 47 Pl_OStream.cc
49 48 Pl_PNGFilter.cc
50 49 Pl_QPDFTokenizer.cc
... ...
libqpdf/MD5.cc
1 1 #include <qpdf/MD5.hh>
2 2  
  3 +#include <qpdf/Pl_MD5.hh>
  4 +
3 5 #include <qpdf/QIntC.hh>
4 6 #include <qpdf/QPDFCryptoProvider.hh>
5 7 #include <qpdf/QUtil.hh>
... ... @@ -163,3 +165,29 @@ MD5::checkFileChecksum(char const* const checksum, char const* filename, qpdf_of
163 165 }
164 166 return result;
165 167 }
  168 +
  169 +void
  170 +Pl_MD5::write(unsigned char const* buf, size_t len)
  171 +{
  172 + if (enabled) {
  173 + if (!in_progress) {
  174 + md5.reset();
  175 + in_progress = true;
  176 + }
  177 +
  178 + // Write in chunks in case len is too big to fit in an int. Assume int is at least 32 bits.
  179 + static size_t const max_bytes = 1 << 30;
  180 + size_t bytes_left = len;
  181 + unsigned char const* data = buf;
  182 + while (bytes_left > 0) {
  183 + size_t bytes = (bytes_left >= max_bytes ? max_bytes : bytes_left);
  184 + md5.encodeDataIncrementally(reinterpret_cast<char const*>(data), bytes);
  185 + bytes_left -= bytes;
  186 + data += bytes;
  187 + }
  188 + }
  189 +
  190 + if (next()) {
  191 + next()->write(buf, len);
  192 + }
  193 +}
... ...
libqpdf/Pl_MD5.cc deleted
1   -#include <qpdf/Pl_MD5.hh>
2   -
3   -#include <stdexcept>
4   -
5   -Pl_MD5::Pl_MD5(char const* identifier, Pipeline* next) :
6   - Pipeline(identifier, next)
7   -{
8   - if (!next) {
9   - throw std::logic_error("Attempt to create Pl_MD5 with nullptr as next");
10   - }
11   -}
12   -
13   -void
14   -Pl_MD5::write(unsigned char const* buf, size_t len)
15   -{
16   - if (enabled) {
17   - if (!in_progress) {
18   - md5.reset();
19   - in_progress = true;
20   - }
21   -
22   - // Write in chunks in case len is too big to fit in an int. Assume int is at least 32 bits.
23   - static size_t const max_bytes = 1 << 30;
24   - size_t bytes_left = len;
25   - unsigned char const* data = buf;
26   - while (bytes_left > 0) {
27   - size_t bytes = (bytes_left >= max_bytes ? max_bytes : bytes_left);
28   - md5.encodeDataIncrementally(reinterpret_cast<char const*>(data), bytes);
29   - bytes_left -= bytes;
30   - data += bytes;
31   - }
32   - }
33   -
34   - next()->write(buf, len);
35   -}
36   -
37   -void
38   -Pl_MD5::finish()
39   -{
40   - next()->finish();
41   - if (!persist_across_finish) {
42   - in_progress = false;
43   - }
44   -}
45   -
46   -void
47   -Pl_MD5::enable(bool is_enabled)
48   -{
49   - enabled = is_enabled;
50   -}
51   -
52   -void
53   -Pl_MD5::persistAcrossFinish(bool persist)
54   -{
55   - persist_across_finish = persist;
56   -}
57   -
58   -std::string
59   -Pl_MD5::getHexDigest()
60   -{
61   - if (!enabled) {
62   - throw std::logic_error("digest requested for a disabled MD5 Pipeline");
63   - }
64   - in_progress = false;
65   - return md5.unparse();
66   -}
libqpdf/QPDFEFStreamObjectHelper.cc
1 1 #include <qpdf/QPDFEFStreamObjectHelper.hh>
2 2  
3   -#include <qpdf/Pl_Count.hh>
4   -#include <qpdf/Pl_Discard.hh>
  3 +#include <qpdf/Pipeline_private.hh>
5 4 #include <qpdf/Pl_MD5.hh>
6 5 #include <qpdf/QIntC.hh>
7 6 #include <qpdf/QPDF.hh>
  7 +#include <qpdf/QPDFObjectHandle_private.hh>
8 8 #include <qpdf/QUtil.hh>
9 9  
  10 +using namespace qpdf;
  11 +
  12 +class QPDFEFStreamObjectHelper::Members
  13 +{
  14 +};
  15 +
10 16 QPDFEFStreamObjectHelper::QPDFEFStreamObjectHelper(QPDFObjectHandle oh) :
11   - QPDFObjectHelper(oh),
12   - m(new Members())
  17 + QPDFObjectHelper(oh)
13 18 {
14 19 }
15 20  
16 21 QPDFObjectHandle
17 22 QPDFEFStreamObjectHelper::getParam(std::string const& pkey)
18 23 {
19   - auto params = oh().getDict().getKey("/Params");
20   - if (params.isDictionary()) {
21   - return params.getKey(pkey);
  24 + if (auto result = oh().getDict()["/Params"][pkey]) {
  25 + return result;
22 26 }
23   - return QPDFObjectHandle::newNull();
  27 + return {};
24 28 }
25 29  
26 30 void
27 31 QPDFEFStreamObjectHelper::setParam(std::string const& pkey, QPDFObjectHandle const& pval)
28 32 {
29   - auto params = oh().getDict().getKey("/Params");
30   - if (!params.isDictionary()) {
31   - params = oh().getDict().replaceKeyAndGetNew("/Params", QPDFObjectHandle::newDictionary());
  33 + if (Dictionary Params = oh().getDict()["/Params"]) {
  34 + Params.replaceKey(pkey, pval);
  35 + return;
32 36 }
33   - params.replaceKey(pkey, pval);
  37 + oh().getDict().replaceKey("/Params", Dictionary({{pkey, pval}}));
34 38 }
35 39  
36 40 std::string
37 41 QPDFEFStreamObjectHelper::getCreationDate()
38 42 {
39   - auto val = getParam("/CreationDate");
40   - if (val.isString()) {
41   - return val.getUTF8Value();
  43 + if (String CreationDate = getParam("/CreationDate")) {
  44 + return CreationDate.utf8_value();
42 45 }
43   - return "";
  46 + return {};
44 47 }
45 48  
46 49 std::string
47 50 QPDFEFStreamObjectHelper::getModDate()
48 51 {
49   - auto val = getParam("/ModDate");
50   - if (val.isString()) {
51   - return val.getUTF8Value();
  52 + if (String ModDate = getParam("/ModDate")) {
  53 + return ModDate.utf8_value();
52 54 }
53   - return "";
  55 + return {};
54 56 }
55 57  
56 58 size_t
57 59 QPDFEFStreamObjectHelper::getSize()
58 60 {
59   - auto val = getParam("/Size");
60   - if (val.isInteger()) {
61   - return QIntC::to_size(val.getUIntValueAsUInt());
  61 + if (Integer Size = getParam("/Size")) {
  62 + size_t result = Size;
  63 + return result;
62 64 }
63 65 return 0;
64 66 }
... ... @@ -66,30 +68,27 @@ QPDFEFStreamObjectHelper::getSize()
66 68 std::string
67 69 QPDFEFStreamObjectHelper::getSubtype()
68 70 {
69   - auto val = oh().getDict().getKey("/Subtype");
70   - if (val.isName()) {
71   - auto n = val.getName();
72   - if (n.length() > 1) {
73   - return n.substr(1);
  71 + if (Name Subtype = oh().getDict()["/Subtype"]) {
  72 + if (Subtype.value().size() > 1) {
  73 + return Subtype.value().substr(1);
74 74 }
75 75 }
76   - return "";
  76 + return {};
77 77 }
78 78  
79 79 std::string
80 80 QPDFEFStreamObjectHelper::getChecksum()
81 81 {
82   - auto val = getParam("/CheckSum");
83   - if (val.isString()) {
84   - return val.getStringValue();
  82 + if (String CheckSum = getParam("/CheckSum")) {
  83 + return CheckSum.value();
85 84 }
86   - return "";
  85 + return {};
87 86 }
88 87  
89 88 QPDFEFStreamObjectHelper
90 89 QPDFEFStreamObjectHelper::createEFStream(QPDF& qpdf, std::shared_ptr<Buffer> data)
91 90 {
92   - return newFromStream(qpdf.newStream(data));
  91 + return newFromStream(qpdf.newStream(std::move(data)));
93 92 }
94 93  
95 94 QPDFEFStreamObjectHelper
... ... @@ -102,28 +101,28 @@ QPDFEFStreamObjectHelper
102 101 QPDFEFStreamObjectHelper::createEFStream(QPDF& qpdf, std::function<void(Pipeline*)> provider)
103 102 {
104 103 auto stream = qpdf.newStream();
105   - stream.replaceStreamData(provider, QPDFObjectHandle::newNull(), QPDFObjectHandle::newNull());
  104 + stream.replaceStreamData(provider, {}, {});
106 105 return newFromStream(stream);
107 106 }
108 107  
109 108 QPDFEFStreamObjectHelper&
110 109 QPDFEFStreamObjectHelper::setCreationDate(std::string const& date)
111 110 {
112   - setParam("/CreationDate", QPDFObjectHandle::newString(date));
  111 + setParam("/CreationDate", String(date));
113 112 return *this;
114 113 }
115 114  
116 115 QPDFEFStreamObjectHelper&
117 116 QPDFEFStreamObjectHelper::setModDate(std::string const& date)
118 117 {
119   - setParam("/ModDate", QPDFObjectHandle::newString(date));
  118 + setParam("/ModDate", String(date));
120 119 return *this;
121 120 }
122 121  
123 122 QPDFEFStreamObjectHelper&
124 123 QPDFEFStreamObjectHelper::setSubtype(std::string const& subtype)
125 124 {
126   - oh().getDict().replaceKey("/Subtype", QPDFObjectHandle::newName("/" + subtype));
  125 + oh().getDict().replaceKey("/Subtype", Name("/" + subtype));
127 126 return *this;
128 127 }
129 128  
... ... @@ -131,18 +130,16 @@ QPDFEFStreamObjectHelper
131 130 QPDFEFStreamObjectHelper::newFromStream(QPDFObjectHandle stream)
132 131 {
133 132 QPDFEFStreamObjectHelper result(stream);
134   - stream.getDict().replaceKey("/Type", QPDFObjectHandle::newName("/EmbeddedFile"));
135   - Pl_Discard discard;
  133 + stream.getDict().replaceKey("/Type", Name("/EmbeddedFile"));
136 134 // The PDF spec specifies use of MD5 here and notes that it is not to be used for security. MD5
137 135 // is known to be insecure.
138   - Pl_MD5 md5("EF md5", &discard);
139   - Pl_Count count("EF size", &md5);
  136 + Pl_MD5 md5("EF md5");
  137 + pl::Count count(0, &md5);
140 138 if (!stream.pipeStreamData(&count, nullptr, 0, qpdf_dl_all)) {
141 139 stream.warn("unable to get stream data for new embedded file stream");
142 140 } else {
143   - result.setParam("/Size", QPDFObjectHandle::newInteger(count.getCount()));
144   - result.setParam(
145   - "/CheckSum", QPDFObjectHandle::newString(QUtil::hex_decode(md5.getHexDigest())));
  141 + result.setParam("/Size", Integer(count.getCount()));
  142 + result.setParam("/CheckSum", String(QUtil::hex_decode(md5.getHexDigest())));
146 143 }
147 144 return result;
148 145 }
... ...
libqpdf/QPDFFileSpecObjectHelper.cc
1 1 #include <qpdf/QPDFFileSpecObjectHelper.hh>
2 2  
3 3 #include <qpdf/QPDF.hh>
  4 +#include <qpdf/QPDFObjectHandle_private.hh>
4 5 #include <qpdf/QTC.hh>
5 6 #include <qpdf/QUtil.hh>
6 7  
... ... @@ -8,6 +9,11 @@
8 9 #include <string>
9 10  
10 11 using namespace std::literals;
  12 +using namespace qpdf;
  13 +
  14 +class QPDFFileSpecObjectHelper::Members
  15 +{
  16 +};
11 17  
12 18 QPDFFileSpecObjectHelper::QPDFFileSpecObjectHelper(QPDFObjectHandle oh) :
13 19 QPDFObjectHelper(oh)
... ... @@ -26,9 +32,8 @@ static const std::array&lt;std::string, 5&gt; name_keys = {&quot;/UF&quot;s, &quot;/F&quot;s, &quot;/Unix&quot;s, &quot;/
26 32 std::string
27 33 QPDFFileSpecObjectHelper::getDescription()
28 34 {
29   - auto desc = oh().getKey("/Desc");
30   - if (desc.isString()) {
31   - return desc.getUTF8Value();
  35 + if (String Desc = oh().getKey("/Desc")) {
  36 + return Desc.utf8_value();
32 37 }
33 38 return {};
34 39 }
... ... @@ -37,12 +42,11 @@ std::string
37 42 QPDFFileSpecObjectHelper::getFilename()
38 43 {
39 44 for (auto const& i: name_keys) {
40   - auto k = oh().getKey(i);
41   - if (k.isString()) {
42   - return k.getUTF8Value();
  45 + if (String k = oh()[i]) {
  46 + return k.utf8_value();
43 47 }
44 48 }
45   - return "";
  49 + return {};
46 50 }
47 51  
48 52 std::map<std::string, std::string>
... ... @@ -50,9 +54,8 @@ QPDFFileSpecObjectHelper::getFilenames()
50 54 {
51 55 std::map<std::string, std::string> result;
52 56 for (auto const& i: name_keys) {
53   - auto k = oh().getKey(i);
54   - if (k.isString()) {
55   - result[i] = k.getUTF8Value();
  57 + if (String k = oh()[i]) {
  58 + result[i] = k.utf8_value();
56 59 }
57 60 }
58 61 return result;
... ... @@ -61,17 +64,16 @@ QPDFFileSpecObjectHelper::getFilenames()
61 64 QPDFObjectHandle
62 65 QPDFFileSpecObjectHelper::getEmbeddedFileStream(std::string const& key)
63 66 {
64   - auto ef = oh().getKey("/EF");
65   - if (!ef.isDictionary()) {
66   - return QPDFObjectHandle::newNull();
67   - }
68   - if (!key.empty()) {
69   - return ef.getKey(key);
70   - }
71   - for (auto const& i: name_keys) {
72   - auto k = ef.getKey(i);
73   - if (k.isStream()) {
74   - return k;
  67 + if (Dictionary EF = oh()["/EF"]) {
  68 + if (!key.empty() && EF.contains(key)) {
  69 + if (auto result = EF[key]) {
  70 + return result;
  71 + }
  72 + }
  73 + for (auto const& i: name_keys) {
  74 + if (Stream k = EF[i]) {
  75 + return k;
  76 + }
75 77 }
76 78 }
77 79 return QPDFObjectHandle::newNull();
... ... @@ -97,21 +99,18 @@ QPDFFileSpecObjectHelper
97 99 QPDFFileSpecObjectHelper::createFileSpec(
98 100 QPDF& qpdf, std::string const& filename, QPDFEFStreamObjectHelper efsoh)
99 101 {
100   - auto oh = qpdf.makeIndirectObject(QPDFObjectHandle::newDictionary());
101   - oh.replaceKey("/Type", QPDFObjectHandle::newName("/Filespec"));
102   - QPDFFileSpecObjectHelper result(oh);
103   - result.setFilename(filename);
104   - auto ef = QPDFObjectHandle::newDictionary();
105   - ef.replaceKey("/F", efsoh.getObjectHandle());
106   - ef.replaceKey("/UF", efsoh.getObjectHandle());
107   - oh.replaceKey("/EF", ef);
108   - return result;
  102 + auto UF = String::utf16(filename);
  103 + return {qpdf.makeIndirectObject(Dictionary(
  104 + {{"/Type", Name("/Filespec")},
  105 + {"/F", UF},
  106 + {"/UF", UF},
  107 + {"/EF", Dictionary({{"/F", efsoh}, {"/UF", efsoh}})}}))};
109 108 }
110 109  
111 110 QPDFFileSpecObjectHelper&
112 111 QPDFFileSpecObjectHelper::setDescription(std::string const& desc)
113 112 {
114   - oh().replaceKey("/Desc", QPDFObjectHandle::newUnicodeString(desc));
  113 + oh().replaceKey("/Desc", String::utf16(desc));
115 114 return *this;
116 115 }
117 116  
... ... @@ -119,14 +118,12 @@ QPDFFileSpecObjectHelper&amp;
119 118 QPDFFileSpecObjectHelper::setFilename(
120 119 std::string const& unicode_name, std::string const& compat_name)
121 120 {
122   - auto uf = QPDFObjectHandle::newUnicodeString(unicode_name);
  121 + auto uf = String::utf16(unicode_name);
123 122 oh().replaceKey("/UF", uf);
124 123 if (compat_name.empty()) {
125   - QTC::TC("qpdf", "QPDFFileSpecObjectHelper empty compat_name");
126 124 oh().replaceKey("/F", uf);
127 125 } else {
128   - QTC::TC("qpdf", "QPDFFileSpecObjectHelper non-empty compat_name");
129   - oh().replaceKey("/F", QPDFObjectHandle::newString(compat_name));
  126 + oh().replaceKey("/F", String(compat_name));
130 127 }
131 128 return *this;
132 129 }
... ...
libqpdf/qpdf/Pl_MD5.hh
... ... @@ -4,6 +4,8 @@
4 4 #include <qpdf/MD5.hh>
5 5 #include <qpdf/Pipeline.hh>
6 6  
  7 +#include <stdexcept>
  8 +
7 9 // This pipeline sends its output to its successor unmodified. After calling finish, the MD5
8 10 // checksum of the data that passed through the pipeline is available.
9 11  
... ... @@ -12,18 +14,47 @@
12 14 class Pl_MD5 final: public Pipeline
13 15 {
14 16 public:
15   - Pl_MD5(char const* identifier, Pipeline* next);
  17 + Pl_MD5(char const* identifier, Pipeline* next = nullptr) :
  18 + Pipeline(identifier, next)
  19 + {
  20 + }
  21 +
16 22 ~Pl_MD5() final = default;
17 23 void write(unsigned char const*, size_t) final;
18   - void finish() final;
19   - std::string getHexDigest();
  24 + void
  25 + finish() final
  26 + {
  27 + if (next()) {
  28 + next()->finish();
  29 + }
  30 + if (!persist_across_finish) {
  31 + in_progress = false;
  32 + }
  33 + }
  34 + std::string
  35 + getHexDigest()
  36 + {
  37 + if (!enabled) {
  38 + throw std::logic_error("digest requested for a disabled MD5 Pipeline");
  39 + }
  40 + in_progress = false;
  41 + return md5.unparse();
  42 + }
20 43 // Enable/disable. Disabling the pipeline causes it to become a pass-through. This makes it
21 44 // possible to stick an MD5 pipeline in a pipeline when it may or may not be required. Disabling
22 45 // it avoids incurring the runtime overhead of doing needless digest computation.
23   - void enable(bool enabled);
  46 + void
  47 + enable(bool val)
  48 + {
  49 + enabled = val;
  50 + }
24 51 // If persistAcrossFinish is called, calls to finish do not finalize the underlying md5 object.
25 52 // In this case, the object is not finalized until getHexDigest() is called.
26   - void persistAcrossFinish(bool);
  53 + void
  54 + persistAcrossFinish(bool val)
  55 + {
  56 + persist_across_finish = val;
  57 + }
27 58  
28 59 private:
29 60 bool in_progress{false};
... ...
qpdf/qpdf.testcov
... ... @@ -325,8 +325,6 @@ QPDFPageObjectHelper::forEachXObject 3
325 325 NNTree erased last kid/item in tree 1
326 326 QPDFPageObjectHelper unresolved names 0
327 327 QPDFPageObjectHelper resolving unresolved 0
328   -QPDFFileSpecObjectHelper empty compat_name 0
329   -QPDFFileSpecObjectHelper non-empty compat_name 0
330 328 QPDFAcroFormDocumentHelper copy annotation 3
331 329 QPDFAcroFormDocumentHelper field with parent 3
332 330 QPDFObjectHandle merge reuse 0
... ...
qpdf/test_driver.cc
... ... @@ -2644,6 +2644,8 @@ test_76(QPDF&amp; pdf, char const* arg2)
2644 2644 "att2", QPDFFileSpecObjectHelper::createFileSpec(pdf, "att2.txt", efs2));
2645 2645 auto fs3 = QPDFFileSpecObjectHelper::createFileSpec(pdf, "att3.txt", efs3);
2646 2646 efdh.replaceEmbeddedFile("att3", fs3);
  2647 + fs3.setFilename("\xcf\x80.txt");
  2648 + assert(fs3.getFilename() == "\xcf\x80.txt");
2647 2649 fs3.setFilename("\xcf\x80.txt", "att3.txt");
2648 2650  
2649 2651 assert(efs1.getCreationDate() == "D:20210207191121-05'00'");
... ...