Commit d16e822d77d41c026e44bce459e5417c4e7b885d
Committed by
GitHub
Merge pull request #1532 from m-holger/oh_dict
Refactor private-API class Dictionary
Showing
8 changed files
with
306 additions
and
271 deletions
include/qpdf/ObjectHandle.hh
| @@ -61,6 +61,13 @@ namespace qpdf | @@ -61,6 +61,13 @@ namespace qpdf | ||
| 61 | 61 | ||
| 62 | // The rest of the header file is for qpdf internal use only. | 62 | // The rest of the header file is for qpdf internal use only. |
| 63 | 63 | ||
| 64 | + // Return true if both object handles refer to the same underlying object. | ||
| 65 | + bool | ||
| 66 | + operator==(BaseHandle const& other) const | ||
| 67 | + { | ||
| 68 | + return obj == other.obj; | ||
| 69 | + } | ||
| 70 | + | ||
| 64 | // For arrays, return the number of items in the array. | 71 | // For arrays, return the number of items in the array. |
| 65 | // For null-like objects, return 0. | 72 | // For null-like objects, return 0. |
| 66 | // For all other objects, return 1. | 73 | // For all other objects, return 1. |
| @@ -76,6 +83,10 @@ namespace qpdf | @@ -76,6 +83,10 @@ namespace qpdf | ||
| 76 | QPDFObjectHandle operator[](size_t n) const; | 83 | QPDFObjectHandle operator[](size_t n) const; |
| 77 | QPDFObjectHandle operator[](int n) const; | 84 | QPDFObjectHandle operator[](int n) const; |
| 78 | 85 | ||
| 86 | + bool contains(std::string const& key) const; | ||
| 87 | + size_t erase(std::string const& key); | ||
| 88 | + QPDFObjectHandle const& operator[](std::string const& key) const; | ||
| 89 | + | ||
| 79 | std::shared_ptr<QPDFObject> copy(bool shallow = false) const; | 90 | std::shared_ptr<QPDFObject> copy(bool shallow = false) const; |
| 80 | // Recursively remove association with any QPDF object. This method may only be called | 91 | // Recursively remove association with any QPDF object. This method may only be called |
| 81 | // during final destruction. | 92 | // during final destruction. |
libqpdf/NNTree.cc
| @@ -57,75 +57,64 @@ NNTreeIterator::updateIValue(bool allow_invalid) | @@ -57,75 +57,64 @@ NNTreeIterator::updateIValue(bool allow_invalid) | ||
| 57 | // we must call updateIValue as well. These cases are handled, and for good measure, we also | 57 | // we must call updateIValue as well. These cases are handled, and for good measure, we also |
| 58 | // call updateIValue in operator* and operator->. | 58 | // call updateIValue in operator* and operator->. |
| 59 | 59 | ||
| 60 | - if (item_number < 0 || !node.isDictionary()) { | 60 | + Array items = node[impl.itemsKey()]; |
| 61 | + ivalue.first = items[item_number]; | ||
| 62 | + ivalue.second = items[item_number + 1]; | ||
| 63 | + if (ivalue.second) { | ||
| 64 | + return; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + if (item_number < 0 || !node) { | ||
| 61 | if (!allow_invalid) { | 68 | if (!allow_invalid) { |
| 62 | throw std::logic_error( | 69 | throw std::logic_error( |
| 63 | "attempt made to dereference an invalid name/number tree iterator"); | 70 | "attempt made to dereference an invalid name/number tree iterator"); |
| 64 | } | 71 | } |
| 65 | - ivalue.first = QPDFObjectHandle(); | ||
| 66 | - ivalue.second = QPDFObjectHandle(); | ||
| 67 | return; | 72 | return; |
| 68 | } | 73 | } |
| 69 | - Array items = node.getKey(impl.itemsKey()); | ||
| 70 | - if (!std::cmp_less(item_number + 1, items.size())) { | ||
| 71 | - impl.error(node, "update ivalue: items array is too short"); | ||
| 72 | - } | ||
| 73 | - ivalue.first = items[item_number]; | ||
| 74 | - ivalue.second = items[1 + item_number]; | ||
| 75 | -} | ||
| 76 | - | ||
| 77 | -NNTreeIterator::PathElement::PathElement(QPDFObjectHandle const& node, int kid_number) : | ||
| 78 | - node(node), | ||
| 79 | - kid_number(kid_number) | ||
| 80 | -{ | 74 | + impl.error(node, "update ivalue: items array is too short"); |
| 81 | } | 75 | } |
| 82 | 76 | ||
| 83 | -QPDFObjectHandle | 77 | +Dictionary |
| 84 | NNTreeIterator::getNextKid(PathElement& pe, bool backward) | 78 | NNTreeIterator::getNextKid(PathElement& pe, bool backward) |
| 85 | { | 79 | { |
| 86 | while (true) { | 80 | while (true) { |
| 87 | pe.kid_number += backward ? -1 : 1; | 81 | pe.kid_number += backward ? -1 : 1; |
| 88 | - Array kids = pe.node.getKey("/Kids"); | ||
| 89 | - if (pe.kid_number >= 0 && std::cmp_less(pe.kid_number, kids.size())) { | ||
| 90 | - auto result = kids[pe.kid_number]; | ||
| 91 | - if (result.isDictionary() && | ||
| 92 | - (result.hasKey("/Kids") || result.hasKey(impl.itemsKey()))) { | ||
| 93 | - return result; | ||
| 94 | - } else { | ||
| 95 | - impl.warn( | ||
| 96 | - pe.node, "skipping over invalid kid at index " + std::to_string(pe.kid_number)); | ||
| 97 | - } | ||
| 98 | - } else { | ||
| 99 | - return QPDFObjectHandle::newNull(); | 82 | + Dictionary result = pe.node["/Kids"][pe.kid_number]; |
| 83 | + if (result.contains("/Kids") || result.contains(impl.itemsKey())) { | ||
| 84 | + return result; | ||
| 85 | + } | ||
| 86 | + if (pe.kid_number < 0 || std::cmp_greater_equal(pe.kid_number, pe.node["/Kids"].size())) { | ||
| 87 | + return {}; | ||
| 100 | } | 88 | } |
| 89 | + impl.warn(pe.node, "skipping over invalid kid at index " + std::to_string(pe.kid_number)); | ||
| 101 | } | 90 | } |
| 102 | } | 91 | } |
| 103 | void | 92 | void |
| 104 | NNTreeIterator::increment(bool backward) | 93 | NNTreeIterator::increment(bool backward) |
| 105 | { | 94 | { |
| 106 | if (item_number < 0) { | 95 | if (item_number < 0) { |
| 107 | - deepen(impl.oh, !backward, true); | 96 | + deepen(impl.tree_root, !backward, true); |
| 108 | return; | 97 | return; |
| 109 | } | 98 | } |
| 110 | 99 | ||
| 111 | while (valid()) { | 100 | while (valid()) { |
| 112 | item_number += backward ? -2 : 2; | 101 | item_number += backward ? -2 : 2; |
| 113 | - Array items = node.getKey(impl.itemsKey()); | 102 | + Array items = node[impl.itemsKey()]; |
| 114 | if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) { | 103 | if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) { |
| 115 | - bool found = false; | ||
| 116 | setItemNumber(QPDFObjectHandle(), -1); | 104 | setItemNumber(QPDFObjectHandle(), -1); |
| 117 | - while (!(found || path.empty())) { | 105 | + while (!path.empty()) { |
| 118 | auto& element = path.back(); | 106 | auto& element = path.back(); |
| 119 | - auto pe_node = getNextKid(element, backward); | ||
| 120 | - if (pe_node.null()) { | ||
| 121 | - path.pop_back(); | 107 | + if (auto pe_node = getNextKid(element, backward)) { |
| 108 | + if (deepen(pe_node, !backward, false)) { | ||
| 109 | + break; | ||
| 110 | + } | ||
| 122 | } else { | 111 | } else { |
| 123 | - found = deepen(pe_node, !backward, false); | 112 | + path.pop_back(); |
| 124 | } | 113 | } |
| 125 | } | 114 | } |
| 126 | } | 115 | } |
| 127 | if (item_number >= 0) { | 116 | if (item_number >= 0) { |
| 128 | - items = node.getKey(impl.itemsKey()); | 117 | + items = node[impl.itemsKey()]; |
| 129 | if (std::cmp_greater_equal(item_number + 1, items.size())) { | 118 | if (std::cmp_greater_equal(item_number + 1, items.size())) { |
| 130 | impl.warn(node, "items array doesn't have enough elements"); | 119 | impl.warn(node, "items array doesn't have enough elements"); |
| 131 | } else if (!impl.keyValid(items[item_number])) { | 120 | } else if (!impl.keyValid(items[item_number])) { |
| @@ -140,65 +129,59 @@ NNTreeIterator::increment(bool backward) | @@ -140,65 +129,59 @@ NNTreeIterator::increment(bool backward) | ||
| 140 | } | 129 | } |
| 141 | 130 | ||
| 142 | void | 131 | void |
| 143 | -NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list<PathElement>::iterator parent) | 132 | +NNTreeIterator::resetLimits(Dictionary a_node, std::list<PathElement>::iterator parent) |
| 144 | { | 133 | { |
| 145 | while (true) { | 134 | while (true) { |
| 146 | if (parent == path.end()) { | 135 | if (parent == path.end()) { |
| 147 | - a_node.removeKey("/Limits"); | ||
| 148 | - break; | 136 | + a_node.erase("/Limits"); |
| 137 | + return; | ||
| 149 | } | 138 | } |
| 150 | - Array kids = a_node.getKey("/Kids"); | ||
| 151 | - size_t nkids = kids.size(); | ||
| 152 | - Array items = a_node.getKey(impl.itemsKey()); | ||
| 153 | - size_t nitems = items.size(); | ||
| 154 | 139 | ||
| 155 | - bool changed = true; | ||
| 156 | QPDFObjectHandle first; | 140 | QPDFObjectHandle first; |
| 157 | QPDFObjectHandle last; | 141 | QPDFObjectHandle last; |
| 142 | + Array items = a_node[impl.itemsKey()]; | ||
| 143 | + size_t nitems = items.size(); | ||
| 158 | if (nitems >= 2) { | 144 | if (nitems >= 2) { |
| 159 | first = items[0]; | 145 | first = items[0]; |
| 160 | last = items[(nitems - 1u) & ~1u]; | 146 | last = items[(nitems - 1u) & ~1u]; |
| 161 | - } else if (nkids > 0) { | ||
| 162 | - auto first_kid = kids[0]; | ||
| 163 | - auto last_kid = kids[nkids - 1u]; | ||
| 164 | - if (first_kid.isDictionary() && last_kid.isDictionary()) { | ||
| 165 | - Array first_limits = first_kid.getKey("/Limits"); | ||
| 166 | - Array last_limits = last_kid.getKey("/Limits"); | ||
| 167 | - if (first_limits.size() >= 2 && last_limits.size() >= 2) { | 147 | + } else { |
| 148 | + Array kids = a_node["/Kids"]; | ||
| 149 | + size_t nkids = kids.size(); | ||
| 150 | + if (nkids > 0) { | ||
| 151 | + Array first_limits = kids[0]["/Limits"]; | ||
| 152 | + if (first_limits.size() >= 2) { | ||
| 168 | first = first_limits[0]; | 153 | first = first_limits[0]; |
| 169 | - last = last_limits[1]; | 154 | + last = kids[nkids - 1u]["/Limits"][1]; |
| 170 | } | 155 | } |
| 171 | } | 156 | } |
| 172 | } | 157 | } |
| 173 | - if (first && last) { | ||
| 174 | - Array limits({first, last}); | ||
| 175 | - Array olimits = a_node.getKey("/Limits"); | 158 | + if (!(first && last)) { |
| 159 | + impl.warn(a_node, "unable to determine limits"); | ||
| 160 | + } else { | ||
| 161 | + Array olimits = a_node["/Limits"]; | ||
| 176 | if (olimits.size() == 2) { | 162 | if (olimits.size() == 2) { |
| 177 | auto ofirst = olimits[0]; | 163 | auto ofirst = olimits[0]; |
| 178 | auto olast = olimits[1]; | 164 | auto olast = olimits[1]; |
| 179 | if (impl.keyValid(ofirst) && impl.keyValid(olast) && | 165 | if (impl.keyValid(ofirst) && impl.keyValid(olast) && |
| 180 | impl.compareKeys(first, ofirst) == 0 && impl.compareKeys(last, olast) == 0) { | 166 | impl.compareKeys(first, ofirst) == 0 && impl.compareKeys(last, olast) == 0) { |
| 181 | - changed = false; | 167 | + return; |
| 182 | } | 168 | } |
| 183 | } | 169 | } |
| 184 | - if (changed && !a_node.isSameObjectAs(path.begin()->node)) { | ||
| 185 | - a_node.replaceKey("/Limits", limits); | 170 | + if (a_node != path.begin()->node) { |
| 171 | + a_node.replaceKey("/Limits", Array({first, last})); | ||
| 186 | } | 172 | } |
| 187 | - } else { | ||
| 188 | - impl.warn(a_node, "unable to determine limits"); | ||
| 189 | } | 173 | } |
| 190 | 174 | ||
| 191 | - if (!changed || parent == path.begin()) { | ||
| 192 | - break; | ||
| 193 | - } else { | ||
| 194 | - a_node = parent->node; | ||
| 195 | - --parent; | 175 | + if (parent == path.begin()) { |
| 176 | + return; | ||
| 196 | } | 177 | } |
| 178 | + a_node = parent->node; | ||
| 179 | + --parent; | ||
| 197 | } | 180 | } |
| 198 | } | 181 | } |
| 199 | 182 | ||
| 200 | void | 183 | void |
| 201 | -NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterator parent) | 184 | +NNTreeIterator::split(Dictionary to_split, std::list<PathElement>::iterator parent) |
| 202 | { | 185 | { |
| 203 | // Split some node along the path to the item pointed to by this iterator, and adjust the | 186 | // Split some node along the path to the item pointed to by this iterator, and adjust the |
| 204 | // iterator so it points to the same item. | 187 | // iterator so it points to the same item. |
| @@ -232,9 +215,9 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | @@ -232,9 +215,9 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | ||
| 232 | } | 215 | } |
| 233 | 216 | ||
| 234 | // Find the array we actually need to split, which is either this node's kids or items. | 217 | // Find the array we actually need to split, which is either this node's kids or items. |
| 235 | - Array kids = to_split.getKey("/Kids"); | 218 | + Array kids = to_split["/Kids"]; |
| 236 | size_t nkids = kids.size(); | 219 | size_t nkids = kids.size(); |
| 237 | - Array items = to_split.getKey(impl.itemsKey()); | 220 | + Array items = to_split[impl.itemsKey()]; |
| 238 | size_t nitems = items.size(); | 221 | size_t nitems = items.size(); |
| 239 | 222 | ||
| 240 | Array first_half; | 223 | Array first_half; |
| @@ -278,12 +261,11 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | @@ -278,12 +261,11 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | ||
| 278 | // is the new first half. In this way, we make the root case identical to the non-root case | 261 | // is the new first half. In this way, we make the root case identical to the non-root case |
| 279 | // so remaining logic can handle them in the same way. | 262 | // so remaining logic can handle them in the same way. |
| 280 | 263 | ||
| 281 | - auto first_node = impl.qpdf.makeIndirectObject(QPDFObjectHandle::newDictionary()); | ||
| 282 | - first_node.replaceKey(key, first_half); | ||
| 283 | - Array new_kids; | 264 | + Dictionary first_node = impl.qpdf.makeIndirectObject(Dictionary({{key, first_half}})); |
| 265 | + auto new_kids = Array::empty(); | ||
| 284 | new_kids.push_back(first_node); | 266 | new_kids.push_back(first_node); |
| 285 | - to_split.removeKey("/Limits"); // already shouldn't be there for root | ||
| 286 | - to_split.removeKey(impl.itemsKey()); | 267 | + to_split.erase("/Limits"); // already shouldn't be there for root |
| 268 | + to_split.erase(impl.itemsKey()); | ||
| 287 | to_split.replaceKey("/Kids", new_kids); | 269 | to_split.replaceKey("/Kids", new_kids); |
| 288 | if (is_leaf) { | 270 | if (is_leaf) { |
| 289 | node = first_node; | 271 | node = first_node; |
| @@ -301,7 +283,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | @@ -301,7 +283,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | ||
| 301 | 283 | ||
| 302 | // Create a second half array, and transfer the second half of the items into the second half | 284 | // Create a second half array, and transfer the second half of the items into the second half |
| 303 | // array. | 285 | // array. |
| 304 | - Array second_half; | 286 | + auto second_half = Array::empty(); |
| 305 | auto start_idx = static_cast<int>((n / 2) & ~1u); | 287 | auto start_idx = static_cast<int>((n / 2) & ~1u); |
| 306 | while (std::cmp_greater(first_half.size(), start_idx)) { | 288 | while (std::cmp_greater(first_half.size(), start_idx)) { |
| 307 | second_half.push_back(first_half[start_idx]); | 289 | second_half.push_back(first_half[start_idx]); |
| @@ -310,8 +292,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | @@ -310,8 +292,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | ||
| 310 | resetLimits(to_split, parent); | 292 | resetLimits(to_split, parent); |
| 311 | 293 | ||
| 312 | // Create a new node to contain the second half | 294 | // Create a new node to contain the second half |
| 313 | - QPDFObjectHandle second_node = impl.qpdf.makeIndirectObject(QPDFObjectHandle::newDictionary()); | ||
| 314 | - second_node.replaceKey(key, second_half); | 295 | + Dictionary second_node = impl.qpdf.makeIndirectObject(Dictionary({{key, second_half}})); |
| 315 | resetLimits(second_node, parent); | 296 | resetLimits(second_node, parent); |
| 316 | 297 | ||
| 317 | // CURRENT STATE: half the items from the kids or items array in the node being split have been | 298 | // CURRENT STATE: half the items from the kids or items array in the node being split have been |
| @@ -322,7 +303,10 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | @@ -322,7 +303,10 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | ||
| 322 | // kid_number to traverse through it. We need to update to_split's path element, or the node if | 303 | // kid_number to traverse through it. We need to update to_split's path element, or the node if |
| 323 | // this is a leaf, so that the kid/item number points to the right place. | 304 | // this is a leaf, so that the kid/item number points to the right place. |
| 324 | 305 | ||
| 325 | - Array parent_kids = parent->node.getKey("/Kids"); | 306 | + Array parent_kids = parent->node["/Kids"]; |
| 307 | + if (!parent_kids) { | ||
| 308 | + impl.error(parent->node, "parent node has no /Kids array"); | ||
| 309 | + } | ||
| 326 | parent_kids.insert(parent->kid_number + 1, second_node); | 310 | parent_kids.insert(parent->kid_number + 1, second_node); |
| 327 | auto cur_elem = parent; | 311 | auto cur_elem = parent; |
| 328 | ++cur_elem; // points to end() for leaf nodes | 312 | ++cur_elem; // points to end() for leaf nodes |
| @@ -347,11 +331,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | @@ -347,11 +331,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato | ||
| 347 | std::list<NNTreeIterator::PathElement>::iterator | 331 | std::list<NNTreeIterator::PathElement>::iterator |
| 348 | NNTreeIterator::lastPathElement() | 332 | NNTreeIterator::lastPathElement() |
| 349 | { | 333 | { |
| 350 | - auto result = path.end(); | ||
| 351 | - if (!path.empty()) { | ||
| 352 | - --result; | ||
| 353 | - } | ||
| 354 | - return result; | 334 | + return path.empty() ? path.end() : std::prev(path.end()); |
| 355 | } | 335 | } |
| 356 | 336 | ||
| 357 | void | 337 | void |
| @@ -359,11 +339,11 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& | @@ -359,11 +339,11 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& | ||
| 359 | { | 339 | { |
| 360 | if (!valid()) { | 340 | if (!valid()) { |
| 361 | impl.insertFirst(key, value); | 341 | impl.insertFirst(key, value); |
| 362 | - deepen(impl.oh, true, false); | 342 | + deepen(impl.tree_root, true, false); |
| 363 | return; | 343 | return; |
| 364 | } | 344 | } |
| 365 | 345 | ||
| 366 | - Array items = node.getKey(impl.itemsKey()); | 346 | + Array items = node[impl.itemsKey()]; |
| 367 | if (!items) { | 347 | if (!items) { |
| 368 | impl.error(node, "node contains no items array"); | 348 | impl.error(node, "node contains no items array"); |
| 369 | } | 349 | } |
| @@ -392,7 +372,7 @@ NNTreeIterator::remove() | @@ -392,7 +372,7 @@ NNTreeIterator::remove() | ||
| 392 | if (!valid()) { | 372 | if (!valid()) { |
| 393 | throw std::logic_error("attempt made to remove an invalid iterator"); | 373 | throw std::logic_error("attempt made to remove an invalid iterator"); |
| 394 | } | 374 | } |
| 395 | - Array items = node.getKey(impl.itemsKey()); | 375 | + Array items = node[impl.itemsKey()]; |
| 396 | int nitems = static_cast<int>(items.size()); | 376 | int nitems = static_cast<int>(items.size()); |
| 397 | if (std::cmp_greater(item_number + 2, nitems)) { | 377 | if (std::cmp_greater(item_number + 2, nitems)) { |
| 398 | impl.error(node, "found short items array while removing an item"); | 378 | impl.error(node, "found short items array while removing an item"); |
| @@ -429,7 +409,7 @@ NNTreeIterator::remove() | @@ -429,7 +409,7 @@ NNTreeIterator::remove() | ||
| 429 | 409 | ||
| 430 | if (path.empty()) { | 410 | if (path.empty()) { |
| 431 | // Special case: if this is the root node, we can leave it empty. | 411 | // Special case: if this is the root node, we can leave it empty. |
| 432 | - setItemNumber(impl.oh, -1); | 412 | + setItemNumber(impl.tree_root, -1); |
| 433 | return; | 413 | return; |
| 434 | } | 414 | } |
| 435 | 415 | ||
| @@ -439,7 +419,7 @@ NNTreeIterator::remove() | @@ -439,7 +419,7 @@ NNTreeIterator::remove() | ||
| 439 | auto element = lastPathElement(); | 419 | auto element = lastPathElement(); |
| 440 | auto parent = element; | 420 | auto parent = element; |
| 441 | --parent; | 421 | --parent; |
| 442 | - Array kids = element->node.getKey("/Kids"); | 422 | + Array kids = element->node["/Kids"]; |
| 443 | kids.erase(element->kid_number); | 423 | kids.erase(element->kid_number); |
| 444 | auto nkids = kids.size(); | 424 | auto nkids = kids.size(); |
| 445 | if (nkids > 0) { | 425 | if (nkids > 0) { |
| @@ -465,10 +445,10 @@ NNTreeIterator::remove() | @@ -465,10 +445,10 @@ NNTreeIterator::remove() | ||
| 465 | 445 | ||
| 466 | if (parent == path.end()) { | 446 | if (parent == path.end()) { |
| 467 | // We erased the very last item. Convert the root to an empty items array. | 447 | // We erased the very last item. Convert the root to an empty items array. |
| 468 | - element->node.removeKey("/Kids"); | ||
| 469 | - element->node.replaceKey(impl.itemsKey(), Array()); | 448 | + element->node.erase("/Kids"); |
| 449 | + element->node.replaceKey(impl.itemsKey(), Array::empty()); | ||
| 470 | path.clear(); | 450 | path.clear(); |
| 471 | - setItemNumber(impl.oh, -1); | 451 | + setItemNumber(impl.tree_root, -1); |
| 472 | return; | 452 | return; |
| 473 | } | 453 | } |
| 474 | 454 | ||
| @@ -499,14 +479,14 @@ NNTreeIterator::operator==(NNTreeIterator const& other) const | @@ -499,14 +479,14 @@ NNTreeIterator::operator==(NNTreeIterator const& other) const | ||
| 499 | } | 479 | } |
| 500 | 480 | ||
| 501 | bool | 481 | bool |
| 502 | -NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) | 482 | +NNTreeIterator::deepen(Dictionary a_node, bool first, bool allow_empty) |
| 503 | { | 483 | { |
| 504 | // Starting at this node, descend through the first or last kid until we reach a node with | 484 | // Starting at this node, descend through the first or last kid until we reach a node with |
| 505 | // items. If we succeed, return true; otherwise return false and leave path alone. | 485 | // items. If we succeed, return true; otherwise return false and leave path alone. |
| 506 | 486 | ||
| 507 | auto opath = path; | 487 | auto opath = path; |
| 508 | 488 | ||
| 509 | - auto fail = [this, &opath](QPDFObjectHandle const& failed_node, std::string const& msg) { | 489 | + auto fail = [this, &opath](Dictionary const& failed_node, std::string const& msg) { |
| 510 | impl.warn(failed_node, msg); | 490 | impl.warn(failed_node, msg); |
| 511 | path = opath; | 491 | path = opath; |
| 512 | return false; | 492 | return false; |
| @@ -521,58 +501,59 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) | @@ -521,58 +501,59 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) | ||
| 521 | return fail(a_node, "loop detected while traversing name/number tree"); | 501 | return fail(a_node, "loop detected while traversing name/number tree"); |
| 522 | } | 502 | } |
| 523 | 503 | ||
| 524 | - if (!a_node.isDictionary()) { | 504 | + if (!a_node) { |
| 525 | return fail(a_node, "non-dictionary node while traversing name/number tree"); | 505 | return fail(a_node, "non-dictionary node while traversing name/number tree"); |
| 526 | } | 506 | } |
| 527 | 507 | ||
| 528 | - Array items = a_node.getKey(impl.itemsKey()); | 508 | + Array items = a_node[impl.itemsKey()]; |
| 529 | int nitems = static_cast<int>(items.size()); | 509 | int nitems = static_cast<int>(items.size()); |
| 530 | if (nitems > 1) { | 510 | if (nitems > 1) { |
| 531 | setItemNumber(a_node, first ? 0 : nitems - 2); | 511 | setItemNumber(a_node, first ? 0 : nitems - 2); |
| 532 | - break; | 512 | + return true; |
| 533 | } | 513 | } |
| 534 | 514 | ||
| 535 | - Array kids = a_node.getKey("/Kids"); | 515 | + Array kids = a_node["/Kids"]; |
| 536 | int nkids = static_cast<int>(kids.size()); | 516 | int nkids = static_cast<int>(kids.size()); |
| 537 | - if (nkids > 0) { | ||
| 538 | - int kid_number = first ? 0 : nkids - 1; | ||
| 539 | - addPathElement(a_node, kid_number); | ||
| 540 | - auto next = kids[kid_number]; | ||
| 541 | - if (!next) { | ||
| 542 | - return fail(a_node, "kid number " + std::to_string(kid_number) + " is invalid"); | ||
| 543 | - } | ||
| 544 | - if (!next.indirect()) { | ||
| 545 | - if (impl.auto_repair) { | ||
| 546 | - impl.warn( | ||
| 547 | - a_node, | ||
| 548 | - "converting kid number " + std::to_string(kid_number) + | ||
| 549 | - " to an indirect object"); | ||
| 550 | - next = impl.qpdf.makeIndirectObject(next); | ||
| 551 | - kids.set(kid_number, next); | ||
| 552 | - } else { | ||
| 553 | - impl.warn( | ||
| 554 | - a_node, | ||
| 555 | - "kid number " + std::to_string(kid_number) + " is not an indirect object"); | ||
| 556 | - } | 517 | + if (nkids == 0) { |
| 518 | + if (allow_empty && items) { | ||
| 519 | + setItemNumber(a_node, -1); | ||
| 520 | + return true; | ||
| 557 | } | 521 | } |
| 558 | - a_node = next; | ||
| 559 | - } else if (allow_empty && items) { | ||
| 560 | - setItemNumber(a_node, -1); | ||
| 561 | - break; | ||
| 562 | - } else { | ||
| 563 | return fail( | 522 | return fail( |
| 564 | a_node, | 523 | a_node, |
| 565 | "name/number tree node has neither non-empty " + impl.itemsKey() + " nor /Kids"); | 524 | "name/number tree node has neither non-empty " + impl.itemsKey() + " nor /Kids"); |
| 566 | } | 525 | } |
| 526 | + | ||
| 527 | + int kid_number = first ? 0 : nkids - 1; | ||
| 528 | + addPathElement(a_node, kid_number); | ||
| 529 | + Dictionary next = kids[kid_number]; | ||
| 530 | + if (!next) { | ||
| 531 | + return fail(a_node, "kid number " + std::to_string(kid_number) + " is invalid"); | ||
| 532 | + } | ||
| 533 | + if (!next.indirect()) { | ||
| 534 | + if (impl.auto_repair) { | ||
| 535 | + impl.warn( | ||
| 536 | + a_node, | ||
| 537 | + "converting kid number " + std::to_string(kid_number) + | ||
| 538 | + " to an indirect object"); | ||
| 539 | + next = impl.qpdf.makeIndirectObject(next); | ||
| 540 | + kids.set(kid_number, next); | ||
| 541 | + } else { | ||
| 542 | + impl.warn( | ||
| 543 | + a_node, | ||
| 544 | + "kid number " + std::to_string(kid_number) + " is not an indirect object"); | ||
| 545 | + } | ||
| 546 | + } | ||
| 547 | + | ||
| 548 | + a_node = next; | ||
| 567 | } | 549 | } |
| 568 | - return true; | ||
| 569 | } | 550 | } |
| 570 | 551 | ||
| 571 | NNTreeImpl::iterator | 552 | NNTreeImpl::iterator |
| 572 | NNTreeImpl::begin() | 553 | NNTreeImpl::begin() |
| 573 | { | 554 | { |
| 574 | iterator result(*this); | 555 | iterator result(*this); |
| 575 | - result.deepen(oh, true, true); | 556 | + result.deepen(tree_root, true, true); |
| 576 | return result; | 557 | return result; |
| 577 | } | 558 | } |
| 578 | 559 | ||
| @@ -580,7 +561,7 @@ NNTreeImpl::iterator | @@ -580,7 +561,7 @@ NNTreeImpl::iterator | ||
| 580 | NNTreeImpl::last() | 561 | NNTreeImpl::last() |
| 581 | { | 562 | { |
| 582 | iterator result(*this); | 563 | iterator result(*this); |
| 583 | - result.deepen(oh, false, true); | 564 | + result.deepen(tree_root, false, true); |
| 584 | return result; | 565 | return result; |
| 585 | } | 566 | } |
| 586 | 567 | ||
| @@ -606,19 +587,19 @@ NNTreeImpl::binarySearch( | @@ -606,19 +587,19 @@ NNTreeImpl::binarySearch( | ||
| 606 | Array const& items, | 587 | Array const& items, |
| 607 | size_t num_items, | 588 | size_t num_items, |
| 608 | bool return_prev_if_not_found, | 589 | bool return_prev_if_not_found, |
| 609 | - int (NNTreeImpl::*compare)(QPDFObjectHandle const& key, Array const& arr, int item) const) const | 590 | + bool search_kids) const |
| 610 | { | 591 | { |
| 611 | size_t max_idx = std::bit_ceil(num_items); | 592 | size_t max_idx = std::bit_ceil(num_items); |
| 612 | 593 | ||
| 613 | int step = static_cast<int>(max_idx / 2); | 594 | int step = static_cast<int>(max_idx / 2); |
| 614 | - size_t checks = max_idx; | 595 | + int checks = static_cast<int>(std::bit_width(max_idx)); // AppImage gcc version returns size_t |
| 615 | int idx = step; | 596 | int idx = step; |
| 616 | int found_idx = -1; | 597 | int found_idx = -1; |
| 617 | 598 | ||
| 618 | - while (checks > 0) { | 599 | + for (int i = 0; i < checks; ++i) { |
| 619 | int status = -1; | 600 | int status = -1; |
| 620 | if (std::cmp_less(idx, num_items)) { | 601 | if (std::cmp_less(idx, num_items)) { |
| 621 | - status = (this->*compare)(key, items, idx); | 602 | + status = search_kids ? compareKeyKid(key, items, idx) : compareKeyItem(key, items, idx); |
| 622 | if (status == 0) { | 603 | if (status == 0) { |
| 623 | return idx; | 604 | return idx; |
| 624 | } | 605 | } |
| @@ -626,21 +607,9 @@ NNTreeImpl::binarySearch( | @@ -626,21 +607,9 @@ NNTreeImpl::binarySearch( | ||
| 626 | found_idx = idx; | 607 | found_idx = idx; |
| 627 | } | 608 | } |
| 628 | } | 609 | } |
| 629 | - checks >>= 1; | ||
| 630 | - if (checks > 0) { | ||
| 631 | - step >>= 1; | ||
| 632 | - if (step == 0) { | ||
| 633 | - step = 1; | ||
| 634 | - } | ||
| 635 | - | ||
| 636 | - if (status < 0) { | ||
| 637 | - idx -= step; | ||
| 638 | - } else { | ||
| 639 | - idx += step; | ||
| 640 | - } | ||
| 641 | - } | 610 | + step = std::max(step / 2, 1); |
| 611 | + idx += status * step; | ||
| 642 | } | 612 | } |
| 643 | - | ||
| 644 | return return_prev_if_not_found ? found_idx : -1; | 613 | return return_prev_if_not_found ? found_idx : -1; |
| 645 | } | 614 | } |
| 646 | 615 | ||
| @@ -648,7 +617,7 @@ int | @@ -648,7 +617,7 @@ int | ||
| 648 | NNTreeImpl::compareKeyItem(QPDFObjectHandle const& key, Array const& items, int idx) const | 617 | NNTreeImpl::compareKeyItem(QPDFObjectHandle const& key, Array const& items, int idx) const |
| 649 | { | 618 | { |
| 650 | if (!keyValid(items[2 * idx])) { | 619 | if (!keyValid(items[2 * idx])) { |
| 651 | - error(oh, ("item at index " + std::to_string(2 * idx) + " is not the right type")); | 620 | + error(tree_root, ("item at index " + std::to_string(2 * idx) + " is not the right type")); |
| 652 | } | 621 | } |
| 653 | return compareKeys(key, items[2 * idx]); | 622 | return compareKeys(key, items[2 * idx]); |
| 654 | } | 623 | } |
| @@ -656,10 +625,11 @@ NNTreeImpl::compareKeyItem(QPDFObjectHandle const& key, Array const& items, int | @@ -656,10 +625,11 @@ NNTreeImpl::compareKeyItem(QPDFObjectHandle const& key, Array const& items, int | ||
| 656 | int | 625 | int |
| 657 | NNTreeImpl::compareKeyKid(QPDFObjectHandle const& key, Array const& kids, int idx) const | 626 | NNTreeImpl::compareKeyKid(QPDFObjectHandle const& key, Array const& kids, int idx) const |
| 658 | { | 627 | { |
| 659 | - if (!kids[idx].isDictionary()) { | ||
| 660 | - error(oh, "invalid kid at index " + std::to_string(idx)); | 628 | + Dictionary kid = kids[idx]; |
| 629 | + if (!kid) { | ||
| 630 | + error(tree_root, "invalid kid at index " + std::to_string(idx)); | ||
| 661 | } | 631 | } |
| 662 | - Array limits = kids[idx].getKey("/Limits"); | 632 | + Array limits = kid["/Limits"]; |
| 663 | if (!(keyValid(limits[0]) && keyValid(limits[1]))) { | 633 | if (!(keyValid(limits[0]) && keyValid(limits[1]))) { |
| 664 | error(kids[idx], "node is missing /Limits"); | 634 | error(kids[idx], "node is missing /Limits"); |
| 665 | } | 635 | } |
| @@ -675,26 +645,25 @@ NNTreeImpl::compareKeyKid(QPDFObjectHandle const& key, Array const& kids, int id | @@ -675,26 +645,25 @@ NNTreeImpl::compareKeyKid(QPDFObjectHandle const& key, Array const& kids, int id | ||
| 675 | void | 645 | void |
| 676 | NNTreeImpl::repair() | 646 | NNTreeImpl::repair() |
| 677 | { | 647 | { |
| 678 | - auto new_node = QPDFObjectHandle::newDictionary(); | ||
| 679 | - new_node.replaceKey(itemsKey(), Array()); | 648 | + auto new_node = Dictionary({{itemsKey(), Array::empty()}}); |
| 680 | NNTreeImpl repl(qpdf, new_node, key_type, value_valid, false); | 649 | NNTreeImpl repl(qpdf, new_node, key_type, value_valid, false); |
| 681 | for (auto const& [key, value]: *this) { | 650 | for (auto const& [key, value]: *this) { |
| 682 | if (key && value) { | 651 | if (key && value) { |
| 683 | repl.insert(key, value); | 652 | repl.insert(key, value); |
| 684 | } | 653 | } |
| 685 | } | 654 | } |
| 686 | - oh.replaceKey("/Kids", new_node.getKey("/Kids")); | ||
| 687 | - oh.replaceKey(itemsKey(), new_node.getKey(itemsKey())); | 655 | + tree_root.replaceKey("/Kids", new_node["/Kids"]); |
| 656 | + tree_root.replaceKey(itemsKey(), new_node[itemsKey()]); | ||
| 688 | } | 657 | } |
| 689 | 658 | ||
| 690 | NNTreeImpl::iterator | 659 | NNTreeImpl::iterator |
| 691 | -NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) | 660 | +NNTreeImpl::find(QPDFObjectHandle const& key, bool return_prev_if_not_found) |
| 692 | { | 661 | { |
| 693 | try { | 662 | try { |
| 694 | return findInternal(key, return_prev_if_not_found); | 663 | return findInternal(key, return_prev_if_not_found); |
| 695 | } catch (QPDFExc& e) { | 664 | } catch (QPDFExc& e) { |
| 696 | if (auto_repair) { | 665 | if (auto_repair) { |
| 697 | - warn(oh, std::string("attempting to repair after error: ") + e.what()); | 666 | + warn(tree_root, std::string("attempting to repair after error: ") + e.what()); |
| 698 | repair(); | 667 | repair(); |
| 699 | return findInternal(key, return_prev_if_not_found); | 668 | return findInternal(key, return_prev_if_not_found); |
| 700 | } else { | 669 | } else { |
| @@ -707,26 +676,22 @@ NNTreeImpl::iterator | @@ -707,26 +676,22 @@ NNTreeImpl::iterator | ||
| 707 | NNTreeImpl::findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_found) | 676 | NNTreeImpl::findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_found) |
| 708 | { | 677 | { |
| 709 | auto first_item = begin(); | 678 | auto first_item = begin(); |
| 710 | - auto last_item = end(); | ||
| 711 | - if (first_item == end()) { | 679 | + if (!first_item.valid()) { |
| 712 | return end(); | 680 | return end(); |
| 713 | } | 681 | } |
| 714 | - if (first_item.valid()) { | ||
| 715 | - if (!keyValid(first_item->first)) { | ||
| 716 | - error(oh, "encountered invalid key in find"); | ||
| 717 | - } | ||
| 718 | - if (!value_valid(first_item->second)) { | ||
| 719 | - error(oh, "encountered invalid value in find"); | ||
| 720 | - } | ||
| 721 | - if (compareKeys(key, first_item->first) < 0) { | ||
| 722 | - // Before the first key | ||
| 723 | - return end(); | ||
| 724 | - } | 682 | + if (!keyValid(first_item->first)) { |
| 683 | + error(tree_root, "encountered invalid key in find"); | ||
| 684 | + } | ||
| 685 | + if (!value_valid(first_item->second)) { | ||
| 686 | + error(tree_root, "encountered invalid value in find"); | ||
| 687 | + } | ||
| 688 | + if (compareKeys(key, first_item->first) < 0) { | ||
| 689 | + // Before the first key | ||
| 690 | + return end(); | ||
| 725 | } | 691 | } |
| 726 | - qpdf_assert_debug(!last_item.valid()); | ||
| 727 | 692 | ||
| 728 | QPDFObjGen::set seen; | 693 | QPDFObjGen::set seen; |
| 729 | - auto node = oh; | 694 | + auto node = tree_root; |
| 730 | iterator result(*this); | 695 | iterator result(*this); |
| 731 | 696 | ||
| 732 | while (true) { | 697 | while (true) { |
| @@ -734,35 +699,33 @@ NNTreeImpl::findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_fo | @@ -734,35 +699,33 @@ NNTreeImpl::findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_fo | ||
| 734 | error(node, "loop detected in find"); | 699 | error(node, "loop detected in find"); |
| 735 | } | 700 | } |
| 736 | 701 | ||
| 737 | - Array items = node.getKey(itemsKey()); | 702 | + Array items = node[itemsKey()]; |
| 738 | size_t nitems = items.size(); | 703 | size_t nitems = items.size(); |
| 739 | if (nitems > 1) { | 704 | if (nitems > 1) { |
| 740 | - int idx = binarySearch( | ||
| 741 | - key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem); | 705 | + int idx = binarySearch(key, items, nitems / 2, return_prev_if_not_found, false); |
| 742 | if (idx >= 0) { | 706 | if (idx >= 0) { |
| 743 | result.setItemNumber(node, 2 * idx); | 707 | result.setItemNumber(node, 2 * idx); |
| 744 | if (!result.impl.keyValid(result.ivalue.first)) { | 708 | if (!result.impl.keyValid(result.ivalue.first)) { |
| 745 | error(node, "encountered invalid key in find"); | 709 | error(node, "encountered invalid key in find"); |
| 746 | } | 710 | } |
| 747 | if (!result.impl.value_valid(result.ivalue.second)) { | 711 | if (!result.impl.value_valid(result.ivalue.second)) { |
| 748 | - error(oh, "encountered invalid value in find"); | 712 | + error(tree_root, "encountered invalid value in find"); |
| 749 | } | 713 | } |
| 750 | } | 714 | } |
| 751 | return result; | 715 | return result; |
| 752 | } | 716 | } |
| 753 | 717 | ||
| 754 | - Array kids = node.getKey("/Kids"); | 718 | + Array kids = node["/Kids"]; |
| 755 | size_t nkids = kids.size(); | 719 | size_t nkids = kids.size(); |
| 756 | - if (nkids > 0) { | ||
| 757 | - int idx = binarySearch(key, kids, nkids, true, &NNTreeImpl::compareKeyKid); | ||
| 758 | - if (idx == -1) { | ||
| 759 | - error(node, "unexpected -1 from binary search of kids; limits may by wrong"); | ||
| 760 | - } | ||
| 761 | - result.addPathElement(node, idx); | ||
| 762 | - node = kids[idx]; | ||
| 763 | - } else { | 720 | + if (nkids == 0) { |
| 764 | error(node, "bad node during find"); | 721 | error(node, "bad node during find"); |
| 765 | } | 722 | } |
| 723 | + int idx = binarySearch(key, kids, nkids, true, true); | ||
| 724 | + if (idx == -1) { | ||
| 725 | + error(node, "unexpected -1 from binary search of kids; limits may by wrong"); | ||
| 726 | + } | ||
| 727 | + result.addPathElement(node, idx); | ||
| 728 | + node = kids[idx]; | ||
| 766 | } | 729 | } |
| 767 | } | 730 | } |
| 768 | 731 | ||
| @@ -770,18 +733,15 @@ NNTreeImpl::iterator | @@ -770,18 +733,15 @@ NNTreeImpl::iterator | ||
| 770 | NNTreeImpl::insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value) | 733 | NNTreeImpl::insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value) |
| 771 | { | 734 | { |
| 772 | auto iter = begin(); | 735 | auto iter = begin(); |
| 773 | - Array items(nullptr); | ||
| 774 | - if (iter.node.isDictionary()) { | ||
| 775 | - items = iter.node.getKey(itemsKey()); | ||
| 776 | - } | 736 | + Array items = iter.node[items_key]; |
| 777 | if (!items) { | 737 | if (!items) { |
| 778 | - error(oh, "unable to find a valid items node"); | 738 | + error(tree_root, "unable to find a valid items node"); |
| 779 | } | 739 | } |
| 780 | if (!(key && value)) { | 740 | if (!(key && value)) { |
| 781 | - error(oh, "unable to insert null key or value"); | 741 | + error(tree_root, "unable to insert null key or value"); |
| 782 | } | 742 | } |
| 783 | if (!value_valid(value)) { | 743 | if (!value_valid(value)) { |
| 784 | - error(oh, "attempting to insert an invalid value"); | 744 | + error(tree_root, "attempting to insert an invalid value"); |
| 785 | } | 745 | } |
| 786 | items.insert(0, key); | 746 | items.insert(0, key); |
| 787 | items.insert(1, value); | 747 | items.insert(1, value); |
| @@ -798,7 +758,7 @@ NNTreeImpl::insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value) | @@ -798,7 +758,7 @@ NNTreeImpl::insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value) | ||
| 798 | if (!iter.valid()) { | 758 | if (!iter.valid()) { |
| 799 | return insertFirst(key, value); | 759 | return insertFirst(key, value); |
| 800 | } else if (compareKeys(key, iter->first) == 0) { | 760 | } else if (compareKeys(key, iter->first) == 0) { |
| 801 | - Array items = iter.node.getKey(itemsKey()); | 761 | + Array items = iter.node[itemsKey()]; |
| 802 | items.set(iter.item_number + 1, value); | 762 | items.set(iter.item_number + 1, value); |
| 803 | iter.updateIValue(); | 763 | iter.updateIValue(); |
| 804 | } else { | 764 | } else { |
| @@ -829,21 +789,21 @@ NNTreeImpl::validate(bool a_repair) | @@ -829,21 +789,21 @@ NNTreeImpl::validate(bool a_repair) | ||
| 829 | try { | 789 | try { |
| 830 | for (auto const& [key, value]: *this) { | 790 | for (auto const& [key, value]: *this) { |
| 831 | if (!keyValid(key)) { | 791 | if (!keyValid(key)) { |
| 832 | - error(oh, "invalid key in validate"); | 792 | + error(tree_root, "invalid key in validate"); |
| 833 | } | 793 | } |
| 834 | if (!value_valid(value)) { | 794 | if (!value_valid(value)) { |
| 835 | - error(oh, "invalid value in validate"); | 795 | + error(tree_root, "invalid value in validate"); |
| 836 | } | 796 | } |
| 837 | if (first) { | 797 | if (first) { |
| 838 | first = false; | 798 | first = false; |
| 839 | } else if (last_key && compareKeys(last_key, key) != -1) { | 799 | } else if (last_key && compareKeys(last_key, key) != -1) { |
| 840 | - error(oh, "keys are not sorted in validate"); | 800 | + error(tree_root, "keys are not sorted in validate"); |
| 841 | } | 801 | } |
| 842 | last_key = key; | 802 | last_key = key; |
| 843 | } | 803 | } |
| 844 | } catch (QPDFExc& e) { | 804 | } catch (QPDFExc& e) { |
| 845 | if (a_repair) { | 805 | if (a_repair) { |
| 846 | - warn(oh, std::string("attempting to repair after error: ") + e.what()); | 806 | + warn(tree_root, std::string("attempting to repair after error: ") + e.what()); |
| 847 | repair(); | 807 | repair(); |
| 848 | } | 808 | } |
| 849 | return false; | 809 | return false; |
libqpdf/QPDF_Array.cc
| @@ -61,11 +61,6 @@ Array::array() const | @@ -61,11 +61,6 @@ Array::array() const | ||
| 61 | return nullptr; // unreachable | 61 | return nullptr; // unreachable |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | -Array::Array(bool empty) : | ||
| 65 | - BaseHandle(empty ? QPDFObject::create<QPDF_Array>() : nullptr) | ||
| 66 | -{ | ||
| 67 | -} | ||
| 68 | - | ||
| 69 | Array::Array(std::vector<QPDFObjectHandle> const& items) : | 64 | Array::Array(std::vector<QPDFObjectHandle> const& items) : |
| 70 | BaseHandle(QPDFObject::create<QPDF_Array>(items)) | 65 | BaseHandle(QPDFObject::create<QPDF_Array>(items)) |
| 71 | { | 66 | { |
| @@ -76,6 +71,12 @@ Array::Array(std::vector<QPDFObjectHandle>&& items) : | @@ -76,6 +71,12 @@ Array::Array(std::vector<QPDFObjectHandle>&& items) : | ||
| 76 | { | 71 | { |
| 77 | } | 72 | } |
| 78 | 73 | ||
| 74 | +Array | ||
| 75 | +Array::empty() | ||
| 76 | +{ | ||
| 77 | + return Array(std::vector<QPDFObjectHandle>()); | ||
| 78 | +} | ||
| 79 | + | ||
| 79 | Array::iterator | 80 | Array::iterator |
| 80 | Array::begin() | 81 | Array::begin() |
| 81 | { | 82 | { |
| @@ -399,7 +400,6 @@ QPDFObjectHandle::getArrayItem(int n) const | @@ -399,7 +400,6 @@ QPDFObjectHandle::getArrayItem(int n) const | ||
| 399 | return newNull(); | 400 | return newNull(); |
| 400 | } | 401 | } |
| 401 | objectWarning("returning null for out of bounds array access"); | 402 | objectWarning("returning null for out of bounds array access"); |
| 402 | - | ||
| 403 | } else { | 403 | } else { |
| 404 | typeWarning("array", "returning null"); | 404 | typeWarning("array", "returning null"); |
| 405 | } | 405 | } |
libqpdf/QPDF_Dictionary.cc
| @@ -16,26 +16,23 @@ BaseDictionary::dict() const | @@ -16,26 +16,23 @@ BaseDictionary::dict() const | ||
| 16 | return nullptr; // unreachable | 16 | return nullptr; // unreachable |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | -bool | ||
| 20 | -BaseDictionary::hasKey(std::string const& key) const | 19 | +QPDFObjectHandle const& |
| 20 | +BaseHandle::operator[](std::string const& key) const | ||
| 21 | { | 21 | { |
| 22 | - auto d = dict(); | ||
| 23 | - return d->items.contains(key) && !d->items[key].null(); | 22 | + if (auto d = as<QPDF_Dictionary>()) { |
| 23 | + auto it = d->items.find(key); | ||
| 24 | + if (it != d->items.end()) { | ||
| 25 | + return it->second; | ||
| 26 | + } | ||
| 27 | + } | ||
| 28 | + static const QPDFObjectHandle null_obj; | ||
| 29 | + return null_obj; | ||
| 24 | } | 30 | } |
| 25 | 31 | ||
| 26 | -QPDFObjectHandle | ||
| 27 | -BaseDictionary::getKey(std::string const& key) const | 32 | +bool |
| 33 | +BaseHandle::contains(std::string const& key) const | ||
| 28 | { | 34 | { |
| 29 | - auto d = dict(); | ||
| 30 | - | ||
| 31 | - // PDF spec says fetching a non-existent key from a dictionary returns the null object. | ||
| 32 | - auto item = d->items.find(key); | ||
| 33 | - if (item != d->items.end()) { | ||
| 34 | - // May be a null object | ||
| 35 | - return item->second; | ||
| 36 | - } | ||
| 37 | - static auto constexpr msg = " -> dictionary key $VD"sv; | ||
| 38 | - return QPDF_Null::create(obj, msg, key); | 35 | + return !(*this)[key].null(); |
| 39 | } | 36 | } |
| 40 | 37 | ||
| 41 | std::set<std::string> | 38 | std::set<std::string> |
| @@ -56,11 +53,14 @@ BaseDictionary::getAsMap() const | @@ -56,11 +53,14 @@ BaseDictionary::getAsMap() const | ||
| 56 | return dict()->items; | 53 | return dict()->items; |
| 57 | } | 54 | } |
| 58 | 55 | ||
| 59 | -void | ||
| 60 | -BaseDictionary::removeKey(std::string const& key) | 56 | +size_t |
| 57 | +BaseHandle::erase(const std::string& key) | ||
| 61 | { | 58 | { |
| 62 | // no-op if key does not exist | 59 | // no-op if key does not exist |
| 63 | - dict()->items.erase(key); | 60 | + if (auto d = as<QPDF_Dictionary>()) { |
| 61 | + return d->items.erase(key); | ||
| 62 | + } | ||
| 63 | + return 0; | ||
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | void | 66 | void |
| @@ -78,13 +78,28 @@ BaseDictionary::replaceKey(std::string const& key, QPDFObjectHandle value) | @@ -78,13 +78,28 @@ BaseDictionary::replaceKey(std::string const& key, QPDFObjectHandle value) | ||
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | +Dictionary::Dictionary(std::map<std::string, QPDFObjectHandle>&& dict) : | ||
| 82 | + BaseDictionary(std::move(dict)) | ||
| 83 | +{ | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +Dictionary::Dictionary(std::shared_ptr<QPDFObject> const& obj) : | ||
| 87 | + BaseDictionary(obj) | ||
| 88 | +{ | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +Dictionary | ||
| 92 | +Dictionary::empty() | ||
| 93 | +{ | ||
| 94 | + return Dictionary(std::map<std::string, QPDFObjectHandle>()); | ||
| 95 | +} | ||
| 96 | + | ||
| 81 | void | 97 | void |
| 82 | QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const | 98 | QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const |
| 83 | { | 99 | { |
| 84 | auto qpdf = getOwningQPDF(); | 100 | auto qpdf = getOwningQPDF(); |
| 85 | auto item_qpdf = item.getOwningQPDF(); | 101 | auto item_qpdf = item.getOwningQPDF(); |
| 86 | if (qpdf && item_qpdf && qpdf != item_qpdf) { | 102 | if (qpdf && item_qpdf && qpdf != item_qpdf) { |
| 87 | - QTC::TC("qpdf", "QPDFObjectHandle check ownership"); | ||
| 88 | throw std::logic_error( | 103 | throw std::logic_error( |
| 89 | "Attempting to add an object from a different QPDF. Use " | 104 | "Attempting to add an object from a different QPDF. Use " |
| 90 | "QPDF::copyForeignObject to add objects from another file."); | 105 | "QPDF::copyForeignObject to add objects from another file."); |
| @@ -94,9 +109,8 @@ QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const | @@ -94,9 +109,8 @@ QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const | ||
| 94 | bool | 109 | bool |
| 95 | QPDFObjectHandle::hasKey(std::string const& key) const | 110 | QPDFObjectHandle::hasKey(std::string const& key) const |
| 96 | { | 111 | { |
| 97 | - auto dict = as_dictionary(strict); | ||
| 98 | - if (dict) { | ||
| 99 | - return dict.hasKey(key); | 112 | + if (Dictionary dict = *this) { |
| 113 | + return dict.contains(key); | ||
| 100 | } else { | 114 | } else { |
| 101 | typeWarning("dictionary", "returning false for a key containment request"); | 115 | typeWarning("dictionary", "returning false for a key containment request"); |
| 102 | QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey"); | 116 | QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey"); |
| @@ -107,11 +121,14 @@ QPDFObjectHandle::hasKey(std::string const& key) const | @@ -107,11 +121,14 @@ QPDFObjectHandle::hasKey(std::string const& key) const | ||
| 107 | QPDFObjectHandle | 121 | QPDFObjectHandle |
| 108 | QPDFObjectHandle::getKey(std::string const& key) const | 122 | QPDFObjectHandle::getKey(std::string const& key) const |
| 109 | { | 123 | { |
| 110 | - if (auto dict = as_dictionary(strict)) { | ||
| 111 | - return dict.getKey(key); | 124 | + if (auto result = (*this)[key]) { |
| 125 | + return result; | ||
| 126 | + } | ||
| 127 | + if (isDictionary()) { | ||
| 128 | + static auto constexpr msg = " -> dictionary key $VD"sv; | ||
| 129 | + return QPDF_Null::create(obj, msg, key); | ||
| 112 | } | 130 | } |
| 113 | typeWarning("dictionary", "returning null for attempted key retrieval"); | 131 | typeWarning("dictionary", "returning null for attempted key retrieval"); |
| 114 | - QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey"); | ||
| 115 | static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv; | 132 | static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv; |
| 116 | return QPDF_Null::create(obj, msg, ""); | 133 | return QPDF_Null::create(obj, msg, ""); |
| 117 | } | 134 | } |
| @@ -174,21 +191,16 @@ QPDFObjectHandle::replaceKeyAndGetOld(std::string const& key, QPDFObjectHandle c | @@ -174,21 +191,16 @@ QPDFObjectHandle::replaceKeyAndGetOld(std::string const& key, QPDFObjectHandle c | ||
| 174 | void | 191 | void |
| 175 | QPDFObjectHandle::removeKey(std::string const& key) | 192 | QPDFObjectHandle::removeKey(std::string const& key) |
| 176 | { | 193 | { |
| 177 | - if (auto dict = as_dictionary(strict)) { | ||
| 178 | - dict.removeKey(key); | 194 | + if (erase(key) || isDictionary()) { |
| 179 | return; | 195 | return; |
| 180 | } | 196 | } |
| 181 | typeWarning("dictionary", "ignoring key removal request"); | 197 | typeWarning("dictionary", "ignoring key removal request"); |
| 182 | - QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey"); | ||
| 183 | } | 198 | } |
| 184 | 199 | ||
| 185 | QPDFObjectHandle | 200 | QPDFObjectHandle |
| 186 | QPDFObjectHandle::removeKeyAndGetOld(std::string const& key) | 201 | QPDFObjectHandle::removeKeyAndGetOld(std::string const& key) |
| 187 | { | 202 | { |
| 188 | - auto result = QPDFObjectHandle::newNull(); | ||
| 189 | - if (auto dict = as_dictionary(strict)) { | ||
| 190 | - result = dict.getKey(key); | ||
| 191 | - } | ||
| 192 | - removeKey(key); | ||
| 193 | - return result; | 203 | + auto result = (*this)[key]; |
| 204 | + erase(key); | ||
| 205 | + return result ? result : newNull(); | ||
| 194 | } | 206 | } |
libqpdf/qpdf/NNTree.hh
| @@ -81,9 +81,13 @@ class NNTreeIterator final | @@ -81,9 +81,13 @@ class NNTreeIterator final | ||
| 81 | class PathElement | 81 | class PathElement |
| 82 | { | 82 | { |
| 83 | public: | 83 | public: |
| 84 | - PathElement(QPDFObjectHandle const& node, int kid_number); | 84 | + PathElement(qpdf::Dictionary const& node, int kid_number) : |
| 85 | + node(node), | ||
| 86 | + kid_number(kid_number) | ||
| 87 | + { | ||
| 88 | + } | ||
| 85 | 89 | ||
| 86 | - QPDFObjectHandle node; | 90 | + qpdf::Dictionary node; |
| 87 | int kid_number; | 91 | int kid_number; |
| 88 | }; | 92 | }; |
| 89 | 93 | ||
| @@ -92,7 +96,7 @@ class NNTreeIterator final | @@ -92,7 +96,7 @@ class NNTreeIterator final | ||
| 92 | { | 96 | { |
| 93 | } | 97 | } |
| 94 | void updateIValue(bool allow_invalid = true); | 98 | void updateIValue(bool allow_invalid = true); |
| 95 | - bool deepen(QPDFObjectHandle node, bool first, bool allow_empty); | 99 | + bool deepen(qpdf::Dictionary node, bool first, bool allow_empty); |
| 96 | void | 100 | void |
| 97 | setItemNumber(QPDFObjectHandle const& a_node, int n) | 101 | setItemNumber(QPDFObjectHandle const& a_node, int n) |
| 98 | { | 102 | { |
| @@ -105,16 +109,16 @@ class NNTreeIterator final | @@ -105,16 +109,16 @@ class NNTreeIterator final | ||
| 105 | { | 109 | { |
| 106 | path.emplace_back(a_node, kid_number); | 110 | path.emplace_back(a_node, kid_number); |
| 107 | } | 111 | } |
| 108 | - QPDFObjectHandle getNextKid(PathElement& element, bool backward); | 112 | + qpdf::Dictionary getNextKid(PathElement& element, bool backward); |
| 109 | void increment(bool backward); | 113 | void increment(bool backward); |
| 110 | - void resetLimits(QPDFObjectHandle node, std::list<PathElement>::iterator parent); | 114 | + void resetLimits(qpdf::Dictionary node, std::list<PathElement>::iterator parent); |
| 111 | 115 | ||
| 112 | - void split(QPDFObjectHandle to_split, std::list<PathElement>::iterator parent); | 116 | + void split(qpdf::Dictionary to_split, std::list<PathElement>::iterator parent); |
| 113 | std::list<PathElement>::iterator lastPathElement(); | 117 | std::list<PathElement>::iterator lastPathElement(); |
| 114 | 118 | ||
| 115 | NNTreeImpl& impl; | 119 | NNTreeImpl& impl; |
| 116 | std::list<PathElement> path; | 120 | std::list<PathElement> path; |
| 117 | - QPDFObjectHandle node; | 121 | + qpdf::Dictionary node; |
| 118 | int item_number{-1}; | 122 | int item_number{-1}; |
| 119 | value_type ivalue; | 123 | value_type ivalue; |
| 120 | }; | 124 | }; |
| @@ -128,12 +132,12 @@ class NNTreeImpl final | @@ -128,12 +132,12 @@ class NNTreeImpl final | ||
| 128 | 132 | ||
| 129 | NNTreeImpl( | 133 | NNTreeImpl( |
| 130 | QPDF& qpdf, | 134 | QPDF& qpdf, |
| 131 | - QPDFObjectHandle& oh, | 135 | + qpdf::Dictionary tree_root, |
| 132 | qpdf_object_type_e key_type, | 136 | qpdf_object_type_e key_type, |
| 133 | std::function<bool(QPDFObjectHandle const&)> value_validator, | 137 | std::function<bool(QPDFObjectHandle const&)> value_validator, |
| 134 | bool auto_repair) : | 138 | bool auto_repair) : |
| 135 | qpdf(qpdf), | 139 | qpdf(qpdf), |
| 136 | - oh(oh), | 140 | + tree_root(std::move(tree_root)), |
| 137 | key_type(key_type), | 141 | key_type(key_type), |
| 138 | items_key(key_type == ::ot_string ? "/Names" : "/Nums"), | 142 | items_key(key_type == ::ot_string ? "/Names" : "/Nums"), |
| 139 | value_valid(value_validator), | 143 | value_valid(value_validator), |
| @@ -147,7 +151,7 @@ class NNTreeImpl final | @@ -147,7 +151,7 @@ class NNTreeImpl final | ||
| 147 | return {*this}; | 151 | return {*this}; |
| 148 | } | 152 | } |
| 149 | iterator last(); | 153 | iterator last(); |
| 150 | - iterator find(QPDFObjectHandle key, bool return_prev_if_not_found = false); | 154 | + iterator find(QPDFObjectHandle const& key, bool return_prev_if_not_found = false); |
| 151 | iterator insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value); | 155 | iterator insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value); |
| 152 | iterator insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value); | 156 | iterator insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value); |
| 153 | bool remove(QPDFObjectHandle const& key, QPDFObjectHandle* value = nullptr); | 157 | bool remove(QPDFObjectHandle const& key, QPDFObjectHandle* value = nullptr); |
| @@ -170,8 +174,7 @@ class NNTreeImpl final | @@ -170,8 +174,7 @@ class NNTreeImpl final | ||
| 170 | qpdf::Array const& items, | 174 | qpdf::Array const& items, |
| 171 | size_t num_items, | 175 | size_t num_items, |
| 172 | bool return_prev_if_not_found, | 176 | bool return_prev_if_not_found, |
| 173 | - int (NNTreeImpl::*compare)(QPDFObjectHandle const& key, qpdf::Array const& arr, int item) | ||
| 174 | - const) const; | 177 | + bool search_kids) const; |
| 175 | int compareKeyItem(QPDFObjectHandle const& key, qpdf::Array const& items, int idx) const; | 178 | int compareKeyItem(QPDFObjectHandle const& key, qpdf::Array const& items, int idx) const; |
| 176 | int compareKeyKid(QPDFObjectHandle const& key, qpdf::Array const& items, int idx) const; | 179 | int compareKeyKid(QPDFObjectHandle const& key, qpdf::Array const& items, int idx) const; |
| 177 | void warn(QPDFObjectHandle const& node, std::string const& msg); | 180 | void warn(QPDFObjectHandle const& node, std::string const& msg); |
| @@ -191,7 +194,7 @@ class NNTreeImpl final | @@ -191,7 +194,7 @@ class NNTreeImpl final | ||
| 191 | 194 | ||
| 192 | QPDF& qpdf; | 195 | QPDF& qpdf; |
| 193 | int split_threshold{32}; | 196 | int split_threshold{32}; |
| 194 | - QPDFObjectHandle oh; | 197 | + qpdf::Dictionary tree_root; |
| 195 | const qpdf_object_type_e key_type; | 198 | const qpdf_object_type_e key_type; |
| 196 | const std::string items_key; | 199 | const std::string items_key; |
| 197 | const std::function<bool(QPDFObjectHandle const&)> value_valid; | 200 | const std::function<bool(QPDFObjectHandle const&)> value_valid; |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| @@ -12,18 +12,19 @@ namespace qpdf | @@ -12,18 +12,19 @@ namespace qpdf | ||
| 12 | class Array final: public BaseHandle | 12 | class Array final: public BaseHandle |
| 13 | { | 13 | { |
| 14 | public: | 14 | public: |
| 15 | - // Create an empty Array. If 'empty' is false, create a null Array. | ||
| 16 | - Array(bool empty = true); | 15 | + Array() = default; |
| 17 | 16 | ||
| 18 | - Array(std::vector<QPDFObjectHandle> const& items); | 17 | + explicit Array(std::vector<QPDFObjectHandle> const& items); |
| 19 | 18 | ||
| 20 | - Array(std::vector<QPDFObjectHandle>&& items); | 19 | + explicit Array(std::vector<QPDFObjectHandle>&& items); |
| 21 | 20 | ||
| 22 | Array(Array const& other) : | 21 | Array(Array const& other) : |
| 23 | BaseHandle(other.obj) | 22 | BaseHandle(other.obj) |
| 24 | { | 23 | { |
| 25 | } | 24 | } |
| 26 | 25 | ||
| 26 | + static Array empty(); | ||
| 27 | + | ||
| 27 | Array& | 28 | Array& |
| 28 | operator=(Array const& other) | 29 | operator=(Array const& other) |
| 29 | { | 30 | { |
| @@ -118,6 +119,11 @@ namespace qpdf | @@ -118,6 +119,11 @@ namespace qpdf | ||
| 118 | class BaseDictionary: public BaseHandle | 119 | class BaseDictionary: public BaseHandle |
| 119 | { | 120 | { |
| 120 | public: | 121 | public: |
| 122 | + // The following methods are not part of the public API. | ||
| 123 | + std::set<std::string> getKeys(); | ||
| 124 | + std::map<std::string, QPDFObjectHandle> const& getAsMap() const; | ||
| 125 | + void replaceKey(std::string const& key, QPDFObjectHandle value); | ||
| 126 | + | ||
| 121 | using iterator = std::map<std::string, QPDFObjectHandle>::iterator; | 127 | using iterator = std::map<std::string, QPDFObjectHandle>::iterator; |
| 122 | using const_iterator = std::map<std::string, QPDFObjectHandle>::const_iterator; | 128 | using const_iterator = std::map<std::string, QPDFObjectHandle>::const_iterator; |
| 123 | using reverse_iterator = std::map<std::string, QPDFObjectHandle>::reverse_iterator; | 129 | using reverse_iterator = std::map<std::string, QPDFObjectHandle>::reverse_iterator; |
| @@ -196,41 +202,87 @@ namespace qpdf | @@ -196,41 +202,87 @@ namespace qpdf | ||
| 196 | return {}; | 202 | return {}; |
| 197 | } | 203 | } |
| 198 | 204 | ||
| 199 | - // The following methods are not part of the public API. | ||
| 200 | - bool hasKey(std::string const& key) const; | ||
| 201 | - QPDFObjectHandle getKey(std::string const& key) const; | ||
| 202 | - std::set<std::string> getKeys(); | ||
| 203 | - std::map<std::string, QPDFObjectHandle> const& getAsMap() const; | ||
| 204 | - void removeKey(std::string const& key); | ||
| 205 | - void replaceKey(std::string const& key, QPDFObjectHandle value); | ||
| 206 | - | ||
| 207 | protected: | 205 | protected: |
| 208 | BaseDictionary() = default; | 206 | BaseDictionary() = default; |
| 209 | - BaseDictionary(std::shared_ptr<QPDFObject> const& obj) : | ||
| 210 | - BaseHandle(obj) {}; | ||
| 211 | - BaseDictionary(std::shared_ptr<QPDFObject>&& obj) : | ||
| 212 | - BaseHandle(std::move(obj)) {}; | 207 | + |
| 208 | + explicit BaseDictionary(std::map<std::string, QPDFObjectHandle> const& dict) : | ||
| 209 | + BaseHandle(QPDFObject::create<QPDF_Dictionary>(dict)) | ||
| 210 | + { | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + explicit BaseDictionary(std::map<std::string, QPDFObjectHandle>&& dict) : | ||
| 214 | + BaseHandle(QPDFObject::create<QPDF_Dictionary>(std::move(dict))) | ||
| 215 | + { | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + explicit BaseDictionary(std::shared_ptr<QPDFObject> const& obj) : | ||
| 219 | + BaseHandle(obj) | ||
| 220 | + { | ||
| 221 | + } | ||
| 222 | + explicit BaseDictionary(std::shared_ptr<QPDFObject>&& obj) : | ||
| 223 | + BaseHandle(std::move(obj)) | ||
| 224 | + { | ||
| 225 | + } | ||
| 213 | BaseDictionary(BaseDictionary const&) = default; | 226 | BaseDictionary(BaseDictionary const&) = default; |
| 214 | BaseDictionary& operator=(BaseDictionary const&) = default; | 227 | BaseDictionary& operator=(BaseDictionary const&) = default; |
| 215 | BaseDictionary(BaseDictionary&&) = default; | 228 | BaseDictionary(BaseDictionary&&) = default; |
| 216 | BaseDictionary& operator=(BaseDictionary&&) = default; | 229 | BaseDictionary& operator=(BaseDictionary&&) = default; |
| 230 | + | ||
| 231 | + explicit BaseDictionary(QPDFObjectHandle const& oh) : | ||
| 232 | + BaseHandle(oh.resolved_type_code() == ::ot_dictionary ? oh : QPDFObjectHandle()) | ||
| 233 | + { | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + explicit BaseDictionary(QPDFObjectHandle&& oh) : | ||
| 237 | + BaseHandle( | ||
| 238 | + oh.resolved_type_code() == ::ot_dictionary ? std::move(oh) : QPDFObjectHandle()) | ||
| 239 | + { | ||
| 240 | + } | ||
| 217 | ~BaseDictionary() = default; | 241 | ~BaseDictionary() = default; |
| 218 | 242 | ||
| 219 | QPDF_Dictionary* dict() const; | 243 | QPDF_Dictionary* dict() const; |
| 220 | }; | 244 | }; |
| 221 | 245 | ||
| 246 | + // Dictionary only defines con/destructors. All other methods are inherited from BaseDictionary. | ||
| 222 | class Dictionary final: public BaseDictionary | 247 | class Dictionary final: public BaseDictionary |
| 223 | { | 248 | { |
| 224 | public: | 249 | public: |
| 225 | - explicit Dictionary(std::shared_ptr<QPDFObject> const& obj) : | ||
| 226 | - BaseDictionary(obj) | 250 | + Dictionary() = default; |
| 251 | + explicit Dictionary(std::map<std::string, QPDFObjectHandle>&& dict); | ||
| 252 | + explicit Dictionary(std::shared_ptr<QPDFObject> const& obj); | ||
| 253 | + | ||
| 254 | + static Dictionary empty(); | ||
| 255 | + | ||
| 256 | + Dictionary(Dictionary const&) = default; | ||
| 257 | + Dictionary& operator=(Dictionary const&) = default; | ||
| 258 | + Dictionary(Dictionary&&) = default; | ||
| 259 | + Dictionary& operator=(Dictionary&&) = default; | ||
| 260 | + | ||
| 261 | + Dictionary(QPDFObjectHandle const& oh) : | ||
| 262 | + BaseDictionary(oh) | ||
| 227 | { | 263 | { |
| 228 | } | 264 | } |
| 229 | 265 | ||
| 230 | - explicit Dictionary(std::shared_ptr<QPDFObject>&& obj) : | ||
| 231 | - BaseDictionary(std::move(obj)) | 266 | + Dictionary& |
| 267 | + operator=(QPDFObjectHandle const& oh) | ||
| 268 | + { | ||
| 269 | + assign(::ot_dictionary, oh); | ||
| 270 | + return *this; | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + Dictionary(QPDFObjectHandle&& oh) : | ||
| 274 | + BaseDictionary(std::move(oh)) | ||
| 232 | { | 275 | { |
| 233 | } | 276 | } |
| 277 | + | ||
| 278 | + Dictionary& | ||
| 279 | + operator=(QPDFObjectHandle&& oh) | ||
| 280 | + { | ||
| 281 | + assign(::ot_dictionary, std::move(oh)); | ||
| 282 | + return *this; | ||
| 283 | + } | ||
| 284 | + | ||
| 285 | + ~Dictionary() = default; | ||
| 234 | }; | 286 | }; |
| 235 | 287 | ||
| 236 | class Name final: public BaseHandle | 288 | class Name final: public BaseHandle |
qpdf/qpdf.testcov
| @@ -304,11 +304,9 @@ QPDFObjectHandle insert array bounds 0 | @@ -304,11 +304,9 @@ QPDFObjectHandle insert array bounds 0 | ||
| 304 | QPDFObjectHandle array ignoring append item 0 | 304 | QPDFObjectHandle array ignoring append item 0 |
| 305 | QPDFObjectHandle array ignoring erase item 0 | 305 | QPDFObjectHandle array ignoring erase item 0 |
| 306 | QPDFObjectHandle dictionary false for hasKey 0 | 306 | QPDFObjectHandle dictionary false for hasKey 0 |
| 307 | -QPDFObjectHandle dictionary null for getKey 0 | ||
| 308 | QPDFObjectHandle dictionary empty set for getKeys 0 | 307 | QPDFObjectHandle dictionary empty set for getKeys 0 |
| 309 | QPDFObjectHandle dictionary empty map for asMap 0 | 308 | QPDFObjectHandle dictionary empty map for asMap 0 |
| 310 | QPDFObjectHandle dictionary ignoring replaceKey 0 | 309 | QPDFObjectHandle dictionary ignoring replaceKey 0 |
| 311 | -QPDFObjectHandle dictionary ignoring removeKey 0 | ||
| 312 | QPDFObjectHandle numeric non-numeric 0 | 310 | QPDFObjectHandle numeric non-numeric 0 |
| 313 | QPDFObjectHandle erase array bounds 0 | 311 | QPDFObjectHandle erase array bounds 0 |
| 314 | qpdf-c called qpdf_check_pdf 0 | 312 | qpdf-c called qpdf_check_pdf 0 |
| @@ -537,7 +535,6 @@ QPDFWriter preserve object streams 0 | @@ -537,7 +535,6 @@ QPDFWriter preserve object streams 0 | ||
| 537 | QPDFWriter preserve object streams preserve unreferenced 0 | 535 | QPDFWriter preserve object streams preserve unreferenced 0 |
| 538 | QPDFWriter exclude from object stream 0 | 536 | QPDFWriter exclude from object stream 0 |
| 539 | QPDF_pages findPage not found 0 | 537 | QPDF_pages findPage not found 0 |
| 540 | -QPDFObjectHandle check ownership 0 | ||
| 541 | QPDFJob weak crypto error 0 | 538 | QPDFJob weak crypto error 0 |
| 542 | qpdf-c called qpdf_oh_is_initialized 0 | 539 | qpdf-c called qpdf_oh_is_initialized 0 |
| 543 | qpdf-c registered progress reporter 0 | 540 | qpdf-c registered progress reporter 0 |
qpdf/qtest/qpdf/name-tree.out
| @@ -33,7 +33,7 @@ WARNING: name-tree.pdf (Name/Number tree node (object 17)): skipping over invali | @@ -33,7 +33,7 @@ WARNING: name-tree.pdf (Name/Number tree node (object 17)): skipping over invali | ||
| 33 | B | 33 | B |
| 34 | W | 34 | W |
| 35 | /Bad3 -- invalid kid | 35 | /Bad3 -- invalid kid |
| 36 | -WARNING: name-tree.pdf (Name/Number tree node (object 25)): non-dictionary node while traversing name/number tree | 36 | +WARNING: name-tree.pdf (Name/Number tree node (object 22)): kid number 0 is invalid |
| 37 | /Bad4 -- invalid kid | 37 | /Bad4 -- invalid kid |
| 38 | WARNING: name-tree.pdf (Name/Number tree node (object 23)): attempting to repair after error: name-tree.pdf (Name/Number tree node (object 23)): invalid kid at index 1 | 38 | WARNING: name-tree.pdf (Name/Number tree node (object 23)): attempting to repair after error: name-tree.pdf (Name/Number tree node (object 23)): invalid kid at index 1 |
| 39 | WARNING: name-tree.pdf (Name/Number tree node (object 23)): skipping over invalid kid at index 1 | 39 | WARNING: name-tree.pdf (Name/Number tree node (object 23)): skipping over invalid kid at index 1 |