Commit 5ccab4be03701744f9795b37b4e5835b1a7b1c1d

Authored by m-holger
Committed by Jay Berkenbilt
1 parent b9483662

Add private methods QPDF::damagedPDF

include/qpdf/QPDF.hh
@@ -1194,6 +1194,22 @@ class QPDF @@ -1194,6 +1194,22 @@ class QPDF
1194 std::shared_ptr<QPDFObject> const& object, 1194 std::shared_ptr<QPDFObject> const& object,
1195 qpdf_offset_t end_before_space, 1195 qpdf_offset_t end_before_space,
1196 qpdf_offset_t end_after_space); 1196 qpdf_offset_t end_after_space);
  1197 + static QPDFExc damagedPDF(
  1198 + std::shared_ptr<InputSource> const& input,
  1199 + std::string const& object,
  1200 + qpdf_offset_t offset,
  1201 + std::string const& message);
  1202 + QPDFExc damagedPDF(
  1203 + std::shared_ptr<InputSource> const& input,
  1204 + qpdf_offset_t offset,
  1205 + std::string const& message);
  1206 + QPDFExc damagedPDF(
  1207 + std::string const& object,
  1208 + qpdf_offset_t offset,
  1209 + std::string const& message);
  1210 + QPDFExc damagedPDF(std::string const& object, std::string const& message);
  1211 + QPDFExc damagedPDF(qpdf_offset_t offset, std::string const& message);
  1212 + QPDFExc damagedPDF(std::string const& message);
