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