Commit d61ffb65d034848157291b9825f4b33155bd55e7
1 parent
ba814703
Add new constructors for name/number tree helpers
Add constructors that take a QPDF object so we can issue warnings and create new indirect objects.
Showing
12 changed files
with
169 additions
and
29 deletions
ChangeLog
| 1 | 2021-01-16 Jay Berkenbilt <ejb@ql.org> | 1 | 2021-01-16 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * Add new constructors for QPDFNameTreeObjectHelper and | ||
| 4 | + QPDFNumberTreeObjectHelper that take a QPDF object so they can | ||
| 5 | + create indirect objects and issue warnings. The old constructors | ||
| 6 | + are deprecated and will be removed in qpdf 11. Just pass in the | ||
| 7 | + owning QPDF of the object handle used to initialize the helpers. | ||
| 8 | + | ||
| 3 | * Re-implement QPDFNameTreeObjectHelper and | 9 | * Re-implement QPDFNameTreeObjectHelper and |
| 4 | QPDFNumberTreeObjectHelper to be much more efficient and to have | 10 | QPDFNumberTreeObjectHelper to be much more efficient and to have |
| 5 | an iterator-based API in addition to the existing one. This makes | 11 | an iterator-based API in addition to the existing one. This makes |
TODO
| @@ -84,7 +84,12 @@ ABI Changes | @@ -84,7 +84,12 @@ ABI Changes | ||
| 84 | This is a list of changes to make next time there is an ABI change. | 84 | This is a list of changes to make next time there is an ABI change. |
| 85 | Comments appear in the code prefixed by "ABI" | 85 | Comments appear in the code prefixed by "ABI" |
| 86 | 86 | ||
| 87 | +* Search for ABI to find items not listed here. | ||
| 87 | * Merge two versions of QPDFObjectHandle::makeDirect per comment | 88 | * Merge two versions of QPDFObjectHandle::makeDirect per comment |
| 89 | +* After removing legacy QPDFNameTreeObjectHelper and | ||
| 90 | + QPDFNumberTreeObjectHelper constructors, NNTreeImpl can switch to | ||
| 91 | + having a QPDF reference and assume that the reference is always | ||
| 92 | + valid. | ||
| 88 | 93 | ||
| 89 | Page splitting/merging | 94 | Page splitting/merging |
| 90 | ====================== | 95 | ====================== |
include/qpdf/QPDFNameTreeObjectHelper.hh
| @@ -42,6 +42,16 @@ class NNTreeDetails; | @@ -42,6 +42,16 @@ class NNTreeDetails; | ||
| 42 | class QPDFNameTreeObjectHelper: public QPDFObjectHelper | 42 | class QPDFNameTreeObjectHelper: public QPDFObjectHelper |
| 43 | { | 43 | { |
| 44 | public: | 44 | public: |
| 45 | + // The qpdf object is required so that this class can issue | ||
| 46 | + // warnings, attempt repairs, and add indirect objects. | ||
| 47 | + QPDF_DLL | ||
| 48 | + QPDFNameTreeObjectHelper(QPDFObjectHandle, QPDF&, | ||
| 49 | + bool auto_repair = true); | ||
| 50 | + | ||
| 51 | + // ABI: Legacy Constructor will be removed in QPDF 11. A | ||
| 52 | + // QPDFNameTreeObjectHelper constructed in this way can't be | ||
| 53 | + // modified or repaired and will silently ignore problems in the | ||
| 54 | + // structure. | ||
| 45 | QPDF_DLL | 55 | QPDF_DLL |
| 46 | QPDFNameTreeObjectHelper(QPDFObjectHandle); | 56 | QPDFNameTreeObjectHelper(QPDFObjectHandle); |
| 47 | QPDF_DLL | 57 | QPDF_DLL |
| @@ -133,7 +143,7 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper | @@ -133,7 +143,7 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper | ||
| 133 | ~Members(); | 143 | ~Members(); |
| 134 | 144 | ||
| 135 | private: | 145 | private: |
| 136 | - Members(QPDFObjectHandle& oh); | 146 | + Members(QPDFObjectHandle& oh, QPDF*, bool auto_repair); |
| 137 | Members(Members const&) = delete; | 147 | Members(Members const&) = delete; |
| 138 | 148 | ||
| 139 | std::shared_ptr<NNTreeImpl> impl; | 149 | std::shared_ptr<NNTreeImpl> impl; |
include/qpdf/QPDFNumberTreeObjectHelper.hh
| @@ -39,6 +39,16 @@ class NNTreeDetails; | @@ -39,6 +39,16 @@ class NNTreeDetails; | ||
| 39 | class QPDFNumberTreeObjectHelper: public QPDFObjectHelper | 39 | class QPDFNumberTreeObjectHelper: public QPDFObjectHelper |
| 40 | { | 40 | { |
| 41 | public: | 41 | public: |
| 42 | + // The qpdf object is required so that this class can issue | ||
| 43 | + // warnings, attempt repairs, and add indirect objects. | ||
| 44 | + QPDF_DLL | ||
| 45 | + QPDFNumberTreeObjectHelper(QPDFObjectHandle, QPDF&, | ||
| 46 | + bool auto_repair = true); | ||
| 47 | + | ||
| 48 | + // ABI: Legacy Constructor will be removed in QPDF 11. A | ||
| 49 | + // QPDFNumberTreeObjectHelper constructed in this way can't be | ||
| 50 | + // modified or repaired and will silently ignore problems in the | ||
| 51 | + // structure. | ||
| 42 | QPDF_DLL | 52 | QPDF_DLL |
| 43 | QPDFNumberTreeObjectHelper(QPDFObjectHandle); | 53 | QPDFNumberTreeObjectHelper(QPDFObjectHandle); |
| 44 | QPDF_DLL | 54 | QPDF_DLL |
| @@ -154,7 +164,7 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper | @@ -154,7 +164,7 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper | ||
| 154 | ~Members(); | 164 | ~Members(); |
| 155 | 165 | ||
| 156 | private: | 166 | private: |
| 157 | - Members(QPDFObjectHandle& oh); | 167 | + Members(QPDFObjectHandle& oh, QPDF*, bool auto_repair); |
| 158 | Members(Members const&) = delete; | 168 | Members(Members const&) = delete; |
| 159 | 169 | ||
| 160 | std::shared_ptr<NNTreeImpl> impl; | 170 | std::shared_ptr<NNTreeImpl> impl; |
libqpdf/NNTree.cc
| 1 | #include <qpdf/NNTree.hh> | 1 | #include <qpdf/NNTree.hh> |
| 2 | +#include <qpdf/QTC.hh> | ||
| 2 | #include <qpdf/QUtil.hh> | 3 | #include <qpdf/QUtil.hh> |
| 3 | 4 | ||
| 4 | #include <exception> | 5 | #include <exception> |
| 5 | 6 | ||
| 7 | +static std::string | ||
| 8 | +get_description(QPDFObjectHandle& node) | ||
| 9 | +{ | ||
| 10 | + std::string result("Name/Number tree node"); | ||
| 11 | + if (node.isIndirect()) | ||
| 12 | + { | ||
| 13 | + result += " (object " + QUtil::int_to_string(node.getObjectID()) + ")"; | ||
| 14 | + } | ||
| 15 | + return result; | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +static void | ||
| 19 | +warn(QPDF* qpdf, QPDFObjectHandle& node, std::string const& msg) | ||
| 20 | +{ | ||
| 21 | + // ABI: in qpdf 11, change to a reference. | ||
| 22 | + | ||
| 23 | + if (qpdf) | ||
| 24 | + { | ||
| 25 | + qpdf->warn(QPDFExc( | ||
| 26 | + qpdf_e_damaged_pdf, | ||
| 27 | + qpdf->getFilename(), get_description(node), 0, msg)); | ||
| 28 | + } | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +static void | ||
| 32 | +error(QPDF* qpdf, QPDFObjectHandle& node, std::string const& msg) | ||
| 33 | +{ | ||
| 34 | + // ABI: in qpdf 11, change to a reference. | ||
| 35 | + | ||
| 36 | + if (qpdf) | ||
| 37 | + { | ||
| 38 | + throw QPDFExc(qpdf_e_damaged_pdf, | ||
| 39 | + qpdf->getFilename(), get_description(node), 0, msg); | ||
| 40 | + } | ||
| 41 | + else | ||
| 42 | + { | ||
| 43 | + throw std::runtime_error(get_description(node) + ": " + msg); | ||
| 44 | + } | ||
| 45 | +} | ||
| 46 | + | ||
| 6 | NNTreeIterator::PathElement::PathElement( | 47 | NNTreeIterator::PathElement::PathElement( |
| 7 | QPDFObjectHandle const& node, int kid_number) : | 48 | QPDFObjectHandle const& node, int kid_number) : |
| 8 | node(node), | 49 | node(node), |
| @@ -138,6 +179,13 @@ NNTreeIterator::addPathElement(QPDFObjectHandle const& node, | @@ -138,6 +179,13 @@ NNTreeIterator::addPathElement(QPDFObjectHandle const& node, | ||
| 138 | } | 179 | } |
| 139 | 180 | ||
| 140 | void | 181 | void |
| 182 | +NNTreeIterator::reset() | ||
| 183 | +{ | ||
| 184 | + this->path.clear(); | ||
| 185 | + this->item_number = -1; | ||
| 186 | +} | ||
| 187 | + | ||
| 188 | +void | ||
| 141 | NNTreeIterator::deepen(QPDFObjectHandle node, bool first) | 189 | NNTreeIterator::deepen(QPDFObjectHandle node, bool first) |
| 142 | { | 190 | { |
| 143 | std::set<QPDFObjGen> seen; | 191 | std::set<QPDFObjGen> seen; |
| @@ -148,7 +196,11 @@ NNTreeIterator::deepen(QPDFObjectHandle node, bool first) | @@ -148,7 +196,11 @@ NNTreeIterator::deepen(QPDFObjectHandle node, bool first) | ||
| 148 | auto og = node.getObjGen(); | 196 | auto og = node.getObjGen(); |
| 149 | if (seen.count(og)) | 197 | if (seen.count(og)) |
| 150 | { | 198 | { |
| 151 | - throw std::runtime_error("loop detected"); | 199 | + QTC::TC("qpdf", "NNTree deepen: loop"); |
| 200 | + warn(qpdf, node, | ||
| 201 | + "loop detected while traversing name/number tree"); | ||
| 202 | + reset(); | ||
| 203 | + return; | ||
| 152 | } | 204 | } |
| 153 | seen.insert(og); | 205 | seen.insert(og); |
| 154 | } | 206 | } |
| @@ -169,7 +221,11 @@ NNTreeIterator::deepen(QPDFObjectHandle node, bool first) | @@ -169,7 +221,11 @@ NNTreeIterator::deepen(QPDFObjectHandle node, bool first) | ||
| 169 | } | 221 | } |
| 170 | else | 222 | else |
| 171 | { | 223 | { |
| 172 | - throw std::runtime_error("node has neither /Kids nor /Names"); | 224 | + QTC::TC("qpdf", "NNTree deepen: invalid node"); |
| 225 | + warn(qpdf, node, | ||
| 226 | + "name/number tree node has neither /Kids nor /Names"); | ||
| 227 | + reset(); | ||
| 228 | + return; | ||
| 173 | } | 229 | } |
| 174 | } | 230 | } |
| 175 | } | 231 | } |
| @@ -179,6 +235,7 @@ NNTreeImpl::NNTreeImpl(NNTreeDetails const& details, | @@ -179,6 +235,7 @@ NNTreeImpl::NNTreeImpl(NNTreeDetails const& details, | ||
| 179 | QPDFObjectHandle& oh, | 235 | QPDFObjectHandle& oh, |
| 180 | bool auto_repair) : | 236 | bool auto_repair) : |
| 181 | details(details), | 237 | details(details), |
| 238 | + qpdf(qpdf), | ||
| 182 | oh(oh) | 239 | oh(oh) |
| 183 | { | 240 | { |
| 184 | } | 241 | } |
| @@ -186,7 +243,7 @@ NNTreeImpl::NNTreeImpl(NNTreeDetails const& details, | @@ -186,7 +243,7 @@ NNTreeImpl::NNTreeImpl(NNTreeDetails const& details, | ||
| 186 | NNTreeImpl::iterator | 243 | NNTreeImpl::iterator |
| 187 | NNTreeImpl::begin() | 244 | NNTreeImpl::begin() |
| 188 | { | 245 | { |
| 189 | - iterator result(details); | 246 | + iterator result(details, this->qpdf); |
| 190 | result.deepen(this->oh, true); | 247 | result.deepen(this->oh, true); |
| 191 | return result; | 248 | return result; |
| 192 | } | 249 | } |
| @@ -194,13 +251,13 @@ NNTreeImpl::begin() | @@ -194,13 +251,13 @@ NNTreeImpl::begin() | ||
| 194 | NNTreeImpl::iterator | 251 | NNTreeImpl::iterator |
| 195 | NNTreeImpl::end() | 252 | NNTreeImpl::end() |
| 196 | { | 253 | { |
| 197 | - return iterator(details); | 254 | + return iterator(details, this->qpdf); |
| 198 | } | 255 | } |
| 199 | 256 | ||
| 200 | NNTreeImpl::iterator | 257 | NNTreeImpl::iterator |
| 201 | NNTreeImpl::last() | 258 | NNTreeImpl::last() |
| 202 | { | 259 | { |
| 203 | - iterator result(details); | 260 | + iterator result(details, this->qpdf); |
| 204 | result.deepen(this->oh, false); | 261 | result.deepen(this->oh, false); |
| 205 | return result; | 262 | return result; |
| 206 | } | 263 | } |
| @@ -315,21 +372,22 @@ NNTreeImpl::compareKeyItem( | @@ -315,21 +372,22 @@ NNTreeImpl::compareKeyItem( | ||
| 315 | if (! ((items.isArray() && (items.getArrayNItems() > (2 * idx)) && | 372 | if (! ((items.isArray() && (items.getArrayNItems() > (2 * idx)) && |
| 316 | details.keyValid(items.getArrayItem(2 * idx))))) | 373 | details.keyValid(items.getArrayItem(2 * idx))))) |
| 317 | { | 374 | { |
| 318 | - throw std::runtime_error("item at index " + | ||
| 319 | - QUtil::int_to_string(2 * idx) + | ||
| 320 | - " is not the right type"); | 375 | + error(qpdf, this->oh, |
| 376 | + "item at index " + QUtil::int_to_string(2 * idx) + | ||
| 377 | + " is not the right type"); | ||
| 321 | } | 378 | } |
| 322 | return details.compareKeys(key, items.getArrayItem(2 * idx)); | 379 | return details.compareKeys(key, items.getArrayItem(2 * idx)); |
| 323 | } | 380 | } |
| 324 | 381 | ||
| 325 | int | 382 | int |
| 326 | -NNTreeImpl::compareKeyKid(QPDFObjectHandle& key, QPDFObjectHandle& kids, int idx) | 383 | +NNTreeImpl::compareKeyKid( |
| 384 | + QPDFObjectHandle& key, QPDFObjectHandle& kids, int idx) | ||
| 327 | { | 385 | { |
| 328 | if (! (kids.isArray() && (idx < kids.getArrayNItems()) && | 386 | if (! (kids.isArray() && (idx < kids.getArrayNItems()) && |
| 329 | kids.getArrayItem(idx).isDictionary())) | 387 | kids.getArrayItem(idx).isDictionary())) |
| 330 | { | 388 | { |
| 331 | - throw std::runtime_error("invalid kid at index " + | ||
| 332 | - QUtil::int_to_string(idx)); | 389 | + error(qpdf, this->oh, |
| 390 | + "invalid kid at index " + QUtil::int_to_string(idx)); | ||
| 333 | } | 391 | } |
| 334 | return withinLimits(key, kids.getArrayItem(idx)); | 392 | return withinLimits(key, kids.getArrayItem(idx)); |
| 335 | } | 393 | } |
| @@ -364,14 +422,14 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) | @@ -364,14 +422,14 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) | ||
| 364 | 422 | ||
| 365 | std::set<QPDFObjGen> seen; | 423 | std::set<QPDFObjGen> seen; |
| 366 | auto node = this->oh; | 424 | auto node = this->oh; |
| 367 | - iterator result(details); | 425 | + iterator result(details, this->qpdf); |
| 368 | 426 | ||
| 369 | while (true) | 427 | while (true) |
| 370 | { | 428 | { |
| 371 | auto og = node.getObjGen(); | 429 | auto og = node.getObjGen(); |
| 372 | if (seen.count(og)) | 430 | if (seen.count(og)) |
| 373 | { | 431 | { |
| 374 | - throw std::runtime_error("loop detected in find"); | 432 | + error(qpdf, node, "loop detected in find"); |
| 375 | } | 433 | } |
| 376 | seen.insert(og); | 434 | seen.insert(og); |
| 377 | 435 | ||
| @@ -397,16 +455,16 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) | @@ -397,16 +455,16 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) | ||
| 397 | &NNTreeImpl::compareKeyKid); | 455 | &NNTreeImpl::compareKeyKid); |
| 398 | if (idx == -1) | 456 | if (idx == -1) |
| 399 | { | 457 | { |
| 400 | - throw std::runtime_error( | ||
| 401 | - "unexpected -1 from binary search of kids;" | ||
| 402 | - " tree may not be sorted"); | 458 | + error(qpdf, node, |
| 459 | + "unexpected -1 from binary search of kids;" | ||
| 460 | + " tree may not be sorted"); | ||
| 403 | } | 461 | } |
| 404 | result.addPathElement(node, idx); | 462 | result.addPathElement(node, idx); |
| 405 | node = kids.getArrayItem(idx); | 463 | node = kids.getArrayItem(idx); |
| 406 | } | 464 | } |
| 407 | else | 465 | else |
| 408 | { | 466 | { |
| 409 | - throw std::runtime_error("bad node during find"); | 467 | + error(qpdf, node, "bad node during find"); |
| 410 | } | 468 | } |
| 411 | } | 469 | } |
| 412 | 470 |
libqpdf/QPDFNameTreeObjectHelper.cc
| @@ -33,14 +33,22 @@ QPDFNameTreeObjectHelper::Members::~Members() | @@ -33,14 +33,22 @@ QPDFNameTreeObjectHelper::Members::~Members() | ||
| 33 | { | 33 | { |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | -QPDFNameTreeObjectHelper::Members::Members(QPDFObjectHandle& oh) : | ||
| 37 | - impl(std::make_shared<NNTreeImpl>(name_tree_details, nullptr, oh, false)) | 36 | +QPDFNameTreeObjectHelper::Members::Members( |
| 37 | + QPDFObjectHandle& oh, QPDF* q, bool auto_repair) : | ||
| 38 | + impl(std::make_shared<NNTreeImpl>(name_tree_details, q, oh, auto_repair)) | ||
| 39 | +{ | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper( | ||
| 43 | + QPDFObjectHandle oh, QPDF& q, bool auto_repair) : | ||
| 44 | + QPDFObjectHelper(oh), | ||
| 45 | + m(new Members(oh, &q, auto_repair)) | ||
| 38 | { | 46 | { |
| 39 | } | 47 | } |
| 40 | 48 | ||
| 41 | QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(QPDFObjectHandle oh) : | 49 | QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(QPDFObjectHandle oh) : |
| 42 | QPDFObjectHelper(oh), | 50 | QPDFObjectHelper(oh), |
| 43 | - m(new Members(oh)) | 51 | + m(new Members(oh, nullptr, false)) |
| 44 | { | 52 | { |
| 45 | } | 53 | } |
| 46 | 54 |
libqpdf/QPDFNumberTreeObjectHelper.cc
| @@ -33,14 +33,22 @@ QPDFNumberTreeObjectHelper::Members::~Members() | @@ -33,14 +33,22 @@ QPDFNumberTreeObjectHelper::Members::~Members() | ||
| 33 | { | 33 | { |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | -QPDFNumberTreeObjectHelper::Members::Members(QPDFObjectHandle& oh) : | ||
| 37 | - impl(std::make_shared<NNTreeImpl>(number_tree_details, nullptr, oh, false)) | 36 | +QPDFNumberTreeObjectHelper::Members::Members( |
| 37 | + QPDFObjectHandle& oh, QPDF* q, bool auto_repair) : | ||
| 38 | + impl(std::make_shared<NNTreeImpl>(number_tree_details, q, oh, auto_repair)) | ||
| 39 | +{ | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper( | ||
| 43 | + QPDFObjectHandle oh, QPDF& q, bool auto_repair) : | ||
| 44 | + QPDFObjectHelper(oh), | ||
| 45 | + m(new Members(oh, &q, auto_repair)) | ||
| 38 | { | 46 | { |
| 39 | } | 47 | } |
| 40 | 48 | ||
| 41 | QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(QPDFObjectHandle oh) : | 49 | QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(QPDFObjectHandle oh) : |
| 42 | QPDFObjectHelper(oh), | 50 | QPDFObjectHelper(oh), |
| 43 | - m(new Members(oh)) | 51 | + m(new Members(oh, nullptr, false)) |
| 44 | { | 52 | { |
| 45 | } | 53 | } |
| 46 | 54 |
libqpdf/qpdf/NNTree.hh
| @@ -57,17 +57,21 @@ class NNTreeIterator: public std::iterator< | @@ -57,17 +57,21 @@ class NNTreeIterator: public std::iterator< | ||
| 57 | int kid_number; | 57 | int kid_number; |
| 58 | }; | 58 | }; |
| 59 | 59 | ||
| 60 | - NNTreeIterator(NNTreeDetails const& details) : | 60 | + // ABI: for qpdf 11, make qpdf a reference |
| 61 | + NNTreeIterator(NNTreeDetails const& details, QPDF* qpdf) : | ||
| 61 | details(details), | 62 | details(details), |
| 63 | + qpdf(qpdf), | ||
| 62 | item_number(-1) | 64 | item_number(-1) |
| 63 | { | 65 | { |
| 64 | } | 66 | } |
| 67 | + void reset(); | ||
| 65 | void deepen(QPDFObjectHandle node, bool first); | 68 | void deepen(QPDFObjectHandle node, bool first); |
| 66 | void setItemNumber(QPDFObjectHandle const& node, int); | 69 | void setItemNumber(QPDFObjectHandle const& node, int); |
| 67 | void addPathElement(QPDFObjectHandle const& node, int kid_number); | 70 | void addPathElement(QPDFObjectHandle const& node, int kid_number); |
| 68 | void increment(bool backward); | 71 | void increment(bool backward); |
| 69 | 72 | ||
| 70 | NNTreeDetails const& details; | 73 | NNTreeDetails const& details; |
| 74 | + QPDF* qpdf; | ||
| 71 | std::list<PathElement> path; | 75 | std::list<PathElement> path; |
| 72 | QPDFObjectHandle node; | 76 | QPDFObjectHandle node; |
| 73 | int item_number; | 77 | int item_number; |
| @@ -99,6 +103,7 @@ class NNTreeImpl | @@ -99,6 +103,7 @@ class NNTreeImpl | ||
| 99 | QPDFObjectHandle& key, QPDFObjectHandle& items, int idx); | 103 | QPDFObjectHandle& key, QPDFObjectHandle& items, int idx); |
| 100 | 104 | ||
| 101 | NNTreeDetails const& details; | 105 | NNTreeDetails const& details; |
| 106 | + QPDF* qpdf; | ||
| 102 | QPDFObjectHandle oh; | 107 | QPDFObjectHandle oh; |
| 103 | }; | 108 | }; |
| 104 | 109 |
qpdf/qpdf.testcov
| @@ -522,3 +522,5 @@ qpdf-c called qpdf_oh_unparse_resolved 0 | @@ -522,3 +522,5 @@ qpdf-c called qpdf_oh_unparse_resolved 0 | ||
| 522 | qpdf-c called qpdf_oh_unparse_binary 0 | 522 | qpdf-c called qpdf_oh_unparse_binary 0 |
| 523 | QPDFWriter getFilterOnWrite false 0 | 523 | QPDFWriter getFilterOnWrite false 0 |
| 524 | QPDFPageObjectHelper::forEachXObject 3 | 524 | QPDFPageObjectHelper::forEachXObject 3 |
| 525 | +NNTree deepen: invalid node 0 | ||
| 526 | +NNTree deepen: loop 0 |
qpdf/qtest/qpdf/number-tree.out
| @@ -26,4 +26,6 @@ | @@ -26,4 +26,6 @@ | ||
| 26 | 22 twenty-two | 26 | 22 twenty-two |
| 27 | 23 twenty-three | 27 | 23 twenty-three |
| 28 | 29 twenty-nine | 28 | 29 twenty-nine |
| 29 | +WARNING: number-tree.pdf (Name/Number tree node (object 14)): name/number tree node has neither /Kids nor /Names | ||
| 30 | +WARNING: number-tree.pdf (Name/Number tree node (object 13)): loop detected while traversing name/number tree | ||
| 29 | test 46 done | 31 | test 46 done |
qpdf/qtest/qpdf/number-tree.pdf
| @@ -144,9 +144,22 @@ endobj | @@ -144,9 +144,22 @@ endobj | ||
| 144 | >> | 144 | >> |
| 145 | endobj | 145 | endobj |
| 146 | 146 | ||
| 147 | +13 0 obj | ||
| 148 | +<< | ||
| 149 | + /Kids [ | ||
| 150 | + 14 0 R | ||
| 151 | + 13 0 R | ||
| 152 | + ] | ||
| 153 | +>> | ||
| 154 | +endobj | ||
| 155 | + | ||
| 156 | +14 0 obj | ||
| 157 | +<< | ||
| 158 | +>> | ||
| 159 | +endobj | ||
| 147 | 160 | ||
| 148 | xref | 161 | xref |
| 149 | -0 13 | 162 | +0 15 |
| 150 | 0000000000 65535 f | 163 | 0000000000 65535 f |
| 151 | 0000000025 00000 n | 164 | 0000000025 00000 n |
| 152 | 0000000079 00000 n | 165 | 0000000079 00000 n |
| @@ -160,12 +173,15 @@ xref | @@ -160,12 +173,15 @@ xref | ||
| 160 | 0000000791 00000 n | 173 | 0000000791 00000 n |
| 161 | 0000000937 00000 n | 174 | 0000000937 00000 n |
| 162 | 0000001078 00000 n | 175 | 0000001078 00000 n |
| 176 | +0000001214 00000 n | ||
| 177 | +0000001273 00000 n | ||
| 163 | trailer << | 178 | trailer << |
| 164 | /Root 1 0 R | 179 | /Root 1 0 R |
| 165 | /QTest 8 0 R | 180 | /QTest 8 0 R |
| 166 | - /Size 13 | 181 | + /Bad1 13 0 R |
| 182 | + /Size 15 | ||
| 167 | /ID [<2c3b7a6ec7fc61db8a5db4eebf57f540><2c3b7a6ec7fc61db8a5db4eebf57f540>] | 183 | /ID [<2c3b7a6ec7fc61db8a5db4eebf57f540><2c3b7a6ec7fc61db8a5db4eebf57f540>] |
| 168 | >> | 184 | >> |
| 169 | startxref | 185 | startxref |
| 170 | -1215 | 186 | +1296 |
| 171 | %%EOF | 187 | %%EOF |
qpdf/test_driver.cc
| @@ -1775,6 +1775,16 @@ void runtest(int n, char const* filename1, char const* arg2) | @@ -1775,6 +1775,16 @@ void runtest(int n, char const* filename1, char const* arg2) | ||
| 1775 | assert(ntoh.findObjectAtOrBelow(8, oh, offset)); | 1775 | assert(ntoh.findObjectAtOrBelow(8, oh, offset)); |
| 1776 | assert("six" == oh.getStringValue()); | 1776 | assert("six" == oh.getStringValue()); |
| 1777 | assert(2 == offset); | 1777 | assert(2 == offset); |
| 1778 | + | ||
| 1779 | + // Exercise deprecated API until qpdf 11 | ||
| 1780 | + auto bad1 = QPDFNumberTreeObjectHelper( | ||
| 1781 | + pdf.getTrailer().getKey("/Bad1")); | ||
| 1782 | + assert(bad1.begin() == bad1.end()); | ||
| 1783 | + | ||
| 1784 | + bad1 = QPDFNumberTreeObjectHelper( | ||
| 1785 | + pdf.getTrailer().getKey("/Bad1"), pdf); | ||
| 1786 | + assert(bad1.begin() == bad1.end()); | ||
| 1787 | + assert(bad1.last() == bad1.end()); | ||
| 1778 | } | 1788 | } |
| 1779 | else if (n == 47) | 1789 | else if (n == 47) |
| 1780 | { | 1790 | { |