Commit 3fbae967654f175ea1b0bd684947b645bfc69c69

Authored by m-holger
Committed by GitHub
2 parents 587177b1 eee05a0c

Merge pull request #1515 from m-holger/base_oh

Add new private-API methods `BaseHandle::size` and `BaseHandle::empty`
include/qpdf/ObjectHandle.hh
... ... @@ -22,7 +22,10 @@
22 22  
23 23 #include <qpdf/Constants.h>
24 24 #include <qpdf/DLL.h>
  25 +#include <qpdf/Types.h>
  26 +
25 27 #include <qpdf/JSON.hh>
  28 +#include <qpdf/QPDFExc.hh>
26 29 #include <qpdf/QPDFObjGen.hh>
27 30 #include <qpdf/Types.h>
28 31  
... ... @@ -58,6 +61,18 @@ namespace qpdf
58 61  
59 62 // The rest of the header file is for qpdf internal use only.
60 63  
  64 + // For arrays, return the number of items in the array.
  65 + // For null-like objects, return 0.
  66 + // For all other objects, return 1.
  67 + size_t size() const;
  68 +
  69 + // Return 'true' if size() == 0.
  70 + bool
  71 + empty() const
  72 + {
  73 + return size() == 0;
  74 + }
  75 +
61 76 std::shared_ptr<QPDFObject> copy(bool shallow = false) const;
62 77 // Recursively remove association with any QPDF object. This method may only be called
63 78 // during final destruction.
... ... @@ -71,6 +86,9 @@ namespace qpdf
71 86 inline qpdf_object_type_e type_code() const;
72 87 std::string unparse() const;
73 88 void write_json(int json_version, JSON::Writer& p) const;
  89 + static void warn(QPDF*, QPDFExc&&);
  90 + void warn(QPDFExc&&) const;
  91 + void warn(std::string const& warning) const;
74 92  
75 93 protected:
76 94 BaseHandle() = default;
... ... @@ -87,6 +105,11 @@ namespace qpdf
87 105 template <typename T>
88 106 T* as() const;
89 107  
  108 + std::string description() const;
  109 + std::runtime_error type_error(char const* expected_type) const;
  110 + QPDFExc type_error(char const* expected_type, std::string const& message) const;
  111 + char const* type_name() const;
  112 +
90 113 std::shared_ptr<QPDFObject> obj;
91 114 };
92 115  
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -1357,7 +1357,6 @@ class QPDFObjectHandle: public qpdf::BaseHandle
1357 1357 QPDF* context);
1358 1358 std::vector<QPDFObjectHandle>
1359 1359 arrayOrStreamToStreamArray(std::string const& description, std::string& all_description);
1360   - static void warn(QPDF*, QPDFExc const&);
1361 1360 void checkOwnership(QPDFObjectHandle const&) const;
1362 1361 };
1363 1362  
... ...
libqpdf/QPDFAcroFormDocumentHelper.cc
... ... @@ -7,7 +7,10 @@
7 7 #include <qpdf/QUtil.hh>
8 8 #include <qpdf/ResourceFinder.hh>
9 9  
  10 +#include <utility>
  11 +
10 12 using namespace qpdf;
  13 +using namespace std::literals;
