Commit 3d0191215d84d28fe63c2b5d78c0f710fd089c13

Authored by m-holger
1 parent a148b870

Refactor QPDF_Dictionary

Move all dictionary specific methods to BaseDictionary. Create new subclass
Dictionary to allow QPDFObjectHandle to access the moved methods.
include/qpdf/ObjectHandle.hh
@@ -22,9 +22,10 @@ @@ -22,9 +22,10 @@
22 22
23 #include <qpdf/Constants.h> 23 #include <qpdf/Constants.h>
24 #include <qpdf/DLL.h> 24 #include <qpdf/DLL.h>
  25 +#include <qpdf/QPDFObjGen.hh>
25 #include <qpdf/Types.h> 26 #include <qpdf/Types.h>
26 27
27 -#include <qpdf/QPDFObjGen.hh> 28 +#include <cstdint>
28 29
29 class QPDF_Dictionary; 30 class QPDF_Dictionary;
30 class QPDFObject; 31 class QPDFObject;
@@ -32,6 +33,11 @@ class QPDFObjectHandle; @@ -32,6 +33,11 @@ class QPDFObjectHandle;
32 33
33 namespace qpdf 34 namespace qpdf
34 { 35 {
  36 + class Dictionary;
  37 + class BaseDictionary;
  38 +
  39 + enum typed : std::uint8_t { strict = 0, any_flag = 1, optional = 2, any = 3, error = 4};
  40 +
35 // Basehandle is only used as a base-class for QPDFObjectHandle like classes. Currently the only 41 // Basehandle is only used as a base-class for QPDFObjectHandle like classes. Currently the only
36 // methods exposed in public API are operators to convert derived objects to QPDFObjectHandle, 42 // methods exposed in public API are operators to convert derived objects to QPDFObjectHandle,
37 // QPDFObjGen and bool. 43 // QPDFObjGen and bool.
@@ -44,6 +50,8 @@ namespace qpdf @@ -44,6 +50,8 @@ namespace qpdf
44 50
45 // The rest of the header file is for qpdf internal use only. 51 // The rest of the header file is for qpdf internal use only.
46 52
  53 + inline qpdf_object_type_e type_code() const;
  54 +
47 protected: 55 protected:
48 BaseHandle() = default; 56 BaseHandle() = default;
49 BaseHandle(std::shared_ptr<QPDFObject> const& obj) : 57 BaseHandle(std::shared_ptr<QPDFObject> const& obj) :
include/qpdf/QPDFObjectHandle.hh
@@ -304,7 +304,7 @@ class QPDFObjectHandle final: public qpdf::BaseHandle @@ -304,7 +304,7 @@ class QPDFObjectHandle final: public qpdf::BaseHandle
304 // other one changes color." This does not perform a structural comparison of the contents of 304 // other one changes color." This does not perform a structural comparison of the contents of
305 // the objects. 305 // the objects.
306 QPDF_DLL 306 QPDF_DLL
307 - bool isSameObjectAs(QPDFObjectHandle const&) const noexcept; 307 + bool isSameObjectAs(QPDFObjectHandle const&) const;
308 308
309 // Return type code and type name of underlying object. These are useful for doing rapid type 309 // Return type code and type name of underlying object. These are useful for doing rapid type
310 // tests (like switch statements) or for testing and debugging. 310 // tests (like switch statements) or for testing and debugging.
@@ -1355,6 +1355,8 @@ class QPDFObjectHandle final: public qpdf::BaseHandle @@ -1355,6 +1355,8 @@ class QPDFObjectHandle final: public qpdf::BaseHandle
1355 1355
1356 void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false) const; 1356 void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false) const;
1357 1357
  1358 + inline qpdf::Dictionary as_dictionary(qpdf::typed options = qpdf::typed::any) const;
  1359 +
