/src/qpdf/libqpdf/QPDF_Dictionary.cc
Line | Count | Source |
1 | | #include <qpdf/QPDFObjectHandle_private.hh> |
2 | | |
3 | | #include <qpdf/QPDFObject_private.hh> |
4 | | #include <qpdf/QTC.hh> |
5 | | #include <qpdf/Util.hh> |
6 | | |
7 | | using namespace std::literals; |
8 | | using namespace qpdf; |
9 | | |
10 | | QPDF_Dictionary* |
11 | | BaseDictionary::dict() const |
12 | 14.2k | { |
13 | 14.2k | if (auto d = as<QPDF_Dictionary>()) { |
14 | 14.2k | return d; |
15 | 14.2k | } |
16 | 0 | throw std::runtime_error("Expected a dictionary but found a non-dictionary object"); |
17 | 0 | return nullptr; // unreachable |
18 | 14.2k | } |
19 | | |
20 | | QPDFObjectHandle const& |
21 | | BaseHandle::operator[](std::string const& key) const |
22 | 1.48M | { |
23 | 1.48M | if (auto d = as<QPDF_Dictionary>()) { |
24 | 1.46M | auto it = d->items.find(key); |
25 | 1.46M | if (it != d->items.end()) { |
26 | 739k | return it->second; |
27 | 739k | } |
28 | 1.46M | } |
29 | 747k | static const QPDFObjectHandle null_obj; |
30 | 747k | return null_obj; |
31 | 1.48M | } |
32 | | |
33 | | /// Retrieves a reference to the QPDFObjectHandle associated with the given key in the |
34 | | /// dictionary object contained within this instance. |
35 | | /// |
36 | | /// If the current object is not of dictionary type, a `std::runtime_error` is thrown. |
37 | | /// According to the PDF specification, missing keys in the dictionary are treated as |
38 | | /// keys with a `null` value. This behavior is reflected in this function's implementation, |
39 | | /// where a missing key will still return a reference to a newly inserted null value entry. |
40 | | /// |
41 | | /// @param key The key for which the corresponding value in the dictionary is retrieved. |
42 | | /// @return A reference to the QPDFObjectHandle associated with the specified key. |
43 | | /// @throws std::runtime_error if the current object is not a dictionary. |
44 | | QPDFObjectHandle& |
45 | | BaseHandle::at(std::string const& key) const |
46 | 0 | { |
47 | 0 | auto d = as<QPDF_Dictionary>(); |
48 | 0 | if (!d) { |
49 | 0 | throw std::runtime_error("Expected a dictionary but found a non-dictionary object"); |
50 | 0 | } |
51 | 0 | return d->items[key]; |
52 | 0 | } |
53 | | |
54 | | /// @brief Checks if the specified key exists in the object. |
55 | | /// |
56 | | /// This method determines whether the given key is present in the object by verifying if the |
57 | | /// associated value is non-null. |
58 | | /// |
59 | | /// @param key The key to look for in the object. |
60 | | /// @return True if the key exists and its associated value is non-null. Otherwise, returns false. |
61 | | bool |
62 | | BaseHandle::contains(std::string const& key) const |
63 | 116k | { |
64 | 116k | return !(*this)[key].null(); |
65 | 116k | } |
66 | | |
67 | | /// @brief Retrieves the value associated with the given key from dictionary. |
68 | | /// |
69 | | /// This method attempts to find the value corresponding to the specified key for objects that can |
70 | | /// be interpreted as dictionaries. |
71 | | /// |
72 | | /// - If the object is a dictionary and the specified key exists, it returns a reference |
73 | | /// to the associated value. |
74 | | /// - If the object is not a dictionary or the specified key does not exist, it returns |
75 | | /// a reference to a static uninitialized object handle. |
76 | | /// |
77 | | /// @note Modifying the uninitialized object returned when the key is not found is strictly |
78 | | /// prohibited. |
79 | | /// |
80 | | /// @param key The key whose associated value should be retrieved. |
81 | | /// @return A reference to the associated value if the key is found or a reference to a static |
82 | | /// uninitialized object if the key is not found. |
83 | | QPDFObjectHandle& |
84 | | BaseHandle::find(std::string const& key) const |
85 | 0 | { |
86 | 0 | static const QPDFObjectHandle null_obj; |
87 | 0 | qpdf_invariant(!null_obj); |
88 | 0 | if (auto d = as<QPDF_Dictionary>()) { |
89 | 0 | auto it = d->items.find(key); |
90 | 0 | if (it != d->items.end()) { |
91 | 0 | return it->second; |
92 | 0 | } |
93 | 0 | } |
94 | 0 | return const_cast<QPDFObjectHandle&>(null_obj); |
95 | 0 | } |
96 | | |
97 | | std::set<std::string> |
98 | | BaseDictionary::getKeys() |
99 | 14.2k | { |
100 | 14.2k | std::set<std::string> result; |
101 | 64.9k | for (auto& iter: dict()->items) { |
102 | 64.9k | if (!iter.second.null()) { |
103 | 55.5k | result.insert(iter.first); |
104 | 55.5k | } |
105 | 64.9k | } |
106 | 14.2k | return result; |
107 | 14.2k | } |
108 | | |
109 | | std::map<std::string, QPDFObjectHandle> const& |
110 | | BaseDictionary::getAsMap() const |
111 | 0 | { |
112 | 0 | return dict()->items; |
113 | 0 | } |
114 | | |
115 | | size_t |
116 | | BaseHandle::erase(const std::string& key) |
117 | 398k | { |
118 | | // no-op if key does not exist |
119 | 398k | if (auto d = as<QPDF_Dictionary>()) { |
120 | 398k | return d->items.erase(key); |
121 | 398k | } |
122 | 0 | return 0; |
123 | 398k | } |
124 | | |
125 | | bool |
126 | | BaseHandle::replace(std::string const& key, QPDFObjectHandle value) |
127 | 73.0k | { |
128 | 73.0k | if (auto d = as<QPDF_Dictionary>()) { |
129 | 73.0k | if (value.null() && !value.indirect()) { |
130 | | // The PDF spec doesn't distinguish between keys with null values and missing keys. |
131 | | // Allow indirect nulls which are equivalent to a dangling reference, which is permitted |
132 | | // by the spec. |
133 | 0 | d->items.erase(key); |
134 | 73.0k | } else { |
135 | | // add or replace value |
136 | 73.0k | d->items[key] = value; |
137 | 73.0k | } |
138 | 73.0k | return true; |
139 | 73.0k | } |
140 | 0 | return false; |
141 | 73.0k | } |
142 | | |
143 | | void |
144 | | BaseDictionary::replace(std::string const& key, QPDFObjectHandle value) |
145 | 73.0k | { |
146 | 73.0k | if (!BaseHandle::replace(key, value)) { |
147 | 0 | (void)dict(); |
148 | 0 | } |
149 | 73.0k | } |
150 | | |
151 | | Dictionary::Dictionary(std::map<std::string, QPDFObjectHandle>&& dict) : |
152 | 0 | BaseDictionary(std::move(dict)) |
153 | 0 | { |
154 | 0 | } |
155 | | |
156 | | Dictionary::Dictionary(std::shared_ptr<QPDFObject> const& obj) : |
157 | 654k | BaseDictionary(obj) |
158 | 654k | { |
159 | 654k | } |
160 | | |
161 | | Dictionary |
162 | | Dictionary::empty() |
163 | 0 | { |
164 | 0 | return Dictionary(std::map<std::string, QPDFObjectHandle>()); |
165 | 0 | } |
166 | | |
167 | | void |
168 | | QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const |
169 | 73.0k | { |
170 | 73.0k | auto qpdf = getOwningQPDF(); |
171 | 73.0k | auto item_qpdf = item.getOwningQPDF(); |
172 | 73.0k | if (qpdf && item_qpdf && qpdf != item_qpdf) { |
173 | 0 | throw std::logic_error( |
174 | 0 | "Attempting to add an object from a different QPDF. Use " |
175 | 0 | "QPDF::copyForeignObject to add objects from another file."); |
176 | 0 | } |
177 | 73.0k | } |
178 | | |
179 | | bool |
180 | | QPDFObjectHandle::hasKey(std::string const& key) const |
181 | 96.4k | { |
182 | 96.4k | if (Dictionary dict = *this) { |
183 | 96.2k | return dict.contains(key); |
184 | 96.2k | } else { |
185 | 182 | typeWarning("dictionary", "returning false for a key containment request"); |
186 | 182 | QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey"); |
187 | 182 | return false; |
188 | 182 | } |
189 | 96.4k | } |
190 | | |
191 | | QPDFObjectHandle |
192 | | QPDFObjectHandle::getKey(std::string const& key) const |
193 | 432k | { |
194 | 432k | if (auto result = get(key)) { |
195 | 207k | return result; |
196 | 207k | } |
197 | 225k | if (isDictionary()) { |
198 | 225k | static auto constexpr msg = " -> dictionary key $VD"sv; |
199 | 225k | return QPDF_Null::create(obj, msg, key); |
200 | 225k | } |
201 | 0 | typeWarning("dictionary", "returning null for attempted key retrieval"); |
202 | 0 | static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv; |
203 | 0 | return QPDF_Null::create(obj, msg, ""); |
204 | 225k | } |
205 | | |
206 | | QPDFObjectHandle |
207 | | QPDFObjectHandle::getKeyIfDict(std::string const& key) const |
208 | 0 | { |
209 | 0 | return isNull() ? newNull() : getKey(key); |
210 | 0 | } |
211 | | |
212 | | std::set<std::string> |
213 | | QPDFObjectHandle::getKeys() const |
214 | 14.3k | { |
215 | 14.3k | if (auto dict = as_dictionary(strict)) { |
216 | 14.2k | return dict.getKeys(); |
217 | 14.2k | } |
218 | 77 | typeWarning("dictionary", "treating as empty"); |
219 | 77 | QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys"); |
220 | 77 | return {}; |
221 | 14.3k | } |
222 | | |
223 | | std::map<std::string, QPDFObjectHandle> |
224 | | QPDFObjectHandle::getDictAsMap() const |
225 | 0 | { |
226 | 0 | if (auto dict = as_dictionary(strict)) { |
227 | 0 | return dict.getAsMap(); |
228 | 0 | } |
229 | 0 | typeWarning("dictionary", "treating as empty"); |
230 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap"); |
231 | 0 | return {}; |
232 | 0 | } |
233 | | |
234 | | void |
235 | | QPDFObjectHandle::replaceKey(std::string const& key, QPDFObjectHandle const& value) |
236 | 78.0k | { |
237 | 78.0k | if (auto dict = as_dictionary(strict)) { |
238 | 73.0k | checkOwnership(value); |
239 | 73.0k | dict.replace(key, value); |
240 | 73.0k | return; |
241 | 73.0k | } |
242 | 4.97k | typeWarning("dictionary", "ignoring key replacement request"); |
243 | 4.97k | } |
244 | | |
245 | | QPDFObjectHandle |
246 | | QPDFObjectHandle::replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value) |
247 | 351 | { |
248 | 351 | replaceKey(key, value); |
249 | 351 | return value; |
250 | 351 | } |
251 | | |
252 | | QPDFObjectHandle |
253 | | QPDFObjectHandle::replaceKeyAndGetOld(std::string const& key, QPDFObjectHandle const& value) |
254 | 0 | { |
255 | 0 | QPDFObjectHandle old = removeKeyAndGetOld(key); |
256 | 0 | replaceKey(key, value); |
257 | 0 | return old; |
258 | 0 | } |
259 | | |
260 | | void |
261 | | QPDFObjectHandle::removeKey(std::string const& key) |
262 | 124k | { |
263 | 124k | if (erase(key) || isDictionary()) { |
264 | 124k | return; |
265 | 124k | } |
266 | 0 | typeWarning("dictionary", "ignoring key removal request"); |
267 | 0 | } |
268 | | |
269 | | QPDFObjectHandle |
270 | | QPDFObjectHandle::removeKeyAndGetOld(std::string const& key) |
271 | 0 | { |
272 | 0 | auto result = get(key); |
273 | 0 | erase(key); |
274 | 0 | return result ? result : newNull(); |
275 | 0 | } |