11 14  
12 15 QPDFAcroFormDocumentHelper::QPDFAcroFormDocumentHelper(QPDF& qpdf) :
13 16 QPDFDocumentHelper(qpdf),
... ... @@ -138,7 +141,7 @@ QPDFAcroFormDocumentHelper::removeFormFields(std::set&lt;QPDFObjGen&gt; const&amp; to_remo
138 141 }
139 142  
140 143 int i = 0;
141   - while (i < fields.getArrayNItems()) {
  144 + while (std::cmp_less(i, fields.size())) {
142 145 auto field = fields.getArrayItem(i);
143 146 if (to_remove.contains(field.getObjGen())) {
144 147 fields.eraseItem(i);
... ... @@ -251,7 +254,7 @@ QPDFAcroFormDocumentHelper::analyze()
251 254 }
252 255 } else {
253 256 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper fields not array");
254   - acroform.warnIfPossible("/Fields key of /AcroForm dictionary is not an array; ignoring");
  257 + acroform.warn("/Fields key of /AcroForm dictionary is not an array; ignoring");
255 258 fields = QPDFObjectHandle::newArray();
256 259 }
257 260  
... ... @@ -273,9 +276,9 @@ QPDFAcroFormDocumentHelper::analyze()
273 276 // case such as a PDF creator adding a self-contained annotation (merged with the
274 277 // field dictionary) to the page's /Annots array and forgetting to also put it in
275 278 // /AcroForm.
276   - annot.warnIfPossible(
277   - "this widget annotation is not"
278   - " reachable from /AcroForm in the document catalog");
  279 + annot.warn(
  280 + "this widget annotation is not reachable from /AcroForm in the document "
  281 + "catalog");
279 282 m->annotation_to_field[og] = QPDFFormFieldObjectHelper(annot);
280 283 m->field_to_annotations[og].emplace_back(annot);
281 284 }
... ... @@ -292,16 +295,16 @@ QPDFAcroFormDocumentHelper::traverseField(
292 295 // could cause stack overflow.
293 296 return;
294 297 }
295   - if (!field.isIndirect()) {
  298 + if (!field.indirect()) {
296 299 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper direct field");
297   - field.warnIfPossible(
  300 + field.warn(
298 301 "encountered a direct object as a field or annotation while "
299 302 "traversing /AcroForm; ignoring field or annotation");
300 303 return;
301 304 }
302 305 if (!field.isDictionary()) {
303 306 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper non-dictionary field");
304   - field.warnIfPossible(
  307 + field.warn(
305 308 "encountered a non-dictionary as a field or annotation while"
306 309 " traversing /AcroForm; ignoring field or annotation");
307 310 return;
... ... @@ -309,7 +312,7 @@ QPDFAcroFormDocumentHelper::traverseField(
309 312 QPDFObjGen og(field.getObjGen());
310 313 if (!visited.add(og)) {
311 314 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper loop");
312   - field.warnIfPossible("loop detected while traversing /AcroForm");
  315 + field.warn("loop detected while traversing /AcroForm");
313 316 return;
314 317 }
315 318  
... ... @@ -375,7 +378,7 @@ QPDFAcroFormDocumentHelper::setNeedAppearances(bool val)
375 378 {
376 379 QPDFObjectHandle acroform = qpdf.getRoot().getKey("/AcroForm");
377 380 if (!acroform.isDictionary()) {
378   - qpdf.getRoot().warnIfPossible(
  381 + qpdf.getRoot().warn(
379 382 "ignoring call to QPDFAcroFormDocumentHelper::setNeedAppearances"
380 383 " on a file that lacks an /AcroForm dictionary");
381 384 return;
... ... @@ -592,9 +595,7 @@ QPDFAcroFormDocumentHelper::adjustDefaultAppearances(
592 595 } catch (std::exception& e) {
593 596 // No way to reproduce in test suite right now since error conditions are converted to
594 597 // warnings.
595   - obj.warnIfPossible(
596   - std::string("Unable to parse /DA: ") + e.what() +
597   - "; this form field may not update properly");
  598 + obj.warn("Unable to parse /DA: "s + e.what() + "; this form field may not update properly");
598 599 return;
599 600 }
600 601  
... ... @@ -677,16 +678,17 @@ QPDFAcroFormDocumentHelper::adjustAppearanceStream(
677 678 try {
678 679 auto nwarnings = qpdf.numWarnings();
679 680 stream.parseAsContents(&rf);
680   - if (qpdf.numWarnings() > nwarnings) {
681   - QTC::TC("qpdf", "QPDFAcroFormDocumentHelper AP parse error");
682   - }
  681 + QTC::TC(
  682 + "qpdf",
  683 + "QPDFAcroFormDocumentHelper AP parse error",
  684 + qpdf.numWarnings() > nwarnings ? 0 : 1);
683 685 auto rr = new ResourceReplacer(dr_map, rf.getNamesByResourceType());
684 686 auto tf = std::shared_ptr<QPDFObjectHandle::TokenFilter>(rr);
685 687 stream.addTokenFilter(tf);
686 688 } catch (std::exception& e) {
687 689 // No way to reproduce in test suite right now since error conditions are converted to
688 690 // warnings.
689   - stream.warnIfPossible(std::string("Unable to parse appearance stream: ") + e.what());
  691 + stream.warn("Unable to parse appearance stream: "s + e.what());
690 692 }
691 693 }
692 694  
... ... @@ -814,7 +816,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
814 816 QPDFObjGen::set added_new_fields;
815 817 for (auto annot: old_annots.aitems()) {
816 818 if (annot.isStream()) {
817   - annot.warnIfPossible("ignoring annotation that's a stream");
  819 + annot.warn("ignoring annotation that's a stream");
818 820 continue;
819 821 }
820 822  
... ... @@ -847,10 +849,10 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
847 849 bool have_field = false;
848 850 bool have_parent = false;
849 851 if (ffield_oh.isStream()) {
850   - ffield_oh.warnIfPossible("ignoring form field that's a stream");
  852 + ffield.warn("ignoring form field that's a stream");
851 853 } else if ((!ffield_oh.isNull()) && (!ffield_oh.isIndirect())) {
852   - ffield_oh.warnIfPossible("ignoring form field not indirect");
853   - } else if (!ffield_oh.isNull()) {
  854 + ffield.warn("ignoring form field not indirect");
  855 + } else if (!ffield.null()) {
854 856 // A field and its associated annotation can be the same object. This matters because we
855 857 // don't want to clone the annotation and field separately in this case.
856 858 have_field = true;
... ... @@ -888,7 +890,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
888 890 if (orig_to_copy.contains(parent_og)) {
889 891 obj.replaceKey("/Parent", orig_to_copy[parent_og]);
890 892 } else {
891   - parent.warnIfPossible(
  893 + parent.warn(
892 894 "while traversing field " + obj.getObjGen().unparse(',') +
893 895 ", found parent (" + parent_og.unparse(',') +
894 896 ") that had not been seen, indicating likely invalid field "
... ... @@ -896,12 +898,13 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
896 898 }
897 899 }
898 900 auto kids = obj.getKey("/Kids");
899   - if (kids.isArray()) {
900   - for (int i = 0; i < kids.getArrayNItems(); ++i) {
  901 + int sz = static_cast<int>(kids.size());
  902 + if (sz != 1 || kids.isArray()) {
  903 + for (int i = 0; i < sz; ++i) {
901 904 auto kid = kids.getArrayItem(i);
902 905 if (maybe_copy_object(kid)) {
903 906 kids.setArrayItem(i, kid);
904   - queue.push_back(kid);
  907 + queue.emplace_back(kid);
905 908 }
906 909 }
907 910 }
... ... @@ -1013,7 +1016,7 @@ QPDFAcroFormDocumentHelper::fixCopiedAnnotations(
1013 1016 std::set<QPDFObjGen>* added_fields)
1014 1017 {
1015 1018 auto old_annots = from_page.getKey("/Annots");
1016   - if ((!old_annots.isArray()) || (old_annots.getArrayNItems() == 0)) {
  1019 + if (old_annots.empty() || !old_annots.isArray()) {
1017 1020 return;
1018 1021 }
1019 1022  
... ...
libqpdf/QPDFEFStreamObjectHelper.cc
... ... @@ -138,7 +138,7 @@ QPDFEFStreamObjectHelper::newFromStream(QPDFObjectHandle stream)
138 138 Pl_MD5 md5("EF md5", &discard);
139 139 Pl_Count count("EF size", &md5);
140 140 if (!stream.pipeStreamData(&count, nullptr, 0, qpdf_dl_all)) {
141   - stream.warnIfPossible("unable to get stream data for new embedded file stream");
  141 + stream.warn("unable to get stream data for new embedded file stream");
142 142 } else {
143 143 result.setParam("/Size", QPDFObjectHandle::newInteger(count.getCount()));
144 144 result.setParam(
... ...
libqpdf/QPDFFileSpecObjectHelper.cc
... ... @@ -4,32 +4,33 @@
4 4 #include <qpdf/QTC.hh>
5 5 #include <qpdf/QUtil.hh>
6 6  
  7 +#include <array>
7 8 #include <string>
8   -#include <vector>
  9 +
  10 +using namespace std::literals;
9 11  
10 12 QPDFFileSpecObjectHelper::QPDFFileSpecObjectHelper(QPDFObjectHandle oh) :
11 13 QPDFObjectHelper(oh)
12 14 {
13 15 if (!oh.isDictionary()) {
14   - oh.warnIfPossible("Embedded file object is not a dictionary");
  16 + warn("Embedded file object is not a dictionary");
15 17 return;
16 18 }
17 19 if (!oh.isDictionaryOfType("/Filespec")) {
18   - oh.warnIfPossible("Embedded file object's type is not /Filespec");
  20 + warn("Embedded file object's type is not /Filespec");
19 21 }
20 22 }
21 23  
22   -static std::vector<std::string> name_keys = {"/UF", "/F", "/Unix", "/DOS", "/Mac"};
  24 +static const std::array<std::string, 5> name_keys = {"/UF"s, "/F"s, "/Unix"s, "/DOS"s, "/Mac"s};
23 25  
24 26 std::string
25 27 QPDFFileSpecObjectHelper::getDescription()
26 28 {
27   - std::string result;
28 29 auto desc = oh().getKey("/Desc");
29 30 if (desc.isString()) {
30   - result = desc.getUTF8Value();
  31 + return desc.getUTF8Value();
31 32 }
32   - return result;
  33 + return {};
33 34 }
34 35  
35 36 std::string
... ...
libqpdf/QPDFFormFieldObjectHelper.cc
... ... @@ -281,7 +281,7 @@ QPDFFormFieldObjectHelper::getChoices()
281 281 for (auto const& item: getInheritableFieldValue("/Opt").as_array()) {
282 282 if (item.isString()) {
283 283 result.emplace_back(item.getUTF8Value());
284   - } else if (item.isArray() && item.getArrayNItems() == 2) {
  284 + } else if (item.size() == 2) {
285 285 auto display = item.getArrayItem(1);
286 286 if (display.isString()) {
287 287 result.emplace_back(display.getUTF8Value());
... ... @@ -317,18 +317,17 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances)
317 317 setCheckBoxValue((name != "/Off"));
318 318 }
319 319 if (!okay) {
320   - oh().warnIfPossible(
321   - "ignoring attempt to set a checkbox field to a value whose type is not name");
  320 + warn("ignoring attempt to set a checkbox field to a value whose type is not name");
322 321 }
323 322 } else if (isRadioButton()) {
324 323 if (value.isName()) {
325 324 setRadioButtonValue(value);
326 325 } else {
327   - oh().warnIfPossible(
  326 + warn(
328 327 "ignoring attempt to set a radio button field to an object that is not a name");
329 328 }
330 329 } else if (isPushbutton()) {
331   - oh().warnIfPossible("ignoring attempt set the value of a pushbutton field");
  330 + warn("ignoring attempt set the value of a pushbutton field");
332 331 }
333 332 return;
334 333 }
... ... @@ -375,7 +374,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
375 374  
376 375 QPDFObjectHandle kids = oh().getKey("/Kids");
377 376 if (!(isRadioButton() && parent.isNull() && kids.isArray())) {
378   - oh().warnIfPossible("don't know how to set the value of this field as a radio button");
  377 + warn("don't know how to set the value of this field as a radio button");
379 378 return;
380 379 }
381 380 setFieldAttribute("/V", name);
... ... @@ -397,7 +396,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
397 396 }
398 397 if (!annot) {
399 398 QTC::TC("qpdf", "QPDFObjectHandle broken radio button");
400   - oh().warnIfPossible("unable to set the value of this radio button");
  399 + warn("unable to set the value of this radio button");
401 400 continue;
402 401 }
403 402 if (AP.isDictionary() && AP.getKey("/N").isDictionary() &&
... ... @@ -453,7 +452,7 @@ QPDFFormFieldObjectHelper::setCheckBoxValue(bool value)
453 452 setFieldAttribute("/V", name);
454 453 if (!annot) {
455 454 QTC::TC("qpdf", "QPDFObjectHandle broken checkbox");
456   - oh().warnIfPossible("unable to set the value of this checkbox");
  455 + warn("unable to set the value of this checkbox");
457 456 return;
458 457 }
459 458 QTC::TC("qpdf", "QPDFFormFieldObjectHelper set checkbox AS");
... ... @@ -770,18 +769,17 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper&amp; ao
770 769 AP.replaceKey("/N", AS);
771 770 }
772 771 if (!AS.isStream()) {
773   - aoh.getObjectHandle().warnIfPossible("unable to get normal appearance stream for update");
  772 + aoh.warn("unable to get normal appearance stream for update");
774 773 return;
775 774 }
776 775  
777 776 if (AS.getObj().use_count() > 4) {
778   - aoh.getObjectHandle().warnIfPossible(
779   - "unable to generate text appearance from shared appearance stream for update");
  777 + aoh.warn("unable to generate text appearance from shared appearance stream for update");
780 778 return;
781 779 }
782 780 QPDFObjectHandle bbox_obj = AS.getDict().getKey("/BBox");
783 781 if (!bbox_obj.isRectangle()) {
784   - aoh.getObjectHandle().warnIfPossible("unable to get appearance stream bounding box");
  782 + aoh.warn("unable to get appearance stream bounding box");
785 783 return;
786 784 }
787 785 QPDFObjectHandle::Rectangle bbox = bbox_obj.getArrayAsRectangle();
... ...
libqpdf/QPDFJob.cc
... ... @@ -1082,7 +1082,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1082 1082 dp_array = decode_parms;
1083 1083 } else {
1084 1084 dp_array = QPDFObjectHandle::newArray();
1085   - for (int i = 0; i < filters.getArrayNItems(); ++i) {
  1085 + for (size_t i = 0; i < filters.size(); ++i) {
1086 1086 dp_array.appendItem(decode_parms);
1087 1087 }
1088 1088 }
... ... @@ -2322,7 +2322,7 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2322 2322 });
2323 2323  
2324 2324 std::list<QPDFObjectHandle> queue;
2325   - queue.push_back(pdf.getRoot().getKey("/Pages"));
  2325 + queue.emplace_back(pdf.getRoot().getKey("/Pages"));
2326 2326 while (!queue.empty()) {
2327 2327 QPDFObjectHandle node = *queue.begin();
2328 2328 queue.pop_front();
... ... @@ -2341,9 +2341,8 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2341 2341 });
2342 2342 return true;
2343 2343 }
2344   - int n = kids.getArrayNItems();
2345   - for (int i = 0; i < n; ++i) {
2346   - queue.push_back(kids.getArrayItem(i));
  2344 + for (auto const& kid: kids.as_array()) {
  2345 + queue.emplace_back(kid);
2347 2346 }
2348 2347 } else {
2349 2348 // This is a leaf node or a form XObject.
... ... @@ -2670,12 +2669,10 @@ QPDFJob::handlePageSpecs(QPDF&amp; pdf, std::vector&lt;std::unique_ptr&lt;QPDF&gt;&gt;&amp; page_hea
2670 2669 new_fields.appendItem(field);
2671 2670 }
2672 2671 }
2673   - if (new_fields.getArrayNItems() > 0) {
2674   - QTC::TC("qpdf", "QPDFJob keep some fields in pages");
2675   - acroform.replaceKey("/Fields", new_fields);
2676   - } else {
2677   - QTC::TC("qpdf", "QPDFJob no more fields in pages");
  2672 + if (new_fields.empty()) {
2678 2673 pdf.getRoot().removeKey("/AcroForm");
  2674 + } else {
  2675 + acroform.replaceKey("/Fields", new_fields);
2679 2676 }
2680 2677 }
2681 2678 }
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -352,16 +352,15 @@ BaseHandle::unparse() const
352 352 auto const& a = std::get<QPDF_Array>(obj->value);
353 353 std::string result = "[ ";
354 354 if (a.sp) {
355   - int next = 0;
356   - for (auto& item: a.sp->elements) {
357   - int key = item.first;
358   - for (int j = next; j < key; ++j) {
  355 + size_t next = 0;
  356 + for (auto& [key, value]: a.sp->elements) {
  357 + for (size_t j = next; j < key; ++j) {
359 358 result += "null ";
360 359 }
361   - result += item.second.unparse() + " ";
362   - next = ++key;
  360 + result += value.unparse() + " ";
  361 + next = key + 1;
363 362 }
364   - for (int j = next; j < a.sp->size; ++j) {
  363 + for (size_t j = next; j < a.sp->size; ++j) {
365 364 result += "null ";
366 365 }
367 366 } else {
... ... @@ -467,22 +466,21 @@ BaseHandle::write_json(int json_version, JSON::Writer&amp; p) const
467 466 auto const& a = std::get<QPDF_Array>(obj->value);
468 467 p.writeStart('[');
469 468 if (a.sp) {
470   - int next = 0;
471   - for (auto& item: a.sp->elements) {
472   - int key = item.first;
473   - for (int j = next; j < key; ++j) {
  469 + size_t next = 0;
  470 + for (auto& [key, value]: a.sp->elements) {
  471 + for (size_t j = next; j < key; ++j) {
474 472 p.writeNext() << "null";
475 473 }
476 474 p.writeNext();
477   - auto item_og = item.second.getObj()->getObjGen();
  475 + auto item_og = value.getObj()->getObjGen();
478 476 if (item_og.isIndirect()) {
479 477 p << "\"" << item_og.unparse(' ') << " R\"";
480 478 } else {
481   - item.second.write_json(json_version, p);
  479 + value.write_json(json_version, p);
482 480 }
483   - next = ++key;
  481 + next = key + 1;
484 482 }
485   - for (int j = next; j < a.sp->size; ++j) {
  483 + for (size_t j = next; j < a.sp->size; ++j) {
486 484 p.writeNext() << "null";
487 485 }
488 486 } else {
... ... @@ -611,11 +609,11 @@ QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const&amp; rhs) const
611 609 qpdf_object_type_e
612 610 QPDFObjectHandle::getTypeCode() const
613 611 {
614   - return obj ? obj->getResolvedTypeCode() : ::ot_uninitialized;
  612 + return type_code();
615 613 }
616 614  
617 615 char const*
618   -QPDFObjectHandle::getTypeName() const
  616 +BaseHandle::type_name() const
619 617 {
620 618 static constexpr std::array<char const*, 16> tn{
621 619 "uninitialized",
... ... @@ -634,7 +632,13 @@ QPDFObjectHandle::getTypeName() const
634 632 "unresolved",
635 633 "destroyed",
636 634 "reference"};
637   - return obj ? tn[getTypeCode()] : "uninitialized";
  635 + return tn[type_code()];
  636 +}
  637 +
  638 +char const*
  639 +QPDFObjectHandle::getTypeName() const
  640 +{
  641 + return type_name();
638 642 }
639 643  
640 644 bool
... ... @@ -829,20 +833,16 @@ QPDFObjectHandle::getValueAsInt(long long&amp; value) const
829 833 int
830 834 QPDFObjectHandle::getIntValueAsInt() const
831 835 {
832   - int result = 0;
833 836 long long v = getIntValue();
834 837 if (v < INT_MIN) {
835   - QTC::TC("qpdf", "QPDFObjectHandle int returning INT_MIN");
836   - warnIfPossible("requested value of integer is too small; returning INT_MIN");
837   - result = INT_MIN;
838   - } else if (v > INT_MAX) {
839   - QTC::TC("qpdf", "QPDFObjectHandle int returning INT_MAX");
840   - warnIfPossible("requested value of integer is too big; returning INT_MAX");
841   - result = INT_MAX;
842   - } else {
843   - result = static_cast<int>(v);
  838 + warn("requested value of integer is too small; returning INT_MIN");
  839 + return INT_MIN;
844 840 }
845   - return result;
  841 + if (v > INT_MAX) {
  842 + warn("requested value of integer is too big; returning INT_MAX");
  843 + return INT_MAX;
  844 + }
  845 + return static_cast<int>(v);
846 846 }
847 847  
848 848 bool
... ... @@ -860,8 +860,7 @@ QPDFObjectHandle::getUIntValue() const
860 860 {
861 861 long long v = getIntValue();
862 862 if (v < 0) {
863   - QTC::TC("qpdf", "QPDFObjectHandle uint returning 0");
864   - warnIfPossible("unsigned value request for negative number; returning 0");
  863 + warn("unsigned value request for negative number; returning 0");
865 864 return 0;
866 865 } else {
867 866 return static_cast<unsigned long long>(v);
... ... @@ -883,16 +882,14 @@ QPDFObjectHandle::getUIntValueAsUInt() const
883 882 {
884 883 long long v = getIntValue();
885 884 if (v < 0) {
886   - QTC::TC("qpdf", "QPDFObjectHandle uint uint returning 0");
887   - warnIfPossible("unsigned integer value request for negative number; returning 0");
  885 + warn("unsigned integer value request for negative number; returning 0");
888 886 return 0;
889   - } else if (v > UINT_MAX) {
890   - QTC::TC("qpdf", "QPDFObjectHandle uint returning UINT_MAX");
891   - warnIfPossible("requested value of unsigned integer is too big; returning UINT_MAX");
  887 + }
  888 + if (v > UINT_MAX) {
  889 + warn("requested value of unsigned integer is too big; returning UINT_MAX");
892 890 return UINT_MAX;
893   - } else {
894   - return static_cast<unsigned int>(v);
895 891 }
  892 + return static_cast<unsigned int>(v);
896 893 }
897 894  
898 895 bool
... ... @@ -1235,34 +1232,30 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
1235 1232 all_description = description;
1236 1233 std::vector<QPDFObjectHandle> result;
1237 1234 if (auto array = as_array(strict)) {
1238   - int n_items = array.size();
  1235 + int n_items = static_cast<int>(array.size());
1239 1236 for (int i = 0; i < n_items; ++i) {
1240 1237 QPDFObjectHandle item = array.at(i).second;
1241 1238 if (item.isStream()) {
1242 1239 result.emplace_back(item);
1243 1240 } else {
1244 1241 QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array");
1245   - warn(
1246   - item.getOwningQPDF(),
1247   - QPDFExc(
1248   - qpdf_e_damaged_pdf,
1249   - "",
1250   - description + ": item index " + std::to_string(i) + " (from 0)",
1251   - 0,
1252   - "ignoring non-stream in an array of streams"));
  1242 + item.warn(
  1243 + {qpdf_e_damaged_pdf,
  1244 + "",
  1245 + description + ": item index " + std::to_string(i) + " (from 0)",
  1246 + 0,
  1247 + "ignoring non-stream in an array of streams"});
1253 1248 }
1254 1249 }
1255 1250 } else if (isStream()) {
1256 1251 result.emplace_back(*this);
1257   - } else if (!isNull()) {
  1252 + } else if (!null()) {
1258 1253 warn(
1259   - getOwningQPDF(),
1260   - QPDFExc(
1261   - qpdf_e_damaged_pdf,
1262   - "",
1263   - description,
1264   - 0,
1265   - " object is supposed to be a stream or an array of streams but is neither"));
  1254 + {qpdf_e_damaged_pdf,
  1255 + "",
  1256 + description,
  1257 + 0,
  1258 + " object is supposed to be a stream or an array of streams but is neither"});
1266 1259 }
1267 1260  
1268 1261 bool first = true;
... ... @@ -1824,6 +1817,12 @@ QPDFObjectHandle::newReserved(QPDF* qpdf)
1824 1817 return qpdf->newReserved();
1825 1818 }
1826 1819  
  1820 +std::string
  1821 +BaseHandle::description() const
  1822 +{
  1823 + return obj ? obj->getDescription() : ""s;
  1824 +}
  1825 +
1827 1826 void
1828 1827 QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& object_description)
1829 1828 {
... ... @@ -1934,50 +1933,44 @@ QPDFObjectHandle::assertInitialized() const
1934 1933 }
1935 1934 }
1936 1935  
  1936 +std::runtime_error
  1937 +BaseHandle::type_error(char const* expected_type) const
  1938 +{
  1939 + return std::runtime_error(
  1940 + "operation for "s + expected_type + " attempted on object of type " + type_name());
  1941 +}
  1942 +
  1943 +QPDFExc
  1944 +BaseHandle::type_error(char const* expected_type, std::string const& message) const
  1945 +{
  1946 + return {
  1947 + qpdf_e_object,
  1948 + "",
  1949 + description(),
  1950 + 0,
  1951 + "operation for "s + expected_type + " attempted on object of type " + type_name() + ": " +
  1952 + message};
  1953 +}
  1954 +
1937 1955 void
1938   -QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& warning) const
  1956 +QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& message) const
1939 1957 {
1940   - QPDF* context = nullptr;
1941   - std::string description;
1942   - // Type checks above guarantee that the object has been dereferenced. Nevertheless, dereference
1943   - // throws exceptions in the test suite
1944 1958 if (!obj) {
1945 1959 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1946 1960 }
1947   - obj->getDescription(context, description);
1948   - // Null context handled by warn
1949   - warn(
1950   - context,
1951   - QPDFExc(
1952   - qpdf_e_object,
1953   - "",
1954   - description,
1955   - 0,
1956   - std::string("operation for ") + expected_type + " attempted on object of type " +
1957   - QPDFObjectHandle(*this).getTypeName() + ": " + warning));
  1961 + warn(type_error(expected_type, message));
1958 1962 }
1959 1963  
1960 1964 void
1961 1965 QPDFObjectHandle::warnIfPossible(std::string const& warning) const
1962 1966 {
1963   - QPDF* context = nullptr;
1964   - std::string description;
1965   - if (obj && obj->getDescription(context, description)) {
1966   - warn(context, QPDFExc(qpdf_e_damaged_pdf, "", description, 0, warning));
1967   - } else {
1968   - *QPDFLogger::defaultLogger()->getError() << warning << "\n";
1969   - }
  1967 + warn(warning);
1970 1968 }
1971 1969  
1972 1970 void
1973 1971 QPDFObjectHandle::objectWarning(std::string const& warning) const
1974 1972 {
1975   - QPDF* context = nullptr;
1976   - std::string description;
1977   - // Type checks above guarantee that the object is initialized.
1978   - obj->getDescription(context, description);
1979   - // Null context handled by warn
1980   - warn(context, QPDFExc(qpdf_e_object, "", description, 0, warning));
  1973 + warn({qpdf_e_object, "", description(), 0, warning});
1981 1974 }
1982 1975  
1983 1976 void
... ... @@ -2130,15 +2123,30 @@ QPDFObjectHandle::assertPageObject() const
2130 2123 }
2131 2124  
2132 2125 void
2133   -QPDFObjectHandle::warn(QPDF* qpdf, QPDFExc const& e)
  2126 +BaseHandle::warn(QPDF* qpdf, QPDFExc&& e)
2134 2127 {
2135   - // If parsing on behalf of a QPDF object and want to give a warning, we can warn through the
2136   - // object. If parsing for some other reason, such as an explicit creation of an object from a
2137   - // string, then just throw the exception.
2138   - if (qpdf) {
2139   - qpdf->warn(e);
  2128 + if (!qpdf) {
  2129 + throw std::move(e);
  2130 + }
  2131 + qpdf->warn(std::move(e));
  2132 +}
  2133 +
  2134 +void
  2135 +BaseHandle::warn(QPDFExc&& e) const
  2136 +{
  2137 + if (!qpdf()) {
  2138 + throw std::move(e);
  2139 + }
  2140 + qpdf()->warn(std::move(e));
  2141 +}
  2142 +
  2143 +void
  2144 +BaseHandle::warn(std::string const& warning) const
  2145 +{
  2146 + if (qpdf()) {
  2147 + warn({qpdf_e_damaged_pdf, "", description(), 0, warning});
2140 2148 } else {
2141   - throw e;
  2149 + *QPDFLogger::defaultLogger()->getError() << warning << "\n";
2142 2150 }
2143 2151 }
2144 2152  
... ...
libqpdf/QPDFOutlineObjectHelper.cc
... ... @@ -75,7 +75,7 @@ QPDFObjectHandle
75 75 QPDFOutlineObjectHelper::getDestPage()
76 76 {
77 77 QPDFObjectHandle dest = getDest();
78   - if ((dest.isArray()) && (dest.getArrayNItems() > 0)) {
  78 + if (!dest.empty() && dest.isArray()) {
79 79 return dest.getArrayItem(0);
80 80 }
81 81 return QPDFObjectHandle::newNull();
... ...
libqpdf/QPDFPageDocumentHelper.cc
... ... @@ -59,7 +59,7 @@ QPDFPageDocumentHelper::flattenAnnotations(int required_flags, int forbidden_fla
59 59 if (afdh.getNeedAppearances()) {
60 60 qpdf.getRoot()
61 61 .getKey("/AcroForm")
62   - .warnIfPossible(
  62 + .warn(
63 63 "document does not have updated appearance streams, so form fields "
64 64 "will not be flattened");
65 65 }
... ...
libqpdf/QPDFPageObjectHelper.cc
... ... @@ -12,6 +12,8 @@
12 12 #include <qpdf/QUtil.hh>
13 13 #include <qpdf/ResourceFinder.hh>
14 14  
  15 +using namespace std::literals;
  16 +
15 17 namespace
16 18 {
17 19 class ContentProvider: public QPDFObjectHandle::StreamDataProvider
... ... @@ -118,7 +120,7 @@ InlineImageTracker::convertIIDict(QPDFObjectHandle odict)
118 120 QTC::TC("qpdf", "QPDFPageObjectHelper colorspace lookup");
119 121 value = colorspace.getKey(name);
120 122 } else {
121   - resources.warnIfPossible("unable to resolve colorspace " + name);
  123 + resources.warn("unable to resolve colorspace " + name);
122 124 }
123 125 name.clear();
124 126 }
... ... @@ -414,8 +416,8 @@ QPDFPageObjectHelper::externalizeInlineImages(size_t min_size, bool shallow)
414 416 filterContents(&iit, &b);
415 417 filtered = true;
416 418 } catch (std::exception& e) {
417   - oh().warnIfPossible(
418   - std::string("Unable to filter content stream: ") + e.what() +
  419 + warn(
  420 + "Unable to filter content stream: "s + e.what() +
419 421 "; not attempting to externalize inline images from this stream");
420 422 }
421 423 if (filtered && iit.any_images) {
... ... @@ -443,14 +445,9 @@ std::vector&lt;QPDFAnnotationObjectHelper&gt;
443 445 QPDFPageObjectHelper::getAnnotations(std::string const& only_subtype)
444 446 {
445 447 std::vector<QPDFAnnotationObjectHelper> result;
446   - QPDFObjectHandle annots = oh().getKey("/Annots");
447   - if (annots.isArray()) {
448   - int nannots = annots.getArrayNItems();
449   - for (int i = 0; i < nannots; ++i) {
450   - QPDFObjectHandle annot = annots.getArrayItem(i);
451   - if (annot.isDictionaryOfType("", only_subtype)) {
452   - result.emplace_back(annot);
453   - }
  448 + for (auto const& annot: oh().getKey("/Annots").as_array()) {
  449 + if (annot.isDictionaryOfType("", only_subtype)) {
  450 + result.emplace_back(annot);
454 451 }
455 452 }
456 453 return result;
... ... @@ -555,15 +552,15 @@ QPDFPageObjectHelper::removeUnreferencedResourcesHelper(
555 552 ph.parseContents(&rf);
556 553 size_t after_nw = (q ? q->numWarnings() : 0);
557 554 if (after_nw > before_nw) {
558   - ph.oh().warnIfPossible(
  555 + ph.warn(
559 556 "Bad token found while scanning content stream; "
560 557 "not attempting to remove unreferenced objects from this object");
561 558 return false;
562 559 }
563 560 } catch (std::exception& e) {
564 561 QTC::TC("qpdf", "QPDFPageObjectHelper bad token finding names");
565   - ph.oh().warnIfPossible(
566   - std::string("Unable to parse content stream: ") + e.what() +
  562 + ph.warn(
  563 + "Unable to parse content stream: "s + e.what() +
567 564 "; not attempting to remove unreferenced objects from this object");
568 565 return false;
569 566 }
... ... @@ -719,8 +716,7 @@ QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations)
719 716 newdict.replaceKey("/Group", getAttribute("/Group", false).shallowCopy());
720 717 QPDFObjectHandle bbox = getTrimBox(false).shallowCopy();
721 718 if (!bbox.isRectangle()) {
722   - oh().warnIfPossible(
723   - "bounding box is invalid; form XObject created from page will not work");
  719 + warn("bounding box is invalid; form XObject created from page will not work");
724 720 }
725 721 newdict.replaceKey("/BBox", bbox);
726 722 auto provider =
... ...
libqpdf/QPDFWriter.cc
... ... @@ -1396,8 +1396,8 @@ QPDFWriter::willFilterStream(
1396 1396 }
1397 1397 } catch (std::runtime_error& e) {
1398 1398 if (filter && first_attempt) {
1399   - stream.warnIfPossible("error while getting stream data: "s + e.what());
1400   - stream.warnIfPossible("qpdf will attempt to write the damaged stream unchanged");
  1399 + stream.warn("error while getting stream data: "s + e.what());
  1400 + stream.warn("qpdf will attempt to write the damaged stream unchanged");
1401 1401 filter = false;
1402 1402 stream.setFilterOnWrite(false);
1403 1403 continue;
... ... @@ -1538,9 +1538,7 @@ QPDFWriter::unparseObject(
1538 1538 object.removeKey("/Length");
1539 1539  
1540 1540 // If /DecodeParms is an empty list, remove it.
1541   - if (object.getKey("/DecodeParms").isArray() &&
1542   - (0 == object.getKey("/DecodeParms").getArrayNItems())) {
1543   - QTC::TC("qpdf", "QPDFWriter remove empty DecodeParms");
  1541 + if (object.getKey("/DecodeParms").empty()) {
1544 1542 object.removeKey("/DecodeParms");
1545 1543 }
1546 1544  
... ... @@ -1558,22 +1556,19 @@ QPDFWriter::unparseObject(
1558 1556 object.removeKey("/Filter");
1559 1557 object.removeKey("/DecodeParms");
1560 1558 } else {
1561   - int idx = -1;
1562   - for (int i = 0; i < filter.getArrayNItems(); ++i) {
1563   - QPDFObjectHandle item = filter.getArrayItem(i);
  1559 + int idx = 0;
  1560 + for (auto const& item: filter.as_array()) {
1564 1561 if (item.isNameAndEquals("/Crypt")) {
1565   - idx = i;
  1562 + // If filter is an array, then the code in QPDF_Stream has already
  1563 + // verified that DecodeParms and Filters are arrays of the same
  1564 + // length, but if they weren't for some reason, eraseItem does type
  1565 + // and bounds checking. Fuzzing tells us that this can actually
  1566 + // happen.
  1567 + filter.eraseItem(idx);
  1568 + decode_parms.eraseItem(idx);
1566 1569 break;
1567 1570 }
1568   - }
1569   - if (idx >= 0) {
1570   - // If filter is an array, then the code in QPDF_Stream has already
1571   - // verified that DecodeParms and Filters are arrays of the same length,
1572   - // but if they weren't for some reason, eraseItem does type and bounds
1573   - // checking.
1574   - QTC::TC("qpdf", "QPDFWriter remove Crypt");
1575   - filter.eraseItem(idx);
1576   - decode_parms.eraseItem(idx);
  1571 + ++idx;
1577 1572 }
1578 1573 }
1579 1574 }
... ... @@ -1742,7 +1737,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1742 1737 if (obj_to_write.isStream()) {
1743 1738 // This condition occurred in a fuzz input. Ideally we should block it at parse
1744 1739 // time, but it's not clear to me how to construct a case for this.
1745   - obj_to_write.warnIfPossible("stream found inside object stream; treating as null");
  1740 + obj_to_write.warn("stream found inside object stream; treating as null");
1746 1741 obj_to_write = QPDFObjectHandle::newNull();
1747 1742 }
1748 1743 writeObject(obj_to_write, count);
... ... @@ -1965,7 +1960,7 @@ QPDFWriter::initializeSpecialStreams()
1965 1960 QPDFObjectHandle contents = page.getKey("/Contents");
1966 1961 std::vector<QPDFObjGen> contents_objects;
1967 1962 if (contents.isArray()) {
1968   - int n = contents.getArrayNItems();
  1963 + int n = static_cast<int>(contents.size());
1969 1964 for (int i = 0; i < n; ++i) {
1970 1965 contents_objects.push_back(contents.getArrayItem(i).getObjGen());
1971 1966 }
... ...
libqpdf/QPDF_Array.cc
... ... @@ -2,11 +2,25 @@
2 2  
3 3 #include <qpdf/QTC.hh>
4 4  
  5 +#include <utility>
  6 +
5 7 using namespace std::literals;
6 8 using namespace qpdf;
7 9  
8 10 static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull();
9 11  
  12 +static size_t
  13 +to_s(int n)
  14 +{
  15 + return static_cast<size_t>(n);
  16 +}
  17 +
  18 +static int
  19 +to_i(size_t n)
  20 +{
  21 + return static_cast<int>(n);
  22 +}
  23 +
10 24 inline void
11 25 Array::checkOwnership(QPDFObjectHandle const& item) const
12 26 {
... ... @@ -142,24 +156,24 @@ Array::null() const
142 156 return null_oh;
143 157 }
144 158  
145   -int
  159 +size_t
146 160 Array::size() const
147 161 {
148 162 auto a = array();
149   - return a->sp ? a->sp->size : int(a->elements.size());
  163 + return a->sp ? a->sp->size : a->elements.size();
150 164 }
151 165  
152 166 std::pair<bool, QPDFObjectHandle>
153 167 Array::at(int n) const
154 168 {
155 169 auto a = array();
156   - if (n < 0 || n >= size()) {
  170 + if (n < 0 || std::cmp_greater_equal(n, size())) {
157 171 return {false, {}};
158 172 }
159 173 if (!a->sp) {
160   - return {true, a->elements[size_t(n)]};
  174 + return {true, a->elements[to_s(n)]};
161 175 }
162   - auto const& iter = a->sp->elements.find(n);
  176 + auto const& iter = a->sp->elements.find(to_s(n));
163 177 return {true, iter == a->sp->elements.end() ? null() : iter->second};
164 178 }
165 179  
... ... @@ -169,12 +183,12 @@ Array::getAsVector() const
169 183 auto a = array();
170 184 if (a->sp) {
171 185 std::vector<QPDFObjectHandle> v;
172   - v.reserve(size_t(size()));
  186 + v.reserve(size());
173 187 for (auto const& item: a->sp->elements) {
174   - v.resize(size_t(item.first), null_oh);
  188 + v.resize(item.first, null_oh);
175 189 v.emplace_back(item.second);
176 190 }
177   - v.resize(size_t(size()), null_oh);
  191 + v.resize(size(), null_oh);
178 192 return v;
179 193 } else {
180 194 return a->elements;
... ... @@ -184,15 +198,15 @@ Array::getAsVector() const
184 198 bool
185 199 Array::setAt(int at, QPDFObjectHandle const& oh)
186 200 {
187   - if (at < 0 || at >= size()) {
  201 + if (at < 0 || std::cmp_greater_equal(at, size())) {
188 202 return false;
189 203 }
190 204 auto a = array();
191 205 checkOwnership(oh);
192 206 if (a->sp) {
193   - a->sp->elements[at] = oh;
  207 + a->sp->elements[to_s(at)] = oh;
194 208 } else {
195   - a->elements[size_t(at)] = oh;
  209 + a->elements[to_s(at)] = oh;
196 210 }
197 211 return true;
198 212 }
... ... @@ -210,34 +224,39 @@ Array::setFromVector(std::vector&lt;QPDFObjectHandle&gt; const&amp; v)
210 224 }
211 225  
212 226 bool
213   -Array::insert(int at, QPDFObjectHandle const& item)
  227 +Array::insert(int at_i, QPDFObjectHandle const& item)
214 228 {
215 229 auto a = array();
216   - int sz = size();
217   - if (at < 0 || at > sz) {
218   - // As special case, also allow insert beyond the end
  230 + size_t sz = size();
  231 + if (at_i < 0) {
  232 + return false;
  233 + }
  234 + size_t at = to_s(at_i);
  235 + if (at > sz) {
219 236 return false;
220   - } else if (at == sz) {
  237 + }
  238 + if (at == sz) {
  239 + // As special case, also allow insert beyond the end
221 240 push_back(item);
222   - } else {
223   - checkOwnership(item);
224   - if (a->sp) {
225   - auto iter = a->sp->elements.crbegin();
226   - while (iter != a->sp->elements.crend()) {
227   - auto key = (iter++)->first;
228   - if (key >= at) {
229   - auto nh = a->sp->elements.extract(key);
230   - ++nh.key();
231   - a->sp->elements.insert(std::move(nh));
232   - } else {
233   - break;
234   - }
  241 + return true;
  242 + }
  243 + checkOwnership(item);
  244 + if (a->sp) {
  245 + auto iter = a->sp->elements.crbegin();
  246 + while (iter != a->sp->elements.crend()) {
  247 + auto key = (iter++)->first;
  248 + if (key >= at) {
  249 + auto nh = a->sp->elements.extract(key);
  250 + ++nh.key();
  251 + a->sp->elements.insert(std::move(nh));
  252 + } else {
  253 + break;
235 254 }
236   - a->sp->elements[at] = item.getObj();
237   - ++a->sp->size;
238   - } else {
239   - a->elements.insert(a->elements.cbegin() + at, item.getObj());
240 255 }
  256 + a->sp->elements[at] = item.getObj();
  257 + ++a->sp->size;
  258 + } else {
  259 + a->elements.insert(a->elements.cbegin() + at_i, item.getObj());
241 260 }
242 261 return true;
243 262 }
... ... @@ -255,10 +274,14 @@ Array::push_back(QPDFObjectHandle const&amp; item)
255 274 }
256 275  
257 276 bool
258   -Array::erase(int at)
  277 +Array::erase(int at_i)
259 278 {
260 279 auto a = array();
261   - if (at < 0 || at >= size()) {
  280 + if (at_i < 0) {
  281 + return false;
  282 + }
  283 + size_t at = to_s(at_i);
  284 + if (at >= size()) {
262 285 return false;
263 286 }
264 287 if (a->sp) {
... ... @@ -277,7 +300,7 @@ Array::erase(int at)
277 300 }
278 301 --(a->sp->size);
279 302 } else {
280   - a->elements.erase(a->elements.cbegin() + at);
  303 + a->elements.erase(a->elements.cbegin() + at_i);
281 304 }
282 305 return true;
283 306 }
... ... @@ -286,7 +309,7 @@ int
286 309 QPDFObjectHandle::getArrayNItems() const
287 310 {
288 311 if (auto array = as_array(strict)) {
289   - return array.size();
  312 + return to_i(array.size());
290 313 }
291 314 typeWarning("array", "treating as empty");
292 315 QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
... ... @@ -471,7 +494,37 @@ QPDFObjectHandle
471 494 QPDFObjectHandle::eraseItemAndGetOld(int at)
472 495 {
473 496 auto array = as_array(strict);
474   - auto result = (array && at < array.size() && at >= 0) ? array.at(at).second : newNull();
  497 + auto result =
  498 + (array && std::cmp_less(at, array.size()) && at >= 0) ? array.at(at).second : newNull();
475 499 eraseItem(at);
476 500 return result;
477 501 }
  502 +
  503 +size_t
  504 +BaseHandle::size() const
  505 +{
  506 + switch (resolved_type_code()) {
  507 + case ::ot_array:
  508 + return as<QPDF_Array>()->size();
  509 + case ::ot_uninitialized:
  510 + case ::ot_reserved:
  511 + case ::ot_null:
  512 + case ::ot_destroyed:
  513 + case ::ot_unresolved:
  514 + case ::ot_reference:
  515 + return 0;
  516 + case ::ot_boolean:
  517 + case ::ot_integer:
  518 + case ::ot_real:
  519 + case ::ot_string:
  520 + case ::ot_name:
  521 + case ::ot_dictionary:
  522 + case ::ot_stream:
  523 + case ::ot_inlineimage:
  524 + case ::ot_operator:
  525 + return 1;
  526 + default:
  527 + throw std::logic_error("Unexpected type code in size"); // unreachable
  528 + return 0; // unreachable
  529 + }
  530 +}
... ...
libqpdf/QPDF_encryption.cc
... ... @@ -664,7 +664,7 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf)
664 664  
665 665 std::string id1;
666 666 auto id_obj = trailer.getKey("/ID");
667   - if (!id_obj.isArray() || id_obj.getArrayNItems() != 2 || !id_obj.getArrayItem(0).isString()) {
  667 + if (id_obj.size() != 2 || !id_obj.getArrayItem(0).isString()) {
668 668 // Treating a missing ID as the empty string enables qpdf to decrypt some invalid encrypted
669 669 // files with no /ID that poppler can read but Adobe Reader can't.
670 670 qpdf.warn(qpdf.damagedPDF("trailer", "invalid /ID in trailer dictionary"));
... ... @@ -961,19 +961,21 @@ QPDF::decryptStream(
961 961 } else if (
962 962 stream_dict.getKey("/DecodeParms").isArray() &&
963 963 stream_dict.getKey("/Filter").isArray()) {
964   - QPDFObjectHandle filter = stream_dict.getKey("/Filter");
965   - QPDFObjectHandle decode = stream_dict.getKey("/DecodeParms");
966   - if (filter.getArrayNItems() == decode.getArrayNItems()) {
967   - for (int i = 0; i < filter.getArrayNItems(); ++i) {
968   - if (filter.getArrayItem(i).isNameAndEquals("/Crypt")) {
969   - QPDFObjectHandle crypt_params = decode.getArrayItem(i);
  964 + auto filter = stream_dict.getKey("/Filter");
  965 + auto decode = stream_dict.getKey("/DecodeParms");
  966 + if (filter.size() == decode.size()) {
  967 + int i = 0;
  968 + for (auto const& item: filter.as_array()) {
  969 + if (item.isNameAndEquals("/Crypt")) {
  970 + auto crypt_params = decode.getArrayItem(i);
970 971 if (crypt_params.isDictionary() &&
971 972 crypt_params.getKey("/Name").isName()) {
972   - QTC::TC("qpdf", "QPDF_encrypt crypt array");
973 973 method = encp->interpretCF(crypt_params.getKey("/Name"));
974 974 method_source = "stream's Crypt decode parameters (array)";
975 975 }
  976 + break;
976 977 }
  978 + ++i;
977 979 }
978 980 }
979 981 }
... ...
libqpdf/QPDF_linearization.cc
... ... @@ -174,14 +174,13 @@ QPDF::readLinearizationData()
174 174 }
175 175  
176 176 // Hint table array: offset length [ offset length ]
177   - size_t n_H_items = toS(H.getArrayNItems());
178   - if (!((n_H_items == 2) || (n_H_items == 4))) {
  177 + size_t n_H_items = H.size();
  178 + if (!(n_H_items == 2 || n_H_items == 4)) {
179 179 throw damagedPDF("linearization dictionary", "H has the wrong number of items");
180 180 }
181 181  
182 182 std::vector<int> H_items;
183   - for (size_t i = 0; i < n_H_items; ++i) {
184   - QPDFObjectHandle oh(H.getArrayItem(toI(i)));
  183 + for (auto const& oh: H.as_array()) {
185 184 if (oh.isInteger()) {
186 185 H_items.push_back(oh.getIntValueAsInt());
187 186 } else {
... ...
libqpdf/QPDF_objects.cc
... ... @@ -782,7 +782,7 @@ std::pair&lt;int, std::array&lt;int, 3&gt;&gt;
782 782 QPDF::processXRefW(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged)
783 783 {
784 784 auto W_obj = dict.getKey("/W");
785   - if (!(W_obj.isArray() && (W_obj.getArrayNItems() >= 3) && W_obj.getArrayItem(0).isInteger() &&
  785 + if (!(W_obj.size() >= 3 && W_obj.getArrayItem(0).isInteger() &&
786 786 W_obj.getArrayItem(1).isInteger() && W_obj.getArrayItem(2).isInteger())) {
787 787 throw damaged("Cross-reference stream does not have a proper /W key");
788 788 }
... ...
libqpdf/QPDF_pages.cc
... ... @@ -57,7 +57,7 @@ QPDF::getAllPages()
57 57 // Files have been found in the wild where /Pages in the catalog points to the first
58 58 // page. Try to work around this and similar cases with this heuristic.
59 59 if (!warned) {
60   - root.warnIfPossible(
  60 + root.warn(
61 61 "document page tree root (root -> /Pages) doesn't point"
62 62 " to the root of the page tree; attempting to correct");
63 63 warned = true;
... ... @@ -109,7 +109,7 @@ QPDF::getAllPagesInternal(
109 109 // During fuzzing files were encountered where the root object appeared in the pages tree.
110 110 // Unconditionally setting the /Type to /Pages could cause problems, but trying to
111 111 // accommodate the possibility may be excessive.
112   - cur_node.warnIfPossible("/Type key should be /Pages but is not; overriding");
  112 + cur_node.warn("/Type key should be /Pages but is not; overriding");
113 113 cur_node.replaceKey("/Type", "/Pages"_qpdf);
114 114 }
115 115 if (!media_box) {
... ... @@ -134,13 +134,13 @@ QPDF::getAllPagesInternal(
134 134 int errors = 0;
135 135  
136 136 if (!kid.isDictionary()) {
137   - kid.warnIfPossible("Pages tree includes non-dictionary object; ignoring");
  137 + kid.warn("Pages tree includes non-dictionary object; ignoring");
138 138 m->invalid_page_found = true;
139 139 continue;
140 140 }
141 141 if (!kid.isIndirect()) {
142 142 QTC::TC("qpdf", "QPDF handle direct page object");
143   - cur_node.warnIfPossible(
  143 + cur_node.warn(
144 144 "kid " + std::to_string(i) + " (from 0) is direct; converting to indirect");
145 145 kid = makeIndirectObject(kid);
146 146 ++errors;
... ... @@ -150,7 +150,7 @@ QPDF::getAllPagesInternal(
150 150 } else {
151 151 if (!media_box && !kid.getKey("/MediaBox").isRectangle()) {
152 152 QTC::TC("qpdf", "QPDF missing mediabox");
153   - kid.warnIfPossible(
  153 + kid.warn(
154 154 "kid " + std::to_string(i) +
155 155 " (from 0) MediaBox is undefined; setting to letter / ANSI A");
156 156 kid.replaceKey(
... ... @@ -167,7 +167,7 @@ QPDF::getAllPagesInternal(
167 167 // QPDFPageObjectHelper.
168 168 QTC::TC("qpdf", "QPDF resolve duplicated page object");
169 169 if (!m->reconstructed_xref) {
170   - cur_node.warnIfPossible(
  170 + cur_node.warn(
171 171 "kid " + std::to_string(i) +
172 172 " (from 0) appears more than once in the pages tree;"
173 173 " creating a new page object as a copy");
... ... @@ -176,7 +176,7 @@ QPDF::getAllPagesInternal(
176 176 kid = makeIndirectObject(QPDFObjectHandle(kid).shallowCopy());
177 177 seen.add(kid);
178 178 } else {
179   - cur_node.warnIfPossible(
  179 + cur_node.warn(
180 180 "kid " + std::to_string(i) +
181 181 " (from 0) appears more than once in the pages tree; ignoring duplicate");
182 182 m->invalid_page_found = true;
... ... @@ -189,12 +189,12 @@ QPDF::getAllPagesInternal(
189 189 }
190 190 }
191 191 if (!kid.isDictionaryOfType("/Page")) {
192   - kid.warnIfPossible("/Type key should be /Page but is not; overriding");
  192 + kid.warn("/Type key should be /Page but is not; overriding");
193 193 kid.replaceKey("/Type", "/Page"_qpdf);
194 194 ++errors;
195 195 }
196 196 if (m->reconstructed_xref && errors > 2) {
197   - cur_node.warnIfPossible(
  197 + cur_node.warn(
198 198 "kid " + std::to_string(i) + " (from 0) has too many errors; ignoring page");
199 199 m->invalid_page_found = true;
200 200 kid = QPDFObjectHandle::newNull();
... ... @@ -313,7 +313,7 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos)
313 313  
314 314 newpage.replaceKey("/Parent", pages);
315 315 kids.insertItem(pos, newpage);
316   - int npages = kids.getArrayNItems();
  316 + int npages = static_cast<int>(kids.size());
317 317 pages.replaceKey("/Count", QPDFObjectHandle::newInteger(npages));
318 318 m->all_pages.insert(m->all_pages.begin() + pos, newpage);
319 319 for (int i = pos + 1; i < npages; ++i) {
... ... @@ -337,7 +337,7 @@ QPDF::removePage(QPDFObjectHandle page)
337 337 QPDFObjectHandle kids = pages.getKey("/Kids");
338 338  
339 339 kids.eraseItem(pos);
340   - int npages = kids.getArrayNItems();
  340 + int npages = static_cast<int>(kids.size());
341 341 pages.replaceKey("/Count", QPDFObjectHandle::newInteger(npages));
342 342 m->all_pages.erase(m->all_pages.begin() + pos);
343 343 m->pageobj_to_pages_pos.erase(page.getObjGen());
... ...
libqpdf/qpdf/QPDFObjectHandle_private.hh
... ... @@ -38,7 +38,7 @@ namespace qpdf
38 38  
39 39 const_reverse_iterator crend();
40 40  
41   - int size() const;
  41 + size_t size() const;
42 42 std::pair<bool, QPDFObjectHandle> at(int n) const;
43 43 bool setAt(int at, QPDFObjectHandle const& oh);
44 44 bool insert(int at, QPDFObjectHandle const& item);
... ...
libqpdf/qpdf/QPDFObject_private.hh
... ... @@ -35,8 +35,8 @@ class QPDF_Array final
35 35 private:
36 36 struct Sparse
37 37 {
38   - int size{0};
39   - std::map<int, QPDFObjectHandle> elements;
  38 + size_t size{0};
  39 + std::map<size_t, QPDFObjectHandle> elements;
40 40 };
41 41  
42 42 public:
... ... @@ -65,10 +65,10 @@ class QPDF_Array final
65 65 {
66 66 }
67 67  
68   - int
  68 + size_t
69 69 size() const
70 70 {
71   - return sp ? sp->size : int(elements.size());
  71 + return sp ? sp->size : elements.size();
72 72 }
73 73  
74 74 std::unique_ptr<Sparse> sp;
... ... @@ -434,13 +434,6 @@ class QPDFObject
434 434 parsed_offset = offset;
435 435 }
436 436 }
437   - bool
438   - getDescription(QPDF*& a_qpdf, std::string& description)
439   - {
440   - a_qpdf = qpdf;
441   - description = getDescription();
442   - return qpdf != nullptr;
443   - }
444 437 qpdf_offset_t
445 438 getParsedOffset()
446 439 {
... ...
libtests/sparse_array.cc
... ... @@ -7,6 +7,12 @@
7 7 #include <iostream>
8 8  
9 9 int
  10 +to_i(size_t n)
  11 +{
  12 + return static_cast<int>(n);
  13 +}
  14 +
  15 +int
10 16 main()
11 17 {
12 18 auto obj = QPDFObject::create<QPDF_Array>(std::vector<QPDFObjectHandle>(), true);
... ... @@ -65,20 +71,20 @@ main()
65 71 a.setAt(4, QPDFObjectHandle::newNull());
66 72 assert(a.at(4).second.isNull());
67 73  
68   - a.erase(a.size() - 1);
  74 + a.erase(to_i(a.size()) - 1);
69 75 assert(a.size() == 5);
70 76 assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
71 77 assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
72 78 assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third"));
73 79 assert(a.at(4).second.isNull());
74 80  
75   - a.erase(a.size() - 1);
  81 + a.erase(to_i(a.size()) - 1);
76 82 assert(a.size() == 4);
77 83 assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
78 84 assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
79 85 assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third"));
80 86  
81   - a.erase(a.size() - 1);
  87 + a.erase(to_i(a.size()) - 1);
82 88 assert(a.size() == 3);
83 89 assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
84 90 assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
... ...
qpdf/qpdf.testcov
... ... @@ -237,9 +237,7 @@ QPDFWriter remove ADBE 0
237 237 QPDFWriter remove existing Extensions 0
238 238 QPDFWriter preserve ADBE 0
239 239 QPDF_encryption skip 0x28 0
240   -QPDF_encrypt crypt array 0
241 240 QPDF_encryption CFM AESV3 0
242   -QPDFWriter remove Crypt 0
243 241 qpdf-c called qpdf_get_pdf_extension_level 0
244 242 qpdf-c called qpdf_set_r5_encryption_parameters 0
245 243 qpdf-c called qpdf_set_r6_encryption_parameters 0
... ... @@ -423,12 +421,6 @@ QPDFPageObjectHelper externalize inline image 0
423 421 QPDFPageObjectHelper keep inline image 0
424 422 QPDFJob image optimize colorspace 0
425 423 QPDFJob image optimize bits per component 0
426   -QPDFWriter remove empty DecodeParms 0
427   -QPDFObjectHandle uint returning 0 0
428   -QPDFObjectHandle int returning INT_MIN 0
429   -QPDFObjectHandle int returning INT_MAX 0
430   -QPDFObjectHandle uint returning UINT_MAX 0
431   -QPDFObjectHandle uint uint returning 0 0
432 424 QPDF xref skipped space 0
433 425 QPDF eof skipping spaces before xref 1
434 426 QPDF_encryption user matches owner V < 5 0
... ... @@ -580,9 +572,7 @@ QPDFAcroFormDocumentHelper copy annotation 3
580 572 QPDFAcroFormDocumentHelper field with parent 3
581 573 QPDFAcroFormDocumentHelper modify ap matrix 0
582 574 QPDFJob copy form fields in split_pages 0
583   -QPDFJob keep some fields in pages 0
584 575 QPDFJob pages keeping field from original 0
585   -QPDFJob no more fields in pages 0
586 576 QPDFObjectHandle merge reuse 0
587 577 QPDFObjectHandle merge generate 0
588 578 QPDFFormFieldObjectHelper get font from /DR 0
... ... @@ -592,7 +582,7 @@ QPDFAcroFormDocumentHelper replaced DA token 0
592 582 QPDFAcroFormDocumentHelper ap conflict 0
593 583 QPDFAcroFormDocumentHelper ap rename 0
594 584 QPDFAcroFormDocumentHelper /DA parse error 0
595   -QPDFAcroFormDocumentHelper AP parse error 0
  585 +QPDFAcroFormDocumentHelper AP parse error 1
596 586 QPDFJob copy fields not this file 0
597 587 QPDFJob copy fields non-first from orig 0
598 588 QPDF resolve duplicated page in insert 0
... ...