1358 private: 1360 private:
1359 QPDF_Array* asArray() const; 1361 QPDF_Array* asArray() const;
1360 QPDF_Bool* asBool() const; 1362 QPDF_Bool* asBool() const;
libqpdf/QPDFObjectHandle.cc
1 -#include <qpdf/QPDFObjectHandle.hh> 1 +#include <qpdf/QPDFObjectHandle_private.hh>
2 2
3 #include <qpdf/BufferInputSource.hh> 3 #include <qpdf/BufferInputSource.hh>
4 #include <qpdf/JSON_writer.hh> 4 #include <qpdf/JSON_writer.hh>
@@ -38,7 +38,6 @@ @@ -38,7 +38,6 @@
38 #include <stdexcept> 38 #include <stdexcept>
39 39
40 using namespace std::literals; 40 using namespace std::literals;
41 -  
42 using namespace qpdf; 41 using namespace qpdf;
43 42
44 BaseHandle:: 43 BaseHandle::
@@ -230,7 +229,7 @@ LastChar::getLastChar() @@ -230,7 +229,7 @@ LastChar::getLastChar()
230 } 229 }
231 230
232 bool 231 bool
233 -QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const noexcept 232 +QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const
234 { 233 {
235 return this->obj == rhs.obj; 234 return this->obj == rhs.obj;
236 } 235 }
@@ -986,9 +985,9 @@ QPDFObjectHandle::ditems() @@ -986,9 +985,9 @@ QPDFObjectHandle::ditems()
986 bool 985 bool
987 QPDFObjectHandle::hasKey(std::string const& key) const 986 QPDFObjectHandle::hasKey(std::string const& key) const
988 { 987 {
989 - auto dict = asDictionary(); 988 + auto dict = as_dictionary(strict);
990 if (dict) { 989 if (dict) {
991 - return dict->hasKey(key); 990 + return dict.hasKey(key);
992 } else { 991 } else {
993 typeWarning("dictionary", "returning false for a key containment request"); 992 typeWarning("dictionary", "returning false for a key containment request");
994 QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey"); 993 QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
@@ -999,14 +998,13 @@ QPDFObjectHandle::hasKey(std::string const&amp; key) const @@ -999,14 +998,13 @@ QPDFObjectHandle::hasKey(std::string const&amp; key) const
999 QPDFObjectHandle 998 QPDFObjectHandle
1000 QPDFObjectHandle::getKey(std::string const& key) const 999 QPDFObjectHandle::getKey(std::string const& key) const
1001 { 1000 {
1002 - if (auto dict = asDictionary()) {  
1003 - return dict->getKey(key);  
1004 - } else {  
1005 - typeWarning("dictionary", "returning null for attempted key retrieval");  
1006 - QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");  
1007 - static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv;  
1008 - return QPDF_Null::create(obj, msg, ""); 1001 + if (auto dict = as_dictionary(strict)) {
  1002 + return dict.getKey(key);
1009 } 1003 }
  1004 + typeWarning("dictionary", "returning null for attempted key retrieval");
  1005 + QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
  1006 + static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv;
  1007 + return QPDF_Null::create(obj, msg, "");
1010 } 1008 }
1011 1009
1012 QPDFObjectHandle 1010 QPDFObjectHandle
@@ -1018,29 +1016,23 @@ QPDFObjectHandle::getKeyIfDict(std::string const&amp; key) const @@ -1018,29 +1016,23 @@ QPDFObjectHandle::getKeyIfDict(std::string const&amp; key) const
1018 std::set<std::string> 1016 std::set<std::string>
1019 QPDFObjectHandle::getKeys() const 1017 QPDFObjectHandle::getKeys() const
1020 { 1018 {
1021 - std::set<std::string> result;  
1022 - auto dict = asDictionary();  
1023 - if (dict) {  
1024 - result = dict->getKeys();  
1025 - } else {  
1026 - typeWarning("dictionary", "treating as empty");  
1027 - QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys"); 1019 + if (auto dict = as_dictionary(strict)) {
  1020 + return dict.getKeys();
1028 } 1021 }
1029 - return result; 1022 + typeWarning("dictionary", "treating as empty");
  1023 + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
  1024 + return {};
1030 } 1025 }
1031 1026
1032 std::map<std::string, QPDFObjectHandle> 1027 std::map<std::string, QPDFObjectHandle>
1033 QPDFObjectHandle::getDictAsMap() const 1028 QPDFObjectHandle::getDictAsMap() const
1034 { 1029 {
1035 - std::map<std::string, QPDFObjectHandle> result;  
1036 - auto dict = asDictionary();  
1037 - if (dict) {  
1038 - result = dict->getAsMap();  
1039 - } else {  
1040 - typeWarning("dictionary", "treating as empty");  
1041 - QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap"); 1030 + if (auto dict = as_dictionary(strict)) {
  1031 + return dict.getAsMap();
1042 } 1032 }
1043 - return result; 1033 + typeWarning("dictionary", "treating as empty");
  1034 + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
  1035 + return {};
1044 } 1036 }
1045 1037
1046 // Array and Name accessors 1038 // Array and Name accessors
@@ -1221,14 +1213,13 @@ QPDFObjectHandle::getUniqueResourceName( @@ -1221,14 +1213,13 @@ QPDFObjectHandle::getUniqueResourceName(
1221 void 1213 void
1222 QPDFObjectHandle::replaceKey(std::string const& key, QPDFObjectHandle const& value) 1214 QPDFObjectHandle::replaceKey(std::string const& key, QPDFObjectHandle const& value)
1223 { 1215 {
1224 - auto dict = asDictionary();  
1225 - if (dict) { 1216 + if (auto dict = as_dictionary(strict)) {
1226 checkOwnership(value); 1217 checkOwnership(value);
1227 - dict->replaceKey(key, value);  
1228 - } else {  
1229 - typeWarning("dictionary", "ignoring key replacement request");  
1230 - QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey"); 1218 + dict.replaceKey(key, value);
  1219 + return;
1231 } 1220 }
  1221 + typeWarning("dictionary", "ignoring key replacement request");
  1222 + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
1232 } 1223 }
1233 1224
1234 QPDFObjectHandle 1225 QPDFObjectHandle
@@ -1249,22 +1240,20 @@ QPDFObjectHandle::replaceKeyAndGetOld(std::string const&amp; key, QPDFObjectHandle c @@ -1249,22 +1240,20 @@ QPDFObjectHandle::replaceKeyAndGetOld(std::string const&amp; key, QPDFObjectHandle c
1249 void 1240 void
1250 QPDFObjectHandle::removeKey(std::string const& key) 1241 QPDFObjectHandle::removeKey(std::string const& key)
1251 { 1242 {
1252 - auto dict = asDictionary();  
1253 - if (dict) {  
1254 - dict->removeKey(key);  
1255 - } else {  
1256 - typeWarning("dictionary", "ignoring key removal request");  
1257 - QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey"); 1243 + if (auto dict = as_dictionary(strict)) {
  1244 + dict.removeKey(key);
  1245 + return;
1258 } 1246 }
  1247 + typeWarning("dictionary", "ignoring key removal request");
  1248 + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
1259 } 1249 }
1260 1250
1261 QPDFObjectHandle 1251 QPDFObjectHandle
1262 QPDFObjectHandle::removeKeyAndGetOld(std::string const& key) 1252 QPDFObjectHandle::removeKeyAndGetOld(std::string const& key)
1263 { 1253 {
1264 auto result = QPDFObjectHandle::newNull(); 1254 auto result = QPDFObjectHandle::newNull();
1265 - auto dict = asDictionary();  
1266 - if (dict) {  
1267 - result = dict->getKey(key); 1255 + if (auto dict = as_dictionary(strict)) {
  1256 + result = dict.getKey(key);
1268 } 1257 }
1269 removeKey(key); 1258 removeKey(key);
1270 return result; 1259 return result;
@@ -2115,9 +2104,9 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams) @@ -2115,9 +2104,9 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams)
2115 this->obj = QPDF_Array::create(items); 2104 this->obj = QPDF_Array::create(items);
2116 } else if (isDictionary()) { 2105 } else if (isDictionary()) {
2117 std::map<std::string, QPDFObjectHandle> items; 2106 std::map<std::string, QPDFObjectHandle> items;
2118 - auto dict = asDictionary(); 2107 + auto dict = as_dictionary(strict);
2119 for (auto const& key: getKeys()) { 2108 for (auto const& key: getKeys()) {
2120 - items[key] = dict->getKey(key); 2109 + items[key] = dict.getKey(key);
2121 items[key].makeDirect(visited, stop_at_streams); 2110 items[key].makeDirect(visited, stop_at_streams);
2122 } 2111 }
2123 this->obj = QPDF_Dictionary::create(items); 2112 this->obj = QPDF_Dictionary::create(items);
libqpdf/QPDF_Dictionary.cc
1 #include <qpdf/QPDF_Dictionary.hh> 1 #include <qpdf/QPDF_Dictionary.hh>
2 2
3 #include <qpdf/JSON_writer.hh> 3 #include <qpdf/JSON_writer.hh>
4 -#include <qpdf/QPDFObjectHandle.hh> 4 +#include <qpdf/QPDFObjectHandle_private.hh>
5 #include <qpdf/QPDFObject_private.hh> 5 #include <qpdf/QPDFObject_private.hh>
6 #include <qpdf/QPDF_Name.hh> 6 #include <qpdf/QPDF_Name.hh>
7 -#include <qpdf/QPDF_Null.hh> 7 +#include <qpdf/QTC.hh>
8 #include <qpdf/QUtil.hh> 8 #include <qpdf/QUtil.hh>
9 9
10 using namespace std::literals; 10 using namespace std::literals;
@@ -96,31 +96,45 @@ QPDF_Dictionary::writeJSON(int json_version, JSON::Writer&amp; p) @@ -96,31 +96,45 @@ QPDF_Dictionary::writeJSON(int json_version, JSON::Writer&amp; p)
96 p.writeEnd('}'); 96 p.writeEnd('}');
97 } 97 }
98 98
  99 +QPDF_Dictionary*
  100 +BaseDictionary::dict() const
  101 +{
  102 + if (obj) {
  103 + if (auto d = obj->as<QPDF_Dictionary>()) {
  104 + return d;
  105 + }
  106 + }
  107 + throw std::runtime_error("Expected a dictionary but found a non-dictionary object");
  108 + return nullptr; // unreachable
  109 +}
  110 +
99 bool 111 bool
100 -QPDF_Dictionary::hasKey(std::string const& key) 112 +BaseDictionary::hasKey(std::string const& key) const
101 { 113 {
102 - return ((this->items.count(key) > 0) && (!this->items[key].isNull())); 114 + auto d = dict();
  115 + return d->items.count(key) > 0 && !d->items[key].isNull();
103 } 116 }
104 117
105 QPDFObjectHandle 118 QPDFObjectHandle
106 -QPDF_Dictionary::getKey(std::string const& key) 119 +BaseDictionary::getKey(std::string const& key) const
107 { 120 {
  121 + auto d = dict();
  122 +
108 // PDF spec says fetching a non-existent key from a dictionary returns the null object. 123 // PDF spec says fetching a non-existent key from a dictionary returns the null object.
109 - auto item = this->items.find(key);  
110 - if (item != this->items.end()) { 124 + auto item = d->items.find(key);
  125 + if (item != d->items.end()) {
111 // May be a null object 126 // May be a null object
112 return item->second; 127 return item->second;
113 - } else {  
114 - static auto constexpr msg = " -> dictionary key $VD"sv;  
115 - return QPDF_Null::create(shared_from_this(), msg, key);  
116 } 128 }
  129 + static auto constexpr msg = " -> dictionary key $VD"sv;
  130 + return QPDF_Null::create(obj, msg, key);
117 } 131 }
118 132
119 std::set<std::string> 133 std::set<std::string>
120 -QPDF_Dictionary::getKeys() 134 +BaseDictionary::getKeys()
121 { 135 {
122 std::set<std::string> result; 136 std::set<std::string> result;
123 - for (auto& iter: this->items) { 137 + for (auto& iter: dict()->items) {
124 if (!iter.second.isNull()) { 138 if (!iter.second.isNull()) {
125 result.insert(iter.first); 139 result.insert(iter.first);
126 } 140 }
@@ -129,28 +143,29 @@ QPDF_Dictionary::getKeys() @@ -129,28 +143,29 @@ QPDF_Dictionary::getKeys()
129 } 143 }
130 144
131 std::map<std::string, QPDFObjectHandle> const& 145 std::map<std::string, QPDFObjectHandle> const&
132 -QPDF_Dictionary::getAsMap() const 146 +BaseDictionary::getAsMap() const
133 { 147 {
134 - return this->items; 148 + return dict()->items;
135 } 149 }
136 150
137 void 151 void
138 -QPDF_Dictionary::replaceKey(std::string const& key, QPDFObjectHandle value) 152 +BaseDictionary::removeKey(std::string const& key)
139 { 153 {
140 - if (value.isNull() && !value.isIndirect()) {  
141 - // The PDF spec doesn't distinguish between keys with null values and missing keys. Allow  
142 - // indirect nulls which are equivalent to a dangling reference, which is permitted by the  
143 - // spec.  
144 - removeKey(key);  
145 - } else {  
146 - // add or replace value  
147 - this->items[key] = value;  
148 - } 154 + // no-op if key does not exist
  155 + dict()->items.erase(key);
149 } 156 }
150 157
151 void 158 void
152 -QPDF_Dictionary::removeKey(std::string const& key) 159 +BaseDictionary::replaceKey(std::string const& key, QPDFObjectHandle value)
153 { 160 {
154 - // no-op if key does not exist  
155 - this->items.erase(key); 161 + auto d = dict();
  162 + if (value.isNull() && !value.isIndirect()) {
  163 + // The PDF spec doesn't distinguish between keys with null values and missing keys.
  164 + // Allow indirect nulls which are equivalent to a dangling reference, which is
  165 + // permitted by the spec.
  166 + d->items.erase(key);
  167 + } else {
  168 + // add or replace value
  169 + d->items[key] = value;
  170 + }
156 } 171 }
libqpdf/qpdf/QPDFObjectHandle_private.hh 0 โ†’ 100644
  1 +#ifndef OBJECTHANDLE_PRIVATE_HH
  2 +#define OBJECTHANDLE_PRIVATE_HH
  3 +
  4 +#include <qpdf/QPDFObjectHandle.hh>
  5 +
  6 +#include <qpdf/QPDFObject_private.hh>
  7 +#include <qpdf/QPDF_Dictionary.hh>
  8 +
  9 +namespace qpdf
  10 +{
  11 + // BaseDictionary is only used as a base class. It does not contain any methods exposed in the
  12 + // public API.
  13 + class BaseDictionary: public BaseHandle
  14 + {
  15 + public:
  16 + // The following methods are not part of the public API.
  17 + bool hasKey(std::string const& key) const;
  18 + QPDFObjectHandle getKey(std::string const& key) const;
  19 + std::set<std::string> getKeys();
  20 + std::map<std::string, QPDFObjectHandle> const& getAsMap() const;
  21 + void removeKey(std::string const& key);
  22 + void replaceKey(std::string const& key, QPDFObjectHandle value);
  23 +
  24 + protected:
  25 + BaseDictionary() = default;
  26 + BaseDictionary(std::shared_ptr<QPDFObject> const& obj) :
  27 + BaseHandle(obj) {};
  28 + BaseDictionary(std::shared_ptr<QPDFObject>&& obj) :
  29 + BaseHandle(std::move(obj)) {};
  30 + BaseDictionary(BaseDictionary const&) = default;
  31 + BaseDictionary& operator=(BaseDictionary const&) = default;
  32 + BaseDictionary(BaseDictionary&&) = default;
  33 + BaseDictionary& operator=(BaseDictionary&&) = default;
  34 + ~BaseDictionary() = default;
  35 +
  36 + QPDF_Dictionary* dict() const;
  37 + };
  38 +
  39 + class Dictionary final: public BaseDictionary
  40 + {
  41 + public:
  42 + explicit Dictionary(std::shared_ptr<QPDFObject> const& obj) :
  43 + BaseDictionary(obj)
  44 + {
  45 + }
  46 +
  47 + explicit Dictionary(std::shared_ptr<QPDFObject>&& obj) :
  48 + BaseDictionary(std::move(obj))
  49 + {
  50 + }
  51 + };
  52 +
  53 + inline qpdf_object_type_e
  54 + BaseHandle::type_code() const
  55 + {
  56 + return obj ? obj->getResolvedTypeCode() : ::ot_uninitialized;
  57 + }
  58 +
  59 +} // namespace qpdf
  60 +
  61 +inline qpdf::Dictionary
  62 +QPDFObjectHandle::as_dictionary(qpdf::typed options) const
  63 +{
  64 + if (options & qpdf::any_flag || type_code() == ::ot_dictionary ||
  65 + (options & qpdf::optional && type_code() == ::ot_null)) {
  66 + return qpdf::Dictionary(obj);
  67 + }
  68 + if (options & qpdf::error) {
  69 + assertType("dictionary", false);
  70 + }
  71 + return qpdf::Dictionary(std::shared_ptr<QPDFObject>());
  72 +}
  73 +
  74 +#endif // OBJECTHANDLE_PRIVATE_HH
libqpdf/qpdf/QPDF_Dictionary.hh
@@ -6,7 +6,10 @@ @@ -6,7 +6,10 @@
6 #include <map> 6 #include <map>
7 #include <set> 7 #include <set>
8 8
  9 +#include <qpdf/QPDFExc.hh>
9 #include <qpdf/QPDFObjectHandle.hh> 10 #include <qpdf/QPDFObjectHandle.hh>
  11 +#include <qpdf/QPDFObject_private.hh>
  12 +#include <qpdf/QPDF_Null.hh>
10 13
11 class QPDF_Dictionary: public QPDFValue 14 class QPDF_Dictionary: public QPDFValue
12 { 15 {
@@ -19,22 +22,12 @@ class QPDF_Dictionary: public QPDFValue @@ -19,22 +22,12 @@ class QPDF_Dictionary: public QPDFValue
19 void writeJSON(int json_version, JSON::Writer& p) override; 22 void writeJSON(int json_version, JSON::Writer& p) override;
20 void disconnect() override; 23 void disconnect() override;
21 24
22 - // hasKey() and getKeys() treat keys with null values as if they aren't there. getKey() returns  
23 - // null for the value of a non-existent key. This is as per the PDF spec.  
24 - bool hasKey(std::string const&);  
25 - QPDFObjectHandle getKey(std::string const&);  
26 - std::set<std::string> getKeys();  
27 - std::map<std::string, QPDFObjectHandle> const& getAsMap() const;  
28 -  
29 - // If value is null, remove key; otherwise, replace the value of key, adding it if it does not  
30 - // exist.  
31 - void replaceKey(std::string const& key, QPDFObjectHandle value);  
32 - // Remove key, doing nothing if key does not exist  
33 - void removeKey(std::string const& key);  
34 -  
35 private: 25 private:
  26 + friend class qpdf::BaseDictionary;
  27 +
36 QPDF_Dictionary(std::map<std::string, QPDFObjectHandle> const& items); 28 QPDF_Dictionary(std::map<std::string, QPDFObjectHandle> const& items);
37 QPDF_Dictionary(std::map<std::string, QPDFObjectHandle>&& items); 29 QPDF_Dictionary(std::map<std::string, QPDFObjectHandle>&& items);
  30 +
38 std::map<std::string, QPDFObjectHandle> items; 31 std::map<std::string, QPDFObjectHandle> items;
39 }; 32 };
40 33