1197 1213
1198 // Calls finish() on the pipeline when done but does not delete it 1214 // Calls finish() on the pipeline when done but does not delete it
1199 bool pipeStreamData( 1215 bool pipeStreamData(
libqpdf/QPDF.cc
@@ -481,7 +481,7 @@ QPDF::parse(char const* password) @@ -481,7 +481,7 @@ QPDF::parse(char const* password)
481 PatternFinder hf(*this, &QPDF::findHeader); 481 PatternFinder hf(*this, &QPDF::findHeader);
482 if (!this->m->file->findFirst("%PDF-", 0, 1024, hf)) { 482 if (!this->m->file->findFirst("%PDF-", 0, 1024, hf)) {
483 QTC::TC("qpdf", "QPDF not a pdf file"); 483 QTC::TC("qpdf", "QPDF not a pdf file");
484 - warn(qpdf_e_damaged_pdf, "", 0, "can't find PDF header"); 484 + warn(damagedPDF("", 0, "can't find PDF header"));
485 // QPDFWriter writes files that usually require at least 485 // QPDFWriter writes files that usually require at least
486 // version 1.2 for /FlateDecode 486 // version 1.2 for /FlateDecode
487 this->m->pdf_version = "1.2"; 487 this->m->pdf_version = "1.2";
@@ -503,24 +503,15 @@ QPDF::parse(char const* password) @@ -503,24 +503,15 @@ QPDF::parse(char const* password)
503 try { 503 try {
504 if (xref_offset == 0) { 504 if (xref_offset == 0) {
505 QTC::TC("qpdf", "QPDF can't find startxref"); 505 QTC::TC("qpdf", "QPDF can't find startxref");
506 - throw QPDFExc(  
507 - qpdf_e_damaged_pdf,  
508 - this->m->file->getName(),  
509 - "",  
510 - 0,  
511 - "can't find startxref"); 506 + throw damagedPDF("", 0, "can't find startxref");
512 } 507 }
513 try { 508 try {
514 read_xref(xref_offset); 509 read_xref(xref_offset);
515 } catch (QPDFExc&) { 510 } catch (QPDFExc&) {
516 throw; 511 throw;
517 } catch (std::exception& e) { 512 } catch (std::exception& e) {
518 - throw QPDFExc(  
519 - qpdf_e_damaged_pdf,  
520 - this->m->file->getName(),  
521 - "",  
522 - 0,  
523 - std::string("error reading xref: ") + e.what()); 513 + throw damagedPDF(
  514 + "", 0, std::string("error reading xref: ") + e.what());
524 } 515 }
525 } catch (QPDFExc& e) { 516 } catch (QPDFExc& e) {
526 if (this->m->attempt_recovery) { 517 if (this->m->attempt_recovery) {
@@ -589,13 +580,9 @@ QPDF::reconstruct_xref(QPDFExc&amp; e) @@ -589,13 +580,9 @@ QPDF::reconstruct_xref(QPDFExc&amp; e)
589 580
590 this->m->reconstructed_xref = true; 581 this->m->reconstructed_xref = true;
591 582
592 - warn(qpdf_e_damaged_pdf, "", 0, "file is damaged"); 583 + warn(damagedPDF("", 0, "file is damaged"));
593 warn(e); 584 warn(e);
594 - warn(  
595 - qpdf_e_damaged_pdf,  
596 - "",  
597 - 0,  
598 - "Attempting to reconstruct cross-reference table"); 585 + warn(damagedPDF("", 0, "Attempting to reconstruct cross-reference table"));
599 586
600 // Delete all references to type 1 (uncompressed) objects 587 // Delete all references to type 1 (uncompressed) objects
601 std::set<QPDFObjGen> to_delete; 588 std::set<QPDFObjGen> to_delete;
@@ -655,13 +642,10 @@ QPDF::reconstruct_xref(QPDFExc&amp; e) @@ -655,13 +642,10 @@ QPDF::reconstruct_xref(QPDFExc&amp; e)
655 // with bad startxref pointers even when they have object 642 // with bad startxref pointers even when they have object
656 // streams. 643 // streams.
657 644
658 - throw QPDFExc(  
659 - qpdf_e_damaged_pdf,  
660 - this->m->file->getName(), 645 + throw damagedPDF(
661 "", 646 "",
662 0, 647 0,
663 - "unable to find trailer "  
664 - "dictionary while recovering damaged file"); 648 + "unable to find trailer dictionary while recovering damaged file");
665 } 649 }
666 650
667 // We could iterate through the objects looking for streams and 651 // We could iterate through the objects looking for streams and
@@ -716,11 +700,8 @@ QPDF::read_xref(qpdf_offset_t xref_offset) @@ -716,11 +700,8 @@ QPDF::read_xref(qpdf_offset_t xref_offset)
716 if ((strncmp(buf, "xref", 4) == 0) && QUtil::is_space(buf[4])) { 700 if ((strncmp(buf, "xref", 4) == 0) && QUtil::is_space(buf[4])) {
717 if (skipped_space) { 701 if (skipped_space) {
718 QTC::TC("qpdf", "QPDF xref skipped space"); 702 QTC::TC("qpdf", "QPDF xref skipped space");
719 - warn(  
720 - qpdf_e_damaged_pdf,  
721 - "",  
722 - 0,  
723 - "extraneous whitespace seen before xref"); 703 + warn(damagedPDF(
  704 + "", 0, "extraneous whitespace seen before xref"));
724 } 705 }
725 QTC::TC( 706 QTC::TC(
726 "qpdf", 707 "qpdf",
@@ -741,22 +722,12 @@ QPDF::read_xref(qpdf_offset_t xref_offset) @@ -741,22 +722,12 @@ QPDF::read_xref(qpdf_offset_t xref_offset)
741 } 722 }
742 if (visited.count(xref_offset) != 0) { 723 if (visited.count(xref_offset) != 0) {
743 QTC::TC("qpdf", "QPDF xref loop"); 724 QTC::TC("qpdf", "QPDF xref loop");
744 - throw QPDFExc(  
745 - qpdf_e_damaged_pdf,  
746 - this->m->file->getName(),  
747 - "",  
748 - 0,  
749 - "loop detected following xref tables"); 725 + throw damagedPDF("", 0, "loop detected following xref tables");
750 } 726 }
751 } 727 }
752 728
753 if (!this->m->trailer.isInitialized()) { 729 if (!this->m->trailer.isInitialized()) {
754 - throw QPDFExc(  
755 - qpdf_e_damaged_pdf,  
756 - this->m->file->getName(),  
757 - "",  
758 - 0,  
759 - "unable to find trailer while reading xref"); 730 + throw damagedPDF("", 0, "unable to find trailer while reading xref");
760 } 731 }
761 int size = this->m->trailer.getKey("/Size").getIntValueAsInt(); 732 int size = this->m->trailer.getKey("/Size").getIntValueAsInt();
762 int max_obj = 0; 733 int max_obj = 0;
@@ -768,14 +739,12 @@ QPDF::read_xref(qpdf_offset_t xref_offset) @@ -768,14 +739,12 @@ QPDF::read_xref(qpdf_offset_t xref_offset)
768 } 739 }
769 if ((size < 1) || (size - 1 != max_obj)) { 740 if ((size < 1) || (size - 1 != max_obj)) {
770 QTC::TC("qpdf", "QPDF xref size mismatch"); 741 QTC::TC("qpdf", "QPDF xref size mismatch");
771 - warn(  
772 - qpdf_e_damaged_pdf, 742 + warn(damagedPDF(
773 "", 743 "",
774 0, 744 0,
775 - (std::string("reported number of objects (") +  
776 - std::to_string(size) + 745 + ("reported number of objects (" + std::to_string(size) +
777 ") is not one plus the highest object number (" + 746 ") is not one plus the highest object number (" +
778 - std::to_string(max_obj) + ")")); 747 + std::to_string(max_obj) + ")")));
779 } 748 }
780 749
781 // We no longer need the deleted_objects table, so go ahead and 750 // We no longer need the deleted_objects table, so go ahead and
@@ -899,11 +868,7 @@ QPDF::parse_xrefEntry( @@ -899,11 +868,7 @@ QPDF::parse_xrefEntry(
899 } 868 }
900 869
901 if (invalid) { 870 if (invalid) {
902 - warn(  
903 - qpdf_e_damaged_pdf,  
904 - "xref table",  
905 - this->m->file->getLastOffset(),  
906 - "accepting invalid xref table entry"); 871 + warn(damagedPDF("xref table", "accepting invalid xref table entry"));
907 } 872 }
908 873
909 f1 = QUtil::string_to_ll(f1_str.c_str()); 874 f1 = QUtil::string_to_ll(f1_str.c_str());
@@ -929,12 +894,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -929,12 +894,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
929 int bytes = 0; 894 int bytes = 0;
930 if (!parse_xrefFirst(line, obj, num, bytes)) { 895 if (!parse_xrefFirst(line, obj, num, bytes)) {
931 QTC::TC("qpdf", "QPDF invalid xref"); 896 QTC::TC("qpdf", "QPDF invalid xref");
932 - throw QPDFExc(  
933 - qpdf_e_damaged_pdf,  
934 - this->m->file->getName(),  
935 - "xref table",  
936 - this->m->file->getLastOffset(),  
937 - "xref syntax invalid"); 897 + throw damagedPDF("xref table", "xref syntax invalid");
938 } 898 }
939 this->m->file->seek(this->m->file->getLastOffset() + bytes, SEEK_SET); 899 this->m->file->seek(this->m->file->getLastOffset() + bytes, SEEK_SET);
940 for (qpdf_offset_t i = obj; i - num < obj; ++i) { 900 for (qpdf_offset_t i = obj; i - num < obj; ++i) {
@@ -949,11 +909,8 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -949,11 +909,8 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
949 char type = '\0'; 909 char type = '\0';
950 if (!parse_xrefEntry(xref_entry, f1, f2, type)) { 910 if (!parse_xrefEntry(xref_entry, f1, f2, type)) {
951 QTC::TC("qpdf", "QPDF invalid xref entry"); 911 QTC::TC("qpdf", "QPDF invalid xref entry");
952 - throw QPDFExc(  
953 - qpdf_e_damaged_pdf,  
954 - this->m->file->getName(), 912 + throw damagedPDF(
955 "xref table", 913 "xref table",
956 - this->m->file->getLastOffset(),  
957 "invalid xref entry (obj=" + std::to_string(i) + ")"); 914 "invalid xref entry (obj=" + std::to_string(i) + ")");
958 } 915 }
959 if (type == 'f') { 916 if (type == 'f') {
@@ -978,12 +935,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -978,12 +935,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
978 readObject(this->m->file, "trailer", QPDFObjGen(), false); 935 readObject(this->m->file, "trailer", QPDFObjGen(), false);
979 if (!cur_trailer.isDictionary()) { 936 if (!cur_trailer.isDictionary()) {
980 QTC::TC("qpdf", "QPDF missing trailer"); 937 QTC::TC("qpdf", "QPDF missing trailer");
981 - throw QPDFExc(  
982 - qpdf_e_damaged_pdf,  
983 - this->m->file->getName(),  
984 - "",  
985 - this->m->file->getLastOffset(),  
986 - "expected trailer dictionary"); 938 + throw damagedPDF("", "expected trailer dictionary");
987 } 939 }
988 940
989 if (!this->m->trailer.isInitialized()) { 941 if (!this->m->trailer.isInitialized()) {
@@ -991,22 +943,12 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -991,22 +943,12 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
991 943
992 if (!this->m->trailer.hasKey("/Size")) { 944 if (!this->m->trailer.hasKey("/Size")) {
993 QTC::TC("qpdf", "QPDF trailer lacks size"); 945 QTC::TC("qpdf", "QPDF trailer lacks size");
994 - throw QPDFExc(  
995 - qpdf_e_damaged_pdf,  
996 - this->m->file->getName(),  
997 - "trailer",  
998 - this->m->file->getLastOffset(),  
999 - "trailer dictionary lacks /Size key"); 946 + throw damagedPDF("trailer", "trailer dictionary lacks /Size key");
1000 } 947 }
1001 if (!this->m->trailer.getKey("/Size").isInteger()) { 948 if (!this->m->trailer.getKey("/Size").isInteger()) {
1002 QTC::TC("qpdf", "QPDF trailer size not integer"); 949 QTC::TC("qpdf", "QPDF trailer size not integer");
1003 - throw QPDFExc(  
1004 - qpdf_e_damaged_pdf,  
1005 - this->m->file->getName(),  
1006 - "trailer",  
1007 - this->m->file->getLastOffset(),  
1008 - "/Size key in trailer dictionary is not "  
1009 - "an integer"); 950 + throw damagedPDF(
  951 + "trailer", "/Size key in trailer dictionary is not an integer");
1010 } 952 }
1011 } 953 }
1012 954
@@ -1021,12 +963,8 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -1021,12 +963,8 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
1021 (void)read_xrefStream( 963 (void)read_xrefStream(
1022 cur_trailer.getKey("/XRefStm").getIntValue()); 964 cur_trailer.getKey("/XRefStm").getIntValue());
1023 } else { 965 } else {
1024 - throw QPDFExc(  
1025 - qpdf_e_damaged_pdf,  
1026 - this->m->file->getName(),  
1027 - "xref stream",  
1028 - xref_offset,  
1029 - "invalid /XRefStm"); 966 + throw damagedPDF(
  967 + "xref stream", xref_offset, "invalid /XRefStm");
1030 } 968 }
1031 } 969 }
1032 } 970 }
@@ -1039,13 +977,8 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -1039,13 +977,8 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
1039 if (cur_trailer.hasKey("/Prev")) { 977 if (cur_trailer.hasKey("/Prev")) {
1040 if (!cur_trailer.getKey("/Prev").isInteger()) { 978 if (!cur_trailer.getKey("/Prev").isInteger()) {
1041 QTC::TC("qpdf", "QPDF trailer prev not integer"); 979 QTC::TC("qpdf", "QPDF trailer prev not integer");
1042 - throw QPDFExc(  
1043 - qpdf_e_damaged_pdf,  
1044 - this->m->file->getName(),  
1045 - "trailer",  
1046 - this->m->file->getLastOffset(),  
1047 - "/Prev key in trailer dictionary is not "  
1048 - "an integer"); 980 + throw damagedPDF(
  981 + "trailer", "/Prev key in trailer dictionary is not an integer");
1049 } 982 }
1050 QTC::TC("qpdf", "QPDF prev key in trailer dictionary"); 983 QTC::TC("qpdf", "QPDF prev key in trailer dictionary");
1051 xref_offset = cur_trailer.getKey("/Prev").getIntValue(); 984 xref_offset = cur_trailer.getKey("/Prev").getIntValue();
@@ -1078,12 +1011,7 @@ QPDF::read_xrefStream(qpdf_offset_t xref_offset) @@ -1078,12 +1011,7 @@ QPDF::read_xrefStream(qpdf_offset_t xref_offset)
1078 1011
1079 if (!found) { 1012 if (!found) {
1080 QTC::TC("qpdf", "QPDF can't find xref"); 1013 QTC::TC("qpdf", "QPDF can't find xref");
1081 - throw QPDFExc(  
1082 - qpdf_e_damaged_pdf,  
1083 - this->m->file->getName(),  
1084 - "",  
1085 - xref_offset,  
1086 - "xref not found"); 1014 + throw damagedPDF("", xref_offset, "xref not found");
1087 } 1015 }
1088 1016
1089 return xref_offset; 1017 return xref_offset;
@@ -1101,13 +1029,10 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -1101,13 +1029,10 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1101 W_obj.getArrayItem(2).isInteger() && 1029 W_obj.getArrayItem(2).isInteger() &&
1102 dict.getKey("/Size").isInteger() && 1030 dict.getKey("/Size").isInteger() &&
1103 (Index_obj.isArray() || Index_obj.isNull()))) { 1031 (Index_obj.isArray() || Index_obj.isNull()))) {
1104 - throw QPDFExc(  
1105 - qpdf_e_damaged_pdf,  
1106 - this->m->file->getName(), 1032 + throw damagedPDF(
1107 "xref stream", 1033 "xref stream",
1108 xref_offset, 1034 xref_offset,
1109 - "Cross-reference stream does not have"  
1110 - " proper /W and /Index keys"); 1035 + "Cross-reference stream does not have proper /W and /Index keys");
1111 } 1036 }
1112 1037
1113 int W[3]; 1038 int W[3];
@@ -1116,24 +1041,18 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -1116,24 +1041,18 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1116 for (int i = 0; i < 3; ++i) { 1041 for (int i = 0; i < 3; ++i) {
1117 W[i] = W_obj.getArrayItem(i).getIntValueAsInt(); 1042 W[i] = W_obj.getArrayItem(i).getIntValueAsInt();
1118 if (W[i] > max_bytes) { 1043 if (W[i] > max_bytes) {
1119 - throw QPDFExc(  
1120 - qpdf_e_damaged_pdf,  
1121 - this->m->file->getName(), 1044 + throw damagedPDF(
1122 "xref stream", 1045 "xref stream",
1123 xref_offset, 1046 xref_offset,
1124 - "Cross-reference stream's /W contains"  
1125 - " impossibly large values"); 1047 + "Cross-reference stream's /W contains impossibly large values");
1126 } 1048 }
1127 entry_size += toS(W[i]); 1049 entry_size += toS(W[i]);
1128 } 1050 }
1129 if (entry_size == 0) { 1051 if (entry_size == 0) {
1130 - throw QPDFExc(  
1131 - qpdf_e_damaged_pdf,  
1132 - this->m->file->getName(), 1052 + throw damagedPDF(
1133 "xref stream", 1053 "xref stream",
1134 xref_offset, 1054 xref_offset,
1135 - "Cross-reference stream's /W indicates"  
1136 - " entry size of 0"); 1055 + "Cross-reference stream's /W indicates entry size of 0");
1137 } 1056 }
1138 unsigned long long max_num_entries = 1057 unsigned long long max_num_entries =
1139 static_cast<unsigned long long>(-1) / entry_size; 1058 static_cast<unsigned long long>(-1) / entry_size;
@@ -1142,21 +1061,17 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -1142,21 +1061,17 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1142 if (Index_obj.isArray()) { 1061 if (Index_obj.isArray()) {
1143 int n_index = Index_obj.getArrayNItems(); 1062 int n_index = Index_obj.getArrayNItems();
1144 if ((n_index % 2) || (n_index < 2)) { 1063 if ((n_index % 2) || (n_index < 2)) {
1145 - throw QPDFExc(  
1146 - qpdf_e_damaged_pdf,  
1147 - this->m->file->getName(), 1064 + throw damagedPDF(
1148 "xref stream", 1065 "xref stream",
1149 xref_offset, 1066 xref_offset,
1150 - "Cross-reference stream's /Index has an"  
1151 - " invalid number of values"); 1067 + "Cross-reference stream's /Index has an invalid number of "
  1068 + "values");
1152 } 1069 }
1153 for (int i = 0; i < n_index; ++i) { 1070 for (int i = 0; i < n_index; ++i) {
1154 if (Index_obj.getArrayItem(i).isInteger()) { 1071 if (Index_obj.getArrayItem(i).isInteger()) {
1155 indx.push_back(Index_obj.getArrayItem(i).getIntValue()); 1072 indx.push_back(Index_obj.getArrayItem(i).getIntValue());
1156 } else { 1073 } else {
1157 - throw QPDFExc(  
1158 - qpdf_e_damaged_pdf,  
1159 - this->m->file->getName(), 1074 + throw damagedPDF(
1160 "xref stream", 1075 "xref stream",
1161 xref_offset, 1076 xref_offset,
1162 ("Cross-reference stream's /Index's item " + 1077 ("Cross-reference stream's /Index's item " +
@@ -1174,13 +1089,10 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -1174,13 +1089,10 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1174 size_t num_entries = 0; 1089 size_t num_entries = 0;
1175 for (size_t i = 1; i < indx.size(); i += 2) { 1090 for (size_t i = 1; i < indx.size(); i += 2) {
1176 if (indx.at(i) > QIntC::to_longlong(max_num_entries - num_entries)) { 1091 if (indx.at(i) > QIntC::to_longlong(max_num_entries - num_entries)) {
1177 - throw QPDFExc(  
1178 - qpdf_e_damaged_pdf,  
1179 - this->m->file->getName(), 1092 + throw damagedPDF(
1180 "xref stream", 1093 "xref stream",
1181 xref_offset, 1094 xref_offset,
1182 - ("Cross-reference stream claims to contain"  
1183 - " too many entries: " + 1095 + ("Cross-reference stream claims to contain too many entries: " +
1184 std::to_string(indx.at(i)) + " " + 1096 std::to_string(indx.at(i)) + " " +
1185 std::to_string(max_num_entries) + " " + 1097 std::to_string(max_num_entries) + " " +
1186 std::to_string(num_entries))); 1098 std::to_string(num_entries)));
@@ -1196,13 +1108,10 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -1196,13 +1108,10 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1196 size_t actual_size = bp->getSize(); 1108 size_t actual_size = bp->getSize();
1197 1109
1198 if (expected_size != actual_size) { 1110 if (expected_size != actual_size) {
1199 - QPDFExc x(  
1200 - qpdf_e_damaged_pdf,  
1201 - this->m->file->getName(), 1111 + QPDFExc x = damagedPDF(
1202 "xref stream", 1112 "xref stream",
1203 xref_offset, 1113 xref_offset,
1204 - ("Cross-reference stream data has the wrong size;"  
1205 - " expected = " + 1114 + ("Cross-reference stream data has the wrong size; expected = " +
1206 std::to_string(expected_size) + 1115 std::to_string(expected_size) +
1207 "; actual = " + std::to_string(actual_size))); 1116 "; actual = " + std::to_string(actual_size)));
1208 if (expected_size > actual_size) { 1117 if (expected_size > actual_size) {
@@ -1286,13 +1195,9 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -1286,13 +1195,9 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1286 1195
1287 if (dict.hasKey("/Prev")) { 1196 if (dict.hasKey("/Prev")) {
1288 if (!dict.getKey("/Prev").isInteger()) { 1197 if (!dict.getKey("/Prev").isInteger()) {
1289 - throw QPDFExc(  
1290 - qpdf_e_damaged_pdf,  
1291 - this->m->file->getName(), 1198 + throw damagedPDF(
1292 "xref stream", 1199 "xref stream",
1293 - this->m->file->getLastOffset(),  
1294 - "/Prev key in xref stream dictionary is not "  
1295 - "an integer"); 1200 + "/Prev key in xref stream dictionary is not an integer");
1296 } 1201 }
1297 QTC::TC("qpdf", "QPDF prev key in xref stream dictionary"); 1202 QTC::TC("qpdf", "QPDF prev key in xref stream dictionary");
1298 xref_offset = dict.getKey("/Prev").getIntValue(); 1203 xref_offset = dict.getKey("/Prev").getIntValue();
@@ -1351,11 +1256,8 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2, bool overwrite) @@ -1351,11 +1256,8 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2, bool overwrite)
1351 break; 1256 break;
1352 1257
1353 default: 1258 default:
1354 - throw QPDFExc(  
1355 - qpdf_e_damaged_pdf,  
1356 - this->m->file->getName(), 1259 + throw damagedPDF(
1357 "xref stream", 1260 "xref stream",
1358 - this->m->file->getLastOffset(),  
1359 "unknown xref stream entry type " + std::to_string(f0)); 1261 "unknown xref stream entry type " + std::to_string(f0));
1360 break; 1262 break;
1361 } 1263 }
@@ -1522,12 +1424,8 @@ QPDF::readObject( @@ -1522,12 +1424,8 @@ QPDF::readObject(
1522 // Nothing in the PDF spec appears to allow empty objects, but 1424 // Nothing in the PDF spec appears to allow empty objects, but
1523 // they have been encountered in actual PDF files and Adobe 1425 // they have been encountered in actual PDF files and Adobe
1524 // Reader appears to ignore them. 1426 // Reader appears to ignore them.
1525 - warn(QPDFExc(  
1526 - qpdf_e_damaged_pdf,  
1527 - input->getName(),  
1528 - this->m->last_object_description,  
1529 - input->getLastOffset(),  
1530 - "empty object treated as null")); 1427 + warn(damagedPDF(
  1428 + input, input->getLastOffset(), "empty object treated as null"));
1531 } else if (object.isDictionary() && (!in_object_stream)) { 1429 } else if (object.isDictionary() && (!in_object_stream)) {
1532 // check for stream 1430 // check for stream
1533 qpdf_offset_t cur_offset = input->tell(); 1431 qpdf_offset_t cur_offset = input->tell();
@@ -1569,34 +1467,27 @@ QPDF::readObject( @@ -1569,34 +1467,27 @@ QPDF::readObject(
1569 // of not having seen a newline. 1467 // of not having seen a newline.
1570 QTC::TC("qpdf", "QPDF stream with CR only"); 1468 QTC::TC("qpdf", "QPDF stream with CR only");
1571 input->unreadCh(ch); 1469 input->unreadCh(ch);
1572 - warn(QPDFExc(  
1573 - qpdf_e_damaged_pdf,  
1574 - input->getName(),  
1575 - this->m->last_object_description, 1470 + warn(damagedPDF(
  1471 + input,
1576 input->tell(), 1472 input->tell(),
1577 - "stream keyword followed"  
1578 - " by carriage return only")); 1473 + "stream keyword followed by carriage return "
  1474 + "only"));
1579 } 1475 }
1580 } 1476 }
1581 } else if (QUtil::is_space(ch)) { 1477 } else if (QUtil::is_space(ch)) {
1582 - warn(QPDFExc(  
1583 - qpdf_e_damaged_pdf,  
1584 - input->getName(),  
1585 - this->m->last_object_description, 1478 + warn(damagedPDF(
  1479 + input,
1586 input->tell(), 1480 input->tell(),
1587 - "stream keyword followed by"  
1588 - " extraneous whitespace")); 1481 + "stream keyword followed by extraneous whitespace"));
1589 done = false; 1482 done = false;
1590 } else { 1483 } else {
1591 QTC::TC("qpdf", "QPDF stream without newline"); 1484 QTC::TC("qpdf", "QPDF stream without newline");
1592 input->unreadCh(ch); 1485 input->unreadCh(ch);
1593 - warn(QPDFExc(  
1594 - qpdf_e_damaged_pdf,  
1595 - input->getName(),  
1596 - this->m->last_object_description, 1486 + warn(damagedPDF(
  1487 + input,
1597 input->tell(), 1488 input->tell(),
1598 - "stream keyword not followed"  
1599 - " by proper line terminator")); 1489 + "stream keyword not followed by proper line "
  1490 + "terminator"));
1600 } 1491 }
1601 } 1492 }
1602 1493
@@ -1612,21 +1503,15 @@ QPDF::readObject( @@ -1612,21 +1503,15 @@ QPDF::readObject(
1612 1503
1613 if (dict.count("/Length") == 0) { 1504 if (dict.count("/Length") == 0) {
1614 QTC::TC("qpdf", "QPDF stream without length"); 1505 QTC::TC("qpdf", "QPDF stream without length");
1615 - throw QPDFExc(  
1616 - qpdf_e_damaged_pdf,  
1617 - input->getName(),  
1618 - this->m->last_object_description,  
1619 - offset,  
1620 - "stream dictionary lacks /Length key"); 1506 + throw damagedPDF(
  1507 + input, offset, "stream dictionary lacks /Length key");
1621 } 1508 }
1622 1509
1623 QPDFObjectHandle length_obj = dict["/Length"]; 1510 QPDFObjectHandle length_obj = dict["/Length"];
1624 if (!length_obj.isInteger()) { 1511 if (!length_obj.isInteger()) {
1625 QTC::TC("qpdf", "QPDF stream length not integer"); 1512 QTC::TC("qpdf", "QPDF stream length not integer");
1626 - throw QPDFExc(  
1627 - qpdf_e_damaged_pdf,  
1628 - input->getName(),  
1629 - this->m->last_object_description, 1513 + throw damagedPDF(
  1514 + input,
1630 offset, 1515 offset,
1631 "/Length key in stream dictionary is not " 1516 "/Length key in stream dictionary is not "
1632 "an integer"); 1517 "an integer");
@@ -1640,12 +1525,8 @@ QPDF::readObject( @@ -1640,12 +1525,8 @@ QPDF::readObject(
1640 QPDFTokenizer::Token( 1525 QPDFTokenizer::Token(
1641 QPDFTokenizer::tt_word, "endstream"))) { 1526 QPDFTokenizer::tt_word, "endstream"))) {
1642 QTC::TC("qpdf", "QPDF missing endstream"); 1527 QTC::TC("qpdf", "QPDF missing endstream");
1643 - throw QPDFExc(  
1644 - qpdf_e_damaged_pdf,  
1645 - input->getName(),  
1646 - this->m->last_object_description,  
1647 - input->getLastOffset(),  
1648 - "expected endstream"); 1528 + throw damagedPDF(
  1529 + input, input->getLastOffset(), "expected endstream");
1649 } 1530 }
1650 } catch (QPDFExc& e) { 1531 } catch (QPDFExc& e) {
1651 if (this->m->attempt_recovery) { 1532 if (this->m->attempt_recovery) {
@@ -1689,12 +1570,8 @@ QPDF::recoverStreamLength( @@ -1689,12 +1570,8 @@ QPDF::recoverStreamLength(
1689 { 1570 {
1690 // Try to reconstruct stream length by looking for 1571 // Try to reconstruct stream length by looking for
1691 // endstream or endobj 1572 // endstream or endobj
1692 - warn(QPDFExc(  
1693 - qpdf_e_damaged_pdf,  
1694 - input->getName(),  
1695 - this->m->last_object_description,  
1696 - stream_offset,  
1697 - "attempting to recover stream length")); 1573 + warn(damagedPDF(
  1574 + input, stream_offset, "attempting to recover stream length"));
1698 1575
1699 PatternFinder ef(*this, &QPDF::findEndstream); 1576 PatternFinder ef(*this, &QPDF::findEndstream);
1700 size_t length = 0; 1577 size_t length = 0;
@@ -1734,18 +1611,13 @@ QPDF::recoverStreamLength( @@ -1734,18 +1611,13 @@ QPDF::recoverStreamLength(
1734 } 1611 }
1735 1612
1736 if (length == 0) { 1613 if (length == 0) {
1737 - warn(QPDFExc(  
1738 - qpdf_e_damaged_pdf,  
1739 - input->getName(),  
1740 - this->m->last_object_description, 1614 + warn(damagedPDF(
  1615 + input,
1741 stream_offset, 1616 stream_offset,
1742 - "unable to recover stream data;"  
1743 - " treating stream as empty")); 1617 + "unable to recover stream data; treating stream as empty"));
1744 } else { 1618 } else {
1745 - warn(QPDFExc(  
1746 - qpdf_e_damaged_pdf,  
1747 - input->getName(),  
1748 - this->m->last_object_description, 1619 + warn(damagedPDF(
  1620 + input,
1749 stream_offset, 1621 stream_offset,
1750 "recovered stream length: " + std::to_string(length))); 1622 "recovered stream length: " + std::to_string(length)));
1751 } 1623 }
@@ -1795,11 +1667,7 @@ QPDF::readObjectAtOffset( @@ -1795,11 +1667,7 @@ QPDF::readObjectAtOffset(
1795 // ignore these. 1667 // ignore these.
1796 if (offset == 0) { 1668 if (offset == 0) {
1797 QTC::TC("qpdf", "QPDF bogus 0 offset", 0); 1669 QTC::TC("qpdf", "QPDF bogus 0 offset", 0);
1798 - warn(  
1799 - qpdf_e_damaged_pdf,  
1800 - this->m->last_object_description,  
1801 - 0,  
1802 - "object has offset 0"); 1670 + warn(damagedPDF(0, "object has offset 0"));
1803 return QPDFObjectHandle::newNull(); 1671 return QPDFObjectHandle::newNull();
1804 } 1672 }
1805 1673
@@ -1820,33 +1688,19 @@ QPDF::readObjectAtOffset( @@ -1820,33 +1688,19 @@ QPDF::readObjectAtOffset(
1820 try { 1688 try {
1821 if (!(objidok && genok && objok)) { 1689 if (!(objidok && genok && objok)) {
1822 QTC::TC("qpdf", "QPDF expected n n obj"); 1690 QTC::TC("qpdf", "QPDF expected n n obj");
1823 - throw QPDFExc(  
1824 - qpdf_e_damaged_pdf,  
1825 - this->m->file->getName(),  
1826 - this->m->last_object_description,  
1827 - offset,  
1828 - "expected n n obj"); 1691 + throw damagedPDF(offset, "expected n n obj");
1829 } 1692 }
1830 int objid = QUtil::string_to_int(tobjid.getValue().c_str()); 1693 int objid = QUtil::string_to_int(tobjid.getValue().c_str());
1831 int generation = QUtil::string_to_int(tgen.getValue().c_str()); 1694 int generation = QUtil::string_to_int(tgen.getValue().c_str());
1832 og = QPDFObjGen(objid, generation); 1695 og = QPDFObjGen(objid, generation);
1833 if (objid == 0) { 1696 if (objid == 0) {
1834 QTC::TC("qpdf", "QPDF object id 0"); 1697 QTC::TC("qpdf", "QPDF object id 0");
1835 - throw QPDFExc(  
1836 - qpdf_e_damaged_pdf,  
1837 - this->m->file->getName(),  
1838 - this->m->last_object_description,  
1839 - offset,  
1840 - "object with ID 0"); 1698 + throw damagedPDF(offset, "object with ID 0");
1841 } 1699 }
1842 if (check_og && (exp_og != og)) { 1700 if (check_og && (exp_og != og)) {
1843 QTC::TC("qpdf", "QPDF err wrong objid/generation"); 1701 QTC::TC("qpdf", "QPDF err wrong objid/generation");
1844 - QPDFExc e(  
1845 - qpdf_e_damaged_pdf,  
1846 - this->m->file->getName(),  
1847 - this->m->last_object_description,  
1848 - offset,  
1849 - (std::string("expected ") + exp_og.unparse(' ') + " obj")); 1702 + QPDFExc e =
  1703 + damagedPDF(offset, "expected " + exp_og.unparse(' ') + " obj");
1850 if (try_recovery) { 1704 if (try_recovery) {
1851 // Will be retried below 1705 // Will be retried below
1852 throw e; 1706 throw e;
@@ -1870,14 +1724,12 @@ QPDF::readObjectAtOffset( @@ -1870,14 +1724,12 @@ QPDF::readObjectAtOffset(
1870 return result; 1724 return result;
1871 } else { 1725 } else {
1872 QTC::TC("qpdf", "QPDF object gone after xref reconstruction"); 1726 QTC::TC("qpdf", "QPDF object gone after xref reconstruction");
1873 - warn(  
1874 - qpdf_e_damaged_pdf, 1727 + warn(damagedPDF(
1875 "", 1728 "",
1876 0, 1729 0,
1877 - std::string(  
1878 - "object " + exp_og.unparse(' ') +  
1879 - " not found in file after regenerating"  
1880 - " cross reference table")); 1730 + ("object " + exp_og.unparse(' ') +
  1731 + " not found in file after regenerating cross reference "
  1732 + "table")));
1881 return QPDFObjectHandle::newNull(); 1733 return QPDFObjectHandle::newNull();
1882 } 1734 }
1883 } else { 1735 } else {
@@ -1890,11 +1742,7 @@ QPDF::readObjectAtOffset( @@ -1890,11 +1742,7 @@ QPDF::readObjectAtOffset(
1890 if (!(readToken(this->m->file) == 1742 if (!(readToken(this->m->file) ==
1891 QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj"))) { 1743 QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj"))) {
1892 QTC::TC("qpdf", "QPDF err expected endobj"); 1744 QTC::TC("qpdf", "QPDF err expected endobj");
1893 - warn(  
1894 - qpdf_e_damaged_pdf,  
1895 - this->m->last_object_description,  
1896 - this->m->file->getLastOffset(),  
1897 - "expected endobj"); 1745 + warn(damagedPDF("expected endobj"));
1898 } 1746 }
1899 1747
1900 if (isUnresolved(og)) { 1748 if (isUnresolved(og)) {
@@ -1919,12 +1767,7 @@ QPDF::readObjectAtOffset( @@ -1919,12 +1767,7 @@ QPDF::readObjectAtOffset(
1919 break; 1767 break;
1920 } 1768 }
1921 } else { 1769 } else {
1922 - throw QPDFExc(  
1923 - qpdf_e_damaged_pdf,  
1924 - this->m->file->getName(),  
1925 - this->m->last_object_description,  
1926 - this->m->file->tell(),  
1927 - "EOF after endobj"); 1770 + throw damagedPDF(m->file->tell(), "EOF after endobj");
1928 } 1771 }
1929 } 1772 }
1930 qpdf_offset_t end_after_space = this->m->file->tell(); 1773 qpdf_offset_t end_after_space = this->m->file->tell();
@@ -1950,11 +1793,8 @@ QPDF::resolve(QPDFObjGen const&amp; og) @@ -1950,11 +1793,8 @@ QPDF::resolve(QPDFObjGen const&amp; og)
1950 // indirectly in some key that has to be resolved during 1793 // indirectly in some key that has to be resolved during
1951 // object parsing, such as stream length. 1794 // object parsing, such as stream length.
1952 QTC::TC("qpdf", "QPDF recursion loop in resolve"); 1795 QTC::TC("qpdf", "QPDF recursion loop in resolve");
1953 - warn(  
1954 - qpdf_e_damaged_pdf,  
1955 - "",  
1956 - this->m->file->getLastOffset(),  
1957 - ("loop detected resolving object " + og.unparse(' '))); 1796 + warn(damagedPDF(
  1797 + "", "loop detected resolving object " + og.unparse(' ')));
1958 updateCache(og, QPDF_Null::create(), -1, -1); 1798 updateCache(og, QPDF_Null::create(), -1, -1);
1959 return; 1799 return;
1960 } 1800 }
@@ -1979,9 +1819,7 @@ QPDF::resolve(QPDFObjGen const&amp; og) @@ -1979,9 +1819,7 @@ QPDF::resolve(QPDFObjGen const&amp; og)
1979 break; 1819 break;
1980 1820
1981 default: 1821 default:
1982 - throw QPDFExc(  
1983 - qpdf_e_damaged_pdf,  
1984 - this->m->file->getName(), 1822 + throw damagedPDF(
1985 "", 1823 "",
1986 0, 1824 0,
1987 ("object " + og.unparse('/') + 1825 ("object " + og.unparse('/') +
@@ -1990,12 +1828,11 @@ QPDF::resolve(QPDFObjGen const&amp; og) @@ -1990,12 +1828,11 @@ QPDF::resolve(QPDFObjGen const&amp; og)
1990 } catch (QPDFExc& e) { 1828 } catch (QPDFExc& e) {
1991 warn(e); 1829 warn(e);
1992 } catch (std::exception& e) { 1830 } catch (std::exception& e) {
1993 - warn(  
1994 - qpdf_e_damaged_pdf, 1831 + warn(damagedPDF(
1995 "", 1832 "",
1996 0, 1833 0,
1997 ("object " + og.unparse('/') + 1834 ("object " + og.unparse('/') +
1998 - ": error reading object: " + e.what())); 1835 + ": error reading object: " + e.what())));
1999 } 1836 }
2000 } 1837 }
2001 1838
@@ -2021,13 +1858,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -2021,13 +1858,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
2021 // Force resolution of object stream 1858 // Force resolution of object stream
2022 QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0); 1859 QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0);
2023 if (!obj_stream.isStream()) { 1860 if (!obj_stream.isStream()) {
2024 - throw QPDFExc(  
2025 - qpdf_e_damaged_pdf,  
2026 - this->m->file->getName(),  
2027 - this->m->last_object_description,  
2028 - this->m->file->getLastOffset(),  
2029 - ("supposed object stream " + std::to_string(obj_stream_number) +  
2030 - " is not a stream")); 1861 + throw damagedPDF(
  1862 + "supposed object stream " + std::to_string(obj_stream_number) +
  1863 + " is not a stream");
2031 } 1864 }
2032 1865
2033 // For linearization data in the object, use the data from the 1866 // For linearization data in the object, use the data from the
@@ -2041,20 +1874,13 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -2041,20 +1874,13 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
2041 QPDFObjectHandle dict = obj_stream.getDict(); 1874 QPDFObjectHandle dict = obj_stream.getDict();
2042 if (!dict.isDictionaryOfType("/ObjStm")) { 1875 if (!dict.isDictionaryOfType("/ObjStm")) {
2043 QTC::TC("qpdf", "QPDF ERR object stream with wrong type"); 1876 QTC::TC("qpdf", "QPDF ERR object stream with wrong type");
2044 - warn(  
2045 - qpdf_e_damaged_pdf,  
2046 - this->m->last_object_description,  
2047 - this->m->file->getLastOffset(),  
2048 - ("supposed object stream " + std::to_string(obj_stream_number) +  
2049 - " has wrong type")); 1877 + warn(damagedPDF(
  1878 + "supposed object stream " + std::to_string(obj_stream_number) +
  1879 + " has wrong type"));
2050 } 1880 }
2051 1881
2052 if (!(dict.getKey("/N").isInteger() && dict.getKey("/First").isInteger())) { 1882 if (!(dict.getKey("/N").isInteger() && dict.getKey("/First").isInteger())) {
2053 - throw QPDFExc(  
2054 - qpdf_e_damaged_pdf,  
2055 - this->m->file->getName(),  
2056 - this->m->last_object_description,  
2057 - this->m->file->getLastOffset(), 1883 + throw damagedPDF(
2058 ("object stream " + std::to_string(obj_stream_number) + 1884 ("object stream " + std::to_string(obj_stream_number) +
2059 " has incorrect keys")); 1885 " has incorrect keys"));
2060 } 1886 }
@@ -2077,10 +1903,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -2077,10 +1903,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
2077 QPDFTokenizer::Token toffset = readToken(input); 1903 QPDFTokenizer::Token toffset = readToken(input);
2078 if (!((tnum.getType() == QPDFTokenizer::tt_integer) && 1904 if (!((tnum.getType() == QPDFTokenizer::tt_integer) &&
2079 (toffset.getType() == QPDFTokenizer::tt_integer))) { 1905 (toffset.getType() == QPDFTokenizer::tt_integer))) {
2080 - throw QPDFExc(  
2081 - qpdf_e_damaged_pdf,  
2082 - input->getName(),  
2083 - this->m->last_object_description, 1906 + throw damagedPDF(
  1907 + input,
2084 input->getLastOffset(), 1908 input->getLastOffset(),
2085 "expected integer in object stream header"); 1909 "expected integer in object stream header");
2086 } 1910 }
@@ -2618,12 +2442,7 @@ QPDF::getRoot() @@ -2618,12 +2442,7 @@ QPDF::getRoot()
2618 { 2442 {
2619 QPDFObjectHandle root = this->m->trailer.getKey("/Root"); 2443 QPDFObjectHandle root = this->m->trailer.getKey("/Root");
2620 if (!root.isDictionary()) { 2444 if (!root.isDictionary()) {
2621 - throw QPDFExc(  
2622 - qpdf_e_damaged_pdf,  
2623 - this->m->file->getName(),  
2624 - "",  
2625 - 0,  
2626 - "unable to find /Root dictionary"); 2445 + throw damagedPDF("", 0, "unable to find /Root dictionary");
2627 } 2446 }
2628 return root; 2447 return root;
2629 } 2448 }
@@ -2751,9 +2570,8 @@ QPDF::pipeStreamData( @@ -2751,9 +2570,8 @@ QPDF::pipeStreamData(
2751 size_t to_read = (sizeof(buf) < length ? sizeof(buf) : length); 2570 size_t to_read = (sizeof(buf) < length ? sizeof(buf) : length);
2752 size_t len = file->read(buf, to_read); 2571 size_t len = file->read(buf, to_read);
2753 if (len == 0) { 2572 if (len == 0) {
2754 - throw QPDFExc(  
2755 - qpdf_e_damaged_pdf,  
2756 - file->getName(), 2573 + throw damagedPDF(
  2574 + file,
2757 "", 2575 "",
2758 file->getLastOffset(), 2576 file->getLastOffset(),
2759 "unexpected EOF reading stream data"); 2577 "unexpected EOF reading stream data");
@@ -2772,9 +2590,8 @@ QPDF::pipeStreamData( @@ -2772,9 +2590,8 @@ QPDF::pipeStreamData(
2772 QTC::TC("qpdf", "QPDF decoding error warning"); 2590 QTC::TC("qpdf", "QPDF decoding error warning");
2773 qpdf_for_warning.warn( 2591 qpdf_for_warning.warn(
2774 // line-break 2592 // line-break
2775 - QPDFExc(  
2776 - qpdf_e_damaged_pdf,  
2777 - file->getName(), 2593 + damagedPDF(
  2594 + file,
2778 "", 2595 "",
2779 file->getLastOffset(), 2596 file->getLastOffset(),
2780 ("error decoding stream data for object " + 2597 ("error decoding stream data for object " +
@@ -2782,9 +2599,8 @@ QPDF::pipeStreamData( @@ -2782,9 +2599,8 @@ QPDF::pipeStreamData(
2782 if (will_retry) { 2599 if (will_retry) {
2783 qpdf_for_warning.warn( 2600 qpdf_for_warning.warn(
2784 // line-break 2601 // line-break
2785 - QPDFExc(  
2786 - qpdf_e_damaged_pdf,  
2787 - file->getName(), 2602 + damagedPDF(
  2603 + file,
2788 "", 2604 "",
2789 file->getLastOffset(), 2605 file->getLastOffset(),
2790 "stream will be re-processed without" 2606 "stream will be re-processed without"
@@ -2848,18 +2664,72 @@ QPDF::pipeForeignStreamData( @@ -2848,18 +2664,72 @@ QPDF::pipeForeignStreamData(
2848 will_retry); 2664 will_retry);
2849 } 2665 }
2850 2666
  2667 +// Throw a generic exception when we lack context for something
  2668 +// more specific. New code should not use this. This method exists
  2669 +// to improve somewhat from calling assert in very old code.
2851 void 2670 void
2852 QPDF::stopOnError(std::string const& message) 2671 QPDF::stopOnError(std::string const& message)
2853 { 2672 {
2854 - // Throw a generic exception when we lack context for something  
2855 - // more specific. New code should not use this. This method exists  
2856 - // to improve somewhat from calling assert in very old code.  
2857 - throw QPDFExc(  
2858 - qpdf_e_damaged_pdf,  
2859 - this->m->file->getName(),  
2860 - "",  
2861 - this->m->file->getLastOffset(),  
2862 - message); 2673 + throw damagedPDF("", message);
  2674 +}
  2675 +
  2676 +// Return an exception of type qpdf_e_damaged_pdf.
  2677 +QPDFExc
  2678 +QPDF::damagedPDF(
  2679 + std::shared_ptr<InputSource> const& input,
  2680 + std::string const& object,
  2681 + qpdf_offset_t offset,
  2682 + std::string const& message)
  2683 +{
  2684 + return QPDFExc(
  2685 + qpdf_e_damaged_pdf, input->getName(), object, offset, message);
  2686 +}
  2687 +
  2688 +// Return an exception of type qpdf_e_damaged_pdf. The object is taken from
  2689 +// m->last_object_description.
  2690 +QPDFExc
  2691 +QPDF::damagedPDF(
  2692 + std::shared_ptr<InputSource> const& input,
  2693 + qpdf_offset_t offset,
  2694 + std::string const& message)
  2695 +{
  2696 + return damagedPDF(input, m->last_object_description, offset, message);
  2697 +}
  2698 +
  2699 +// Return an exception of type qpdf_e_damaged_pdf. The filename is taken from
  2700 +// m->file.
  2701 +QPDFExc
  2702 +QPDF::damagedPDF(
  2703 + std::string const& object, qpdf_offset_t offset, std::string const& message)
  2704 +{
  2705 + return QPDFExc(
  2706 + qpdf_e_damaged_pdf, m->file->getName(), object, offset, message);
  2707 +}
  2708 +
  2709 +// Return an exception of type qpdf_e_damaged_pdf. The filename is taken from
  2710 +// m->file and the offset from .m->file->getLastOffset().
  2711 +QPDFExc
  2712 +QPDF::damagedPDF(std::string const& object, std::string const& message)
  2713 +{
  2714 + return damagedPDF(object, m->file->getLastOffset(), message);
  2715 +}
  2716 +
  2717 +// Return an exception of type qpdf_e_damaged_pdf. The filename is taken from
  2718 +// m->file and the object from .m->last_object_description.
  2719 +QPDFExc
  2720 +QPDF::damagedPDF(qpdf_offset_t offset, std::string const& message)
  2721 +{
  2722 + return damagedPDF(m->last_object_description, offset, message);
  2723 +}
  2724 +
  2725 +// Return an exception of type qpdf_e_damaged_pdf. The filename is taken from
  2726 +// m->file, the object from m->last_object_description and the offset from
  2727 +// m->file->getLastOffset().
  2728 +QPDFExc
  2729 +QPDF::damagedPDF(std::string const& message)
  2730 +{
  2731 + return damagedPDF(
  2732 + m->last_object_description, m->file->getLastOffset(), message);
2863 } 2733 }
2864 2734
2865 bool 2735 bool
libqpdf/QPDF_encryption.cc
@@ -804,21 +804,12 @@ QPDF::initializeEncryption() @@ -804,21 +804,12 @@ QPDF::initializeEncryption()
804 // Treating a missing ID as the empty string enables qpdf to 804 // Treating a missing ID as the empty string enables qpdf to
805 // decrypt some invalid encrypted files with no /ID that 805 // decrypt some invalid encrypted files with no /ID that
806 // poppler can read but Adobe Reader can't. 806 // poppler can read but Adobe Reader can't.
807 - warn(  
808 - qpdf_e_damaged_pdf,  
809 - "trailer",  
810 - this->m->file->getLastOffset(),  
811 - "invalid /ID in trailer dictionary"); 807 + warn(damagedPDF("trailer", "invalid /ID in trailer dictionary"));
812 } 808 }
813 809
814 QPDFObjectHandle encryption_dict = this->m->trailer.getKey("/Encrypt"); 810 QPDFObjectHandle encryption_dict = this->m->trailer.getKey("/Encrypt");
815 if (!encryption_dict.isDictionary()) { 811 if (!encryption_dict.isDictionary()) {
816 - throw QPDFExc(  
817 - qpdf_e_damaged_pdf,  
818 - this->m->file->getName(),  
819 - this->m->last_object_description,  
820 - this->m->file->getLastOffset(),  
821 - "/Encrypt in trailer dictionary is not a dictionary"); 812 + throw damagedPDF("/Encrypt in trailer dictionary is not a dictionary");
822 } 813 }
823 814
824 if (!(encryption_dict.getKey("/Filter").isName() && 815 if (!(encryption_dict.getKey("/Filter").isName() &&
@@ -835,8 +826,7 @@ QPDF::initializeEncryption() @@ -835,8 +826,7 @@ QPDF::initializeEncryption()
835 qpdf_e_unsupported, 826 qpdf_e_unsupported,
836 "encryption dictionary", 827 "encryption dictionary",
837 this->m->file->getLastOffset(), 828 this->m->file->getLastOffset(),
838 - "file uses encryption SubFilters,"  
839 - " which qpdf does not support"); 829 + "file uses encryption SubFilters, which qpdf does not support");
840 } 830 }
841 831
842 if (!(encryption_dict.getKey("/V").isInteger() && 832 if (!(encryption_dict.getKey("/V").isInteger() &&
@@ -844,13 +834,10 @@ QPDF::initializeEncryption() @@ -844,13 +834,10 @@ QPDF::initializeEncryption()
844 encryption_dict.getKey("/O").isString() && 834 encryption_dict.getKey("/O").isString() &&
845 encryption_dict.getKey("/U").isString() && 835 encryption_dict.getKey("/U").isString() &&
846 encryption_dict.getKey("/P").isInteger())) { 836 encryption_dict.getKey("/P").isInteger())) {
847 - throw QPDFExc(  
848 - qpdf_e_damaged_pdf,  
849 - this->m->file->getName(), 837 + throw damagedPDF(
850 "encryption dictionary", 838 "encryption dictionary",
851 - this->m->file->getLastOffset(),  
852 - "some encryption dictionary parameters are missing "  
853 - "or the wrong type"); 839 + "some encryption dictionary parameters are missing or the wrong "
  840 + "type");
854 } 841 }
855 842
856 int V = encryption_dict.getKey("/V").getIntValueAsInt(); 843 int V = encryption_dict.getKey("/V").getIntValueAsInt();
@@ -886,25 +873,18 @@ QPDF::initializeEncryption() @@ -886,25 +873,18 @@ QPDF::initializeEncryption()
886 pad_short_parameter(O, key_bytes); 873 pad_short_parameter(O, key_bytes);
887 pad_short_parameter(U, key_bytes); 874 pad_short_parameter(U, key_bytes);
888 if (!((O.length() == key_bytes) && (U.length() == key_bytes))) { 875 if (!((O.length() == key_bytes) && (U.length() == key_bytes))) {
889 - throw QPDFExc(  
890 - qpdf_e_damaged_pdf,  
891 - this->m->file->getName(), 876 + throw damagedPDF(
892 "encryption dictionary", 877 "encryption dictionary",
893 - this->m->file->getLastOffset(),  
894 - "incorrect length for /O and/or /U in "  
895 - "encryption dictionary"); 878 + "incorrect length for /O and/or /U in encryption dictionary");
896 } 879 }
897 } else { 880 } else {
898 if (!(encryption_dict.getKey("/OE").isString() && 881 if (!(encryption_dict.getKey("/OE").isString() &&
899 encryption_dict.getKey("/UE").isString() && 882 encryption_dict.getKey("/UE").isString() &&
900 encryption_dict.getKey("/Perms").isString())) { 883 encryption_dict.getKey("/Perms").isString())) {
901 - throw QPDFExc(  
902 - qpdf_e_damaged_pdf,  
903 - this->m->file->getName(), 884 + throw damagedPDF(
904 "encryption dictionary", 885 "encryption dictionary",
905 - this->m->file->getLastOffset(),  
906 - "some V=5 encryption dictionary parameters are "  
907 - "missing or the wrong type"); 886 + "some V=5 encryption dictionary parameters are missing or the "
  887 + "wrong type");
908 } 888 }
909 OE = encryption_dict.getKey("/OE").getStringValue(); 889 OE = encryption_dict.getKey("/OE").getStringValue();
910 UE = encryption_dict.getKey("/UE").getStringValue(); 890 UE = encryption_dict.getKey("/UE").getStringValue();
@@ -1062,12 +1042,10 @@ QPDF::initializeEncryption() @@ -1062,12 +1042,10 @@ QPDF::initializeEncryption()
1062 this->m->encp->encryption_key = recover_encryption_key_with_password( 1042 this->m->encp->encryption_key = recover_encryption_key_with_password(
1063 this->m->encp->provided_password, data, perms_valid); 1043 this->m->encp->provided_password, data, perms_valid);
1064 if (!perms_valid) { 1044 if (!perms_valid) {
1065 - warn(  
1066 - qpdf_e_damaged_pdf, 1045 + warn(damagedPDF(
1067 "encryption dictionary", 1046 "encryption dictionary",
1068 - this->m->file->getLastOffset(),  
1069 - "/Perms field in encryption dictionary"  
1070 - " doesn't match expected value"); 1047 + "/Perms field in encryption dictionary doesn't match expected "
  1048 + "value"));
1071 } 1049 }
1072 } 1050 }
1073 } 1051 }
@@ -1121,13 +1099,9 @@ QPDF::decryptString(std::string&amp; str, QPDFObjGen const&amp; og) @@ -1121,13 +1099,9 @@ QPDF::decryptString(std::string&amp; str, QPDFObjGen const&amp; og)
1121 break; 1099 break;
1122 1100
1123 default: 1101 default:
1124 - warn(  
1125 - qpdf_e_damaged_pdf,  
1126 - this->m->last_object_description,  
1127 - this->m->file->getLastOffset(),  
1128 - "unknown encryption filter for strings"  
1129 - " (check /StrF in /Encrypt dictionary);"  
1130 - " strings may be decrypted improperly"); 1102 + warn(damagedPDF(
  1103 + "unknown encryption filter for strings (check /StrF in "
  1104 + "/Encrypt dictionary); strings may be decrypted improperly"));
1131 // To avoid repeated warnings, reset cf_string. Assume 1105 // To avoid repeated warnings, reset cf_string. Assume
1132 // we'd want to use AES if V == 4. 1106 // we'd want to use AES if V == 4.
1133 this->m->encp->cf_string = e_aes; 1107 this->m->encp->cf_string = e_aes;
@@ -1166,13 +1140,9 @@ QPDF::decryptString(std::string&amp; str, QPDFObjGen const&amp; og) @@ -1166,13 +1140,9 @@ QPDF::decryptString(std::string&amp; str, QPDFObjGen const&amp; og)
1166 } catch (QPDFExc&) { 1140 } catch (QPDFExc&) {
1167 throw; 1141 throw;
1168 } catch (std::runtime_error& e) { 1142 } catch (std::runtime_error& e) {
1169 - throw QPDFExc(  
1170 - qpdf_e_damaged_pdf,  
1171 - this->m->file->getName(),  
1172 - this->m->last_object_description,  
1173 - this->m->file->getLastOffset(), 1143 + throw damagedPDF(
1174 "error decrypting string for object " + og.unparse() + ": " + 1144 "error decrypting string for object " + og.unparse() + ": " +
1175 - e.what()); 1145 + e.what());
1176 } 1146 }
1177 } 1147 }
1178 1148
@@ -1265,11 +1235,8 @@ QPDF::decryptStream( @@ -1265,11 +1235,8 @@ QPDF::decryptStream(
1265 file->getName(), 1235 file->getName(),
1266 "", 1236 "",
1267 file->getLastOffset(), 1237 file->getLastOffset(),
1268 - "unknown encryption filter for streams"  
1269 - " (check " +  
1270 - method_source +  
1271 - ");"  
1272 - " streams may be decrypted improperly")); 1238 + "unknown encryption filter for streams (check " +
  1239 + method_source + "); streams may be decrypted improperly"));
1273 // To avoid repeated warnings, reset cf_stream. Assume 1240 // To avoid repeated warnings, reset cf_stream. Assume
1274 // we'd want to use AES if V == 4. 1241 // we'd want to use AES if V == 4.
1275 encp->cf_stream = e_aes; 1242 encp->cf_stream = e_aes;
libqpdf/QPDF_linearization.cc
@@ -186,24 +186,16 @@ QPDF::readLinearizationData() @@ -186,24 +186,16 @@ QPDF::readLinearizationData()
186 186
187 if (!(H.isArray() && O.isInteger() && E.isInteger() && N.isInteger() && 187 if (!(H.isArray() && O.isInteger() && E.isInteger() && N.isInteger() &&
188 T.isInteger() && (P.isInteger() || P.isNull()))) { 188 T.isInteger() && (P.isInteger() || P.isNull()))) {
189 - throw QPDFExc(  
190 - qpdf_e_damaged_pdf,  
191 - this->m->file->getName(), 189 + throw damagedPDF(
192 "linearization dictionary", 190 "linearization dictionary",
193 - this->m->file->getLastOffset(),  
194 - "some keys in linearization dictionary are of "  
195 - "the wrong type"); 191 + "some keys in linearization dictionary are of the wrong type");
196 } 192 }
197 193
198 // Hint table array: offset length [ offset length ] 194 // Hint table array: offset length [ offset length ]
199 size_t n_H_items = toS(H.getArrayNItems()); 195 size_t n_H_items = toS(H.getArrayNItems());
200 if (!((n_H_items == 2) || (n_H_items == 4))) { 196 if (!((n_H_items == 2) || (n_H_items == 4))) {
201 - throw QPDFExc(  
202 - qpdf_e_damaged_pdf,  
203 - this->m->file->getName(),  
204 - "linearization dictionary",  
205 - this->m->file->getLastOffset(),  
206 - "H has the wrong number of items"); 197 + throw damagedPDF(
  198 + "linearization dictionary", "H has the wrong number of items");
207 } 199 }
208 200
209 std::vector<int> H_items; 201 std::vector<int> H_items;
@@ -212,11 +204,8 @@ QPDF::readLinearizationData() @@ -212,11 +204,8 @@ QPDF::readLinearizationData()
212 if (oh.isInteger()) { 204 if (oh.isInteger()) {
213 H_items.push_back(oh.getIntValueAsInt()); 205 H_items.push_back(oh.getIntValueAsInt());
214 } else { 206 } else {
215 - throw QPDFExc(  
216 - qpdf_e_damaged_pdf,  
217 - this->m->file->getName(), 207 + throw damagedPDF(
218 "linearization dictionary", 208 "linearization dictionary",
219 - this->m->file->getLastOffset(),  
220 "some H items are of the wrong type"); 209 "some H items are of the wrong type");
221 } 210 }
222 } 211 }
@@ -249,12 +238,8 @@ QPDF::readLinearizationData() @@ -249,12 +238,8 @@ QPDF::readLinearizationData()
249 // initialized from N, to pre-allocate memory, so make sure it's 238 // initialized from N, to pre-allocate memory, so make sure it's
250 // accurate and bail right now if it's not. 239 // accurate and bail right now if it's not.
251 if (N.getIntValue() != static_cast<long long>(getAllPages().size())) { 240 if (N.getIntValue() != static_cast<long long>(getAllPages().size())) {
252 - throw QPDFExc(  
253 - qpdf_e_damaged_pdf,  
254 - this->m->file->getName(),  
255 - "linearization hint table",  
256 - this->m->file->getLastOffset(),  
257 - "/N does not match number of pages"); 241 + throw damagedPDF(
  242 + "linearization hint table", "/N does not match number of pages");
258 } 243 }
259 244
260 // file_size initialized by isLinearized() 245 // file_size initialized by isLinearized()
@@ -297,11 +282,8 @@ QPDF::readLinearizationData() @@ -297,11 +282,8 @@ QPDF::readLinearizationData()
297 282
298 int HSi = HS.getIntValueAsInt(); 283 int HSi = HS.getIntValueAsInt();
299 if ((HSi < 0) || (toS(HSi) >= h_size)) { 284 if ((HSi < 0) || (toS(HSi) >= h_size)) {
300 - throw QPDFExc(  
301 - qpdf_e_damaged_pdf,  
302 - this->m->file->getName(), 285 + throw damagedPDF(
303 "linearization hint table", 286 "linearization hint table",
304 - this->m->file->getLastOffset(),  
305 "/S (shared object) offset is out of bounds"); 287 "/S (shared object) offset is out of bounds");
306 } 288 }
307 readHSharedObject(BitStream(h_buf + HSi, h_size - toS(HSi))); 289 readHSharedObject(BitStream(h_buf + HSi, h_size - toS(HSi)));
@@ -309,11 +291,8 @@ QPDF::readLinearizationData() @@ -309,11 +291,8 @@ QPDF::readLinearizationData()
309 if (HO.isInteger()) { 291 if (HO.isInteger()) {
310 int HOi = HO.getIntValueAsInt(); 292 int HOi = HO.getIntValueAsInt();
311 if ((HOi < 0) || (toS(HOi) >= h_size)) { 293 if ((HOi < 0) || (toS(HOi) >= h_size)) {
312 - throw QPDFExc(  
313 - qpdf_e_damaged_pdf,  
314 - this->m->file->getName(), 294 + throw damagedPDF(
315 "linearization hint table", 295 "linearization hint table",
316 - this->m->file->getLastOffset(),  
317 "/O (outline) offset is out of bounds"); 296 "/O (outline) offset is out of bounds");
318 } 297 }
319 readHGeneric( 298 readHGeneric(
@@ -331,12 +310,8 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length) @@ -331,12 +310,8 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length)
331 qpdf_offset_t min_end_offset = oc.end_before_space; 310 qpdf_offset_t min_end_offset = oc.end_before_space;
332 qpdf_offset_t max_end_offset = oc.end_after_space; 311 qpdf_offset_t max_end_offset = oc.end_after_space;
333 if (!H.isStream()) { 312 if (!H.isStream()) {
334 - throw QPDFExc(  
335 - qpdf_e_damaged_pdf,  
336 - this->m->file->getName(),  
337 - "linearization dictionary",  
338 - this->m->file->getLastOffset(),  
339 - "hint table is not a stream"); 313 + throw damagedPDF(
  314 + "linearization dictionary", "hint table is not a stream");
340 } 315 }
341 316
342 QPDFObjectHandle Hdict = H.getDict(); 317 QPDFObjectHandle Hdict = H.getDict();
@@ -362,12 +337,8 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length) @@ -362,12 +337,8 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length)
362 *this->m->log->getError() 337 *this->m->log->getError()
363 << "expected = " << computed_end << "; actual = " << min_end_offset 338 << "expected = " << computed_end << "; actual = " << min_end_offset
364 << ".." << max_end_offset << "\n"; 339 << ".." << max_end_offset << "\n";
365 - throw QPDFExc(  
366 - qpdf_e_damaged_pdf,  
367 - this->m->file->getName(),  
368 - "linearization dictionary",  
369 - this->m->file->getLastOffset(),  
370 - "hint table length mismatch"); 340 + throw damagedPDF(
  341 + "linearization dictionary", "hint table length mismatch");
371 } 342 }
372 H.pipeStreamData(&pl, 0, qpdf_dl_specialized); 343 H.pipeStreamData(&pl, 0, qpdf_dl_specialized);
373 return Hdict; 344 return Hdict;
qpdf/test_char_sign.cc
1 #include <cstdio> 1 #include <cstdio>
2 -int main() 2 +int
  3 +main()
3 { 4 {
4 char ch = '\xf7'; 5 char ch = '\xf7';
5 if (ch < 0) { 6 if (ch < 0) {