Commit cc9facdb645e163ed67d1311a670c68958e8ac4c

Authored by m-holger
1 parent 61879f9a

Refactor `QPDF`: move warnings and damaged PDF handling logic to `Common` class,…

… streamline error handling, and improve encapsulation.
include/qpdf/QPDF.hh
... ... @@ -747,20 +747,7 @@ class QPDF
747 747 class ResolveRecorder;
748 748 class JSONReactor;
749 749  
750   - void stopOnError(std::string const& message);
751   - inline void
752   - no_ci_stop_if(bool condition, std::string const& message, std::string const& context = {});
753 750 void removeObject(QPDFObjGen og);
754   - static QPDFExc damagedPDF(
755   - InputSource& input,
756   - std::string const& object,
757   - qpdf_offset_t offset,
758   - std::string const& message);
759   - QPDFExc damagedPDF(InputSource& input, qpdf_offset_t offset, std::string const& message);
760   - QPDFExc damagedPDF(std::string const& object, qpdf_offset_t offset, std::string const& message);
761   - QPDFExc damagedPDF(std::string const& object, std::string const& message);
762   - QPDFExc damagedPDF(qpdf_offset_t offset, std::string const& message);
763   - QPDFExc damagedPDF(std::string const& message);
764 751  
765 752 // Calls finish() on the pipeline when done but does not delete it
766 753 bool pipeStreamData(
... ...
libqpdf/QPDF.cc
... ... @@ -27,7 +27,9 @@
27 27 using namespace qpdf;
28 28 using namespace std::literals;
29 29  
30   -using Objects = QPDF::Doc::Objects;
  30 +using Doc = QPDF::Doc;
  31 +using Common = Doc::Common;
  32 +using Objects = Doc::Objects;
31 33 using Foreign = Objects::Foreign;
32 34 using Streams = Objects::Streams;
33 35  
... ... @@ -364,6 +366,12 @@ QPDF::findHeader()
364 366 void
365 367 QPDF::warn(QPDFExc const& e)
366 368 {
  369 + m->c.warn(e);
  370 +}
  371 +
  372 +void
  373 +Common::warn(QPDFExc const& e)
  374 +{
367 375 if (m->max_warnings > 0 && m->warnings.size() >= m->max_warnings) {
368 376 stopOnError("Too many warnings - file is too badly damaged");
369 377 }
... ... @@ -380,7 +388,17 @@ QPDF::warn(
380 388 qpdf_offset_t offset,
381 389 std::string const& message)
382 390 {
383   - warn(QPDFExc(error_code, getFilename(), object, offset, message));
  391 + m->c.warn(QPDFExc(error_code, getFilename(), object, offset, message));
  392 +}
  393 +
  394 +void
  395 +Common::warn(
  396 + qpdf_error_code_e error_code,
  397 + std::string const& object,
  398 + qpdf_offset_t offset,
  399 + std::string const& message)
  400 +{
  401 + warn(QPDFExc(error_code, qpdf.getFilename(), object, offset, message));
384 402 }
385 403  
386 404 QPDFObjectHandle
... ... @@ -515,7 +533,7 @@ Objects::Foreign::Copier::copied(QPDFObjectHandle const& foreign)
515 533  
516 534 auto og = foreign.getObjGen();
517 535 if (!object_map.contains(og)) {
518   - qpdf.warn(qpdf.damagedPDF(
  536 + warn(damagedPDF(
519 537 foreign.qpdf()->getFilename() + " object " + og.unparse(' '),
520 538 foreign.offset(),
521 539 "unexpected reference to /Pages object while copying foreign object; replacing with "
... ... @@ -693,12 +711,12 @@ QPDF::getRoot()
693 711 {
694 712 QPDFObjectHandle root = m->trailer.getKey("/Root");
695 713 if (!root.isDictionary()) {
696   - throw damagedPDF("", -1, "unable to find /Root dictionary");
  714 + throw m->c.damagedPDF("", -1, "unable to find /Root dictionary");
697 715 } else if (
698 716 // Check_mode is an interim solution to request #810 pending a more comprehensive review of
699 717 // the approach to more extensive checks and warning levels.
700 718 m->check_mode && !root.getKey("/Type").isNameAndEquals("/Catalog")) {
701   - warn(damagedPDF("", -1, "catalog /Type entry missing or invalid"));
  719 + warn(m->c.damagedPDF("", -1, "catalog /Type entry missing or invalid"));
702 720 root.replaceKey("/Type", "/Catalog"_qpdf);
703 721 }
704 722 return root;
... ... @@ -744,7 +762,7 @@ QPDF::pipeStreamData(
744 762 try {
745 763 auto buf = file->read(length, offset);
746 764 if (buf.size() != length) {
747   - throw damagedPDF(
  765 + throw qpdf_for_warning.m->c.damagedPDF(
748 766 *file, "", offset + toO(buf.size()), "unexpected EOF reading stream data");
749 767 }
750 768 pipeline->write(buf.data(), length);
... ... @@ -760,7 +778,7 @@ QPDF::pipeStreamData(
760 778 QTC::TC("qpdf", "QPDF decoding error warning");
761 779 qpdf_for_warning.warn(
762 780 // line-break
763   - damagedPDF(
  781 + qpdf_for_warning.m->c.damagedPDF(
764 782 *file,
765 783 "",
766 784 file->getLastOffset(),
... ... @@ -769,7 +787,7 @@ QPDF::pipeStreamData(
769 787 if (will_retry) {
770 788 qpdf_for_warning.warn(
771 789 // line-break
772   - damagedPDF(
  790 + qpdf_for_warning.m->c.damagedPDF(
773 791 *file,
774 792 "",
775 793 file->getLastOffset(),
... ... @@ -815,14 +833,14 @@ QPDF::pipeStreamData(
815 833 // Throw a generic exception when we lack context for something more specific. New code should not
816 834 // use this.
817 835 void
818   -QPDF::stopOnError(std::string const& message)
  836 +Common::stopOnError(std::string const& message)
819 837 {
820 838 throw damagedPDF("", message);
821 839 }
822 840  
823 841 // Return an exception of type qpdf_e_damaged_pdf.
824 842 QPDFExc
825   -QPDF::damagedPDF(
  843 +Common::damagedPDF(
826 844 InputSource& input, std::string const& object, qpdf_offset_t offset, std::string const& message)
827 845 {
828 846 return {qpdf_e_damaged_pdf, input.getName(), object, offset, message, true};
... ... @@ -831,14 +849,15 @@ QPDF::damagedPDF(
831 849 // Return an exception of type qpdf_e_damaged_pdf. The object is taken from
832 850 // m->last_object_description.
833 851 QPDFExc
834   -QPDF::damagedPDF(InputSource& input, qpdf_offset_t offset, std::string const& message)
  852 +Common::damagedPDF(InputSource& input, qpdf_offset_t offset, std::string const& message) const
835 853 {
836 854 return damagedPDF(input, m->last_object_description, offset, message);
837 855 }
838 856  
839 857 // Return an exception of type qpdf_e_damaged_pdf. The filename is taken from m->file.
840 858 QPDFExc
841   -QPDF::damagedPDF(std::string const& object, qpdf_offset_t offset, std::string const& message)
  859 +Common::damagedPDF(
  860 + std::string const& object, qpdf_offset_t offset, std::string const& message) const
842 861 {
843 862 return {qpdf_e_damaged_pdf, m->file->getName(), object, offset, message, true};
844 863 }
... ... @@ -846,7 +865,7 @@ QPDF::damagedPDF(std::string const& object, qpdf_offset_t offset, std::string co
846 865 // Return an exception of type qpdf_e_damaged_pdf. The filename is taken from m->file and the
847 866 // offset from .m->file->getLastOffset().
848 867 QPDFExc
849   -QPDF::damagedPDF(std::string const& object, std::string const& message)
  868 +Common::damagedPDF(std::string const& object, std::string const& message) const
850 869 {
851 870 return damagedPDF(object, m->file->getLastOffset(), message);
852 871 }
... ... @@ -854,7 +873,7 @@ QPDF::damagedPDF(std::string const& object, std::string const& message)
854 873 // Return an exception of type qpdf_e_damaged_pdf. The filename is taken from m->file and the object
855 874 // from .m->last_object_description.
856 875 QPDFExc
857   -QPDF::damagedPDF(qpdf_offset_t offset, std::string const& message)
  876 +Common::damagedPDF(qpdf_offset_t offset, std::string const& message) const
858 877 {
859 878 return damagedPDF(m->last_object_description, offset, message);
860 879 }
... ... @@ -862,7 +881,7 @@ QPDF::damagedPDF(qpdf_offset_t offset, std::string const& message)
862 881 // Return an exception of type qpdf_e_damaged_pdf. The filename is taken from m->file, the object
863 882 // from m->last_object_description and the offset from m->file->getLastOffset().
864 883 QPDFExc
865   -QPDF::damagedPDF(std::string const& message)
  884 +Common::damagedPDF(std::string const& message) const
866 885 {
867 886 return damagedPDF(m->last_object_description, m->file->getLastOffset(), message);
868 887 }
... ...
libqpdf/QPDF_encryption.cc
... ... @@ -734,15 +734,16 @@ QPDF::EncryptionParameters::initialize(QPDF& qpdf)
734 734 }
735 735 encryption_initialized = true;
736 736  
  737 + auto& c = qpdf.m->c;
737 738 auto& qm = *qpdf.m;
738 739 auto& trailer = qm.trailer;
739 740 auto& file = qm.file;
740 741  
741   - auto warn_damaged_pdf = [&qpdf](std::string const& msg) {
742   - qpdf.warn(qpdf.damagedPDF("encryption dictionary", msg));
  742 + auto warn_damaged_pdf = [&qpdf, c](std::string const& msg) {
  743 + qpdf.warn(c.damagedPDF("encryption dictionary", msg));
743 744 };
744 745 auto throw_damaged_pdf = [&qpdf](std::string const& msg) {
745   - throw qpdf.damagedPDF("encryption dictionary", msg);
  746 + throw qpdf.m->c.damagedPDF("encryption dictionary", msg);
746 747 };
747 748 auto unsupported = [&file](std::string const& msg) -> QPDFExc {
748 749 return {
... ... @@ -770,14 +771,14 @@ QPDF::EncryptionParameters::initialize(QPDF& qpdf)
770 771 if (id_obj.size() != 2 || !id_obj.getArrayItem(0).isString()) {
771 772 // Treating a missing ID as the empty string enables qpdf to decrypt some invalid encrypted
772 773 // files with no /ID that poppler can read but Adobe Reader can't.
773   - qpdf.warn(qpdf.damagedPDF("trailer", "invalid /ID in trailer dictionary"));
  774 + qpdf.warn(qpdf.m->c.damagedPDF("trailer", "invalid /ID in trailer dictionary"));
774 775 } else {
775 776 id1 = id_obj.getArrayItem(0).getStringValue();
776 777 }
777 778  
778 779 auto encryption_dict = trailer.getKey("/Encrypt");
779 780 if (!encryption_dict.isDictionary()) {
780   - throw qpdf.damagedPDF("/Encrypt in trailer dictionary is not a dictionary");
  781 + throw qpdf.m->c.damagedPDF("/Encrypt in trailer dictionary is not a dictionary");
781 782 }
782 783  
783 784 if (Name(encryption_dict["/Filter"]) != "/Standard") {
... ... @@ -984,7 +985,7 @@ QPDF::decryptString(std::string& str, QPDFObjGen og)
984 985 break;
985 986  
986 987 default:
987   - warn(damagedPDF(
  988 + warn(m->c.damagedPDF(
988 989 "unknown encryption filter for strings (check /StrF in "
989 990 "/Encrypt dictionary); strings may be decrypted improperly"));
990 991 // To avoid repeated warnings, reset cf_string. Assume we'd want to use AES if V == 4.
... ... @@ -1017,7 +1018,8 @@ QPDF::decryptString(std::string& str, QPDFObjGen og)
1017 1018 } catch (QPDFExc&) {
1018 1019 throw;
1019 1020 } catch (std::runtime_error& e) {
1020   - throw damagedPDF("error decrypting string for object " + og.unparse() + ": " + e.what());
  1021 + throw m->c.damagedPDF(
  1022 + "error decrypting string for object " + og.unparse() + ": " + e.what());
1021 1023 }
1022 1024 }
1023 1025  
... ...
libqpdf/QPDF_linearization.cc
... ... @@ -73,7 +73,7 @@ void
73 73 Lin::linearizationWarning(std::string_view msg)
74 74 {
75 75 m->linearization_warnings = true;
76   - qpdf.warn(qpdf_e_linearization, "", 0, std::string(msg));
  76 + warn(qpdf_e_linearization, "", 0, std::string(msg));
77 77 }
78 78  
79 79 bool
... ... @@ -166,19 +166,19 @@ Lin::readLinearizationData()
166 166 Integer P = P_oh; // first page number
167 167 QTC::TC("qpdf", "QPDF P absent in lindict", P ? 0 : 1);
168 168  
169   - qpdf.no_ci_stop_if(
  169 + no_ci_stop_if(
170 170 !(H && O && E && N && T && (P || P_oh.null())),
171 171 "some keys in linearization dictionary are of the wrong type",
172 172 "linearization dictionary" //
173 173 );
174 174  
175   - qpdf.no_ci_stop_if(
  175 + no_ci_stop_if(
176 176 !(H_size == 2 || H_size == 4),
177 177 "H has the wrong number of items",
178 178 "linearization dictionary" //
179 179 );
180 180  
181   - qpdf.no_ci_stop_if(
  181 + no_ci_stop_if(
182 182 !(H_0 && H_1 && (H_size == 2 || (H_2 && H_3))),
183 183 "some H items are of the wrong type",
184 184 "linearization dictionary" //
... ... @@ -188,7 +188,7 @@ Lin::readLinearizationData()
188 188  
189 189 // Various places in the code use linp.npages, which is initialized from N, to pre-allocate
190 190 // memory, so make sure it's accurate and bail right now if it's not.
191   - qpdf.no_ci_stop_if(
  191 + no_ci_stop_if(
192 192 N != qpdf.getAllPages().size(),
193 193 "/N does not match number of pages",
194 194 "linearization dictionary" //
... ... @@ -234,13 +234,12 @@ Lin::readLinearizationData()
234 234  
235 235 size_t HSi = HS;
236 236 if (HSi < 0 || HSi >= h_size) {
237   - throw qpdf.damagedPDF(
238   - "linearization hint table", "/S (shared object) offset is out of bounds");
  237 + throw damagedPDF("linearization hint table", "/S (shared object) offset is out of bounds");
239 238 }
240 239 readHSharedObject(BitStream(h_buf + HSi, h_size - HSi));
241 240  
242 241 if (HO) {
243   - qpdf.no_ci_stop_if(
  242 + no_ci_stop_if(
244 243 HO < 0 || HO >= h_size,
245 244 "/O (outline) offset is out of bounds",
246 245 "linearization dictionary" //
... ... @@ -257,7 +256,7 @@ Lin::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length)
257 256 ObjCache& oc = m->obj_cache[H];
258 257 qpdf_offset_t min_end_offset = oc.end_before_space;
259 258 qpdf_offset_t max_end_offset = oc.end_after_space;
260   - qpdf.no_ci_stop_if(
  259 + no_ci_stop_if(
261 260 !H.isStream(), "hint table is not a stream", "linearization dictionary" //
262 261 );
263 262  
... ... @@ -275,7 +274,7 @@ Lin::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length)
275 274 QTC::TC("qpdf", "QPDF hint table length direct");
276 275 }
277 276 qpdf_offset_t computed_end = offset + toO(length);
278   - qpdf.no_ci_stop_if(
  277 + no_ci_stop_if(
279 278 computed_end < min_end_offset || computed_end > max_end_offset,
280 279 "hint table length mismatch (expected = " + std::to_string(computed_end) + "; actual = " +
281 280 std::to_string(min_end_offset) + ".." + std::to_string(max_end_offset) + ")",
... ... @@ -464,7 +463,7 @@ Lin::checkLinearizationInternal()
464 463 // are present. In that case, it would probably agree with pdlin. As of this writing, the test
465 464 // suite doesn't contain any files with threads.
466 465  
467   - qpdf.no_ci_stop_if(
  466 + no_ci_stop_if(
468 467 m->part6.empty(), "linearization part 6 unexpectedly empty" //
469 468 );
470 469 qpdf_offset_t min_E = -1;
... ... @@ -494,14 +493,14 @@ Lin::checkLinearizationInternal()
494 493 qpdf_offset_t
495 494 Lin::maxEnd(ObjUser const& ou)
496 495 {
497   - qpdf.no_ci_stop_if(
  496 + no_ci_stop_if(
498 497 !m->obj_user_to_objects.contains(ou),
499 498 "no entry in object user table for requested object user" //
500 499 );
501 500  
502 501 qpdf_offset_t end = 0;
503 502 for (auto const& og: m->obj_user_to_objects[ou]) {
504   - qpdf.no_ci_stop_if(
  503 + no_ci_stop_if(
505 504 !m->obj_cache.contains(og), "unknown object referenced in object user table" //
506 505 );
507 506 end = std::max(end, m->obj_cache[og].end_after_space);
... ... @@ -517,7 +516,7 @@ Lin::getLinearizationOffset(QPDFObjGen og)
517 516 if (typ == 1) {
518 517 return entry.getOffset();
519 518 }
520   - qpdf.no_ci_stop_if(
  519 + no_ci_stop_if(
521 520 typ != 2, "getLinearizationOffset called for xref entry not of type 1 or 2" //
522 521 );
523 522 // For compressed objects, return the offset of the object stream that contains them.
... ... @@ -551,7 +550,7 @@ Lin::lengthNextN(int first_object, int n)
551 550 for (int i = 0; i < n; ++i) {
552 551 QPDFObjGen og(first_object + i, 0);
553 552 if (m->xref_table.contains(og)) {
554   - qpdf.no_ci_stop_if(
  553 + no_ci_stop_if(
555 554 !m->obj_cache.contains(og),
556 555 "found unknown object while calculating length for linearization data" //
557 556 );
... ... @@ -585,7 +584,7 @@ Lin::checkHPageOffset(
585 584 qpdf_offset_t table_offset = adjusted_offset(m->page_offset_hints.first_page_offset);
586 585 QPDFObjGen first_page_og(pages.at(0).getObjGen());
587 586 if (!m->xref_table.contains(first_page_og)) {
588   - qpdf.stopOnError("supposed first page object is not known");
  587 + stopOnError("supposed first page object is not known");
589 588 }
590 589 qpdf_offset_t offset = getLinearizationOffset(first_page_og);
591 590 if (table_offset != offset) {
... ... @@ -596,7 +595,7 @@ Lin::checkHPageOffset(
596 595 QPDFObjGen page_og(pages.at(pageno).getObjGen());
597 596 int first_object = page_og.getObj();
598 597 if (!m->xref_table.contains(page_og)) {
599   - qpdf.stopOnError("unknown object in page offset hint table");
  598 + stopOnError("unknown object in page offset hint table");
600 599 }
601 600 offset = getLinearizationOffset(page_og);
602 601  
... ... @@ -636,7 +635,7 @@ Lin::checkHPageOffset(
636 635  
637 636 for (size_t i = 0; i < toS(he.nshared_objects); ++i) {
638 637 int idx = he.shared_identifiers.at(i);
639   - qpdf.no_ci_stop_if(
  638 + no_ci_stop_if(
640 639 !shared_idx_to_obj.contains(idx),
641 640 "unable to get object for item in shared objects hint table");
642 641  
... ... @@ -645,7 +644,7 @@ Lin::checkHPageOffset(
645 644  
646 645 for (size_t i = 0; i < toS(ce.nshared_objects); ++i) {
647 646 int idx = ce.shared_identifiers.at(i);
648   - qpdf.no_ci_stop_if(
  647 + no_ci_stop_if(
649 648 idx >= m->c_shared_object_data.nshared_total,
650 649 "index out of bounds for shared object hint table" //
651 650 );
... ... @@ -718,7 +717,7 @@ Lin::checkHSharedObject(std::vector&lt;QPDFObjectHandle&gt; const&amp; pages, std::map&lt;int
718 717  
719 718 QPDFObjGen og(cur_object, 0);
720 719 if (!m->xref_table.contains(og)) {
721   - qpdf.stopOnError("unknown object in shared object hint table");
  720 + stopOnError("unknown object in shared object hint table");
722 721 }
723 722 qpdf_offset_t offset = getLinearizationOffset(og);
724 723 qpdf_offset_t h_offset = adjusted_offset(so.first_shared_offset);
... ... @@ -768,7 +767,7 @@ Lin::checkHOutlines()
768 767 return;
769 768 }
770 769 QPDFObjGen og(outlines.getObjGen());
771   - qpdf.no_ci_stop_if(
  770 + no_ci_stop_if(
772 771 !m->xref_table.contains(og), "unknown object in outlines hint table" //
773 772 );
774 773 qpdf_offset_t offset = getLinearizationOffset(og);
... ... @@ -1128,7 +1127,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1128 1127  
1129 1128 // Part 4: open document objects. We don't care about the order.
1130 1129  
1131   - qpdf.no_ci_stop_if(
  1130 + no_ci_stop_if(
1132 1131 lc_root.size() != 1, "found other than one root while calculating linearization data" //
1133 1132 );
1134 1133  
... ... @@ -1142,11 +1141,11 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1142 1141 // any option to set this and also disregards /OpenAction. We will do the same.
1143 1142  
1144 1143 // First, place the actual first page object itself.
1145   - qpdf.no_ci_stop_if(
  1144 + no_ci_stop_if(
1146 1145 pages.empty(), "no pages found while calculating linearization data" //
1147 1146 );
1148 1147 QPDFObjGen first_page_og(pages.at(0).getObjGen());
1149   - qpdf.no_ci_stop_if(
  1148 + no_ci_stop_if(
1150 1149 !lc_first_page_private.erase(first_page_og), "unable to linearize first page" //
1151 1150 );
1152 1151 m->c_linp.first_page_object = pages.at(0).getObjectID();
... ... @@ -1182,7 +1181,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1182 1181 // Place this page's page object
1183 1182  
1184 1183 QPDFObjGen page_og(pages.at(i).getObjGen());
1185   - qpdf.no_ci_stop_if(
  1184 + no_ci_stop_if(
1186 1185 !lc_other_page_private.erase(page_og),
1187 1186 "unable to linearize page " + std::to_string(i) //
1188 1187 );
... ... @@ -1195,7 +1194,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1195 1194 m->c_page_offset_data.entries.at(i).nobjects = 1;
1196 1195  
1197 1196 ObjUser ou(ObjUser::ou_page, i);
1198   - qpdf.no_ci_stop_if(
  1197 + no_ci_stop_if(
1199 1198 !m->obj_user_to_objects.contains(ou),
1200 1199 "found unreferenced page while calculating linearization data" //
1201 1200 );
... ... @@ -1231,7 +1230,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1231 1230 // Place the pages tree.
1232 1231 std::set<QPDFObjGen> pages_ogs =
1233 1232 m->obj_user_to_objects[ObjUser(ObjUser::ou_root_key, "/Pages")];
1234   - qpdf.no_ci_stop_if(
  1233 + no_ci_stop_if(
1235 1234 pages_ogs.empty(), "found empty pages tree while calculating linearization data" //
1236 1235 );
1237 1236 for (auto const& og: pages_ogs) {
... ... @@ -1288,7 +1287,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1288 1287 size_t num_placed =
1289 1288 m->part4.size() + m->part6.size() + m->part7.size() + m->part8.size() + m->part9.size();
1290 1289 size_t num_wanted = m->object_to_obj_users.size();
1291   - qpdf.no_ci_stop_if(
  1290 + no_ci_stop_if(
1292 1291 // This can happen with damaged files, e.g. if the root is part of the the pages tree.
1293 1292 num_placed != num_wanted,
1294 1293 "QPDF::calculateLinearizationData: wrong number of objects placed (num_placed = " +
... ... @@ -1326,7 +1325,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1326 1325 shared.emplace_back(obj);
1327 1326 }
1328 1327 }
1329   - qpdf.no_ci_stop_if(
  1328 + no_ci_stop_if(
1330 1329 std::cmp_not_equal(
1331 1330 m->c_shared_object_data.nshared_total, m->c_shared_object_data.entries.size()),
1332 1331 "shared object hint table has wrong number of entries" //
... ... @@ -1337,7 +1336,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1337 1336 for (size_t i = 1; i < npages; ++i) {
1338 1337 CHPageOffsetEntry& pe = m->c_page_offset_data.entries.at(i);
1339 1338 ObjUser ou(ObjUser::ou_page, i);
1340   - qpdf.no_ci_stop_if(
  1339 + no_ci_stop_if(
1341 1340 !m->obj_user_to_objects.contains(ou),
1342 1341 "found unreferenced page while calculating linearization data" //
1343 1342 );
... ... @@ -1420,12 +1419,12 @@ Lin::outputLengthNextN(
1420 1419  
1421 1420 int first = obj[in_object].renumber;
1422 1421 int last = first + n;
1423   - qpdf.no_ci_stop_if(
  1422 + no_ci_stop_if(
1424 1423 first <= 0, "found object that is not renumbered while writing linearization data");
1425 1424 qpdf_offset_t length = 0;
1426 1425 for (int i = first; i < last; ++i) {
1427 1426 auto l = new_obj[i].length;
1428   - qpdf.no_ci_stop_if(
  1427 + no_ci_stop_if(
1429 1428 l == 0, "found item with unknown length while writing linearization data" //
1430 1429 );
1431 1430 length += l;
... ... @@ -1502,7 +1501,7 @@ Lin::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::Ob
1502 1501 for (auto& phe_i: phe) {
1503 1502 // Adjust delta entries
1504 1503 if (phe_i.delta_nobjects < min_nobjects || phe_i.delta_page_length < min_length) {
1505   - qpdf.stopOnError(
  1504 + stopOnError(
1506 1505 "found too small delta nobjects or delta page length while writing "
1507 1506 "linearization data");
1508 1507 }
... ... @@ -1537,7 +1536,7 @@ Lin::calculateHSharedObject(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::
1537 1536 soe.emplace_back();
1538 1537 soe.at(i).delta_group_length = length;
1539 1538 }
1540   - qpdf.no_ci_stop_if(
  1539 + no_ci_stop_if(
1541 1540 soe.size() != toS(cso.nshared_total), "soe has wrong size after initialization" //
1542 1541 );
1543 1542  
... ... @@ -1553,7 +1552,7 @@ Lin::calculateHSharedObject(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::
1553 1552  
1554 1553 for (size_t i = 0; i < toS(cso.nshared_total); ++i) {
1555 1554 // Adjust deltas
1556   - qpdf.no_ci_stop_if(
  1555 + no_ci_stop_if(
1557 1556 soe.at(i).delta_group_length < min_length,
1558 1557 "found too small group length while writing linearization data" //
1559 1558 );
... ... @@ -1687,7 +1686,7 @@ Lin::writeHSharedObject(BitWriter&amp; w)
1687 1686 for (size_t i = 0; i < toS(nitems); ++i) {
1688 1687 // If signature were present, we'd have to write a 128-bit hash.
1689 1688 if (entries.at(i).signature_present != 0) {
1690   - qpdf.stopOnError("found unexpected signature present while writing linearization data");
  1689 + stopOnError("found unexpected signature present while writing linearization data");
1691 1690 }
1692 1691 }
1693 1692 write_vector_int(w, nitems, entries, t.nbits_nobjects, &HSharedObjectEntry::nobjects_minus_one);
... ...
libqpdf/QPDF_objects.cc
... ... @@ -123,7 +123,7 @@ Objects::parse(char const* password)
123 123 // Find the header anywhere in the first 1024 bytes of the file.
124 124 PatternFinder hf(qpdf, &QPDF::findHeader);
125 125 if (!m->file->findFirst("%PDF-", 0, 1024, hf)) {
126   - qpdf.warn(qpdf.damagedPDF("", -1, "can't find PDF header"));
  126 + warn(damagedPDF("", -1, "can't find PDF header"));
127 127 // QPDFWriter writes files that usually require at least version 1.2 for /FlateDecode
128 128 m->pdf_version = "1.2";
129 129 }
... ... @@ -147,14 +147,14 @@ Objects::parse(char const* password)
147 147  
148 148 try {
149 149 if (xref_offset == 0) {
150   - throw qpdf.damagedPDF("", -1, "can't find startxref");
  150 + throw damagedPDF("", -1, "can't find startxref");
151 151 }
152 152 try {
153 153 read_xref(xref_offset);
154 154 } catch (QPDFExc&) {
155 155 throw;
156 156 } catch (std::exception& e) {
157   - throw qpdf.damagedPDF("", -1, std::string("error reading xref: ") + e.what());
  157 + throw damagedPDF("", -1, std::string("error reading xref: ") + e.what());
158 158 }
159 159 } catch (QPDFExc& e) {
160 160 if (m->attempt_recovery) {
... ... @@ -168,7 +168,7 @@ Objects::parse(char const* password)
168 168 m->parsed = true;
169 169 if (!m->xref_table.empty() && !qpdf.getRoot().getKey("/Pages").isDictionary()) {
170 170 // QPDFs created from JSON have an empty xref table and no root object yet.
171   - throw qpdf.damagedPDF("", -1, "unable to find page tree");
  171 + throw damagedPDF("", -1, "unable to find page tree");
172 172 }
173 173 }
174 174  
... ... @@ -208,8 +208,7 @@ Objects::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
208 208 const auto max_warnings = m->warnings.size() + 1000U;
209 209 auto check_warnings = [this, max_warnings]() {
210 210 if (m->warnings.size() > max_warnings) {
211   - throw qpdf.damagedPDF(
212   - "", -1, "too many errors while reconstructing cross-reference table");
  211 + throw damagedPDF("", -1, "too many errors while reconstructing cross-reference table");
213 212 }
214 213 };
215 214  
... ... @@ -217,9 +216,9 @@ Objects::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
217 216 // We may find more objects, which may contain dangling references.
218 217 m->fixed_dangling_refs = false;
219 218  
220   - qpdf.warn(qpdf.damagedPDF("", -1, "file is damaged"));
221   - qpdf.warn(e);
222   - qpdf.warn(qpdf.damagedPDF("", -1, "Attempting to reconstruct cross-reference table"));
  219 + warn(damagedPDF("", -1, "file is damaged"));
  220 + warn(e);
  221 + warn(damagedPDF("", -1, "Attempting to reconstruct cross-reference table"));
223 222  
224 223 // Delete all references to type 1 (uncompressed) objects
225 224 std::vector<QPDFObjGen> to_delete;
... ... @@ -253,7 +252,7 @@ Objects::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
253 252 if (obj <= m->xref_table_max_id) {
254 253 found_objects.emplace_back(obj, gen, token_start);
255 254 } else {
256   - qpdf.warn(qpdf.damagedPDF(
  255 + warn(damagedPDF(
257 256 "", -1, "ignoring object with impossibly large id " + std::to_string(obj)));
258 257 }
259 258 }
... ... @@ -278,7 +277,7 @@ Objects::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
278 277  
279 278 if (qpdf.getRoot().getKey("/Pages").isDictionary()) {
280 279 QTC::TC("qpdf", "QPDF startxref more than 1024 before end");
281   - qpdf.warn(qpdf.damagedPDF(
  280 + warn(damagedPDF(
282 281 "", -1, "startxref was more than 1024 bytes before end of file"));
283 282 qpdf.initializeEncryption();
284 283 m->parsed = true;
... ... @@ -313,7 +312,7 @@ Objects::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
313 312 m->trailer = t;
314 313 break;
315 314 }
316   - qpdf.warn(qpdf.damagedPDF("trailer", *it, "recovered trailer has no /Root entry"));
  315 + warn(damagedPDF("trailer", *it, "recovered trailer has no /Root entry"));
317 316 }
318 317 check_warnings();
319 318 }
... ... @@ -347,7 +346,7 @@ Objects::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
347 346 try {
348 347 read_xref(max_offset, true);
349 348 } catch (std::exception&) {
350   - qpdf.warn(qpdf.damagedPDF(
  349 + warn(damagedPDF(
351 350 "", -1, "error decoding candidate xref stream while recovering damaged file"));
352 351 }
353 352 QTC::TC("qpdf", "QPDF recover xref stream");
... ... @@ -368,7 +367,7 @@ Objects::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
368 367 }
369 368 if (root) {
370 369 if (!m->trailer) {
371   - qpdf.warn(qpdf.damagedPDF(
  370 + warn(damagedPDF(
372 371 "", -1, "unable to find trailer dictionary while recovering damaged file"));
373 372 m->trailer = QPDFObjectHandle::newDictionary();
374 373 }
... ... @@ -381,13 +380,12 @@ Objects::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
381 380 // could try to get the trailer from there. This may make it possible to recover files with
382 381 // bad startxref pointers even when they have object streams.
383 382  
384   - throw qpdf.damagedPDF(
385   - "", -1, "unable to find trailer dictionary while recovering damaged file");
  383 + throw damagedPDF("", -1, "unable to find trailer dictionary while recovering damaged file");
386 384 }
387 385 if (m->xref_table.empty()) {
388 386 // We cannot check for an empty xref table in parse because empty tables are valid when
389 387 // creating QPDF objects from JSON.
390   - throw qpdf.damagedPDF("", -1, "unable to find objects while recovering damaged file");
  388 + throw damagedPDF("", -1, "unable to find objects while recovering damaged file");
391 389 }
392 390 check_warnings();
393 391 if (!m->parsed) {
... ... @@ -396,7 +394,7 @@ Objects::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
396 394 check_warnings();
397 395 if (m->all_pages.empty()) {
398 396 m->parsed = false;
399   - throw qpdf.damagedPDF("", -1, "unable to find any pages while recovering damaged file");
  397 + throw damagedPDF("", -1, "unable to find any pages while recovering damaged file");
400 398 }
401 399 }
402 400  
... ... @@ -443,7 +441,7 @@ Objects::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery)
443 441 // where it is terminated by arbitrary whitespace.
444 442 if ((strncmp(buf, "xref", 4) == 0) && util::is_space(buf[4])) {
445 443 if (skipped_space) {
446   - qpdf.warn(qpdf.damagedPDF("", -1, "extraneous whitespace seen before xref"));
  444 + warn(damagedPDF("", -1, "extraneous whitespace seen before xref"));
447 445 }
448 446 QTC::TC(
449 447 "qpdf",
... ... @@ -462,12 +460,12 @@ Objects::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery)
462 460 xref_offset = read_xrefStream(xref_offset, in_stream_recovery);
463 461 }
464 462 if (visited.contains(xref_offset)) {
465   - throw qpdf.damagedPDF("", -1, "loop detected following xref tables");
  463 + throw damagedPDF("", -1, "loop detected following xref tables");
466 464 }
467 465 }
468 466  
469 467 if (!m->trailer) {
470   - throw qpdf.damagedPDF("", -1, "unable to find trailer while reading xref");
  468 + throw damagedPDF("", -1, "unable to find trailer while reading xref");
471 469 }
472 470 int size = m->trailer.getKey("/Size").getIntValueAsInt();
473 471 int max_obj = 0;
... ... @@ -478,7 +476,7 @@ Objects::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery)
478 476 max_obj = std::max(max_obj, *(m->deleted_objects.rbegin()));
479 477 }
480 478 if ((size < 1) || (size - 1 != max_obj)) {
481   - qpdf.warn(qpdf.damagedPDF(
  479 + warn(damagedPDF(
482 480 "",
483 481 -1,
484 482 ("reported number of objects (" + std::to_string(size) +
... ... @@ -615,7 +613,7 @@ Objects::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
615 613 }
616 614  
617 615 if (invalid) {
618   - qpdf.warn(qpdf.damagedPDF("xref table", "accepting invalid xref table entry"));
  616 + warn(damagedPDF("xref table", "accepting invalid xref table entry"));
619 617 }
620 618  
621 619 f1 = QUtil::string_to_ll(f1_str.c_str());
... ... @@ -692,7 +690,7 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset)
692 690 int num = 0;
693 691 int bytes = 0;
694 692 if (!parse_xrefFirst(line, obj, num, bytes)) {
695   - throw qpdf.damagedPDF("xref table", "xref syntax invalid");
  693 + throw damagedPDF("xref table", "xref syntax invalid");
696 694 }
697 695 m->file->seek(m->file->getLastOffset() + bytes, SEEK_SET);
698 696 for (qpdf_offset_t i = obj; i - num < obj; ++i) {
... ... @@ -705,7 +703,7 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset)
705 703 int f2 = 0;
706 704 char type = '\0';
707 705 if (!read_xrefEntry(f1, f2, type)) {
708   - throw qpdf.damagedPDF(
  706 + throw damagedPDF(
709 707 "xref table", "invalid xref entry (obj=" + std::to_string(i) + ")");
710 708 }
711 709 if (type == 'f') {
... ... @@ -725,17 +723,17 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset)
725 723 // Set offset to previous xref table if any
726 724 QPDFObjectHandle cur_trailer = m->objects.readTrailer();
727 725 if (!cur_trailer.isDictionary()) {
728   - throw qpdf.damagedPDF("", "expected trailer dictionary");
  726 + throw damagedPDF("", "expected trailer dictionary");
729 727 }
730 728  
731 729 if (!m->trailer) {
732 730 setTrailer(cur_trailer);
733 731  
734 732 if (!m->trailer.hasKey("/Size")) {
735   - throw qpdf.damagedPDF("trailer", "trailer dictionary lacks /Size key");
  733 + throw damagedPDF("trailer", "trailer dictionary lacks /Size key");
736 734 }
737 735 if (!m->trailer.getKey("/Size").isInteger()) {
738   - throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is not an integer");
  736 + throw damagedPDF("trailer", "/Size key in trailer dictionary is not an integer");
739 737 }
740 738 }
741 739  
... ... @@ -748,14 +746,14 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset)
748 746 // /Prev key instead of the xref stream's.
749 747 (void)read_xrefStream(cur_trailer.getKey("/XRefStm").getIntValue());
750 748 } else {
751   - throw qpdf.damagedPDF("xref stream", xref_offset, "invalid /XRefStm");
  749 + throw damagedPDF("xref stream", xref_offset, "invalid /XRefStm");
752 750 }
753 751 }
754 752 }
755 753  
756 754 if (cur_trailer.hasKey("/Prev")) {
757 755 if (!cur_trailer.getKey("/Prev").isInteger()) {
758   - throw qpdf.damagedPDF("trailer", "/Prev key in trailer dictionary is not an integer");
  756 + throw damagedPDF("trailer", "/Prev key in trailer dictionary is not an integer");
759 757 }
760 758 return cur_trailer.getKey("/Prev").getIntValue();
761 759 }
... ... @@ -781,7 +779,7 @@ Objects::read_xrefStream(qpdf_offset_t xref_offset, bool in_stream_recovery)
781 779 }
782 780 }
783 781  
784   - throw qpdf.damagedPDF("", xref_offset, "xref not found");
  782 + throw damagedPDF("", xref_offset, "xref not found");
785 783 return 0; // unreachable
786 784 }
787 785  
... ... @@ -912,7 +910,7 @@ Objects::processXRefStream(
912 910 qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj, bool in_stream_recovery)
913 911 {
914 912 auto damaged = [this, xref_offset](std::string_view msg) -> QPDFExc {
915   - return qpdf.damagedPDF("xref stream", xref_offset, msg.data());
  913 + return damagedPDF("xref stream", xref_offset, msg.data());
916 914 };
917 915  
918 916 auto dict = xref_obj.getDict();
... ... @@ -932,7 +930,7 @@ Objects::processXRefStream(
932 930 if (expected_size > actual_size) {
933 931 throw x;
934 932 } else {
935   - qpdf.warn(x);
  933 + warn(x);
936 934 }
937 935 }
938 936  
... ... @@ -992,7 +990,7 @@ Objects::processXRefStream(
992 990  
993 991 if (dict.hasKey("/Prev")) {
994 992 if (!dict.getKey("/Prev").isInteger()) {
995   - throw qpdf.damagedPDF(
  993 + throw damagedPDF(
996 994 "xref stream", "/Prev key in xref stream dictionary is not an integer");
997 995 }
998 996 return dict.getKey("/Prev").getIntValue();
... ... @@ -1030,13 +1028,13 @@ Objects::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2)
1030 1028  
1031 1029 if (f0 == 2) {
1032 1030 if (f1 == obj) {
1033   - qpdf.warn(qpdf.damagedPDF(
1034   - "xref stream", "self-referential object stream " + std::to_string(obj)));
  1031 + warn(
  1032 + damagedPDF("xref stream", "self-referential object stream " + std::to_string(obj)));
1035 1033 return;
1036 1034 }
1037 1035 if (f1 > m->xref_table_max_id) {
1038 1036 // ignore impossibly large object stream ids
1039   - qpdf.warn(qpdf.damagedPDF(
  1037 + warn(damagedPDF(
1040 1038 "xref stream",
1041 1039 "object stream id " + std::to_string(f1) + " for object " + std::to_string(obj) +
1042 1040 " is impossibly large"));
... ... @@ -1061,8 +1059,7 @@ Objects::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2)
1061 1059 break;
1062 1060  
1063 1061 default:
1064   - throw qpdf.damagedPDF(
1065   - "xref stream", "unknown xref stream entry type " + std::to_string(f0));
  1062 + throw damagedPDF("xref stream", "unknown xref stream entry type " + std::to_string(f0));
1066 1063 break;
1067 1064 }
1068 1065 }
... ... @@ -1182,9 +1179,9 @@ Objects::readTrailer()
1182 1179 if (empty) {
1183 1180 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
1184 1181 // actual PDF files and Adobe Reader appears to ignore them.
1185   - qpdf.warn(qpdf.damagedPDF("trailer", "empty object treated as null"));
  1182 + warn(damagedPDF("trailer", "empty object treated as null"));
1186 1183 } else if (object.isDictionary() && m->objects.readToken(*m->file).isWord("stream")) {
1187   - qpdf.warn(qpdf.damagedPDF("trailer", m->file->tell(), "stream keyword found in trailer"));
  1184 + warn(damagedPDF("trailer", m->file->tell(), "stream keyword found in trailer"));
1188 1185 }
1189 1186 // Override last_offset so that it points to the beginning of the object we just read
1190 1187 m->file->setLastOffset(offset);
... ... @@ -1210,8 +1207,7 @@ Objects::readObject(std::string const&amp; description, QPDFObjGen og)
1210 1207 if (empty) {
1211 1208 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
1212 1209 // actual PDF files and Adobe Reader appears to ignore them.
1213   - qpdf.warn(
1214   - qpdf.damagedPDF(*m->file, m->file->getLastOffset(), "empty object treated as null"));
  1210 + warn(damagedPDF(*m->file, m->file->getLastOffset(), "empty object treated as null"));
1215 1211 return object;
1216 1212 }
1217 1213 auto token = readToken(*m->file);
... ... @@ -1220,7 +1216,7 @@ Objects::readObject(std::string const&amp; description, QPDFObjGen og)
1220 1216 token = readToken(*m->file);
1221 1217 }
1222 1218 if (!token.isWord("endobj")) {
1223   - qpdf.warn(qpdf.damagedPDF("expected endobj"));
  1219 + warn(damagedPDF("expected endobj"));
1224 1220 }
1225 1221 return object;
1226 1222 }
... ... @@ -1241,9 +1237,9 @@ Objects::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offse
1241 1237  
1242 1238 if (!length_obj.isInteger()) {
1243 1239 if (length_obj.null()) {
1244   - throw qpdf.damagedPDF(offset, "stream dictionary lacks /Length key");
  1240 + throw damagedPDF(offset, "stream dictionary lacks /Length key");
1245 1241 }
1246   - throw qpdf.damagedPDF(offset, "/Length key in stream dictionary is not an integer");
  1242 + throw damagedPDF(offset, "/Length key in stream dictionary is not an integer");
1247 1243 }
1248 1244  
1249 1245 length = toS(length_obj.getUIntValue());
... ... @@ -1251,11 +1247,11 @@ Objects::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offse
1251 1247 m->file->seek(stream_offset, SEEK_SET);
1252 1248 m->file->seek(toO(length), SEEK_CUR);
1253 1249 if (!readToken(*m->file).isWord("endstream")) {
1254   - throw qpdf.damagedPDF("expected endstream");
  1250 + throw damagedPDF("expected endstream");
1255 1251 }
1256 1252 } catch (QPDFExc& e) {
1257 1253 if (m->attempt_recovery) {
1258   - qpdf.warn(e);
  1254 + warn(e);
1259 1255 length = recoverStreamLength(m->file, og, stream_offset);
1260 1256 } else {
1261 1257 throw;
... ... @@ -1295,7 +1291,7 @@ Objects::validateStreamLineEnd(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_off
1295 1291 // Treat the \r by itself as the whitespace after endstream and start reading
1296 1292 // stream data in spite of not having seen a newline.
1297 1293 m->file->unreadCh(ch);
1298   - qpdf.warn(qpdf.damagedPDF(
  1294 + warn(damagedPDF(
1299 1295 m->file->tell(), "stream keyword followed by carriage return only"));
1300 1296 }
1301 1297 }
... ... @@ -1303,12 +1299,11 @@ Objects::validateStreamLineEnd(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_off
1303 1299 }
1304 1300 if (!util::is_space(ch)) {
1305 1301 m->file->unreadCh(ch);
1306   - qpdf.warn(qpdf.damagedPDF(
  1302 + warn(damagedPDF(
1307 1303 m->file->tell(), "stream keyword not followed by proper line terminator"));
1308 1304 return;
1309 1305 }
1310   - qpdf.warn(
1311   - qpdf.damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace"));
  1306 + warn(damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace"));
1312 1307 }
1313 1308 }
1314 1309  
... ... @@ -1319,7 +1314,7 @@ Objects::readObjectInStream(is::OffsetBuffer&amp; input, int stream_id, int obj_id)
1319 1314 if (empty) {
1320 1315 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
1321 1316 // actual PDF files and Adobe Reader appears to ignore them.
1322   - qpdf.warn(QPDFExc(
  1317 + warn(QPDFExc(
1323 1318 qpdf_e_damaged_pdf,
1324 1319 m->file->getName() + " object stream " + std::to_string(stream_id),
1325 1320 +"object " + std::to_string(obj_id) + " 0, offset " +
... ... @@ -1347,7 +1342,7 @@ Objects::recoverStreamLength(
1347 1342 std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset)
1348 1343 {
1349 1344 // Try to reconstruct stream length by looking for endstream or endobj
1350   - qpdf.warn(qpdf.damagedPDF(*input, stream_offset, "attempting to recover stream length"));
  1345 + warn(damagedPDF(*input, stream_offset, "attempting to recover stream length"));
1351 1346  
1352 1347 PatternFinder ef(qpdf, &QPDF::findEndstream);
1353 1348 size_t length = 0;
... ... @@ -1386,10 +1381,10 @@ Objects::recoverStreamLength(
1386 1381 }
1387 1382  
1388 1383 if (length == 0) {
1389   - qpdf.warn(qpdf.damagedPDF(
  1384 + warn(damagedPDF(
1390 1385 *input, stream_offset, "unable to recover stream data; treating stream as empty"));
1391 1386 } else {
1392   - qpdf.warn(qpdf.damagedPDF(
  1387 + warn(damagedPDF(
1393 1388 *input, stream_offset, "recovered stream length: " + std::to_string(length)));
1394 1389 }
1395 1390  
... ... @@ -1409,24 +1404,24 @@ Objects::read_object_start(qpdf_offset_t offset)
1409 1404 QPDFTokenizer::Token tobjid = readToken(*m->file);
1410 1405 bool objidok = tobjid.isInteger();
1411 1406 if (!objidok) {
1412   - throw qpdf.damagedPDF(offset, "expected n n obj");
  1407 + throw damagedPDF(offset, "expected n n obj");
1413 1408 }
1414 1409 QPDFTokenizer::Token tgen = readToken(*m->file);
1415 1410 bool genok = tgen.isInteger();
1416 1411 if (!genok) {
1417   - throw qpdf.damagedPDF(offset, "expected n n obj");
  1412 + throw damagedPDF(offset, "expected n n obj");
1418 1413 }
1419 1414 QPDFTokenizer::Token tobj = readToken(*m->file);
1420 1415  
1421 1416 bool objok = tobj.isWord("obj");
1422 1417  
1423 1418 if (!objok) {
1424   - throw qpdf.damagedPDF(offset, "expected n n obj");
  1419 + throw damagedPDF(offset, "expected n n obj");
1425 1420 }
1426 1421 int objid = QUtil::string_to_int(tobjid.getValue().c_str());
1427 1422 int generation = QUtil::string_to_int(tgen.getValue().c_str());
1428 1423 if (objid == 0) {
1429   - throw qpdf.damagedPDF(offset, "object with ID 0");
  1424 + throw damagedPDF(offset, "object with ID 0");
1430 1425 }
1431 1426 return {objid, generation};
1432 1427 }
... ... @@ -1447,20 +1442,20 @@ Objects::readObjectAtOffset(
1447 1442 // "0000000000 00000 n", which is not correct, but it won't hurt anything for us to ignore
1448 1443 // these.
1449 1444 if (offset == 0) {
1450   - qpdf.warn(qpdf.damagedPDF(-1, "object has offset 0"));
  1445 + warn(damagedPDF(-1, "object has offset 0"));
1451 1446 return;
1452 1447 }
1453 1448  
1454 1449 try {
1455 1450 og = read_object_start(offset);
1456 1451 if (exp_og != og) {
1457   - QPDFExc e = qpdf.damagedPDF(offset, "expected " + exp_og.unparse(' ') + " obj");
  1452 + QPDFExc e = damagedPDF(offset, "expected " + exp_og.unparse(' ') + " obj");
1458 1453 if (try_recovery) {
1459 1454 // Will be retried below
1460 1455 throw e;
1461 1456 } else {
1462 1457 // We can try reading the object anyway even if the ID doesn't match.
1463   - qpdf.warn(e);
  1458 + warn(e);
1464 1459 }
1465 1460 }
1466 1461 } catch (QPDFExc& e) {
... ... @@ -1474,7 +1469,7 @@ Objects::readObjectAtOffset(
1474 1469 readObjectAtOffset(false, new_offset, description, exp_og);
1475 1470 return;
1476 1471 }
1477   - qpdf.warn(qpdf.damagedPDF(
  1472 + warn(damagedPDF(
1478 1473 "",
1479 1474 -1,
1480 1475 ("object " + exp_og.unparse(' ') +
... ... @@ -1493,7 +1488,7 @@ Objects::readObjectAtOffset(
1493 1488 while (true) {
1494 1489 char ch;
1495 1490 if (!m->file->read(&ch, 1)) {
1496   - throw qpdf.damagedPDF(m->file->tell(), "EOF after endobj");
  1491 + throw damagedPDF(m->file->tell(), "EOF after endobj");
1497 1492 }
1498 1493 if (!isspace(static_cast<unsigned char>(ch))) {
1499 1494 m->file->seek(-1, SEEK_CUR);
... ... @@ -1552,7 +1547,7 @@ Objects::readObjectAtOffset(
1552 1547 while (true) {
1553 1548 char ch;
1554 1549 if (!m->file->read(&ch, 1)) {
1555   - throw qpdf.damagedPDF(m->file->tell(), "EOF after endobj");
  1550 + throw damagedPDF(m->file->tell(), "EOF after endobj");
1556 1551 }
1557 1552 if (!isspace(static_cast<unsigned char>(ch))) {
1558 1553 m->file->seek(-1, SEEK_CUR);
... ... @@ -1574,7 +1569,7 @@ Objects::resolve(QPDFObjGen og)
1574 1569 if (m->resolving.contains(og)) {
1575 1570 // This can happen if an object references itself directly or indirectly in some key that
1576 1571 // has to be resolved during object parsing, such as stream length.
1577   - qpdf.warn(qpdf.damagedPDF("", "loop detected resolving object " + og.unparse(' ')));
  1572 + warn(damagedPDF("", "loop detected resolving object " + og.unparse(' ')));
1578 1573 updateCache(og, QPDFObject::create<QPDF_Null>(), -1, -1);
1579 1574 return m->obj_cache[og].object;
1580 1575 }
... ... @@ -1594,13 +1589,13 @@ Objects::resolve(QPDFObjGen og)
1594 1589 break;
1595 1590  
1596 1591 default:
1597   - throw qpdf.damagedPDF(
  1592 + throw damagedPDF(
1598 1593 "", -1, ("object " + og.unparse('/') + " has unexpected xref entry type"));
1599 1594 }
1600 1595 } catch (QPDFExc& e) {
1601   - qpdf.warn(e);
  1596 + warn(e);
1602 1597 } catch (std::exception& e) {
1603   - qpdf.warn(qpdf.damagedPDF(
  1598 + warn(damagedPDF(
1604 1599 "", -1, ("object " + og.unparse('/') + ": error reading object: " + e.what())));
1605 1600 }
1606 1601 }
... ... @@ -1636,7 +1631,7 @@ Objects::resolveObjectsInStream(int obj_stream_number)
1636 1631 // Force resolution of object stream
1637 1632 Stream obj_stream = qpdf.getObject(obj_stream_number, 0);
1638 1633 if (!obj_stream) {
1639   - throw qpdf.damagedPDF(
  1634 + throw damagedPDF(
1640 1635 "object " + std::to_string(obj_stream_number) + " 0",
1641 1636 "supposed object stream " + std::to_string(obj_stream_number) + " is not a stream");
1642 1637 }
... ... @@ -1649,7 +1644,7 @@ Objects::resolveObjectsInStream(int obj_stream_number)
1649 1644  
1650 1645 QPDFObjectHandle dict = obj_stream.getDict();
1651 1646 if (!dict.isDictionaryOfType("/ObjStm")) {
1652   - qpdf.warn(qpdf.damagedPDF(
  1647 + warn(damagedPDF(
1653 1648 "object " + std::to_string(obj_stream_number) + " 0",
1654 1649 "supposed object stream " + std::to_string(obj_stream_number) + " has wrong type"));
1655 1650 }
... ... @@ -1657,7 +1652,7 @@ Objects::resolveObjectsInStream(int obj_stream_number)
1657 1652 unsigned int n{0};
1658 1653 int first{0};
1659 1654 if (!(dict.getKey("/N").getValueAsUInt(n) && dict.getKey("/First").getValueAsInt(first))) {
1660   - throw qpdf.damagedPDF(
  1655 + throw damagedPDF(
1661 1656 "object " + std::to_string(obj_stream_number) + " 0",
1662 1657 "object stream " + std::to_string(obj_stream_number) + " has incorrect keys");
1663 1658 }
... ... @@ -1674,7 +1669,7 @@ Objects::resolveObjectsInStream(int obj_stream_number)
1674 1669 auto b_start = stream_data.data();
1675 1670  
1676 1671 if (first >= end_offset) {
1677   - throw qpdf.damagedPDF(
  1672 + throw damagedPDF(
1678 1673 "object " + std::to_string(obj_stream_number) + " 0",
1679 1674 "object stream " + std::to_string(obj_stream_number) + " has invalid /First entry");
1680 1675 }
... ... @@ -1694,17 +1689,17 @@ Objects::resolveObjectsInStream(int obj_stream_number)
1694 1689 long long offset = QUtil::string_to_int(toffset.getValue().c_str());
1695 1690  
1696 1691 if (num == obj_stream_number) {
1697   - qpdf.warn(damaged(num, id_offset, "object stream claims to contain itself"));
  1692 + warn(damaged(num, id_offset, "object stream claims to contain itself"));
1698 1693 continue;
1699 1694 }
1700 1695  
1701 1696 if (num < 1) {
1702   - qpdf.warn(damaged(num, id_offset, "object id is invalid"s));
  1697 + warn(damaged(num, id_offset, "object id is invalid"s));
1703 1698 continue;
1704 1699 }
1705 1700  
1706 1701 if (offset <= last_offset) {
1707   - qpdf.warn(damaged(
  1702 + warn(damaged(
1708 1703 num,
1709 1704 input.getLastOffset(),
1710 1705 "offset " + std::to_string(offset) +
... ... @@ -1718,7 +1713,7 @@ Objects::resolveObjectsInStream(int obj_stream_number)
1718 1713 }
1719 1714  
1720 1715 if (first + offset >= end_offset) {
1721   - qpdf.warn(damaged(
  1716 + warn(damaged(
1722 1717 num, input.getLastOffset(), "offset " + std::to_string(offset) + " is too large"));
1723 1718 continue;
1724 1719 }
... ... @@ -1934,7 +1929,7 @@ Objects::tableSize()
1934 1929 // Temporary fix. Long-term solution is
1935 1930 // - QPDFObjGen to enforce objgens are valid and sensible
1936 1931 // - xref table and obj cache to protect against insertion of impossibly large obj ids
1937   - qpdf.stopOnError("Impossibly large object id encountered.");
  1932 + stopOnError("Impossibly large object id encountered.");
1938 1933 }
1939 1934 if (max_obj < 1.1 * std::max(toI(m->obj_cache.size()), max_xref)) {
1940 1935 return toS(++max_obj);
... ...
libqpdf/QPDF_optimization.cc
... ... @@ -218,7 +218,7 @@ Pages ::pushInheritedAttributesToPageInternal(
218 218 // Warn when flattening, but not if the key is at the top level (i.e. "/Parent" not
219 219 // set), as we don't change these; but flattening removes intermediate /Pages nodes.
220 220 if (warn_skipped_keys && cur_pages.hasKey("/Parent")) {
221   - qpdf.warn(
  221 + warn(
222 222 qpdf_e_pages,
223 223 "Pages object: object " + cur_pages.id_gen().unparse(' '),
224 224 0,
... ...
libqpdf/qpdf/QPDF_private.hh
... ... @@ -322,6 +322,36 @@ class QPDF::Doc
322 322 {
323 323 }
324 324  
  325 + void stopOnError(std::string const& message);
  326 + void warn(QPDFExc const& e);
  327 + void warn(
  328 + qpdf_error_code_e error_code,
  329 + std::string const& object,
  330 + qpdf_offset_t offset,
  331 + std::string const& message);
  332 +
  333 + static QPDFExc damagedPDF(
  334 + InputSource& input,
  335 + std::string const& object,
  336 + qpdf_offset_t offset,
  337 + std::string const& message);
  338 + QPDFExc
  339 + damagedPDF(InputSource& input, qpdf_offset_t offset, std::string const& message) const;
  340 + QPDFExc damagedPDF(
  341 + std::string const& object, qpdf_offset_t offset, std::string const& message) const;
  342 + QPDFExc damagedPDF(std::string const& object, std::string const& message) const;
  343 + QPDFExc damagedPDF(qpdf_offset_t offset, std::string const& message) const;
  344 + QPDFExc damagedPDF(std::string const& message) const;
  345 +
  346 + void
  347 + no_ci_stop_if(
  348 + bool condition, std::string const& message, std::string const& context = {}) const
  349 + {
  350 + if (condition) {
  351 + throw damagedPDF(context, message);
  352 + }
  353 + }
  354 +
325 355 protected:
326 356 QPDF& qpdf;
327 357 QPDF::Members* m;
... ... @@ -989,13 +1019,4 @@ inline QPDF::Doc::Objects::Foreign::Copier::Copier(QPDF&amp; qpdf) :
989 1019 {
990 1020 }
991 1021  
992   -// Throw a generic exception for unusual error conditions that do not be covered during CI testing.
993   -inline void
994   -QPDF::no_ci_stop_if(bool condition, std::string const& message, std::string const& context)
995   -{
996   - if (condition) {
997   - throw damagedPDF(context, message);
998   - }
999   -}
1000   -
1001 1022 #endif // QPDF_PRIVATE_HH
... ...