Commit 3f8c4c273649c857f5a607dcbb422729fce3a166

Authored by Jay Berkenbilt
1 parent b67a3c15

categorize all error messages and include object information if available

git-svn-id: svn+q:///qpdf/trunk@829 71b93d88-0707-0410-a8cf-f5a4172ac649
Showing 95 changed files with 660 additions and 266 deletions
ChangeLog
  1 +2009-10-19 Jay Berkenbilt <jberkenb@argonst.com>
  2 +
  3 + * Include information about the last object read in most error
  4 + messages. Most of the time, this will provide a good hint as to
  5 + which object contains the error, but it's possible that the last
  6 + object read may not necessarily be the one that has the error if
  7 + the erroneous object was previously read and cached.
  8 +
1 9 2009-10-18 Jay Berkenbilt <ejb@ql.org>
2 10  
3 11 * If forcing version, disable object stream creation and/or
... ...
... ... @@ -16,25 +16,10 @@
16 16 * Add comments for the security functions that map them back to the
17 17 items in Adobe's products.
18 18  
19   - * Add error codes to QPDFException. Change the error interface so
20   - that warnings and errors are pointers that can be queried using
21   - more C API functions. We need a way to get a full string as well
22   - as an error code, file name, offset, and message. We should go
23   - through all error messages to try to include all these fields as
24   - appropriate. Make sure invalid password is specifically
25   - detectable. I/O errors and so forth should also be
26   - distinguishable. Make sure all errors include information about
27   - the most recent read location including byte offset and
28   - object/generation number.
29   -
30   - * It might be nice to be able to trap I/O errors separately from
31   - other errors; especially be able to separate errors that the user
32   - can fix (like permission errors) from errors that they probably
33   - can't fix like corrupted PDF files, unsupported filters, or
34   - internal errors. However, only QPDF::processFile(), which does the
35   - initial read, and QPDFWriter::QPDFWriter(), which does the initial
36   - write, are at all likely to generate such errors for a case other
37   - than a catastrophic failure.
  19 + * Change the C error interface so that warnings and errors are
  20 + pointers that can be queried using more C API functions. We need a
  21 + way to get a full string as well as an error code, file name,
  22 + offset, and message.
38 23  
39 24 * "Delphi wrapper unit 'qpdf.pas' created by Zarko Gajic
40 25 (http://delphi.about.com). .. use at your own risk and for whatever
... ...
examples/qtest/bookmarks.test
... ... @@ -30,7 +30,7 @@ $td-&gt;runtest(&quot;no bookmarks&quot;,
30 30 $td->runtest("bad",
31 31 {$td->COMMAND => "pdf-bookmarks 3.pdf"},
32 32 {$td->STRING => "pdf-bookmarks processing file 3.pdf: " .
33   - "3.pdf: offset 0: not a PDF file\n",
  33 + "3.pdf: not a PDF file\n",
34 34 $td->EXIT_STATUS => 2},
35 35 $td->NORMALIZE_NEWLINES);
36 36  
... ...
examples/qtest/npages.test
... ... @@ -16,7 +16,7 @@ $td-&gt;runtest(&quot;normal&quot;,
16 16  
17 17 $td->runtest("error",
18 18 {$td->COMMAND => "pdf-npages bad"},
19   - {$td->STRING => "pdf-npages: bad: offset 0: not a PDF file\n",
  19 + {$td->STRING => "pdf-npages: bad: not a PDF file\n",
20 20 $td->EXIT_STATUS => 2},
21 21 $td->NORMALIZE_NEWLINES);
22 22  
... ...
include/qpdf/QPDF.hh
... ... @@ -371,9 +371,11 @@ class DLL_EXPORT QPDF
371 371 int processXRefStream(off_t offset, QPDFObjectHandle& xref_stream);
372 372 void insertXrefEntry(int obj, int f0, int f1, int f2,
373 373 bool overwrite = false);
  374 + void setLastObjectDescription(std::string const& description,
  375 + int objid, int generation);
374 376 QPDFObjectHandle readObject(
375   - InputSource*, int objid, int generation,
376   - bool in_object_stream);
  377 + InputSource*, std::string const& description,
  378 + int objid, int generation, bool in_object_stream);
377 379 QPDFObjectHandle readObjectInternal(
378 380 InputSource* input, int objid, int generation,
379 381 bool in_object_stream,
... ... @@ -383,7 +385,8 @@ class DLL_EXPORT QPDF
383 385 QPDFTokenizer::Token readToken(InputSource*);
384 386  
385 387 QPDFObjectHandle readObjectAtOffset(
386   - off_t offset,
  388 + bool attempt_recovery,
  389 + off_t offset, std::string const& description,
387 390 int exp_objid, int exp_generation,
388 391 int& act_objid, int& act_generation);
389 392 PointerHolder<QPDFObject> resolve(int objid, int generation);
... ... @@ -734,6 +737,7 @@ class DLL_EXPORT QPDF
734 737  
735 738 QPDFTokenizer tokenizer;
736 739 FileInputSource file;
  740 + std::string last_object_description;
737 741 bool encrypted;
738 742 bool encryption_initialized;
739 743 bool ignore_xref_streams;
... ...
include/qpdf/QPDFExc.hh
... ... @@ -9,15 +9,47 @@
9 9 #define __QPDFEXC_HH__
10 10  
11 11 #include <qpdf/DLL.h>
  12 +#include <qpdf/Constants.h>
12 13 #include <stdexcept>
  14 +#include <stddef.h>
13 15  
14 16 class DLL_EXPORT QPDFExc: public std::runtime_error
15 17 {
16 18 public:
17   - QPDFExc(std::string const& message);
18   - QPDFExc(std::string const& filename, int offset,
  19 + QPDFExc(qpdf_error_code_e error_code,
  20 + std::string const& filename,
  21 + std::string const& object,
  22 + off_t offset,
19 23 std::string const& message);
20 24 virtual ~QPDFExc() throw ();
  25 +
  26 + // To get a complete error string, call what(), provided by
  27 + // std::exception. The accessors below return the original values
  28 + // used to create the exception. Only the error code and message
  29 + // are guaranteed to have non-zero/empty values.
  30 +
  31 + // There is no lookup code that maps numeric error codes into
  32 + // strings. The numeric error code is just another way to get at
  33 + // the underlying issue, but it is more programmer-friendly than
  34 + // trying to parse a string that is subject to change.
  35 +
  36 + qpdf_error_code_e getErrorCode() const;
  37 + std::string const& getFilename() const;
  38 + std::string const& getObject() const;
  39 + off_t getOffset() const;
  40 + std::string const& getMessage() const;
  41 +
  42 + private:
  43 + static std::string createWhat(std::string const& filename,
  44 + std::string const& object,
  45 + off_t offset,
  46 + std::string const& message);
  47 +
  48 + qpdf_error_code_e error_code;
  49 + std::string filename;
  50 + std::string object;
  51 + off_t offset;
  52 + std::string message;
21 53 };
22 54  
23 55 #endif // __QPDFEXC_HH__
... ...
libqpdf/QPDF.cc
... ... @@ -140,7 +140,9 @@ QPDF::FileInputSource::read(char* buffer, int length)
140 140 size_t len = fread(buffer, 1, length, this->file);
141 141 if ((len == 0) && ferror(this->file))
142 142 {
143   - throw QPDFExc(this->filename, this->last_offset,
  143 + throw QPDFExc(qpdf_e_system,
  144 + this->filename, "",
  145 + this->last_offset,
144 146 std::string("read ") +
145 147 QUtil::int_to_string(length) + " bytes");
146 148 }
... ... @@ -325,7 +327,8 @@ QPDF::parse()
325 327 else
326 328 {
327 329 QTC::TC("qpdf", "QPDF not a pdf file");
328   - throw QPDFExc(this->file.getName(), 0, "not a PDF file");
  330 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  331 + "", 0, "not a PDF file");
329 332 }
330 333  
331 334 // PDF spec says %%EOF must be found within the last 1024 bytes of
... ... @@ -369,7 +372,8 @@ QPDF::parse()
369 372 if (! m2)
370 373 {
371 374 QTC::TC("qpdf", "QPDF can't find startxref");
372   - throw QPDFExc(this->file.getName() + ": can't find startxref");
  375 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
  376 + "can't find startxref");
373 377 }
374 378 off_t xref_offset = atoi(m2.getMatch(1).c_str());
375 379 read_xref(xref_offset);
... ... @@ -417,9 +421,28 @@ QPDF::reconstruct_xref(QPDFExc&amp; e)
417 421 static PCRE endobj_re("^endobj\\b");
418 422 static PCRE trailer_re("^trailer\\b");
419 423  
420   - warn(QPDFExc(this->file.getName(), 0, "file is damaged"));
  424 + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
  425 + "file is damaged"));
421 426 warn(e);
422   - warn(QPDFExc("Attempting to reconstruct cross-reference table"));
  427 + warn(QPDFExc(qpdf_e_damaged_pdf, "", "", 0,
  428 + "Attempting to reconstruct cross-reference table"));
  429 +
  430 + // Delete all references to type 1 (uncompressed) objects
  431 + std::set<ObjGen> to_delete;
  432 + for (std::map<ObjGen, QPDFXRefEntry>::iterator iter =
  433 + this->xref_table.begin();
  434 + iter != this->xref_table.end(); ++iter)
  435 + {
  436 + if (((*iter).second).getType() == 1)
  437 + {
  438 + to_delete.insert((*iter).first);
  439 + }
  440 + }
  441 + for (std::set<ObjGen>::iterator iter = to_delete.begin();
  442 + iter != to_delete.end(); ++iter)
  443 + {
  444 + this->xref_table.erase(*iter);
  445 + }
423 446  
424 447 this->file.seek(0, SEEK_END);
425 448 off_t eof = this->file.tell();
... ... @@ -452,7 +475,8 @@ QPDF::reconstruct_xref(QPDFExc&amp; e)
452 475 // read "trailer"
453 476 this->file.seek(this->file.getLastOffset(), SEEK_SET);
454 477 readToken(&this->file);
455   - QPDFObjectHandle t = readObject(&this->file, 0, 0, false);
  478 + QPDFObjectHandle t =
  479 + readObject(&this->file, "trailer", 0, 0, false);
456 480 if (! t.isDictionary())
457 481 {
458 482 // Oh well. It was worth a try.
... ... @@ -473,7 +497,8 @@ QPDF::reconstruct_xref(QPDFExc&amp; e)
473 497 // with bad startxref pointers even when they have object
474 498 // streams.
475 499  
476   - throw QPDFExc(this->file.getName() + ": unable to find trailer "
  500 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
  501 + "unable to find trailer "
477 502 "dictionary while recovering damaged file");
478 503 }
479 504  
... ... @@ -513,8 +538,8 @@ QPDF::read_xref(off_t xref_offset)
513 538 if (size != max_obj + 1)
514 539 {
515 540 QTC::TC("qpdf", "QPDF xref size mismatch");
516   - warn(QPDFExc(this->file.getName() +
517   - std::string(": reported number of objects (") +
  541 + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
  542 + std::string("reported number of objects (") +
518 543 QUtil::int_to_string(size) +
519 544 ") inconsistent with actual number of objects (" +
520 545 QUtil::int_to_string(max_obj + 1) + ")"));
... ... @@ -542,7 +567,8 @@ QPDF::read_xrefTable(off_t xref_offset)
542 567 if (! m1)
543 568 {
544 569 QTC::TC("qpdf", "QPDF invalid xref");
545   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  570 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  571 + "xref table", this->file.getLastOffset(),
546 572 "xref syntax invalid");
547 573 }
548 574 int obj = atoi(m1.getMatch(1).c_str());
... ... @@ -563,7 +589,8 @@ QPDF::read_xrefTable(off_t xref_offset)
563 589 {
564 590 QTC::TC("qpdf", "QPDF invalid xref entry");
565 591 throw QPDFExc(
566   - this->file.getName(), this->file.getLastOffset(),
  592 + qpdf_e_damaged_pdf, this->file.getName(),
  593 + "xref table", this->file.getLastOffset(),
567 594 "invalid xref entry (obj=" +
568 595 QUtil::int_to_string(i) + ")");
569 596 }
... ... @@ -595,11 +622,13 @@ QPDF::read_xrefTable(off_t xref_offset)
595 622 }
596 623  
597 624 // Set offset to previous xref table if any
598   - QPDFObjectHandle cur_trailer = readObject(&this->file, 0, 0, false);
  625 + QPDFObjectHandle cur_trailer =
  626 + readObject(&this->file, "trailer", 0, 0, false);
599 627 if (! cur_trailer.isDictionary())
600 628 {
601 629 QTC::TC("qpdf", "QPDF missing trailer");
602   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  630 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  631 + "", this->file.getLastOffset(),
603 632 "expected trailer dictionary");
604 633 }
605 634  
... ... @@ -610,13 +639,15 @@ QPDF::read_xrefTable(off_t xref_offset)
610 639 if (! this->trailer.hasKey("/Size"))
611 640 {
612 641 QTC::TC("qpdf", "QPDF trailer lacks size");
613   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  642 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  643 + "trailer", this->file.getLastOffset(),
614 644 "trailer dictionary lacks /Size key");
615 645 }
616 646 if (! this->trailer.getKey("/Size").isInteger())
617 647 {
618 648 QTC::TC("qpdf", "QPDF trailer size not integer");
619   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  649 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  650 + "trailer", this->file.getLastOffset(),
620 651 "/Size key in trailer dictionary is not "
621 652 "an integer");
622 653 }
... ... @@ -640,7 +671,8 @@ QPDF::read_xrefTable(off_t xref_offset)
640 671 }
641 672 else
642 673 {
643   - throw QPDFExc(this->file.getName(), xref_offset,
  674 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  675 + "xref stream", xref_offset,
644 676 "invalid /XRefStm");
645 677 }
646 678 }
... ... @@ -659,7 +691,8 @@ QPDF::read_xrefTable(off_t xref_offset)
659 691 if (! cur_trailer.getKey("/Prev").isInteger())
660 692 {
661 693 QTC::TC("qpdf", "QPDF trailer prev not integer");
662   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  694 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  695 + "trailer", this->file.getLastOffset(),
663 696 "/Prev key in trailer dictionary is not "
664 697 "an integer");
665 698 }
... ... @@ -685,7 +718,8 @@ QPDF::read_xrefStream(off_t xref_offset)
685 718 QPDFObjectHandle xref_obj;
686 719 try
687 720 {
688   - xref_obj = readObjectAtOffset(xref_offset, 0, 0, xobj, xgen);
  721 + xref_obj = readObjectAtOffset(
  722 + false, xref_offset, "xref stream", 0, 0, xobj, xgen);
689 723 }
690 724 catch (QPDFExc& e)
691 725 {
... ... @@ -705,7 +739,8 @@ QPDF::read_xrefStream(off_t xref_offset)
705 739 if (! found)
706 740 {
707 741 QTC::TC("qpdf", "QPDF can't find xref");
708   - throw QPDFExc(this->file.getName(), xref_offset, "xref not found");
  742 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  743 + "", xref_offset, "xref not found");
709 744 }
710 745  
711 746 return xref_offset;
... ... @@ -725,7 +760,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj)
725 760 dict.getKey("/Size").isInteger() &&
726 761 (Index_obj.isArray() || Index_obj.isNull())))
727 762 {
728   - throw QPDFExc(this->file.getName(), xref_offset,
  763 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  764 + "xref stream", xref_offset,
729 765 "Cross-reference stream does not have"
730 766 " proper /W and /Index keys");
731 767 }
... ... @@ -735,7 +771,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj)
735 771 int n_index = Index_obj.getArrayNItems();
736 772 if ((n_index % 2) || (n_index < 2))
737 773 {
738   - throw QPDFExc(this->file.getName(), xref_offset,
  774 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  775 + "xref stream", xref_offset,
739 776 "Cross-reference stream's /Index has an"
740 777 " invalid number of values");
741 778 }
... ... @@ -747,7 +784,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj)
747 784 }
748 785 else
749 786 {
750   - throw QPDFExc(this->file.getName(), xref_offset,
  787 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  788 + "xref stream", xref_offset,
751 789 "Cross-reference stream's /Index's item " +
752 790 QUtil::int_to_string(i) +
753 791 " is not an integer");
... ... @@ -785,7 +823,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj)
785 823  
786 824 if (expected_size != actual_size)
787 825 {
788   - QPDFExc x(this->file.getName(), xref_offset,
  826 + QPDFExc x(qpdf_e_damaged_pdf, this->file.getName(),
  827 + "xref stream", xref_offset,
789 828 "Cross-reference stream data has the wrong size;"
790 829 " expected = " + QUtil::int_to_string(expected_size) +
791 830 "; actual = " + QUtil::int_to_string(actual_size));
... ... @@ -866,7 +905,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj)
866 905 {
867 906 if (! dict.getKey("/Prev").isInteger())
868 907 {
869   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  908 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  909 + "xref stream", this->file.getLastOffset(),
870 910 "/Prev key in xref stream dictionary is not "
871 911 "an integer");
872 912 }
... ... @@ -935,7 +975,8 @@ QPDF::insertXrefEntry(int obj, int f0, int f1, int f2, bool overwrite)
935 975 break;
936 976  
937 977 default:
938   - throw QPDFExc(this->file.getName(), 0,
  978 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  979 + "xref stream", this->file.getLastOffset(),
939 980 "unknown xref stream entry type " +
940 981 QUtil::int_to_string(f0));
941 982 break;
... ... @@ -972,10 +1013,32 @@ QPDF::showXRefTable()
972 1013 }
973 1014 }
974 1015  
  1016 +void
  1017 +QPDF::setLastObjectDescription(std::string const& description,
  1018 + int objid, int generation)
  1019 +{
  1020 + this->last_object_description.clear();
  1021 + if (! description.empty())
  1022 + {
  1023 + this->last_object_description += description;
  1024 + if (objid > 0)
  1025 + {
  1026 + this->last_object_description += ": ";
  1027 + }
  1028 + }
  1029 + if (objid > 0)
  1030 + {
  1031 + this->last_object_description += "object " +
  1032 + QUtil::int_to_string(objid) + " " +
  1033 + QUtil::int_to_string(generation);
  1034 + }
  1035 +}
  1036 +
975 1037 QPDFObjectHandle
976   -QPDF::readObject(InputSource* input, int objid, int generation,
977   - bool in_object_stream)
  1038 +QPDF::readObject(InputSource* input, std::string const& description,
  1039 + int objid, int generation, bool in_object_stream)
978 1040 {
  1041 + setLastObjectDescription(description, objid, generation);
979 1042 off_t offset = input->tell();
980 1043 QPDFObjectHandle object = readObjectInternal(
981 1044 input, objid, generation, in_object_stream, false, false);
... ... @@ -1017,7 +1080,9 @@ QPDF::readObjectInternal(InputSource* input,
1017 1080 case QPDFTokenizer::tt_brace_close:
1018 1081 // Don't know what to do with these for now
1019 1082 QTC::TC("qpdf", "QPDF bad brace");
1020   - throw QPDFExc(input->getName(), input->getLastOffset(),
  1083 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1084 + this->last_object_description,
  1085 + input->getLastOffset(),
1021 1086 "unexpected brace token");
1022 1087 break;
1023 1088  
... ... @@ -1029,7 +1094,9 @@ QPDF::readObjectInternal(InputSource* input,
1029 1094 else
1030 1095 {
1031 1096 QTC::TC("qpdf", "QPDF bad array close");
1032   - throw QPDFExc(input->getName(), input->getLastOffset(),
  1097 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1098 + this->last_object_description,
  1099 + input->getLastOffset(),
1033 1100 "unexpected array close token");
1034 1101 }
1035 1102 break;
... ... @@ -1042,7 +1109,9 @@ QPDF::readObjectInternal(InputSource* input,
1042 1109 else
1043 1110 {
1044 1111 QTC::TC("qpdf", "QPDF bad dictionary close");
1045   - throw QPDFExc(input->getName(), input->getLastOffset(),
  1112 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1113 + this->last_object_description,
  1114 + input->getLastOffset(),
1046 1115 "unexpected dictionary close token");
1047 1116 }
1048 1117 break;
... ... @@ -1097,7 +1166,9 @@ QPDF::readObjectInternal(InputSource* input,
1097 1166 }
1098 1167 else
1099 1168 {
1100   - throw QPDFExc(input->getName(), input->getLastOffset(),
  1169 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1170 + this->last_object_description,
  1171 + input->getLastOffset(),
1101 1172 "unknown token while reading object (" +
1102 1173 value + ")");
1103 1174 }
... ... @@ -1116,7 +1187,9 @@ QPDF::readObjectInternal(InputSource* input,
1116 1187 break;
1117 1188  
1118 1189 default:
1119   - throw QPDFExc(input->getName(), input->getLastOffset(),
  1190 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1191 + this->last_object_description,
  1192 + input->getLastOffset(),
1120 1193 "unknown token type while reading object");
1121 1194 break;
1122 1195 }
... ... @@ -1153,7 +1226,8 @@ QPDF::readObjectInternal(InputSource* input,
1153 1226 {
1154 1227 QTC::TC("qpdf", "QPDF dictionary odd number of elements");
1155 1228 throw QPDFExc(
1156   - input->getName(), input->getLastOffset(),
  1229 + qpdf_e_damaged_pdf, input->getName(),
  1230 + this->last_object_description, input->getLastOffset(),
1157 1231 "dictionary ending here has an odd number of elements");
1158 1232 }
1159 1233 for (unsigned int i = 0; i < olist.size(); i += 2)
... ... @@ -1163,7 +1237,8 @@ QPDF::readObjectInternal(InputSource* input,
1163 1237 if (! key_obj.isName())
1164 1238 {
1165 1239 throw QPDFExc(
1166   - input->getName(), offset,
  1240 + qpdf_e_damaged_pdf,
  1241 + input->getName(), this->last_object_description, offset,
1167 1242 std::string("dictionary key not name (") +
1168 1243 key_obj.unparse() + ")");
1169 1244 }
... ... @@ -1209,7 +1284,8 @@ QPDF::readObjectInternal(InputSource* input,
1209 1284 if (dict.count("/Length") == 0)
1210 1285 {
1211 1286 QTC::TC("qpdf", "QPDF stream without length");
1212   - throw QPDFExc(input->getName(), offset,
  1287 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1288 + this->last_object_description, offset,
1213 1289 "stream dictionary lacks /Length key");
1214 1290 }
1215 1291  
... ... @@ -1217,7 +1293,8 @@ QPDF::readObjectInternal(InputSource* input,
1217 1293 if (! length_obj.isInteger())
1218 1294 {
1219 1295 QTC::TC("qpdf", "QPDF stream length not integer");
1220   - throw QPDFExc(input->getName(), offset,
  1296 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1297 + this->last_object_description, offset,
1221 1298 "/Length key in stream dictionary is not "
1222 1299 "an integer");
1223 1300 }
... ... @@ -1229,7 +1306,9 @@ QPDF::readObjectInternal(InputSource* input,
1229 1306 QPDFTokenizer::tt_word, "endstream")))
1230 1307 {
1231 1308 QTC::TC("qpdf", "QPDF missing endstream");
1232   - throw QPDFExc(input->getName(), input->getLastOffset(),
  1309 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1310 + this->last_object_description,
  1311 + input->getLastOffset(),
1233 1312 "expected endstream");
1234 1313 }
1235 1314 }
... ... @@ -1267,7 +1346,8 @@ QPDF::recoverStreamLength(InputSource* input,
1267 1346  
1268 1347 // Try to reconstruct stream length by looking for
1269 1348 // endstream(\r\n?|\n)endobj
1270   - warn(QPDFExc(input->getName(), stream_offset,
  1349 + warn(QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1350 + this->last_object_description, stream_offset,
1271 1351 "attempting to recover stream length"));
1272 1352  
1273 1353 input->seek(0, SEEK_END);
... ... @@ -1336,7 +1416,8 @@ QPDF::recoverStreamLength(InputSource* input,
1336 1416  
1337 1417 if (length == 0)
1338 1418 {
1339   - throw QPDFExc(input->getName(), stream_offset,
  1419 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1420 + this->last_object_description, stream_offset,
1340 1421 "unable to recover stream data");
1341 1422 }
1342 1423  
... ... @@ -1356,7 +1437,9 @@ QPDF::readToken(InputSource* input)
1356 1437 char ch;
1357 1438 if (input->read(&ch, 1) == 0)
1358 1439 {
1359   - throw QPDFExc(input->getName(), offset, "EOF while reading token");
  1440 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1441 + this->last_object_description, offset,
  1442 + "EOF while reading token");
1360 1443 }
1361 1444 else
1362 1445 {
... ... @@ -1376,7 +1459,9 @@ QPDF::readToken(InputSource* input)
1376 1459  
1377 1460 if (token.getType() == QPDFTokenizer::tt_bad)
1378 1461 {
1379   - throw QPDFExc(input->getName(), offset, token.getErrorMessage());
  1462 + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1463 + this->last_object_description, offset,
  1464 + token.getErrorMessage());
1380 1465 }
1381 1466  
1382 1467 input->setLastOffset(offset);
... ... @@ -1385,9 +1470,12 @@ QPDF::readToken(InputSource* input)
1385 1470 }
1386 1471  
1387 1472 QPDFObjectHandle
1388   -QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
  1473 +QPDF::readObjectAtOffset(bool try_recovery,
  1474 + off_t offset, std::string const& description,
  1475 + int exp_objid, int exp_generation,
1389 1476 int& objid, int& generation)
1390 1477 {
  1478 + setLastObjectDescription(description, exp_objid, exp_generation);
1391 1479 this->file.seek(offset, SEEK_SET);
1392 1480  
1393 1481 QPDFTokenizer::Token tobjid = readToken(&this->file);
... ... @@ -1407,7 +1495,9 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1407 1495 if (! (objidok && genok && objok))
1408 1496 {
1409 1497 QTC::TC("qpdf", "QPDF expected n n obj");
1410   - throw QPDFExc(this->file.getName(), offset, "expected n n obj");
  1498 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  1499 + this->last_object_description, offset,
  1500 + "expected n n obj");
1411 1501 }
1412 1502 objid = atoi(tobjid.getValue().c_str());
1413 1503 generation = atoi(tgen.getValue().c_str());
... ... @@ -1416,7 +1506,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1416 1506 (! ((objid == exp_objid) && (generation == exp_generation))))
1417 1507 {
1418 1508 QTC::TC("qpdf", "QPDF err wrong objid/generation");
1419   - throw QPDFExc(this->file.getName(), offset,
  1509 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  1510 + this->last_object_description, offset,
1420 1511 std::string("expected ") +
1421 1512 QUtil::int_to_string(exp_objid) + " " +
1422 1513 QUtil::int_to_string(exp_generation) + " obj");
... ... @@ -1424,7 +1515,7 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1424 1515 }
1425 1516 catch (QPDFExc& e)
1426 1517 {
1427   - if (exp_objid && this->attempt_recovery)
  1518 + if (exp_objid && try_recovery && this->attempt_recovery)
1428 1519 {
1429 1520 // Try again after reconstructing xref table
1430 1521 reconstruct_xref(e);
... ... @@ -1433,13 +1524,27 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1433 1524 (this->xref_table[og].getType() == 1))
1434 1525 {
1435 1526 off_t new_offset = this->xref_table[og].getOffset();
1436   - // Call readObjectAtOffset with 0 for exp_objid to
1437   - // avoid an infinite loop.
1438   - QPDFObjectHandle result =
1439   - readObjectAtOffset(new_offset, 0, 0, objid, generation);
  1527 + QPDFObjectHandle result = readObjectAtOffset(
  1528 + false, new_offset, description,
  1529 + exp_objid, exp_generation, objid, generation);
1440 1530 QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset");
1441 1531 return result;
1442 1532 }
  1533 + else
  1534 + {
  1535 + QTC::TC("qpdf", "QPDF object gone after xref reconstruction");
  1536 + warn(QPDFExc(
  1537 + qpdf_e_damaged_pdf, this->file.getName(),
  1538 + "", 0,
  1539 + std::string(
  1540 + "object " +
  1541 + QUtil::int_to_string(exp_objid) +
  1542 + " " +
  1543 + QUtil::int_to_string(exp_generation) +
  1544 + " not found in file after regenerating"
  1545 + " cross reference table")));
  1546 + return QPDFObjectHandle::newNull();
  1547 + }
1443 1548 }
1444 1549 else
1445 1550 {
... ... @@ -1448,13 +1553,14 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1448 1553 }
1449 1554  
1450 1555 QPDFObjectHandle oh = readObject(
1451   - &this->file, objid, generation, false);
  1556 + &this->file, description, objid, generation, false);
1452 1557  
1453 1558 if (! (readToken(&this->file) ==
1454 1559 QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj")))
1455 1560 {
1456 1561 QTC::TC("qpdf", "QPDF err expected endobj");
1457   - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(),
  1562 + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  1563 + this->last_object_description, this->file.getLastOffset(),
1458 1564 "expected endobj"));
1459 1565 }
1460 1566  
... ... @@ -1487,7 +1593,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1487 1593 }
1488 1594 else
1489 1595 {
1490   - throw QPDFExc(this->file.getName(), offset,
  1596 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  1597 + this->last_object_description, offset,
1491 1598 "EOF after endobj");
1492 1599 }
1493 1600 }
... ... @@ -1526,7 +1633,7 @@ QPDF::resolve(int objid, int generation)
1526 1633 int aobjid;
1527 1634 int ageneration;
1528 1635 QPDFObjectHandle oh =
1529   - readObjectAtOffset(offset, objid, generation,
  1636 + readObjectAtOffset(true, offset, "", objid, generation,
1530 1637 aobjid, ageneration);
1531 1638 }
1532 1639 break;
... ... @@ -1536,7 +1643,7 @@ QPDF::resolve(int objid, int generation)
1536 1643 break;
1537 1644  
1538 1645 default:
1539   - throw QPDFExc(this->file.getName(), 0,
  1646 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
1540 1647 "object " +
1541 1648 QUtil::int_to_string(objid) + "/" +
1542 1649 QUtil::int_to_string(generation) +
... ... @@ -1554,7 +1661,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1554 1661 QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0);
1555 1662 if (! obj_stream.isStream())
1556 1663 {
1557   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  1664 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  1665 + this->last_object_description,
  1666 + this->file.getLastOffset(),
1558 1667 "supposed object stream " +
1559 1668 QUtil::int_to_string(obj_stream_number) +
1560 1669 " is not a stream");
... ... @@ -1570,7 +1679,10 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1570 1679 if (! (dict.getKey("/Type").isName() &&
1571 1680 dict.getKey("/Type").getName() == "/ObjStm"))
1572 1681 {
1573   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  1682 + QTC::TC("qpdf", "QPDF ERR object stream with wrong type");
  1683 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  1684 + this->last_object_description,
  1685 + this->file.getLastOffset(),
1574 1686 "supposed object stream " +
1575 1687 QUtil::int_to_string(obj_stream_number) +
1576 1688 " has wrong type");
... ... @@ -1579,7 +1691,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1579 1691 if (! (dict.getKey("/N").isInteger() &&
1580 1692 dict.getKey("/First").isInteger()))
1581 1693 {
1582   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  1694 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  1695 + this->last_object_description,
  1696 + this->file.getLastOffset(),
1583 1697 "object stream " +
1584 1698 QUtil::int_to_string(obj_stream_number) +
1585 1699 " has incorrect keys");
... ... @@ -1602,7 +1716,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1602 1716 if (! ((tnum.getType() == QPDFTokenizer::tt_integer) &&
1603 1717 (toffset.getType() == QPDFTokenizer::tt_integer)))
1604 1718 {
1605   - throw QPDFExc(input.getName(), input.getLastOffset(),
  1719 + throw QPDFExc(qpdf_e_damaged_pdf, input.getName(),
  1720 + this->last_object_description, input.getLastOffset(),
1606 1721 "expected integer in object stream header");
1607 1722 }
1608 1723  
... ... @@ -1617,7 +1732,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1617 1732 int obj = (*iter).first;
1618 1733 int offset = (*iter).second;
1619 1734 input.seek(offset, SEEK_SET);
1620   - QPDFObjectHandle oh = readObject(&input, obj, 0, true);
  1735 + QPDFObjectHandle oh = readObject(&input, "", obj, 0, true);
1621 1736  
1622 1737 // Store in cache
1623 1738 ObjGen og(obj, 0);
... ... @@ -1830,17 +1945,25 @@ QPDF::pipeStreamData(int objid, int generation,
1830 1945 size_t len = this->file.read(buf, to_read);
1831 1946 if (len == 0)
1832 1947 {
1833   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  1948 + throw QPDFExc(qpdf_e_damaged_pdf,
  1949 + this->file.getName(),
  1950 + this->last_object_description,
  1951 + this->file.getLastOffset(),
1834 1952 "unexpected EOF reading stream data");
1835 1953 }
1836 1954 length -= len;
1837 1955 pipeline->write((unsigned char*)buf, len);
1838 1956 }
1839 1957 }
  1958 + catch (QPDFExc& e)
  1959 + {
  1960 + warn(e);
  1961 + }
1840 1962 catch (std::runtime_error& e)
1841 1963 {
1842 1964 QTC::TC("qpdf", "QPDF decoding error warning");
1843   - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(),
  1965 + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  1966 + "", this->file.getLastOffset(),
1844 1967 "error decoding stream data for object " +
1845 1968 QUtil::int_to_string(objid) + " " +
1846 1969 QUtil::int_to_string(generation) + ": " + e.what()));
... ... @@ -1896,6 +2019,9 @@ QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages,
1896 2019 }
1897 2020 else
1898 2021 {
1899   - throw QPDFExc(this->file.getName() + ": invalid Type in page tree");
  2022 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  2023 + this->last_object_description,
  2024 + this->file.getLastOffset(),
  2025 + + ": invalid Type in page tree");
1900 2026 }
1901 2027 }
... ...
libqpdf/QPDFExc.cc
1 1 #include <qpdf/QPDFExc.hh>
2 2 #include <qpdf/QUtil.hh>
3 3  
4   -QPDFExc::QPDFExc(std::string const& message) :
5   - std::runtime_error(message)
  4 +QPDFExc::QPDFExc(qpdf_error_code_e error_code,
  5 + std::string const& filename,
  6 + std::string const& object,
  7 + off_t offset,
  8 + std::string const& message) :
  9 + std::runtime_error(createWhat(filename, object, offset, message)),
  10 + error_code(error_code),
  11 + filename(filename),
  12 + object(object),
  13 + offset(offset),
  14 + message(message)
6 15 {
7 16 }
8 17  
9   -QPDFExc::QPDFExc(std::string const& filename, int offset,
10   - std::string const& message) :
11   - std::runtime_error(filename + ": offset " + QUtil::int_to_string(offset) +
12   - ": " + message)
  18 +QPDFExc::~QPDFExc() throw ()
13 19 {
14 20 }
15 21  
16   -QPDFExc::~QPDFExc() throw ()
  22 +std::string
  23 +QPDFExc::createWhat(std::string const& filename,
  24 + std::string const& object,
  25 + off_t offset,
  26 + std::string const& message)
17 27 {
  28 + std::string result;
  29 + if (! filename.empty())
  30 + {
  31 + result += filename;
  32 + }
  33 + if (! (object.empty() && offset == 0))
  34 + {
  35 + result += " (";
  36 + if (! object.empty())
  37 + {
  38 + result += object;
  39 + if (offset > 0)
  40 + {
  41 + result += ", ";
  42 + }
  43 + }
  44 + if (offset > 0)
  45 + {
  46 + result += "file position " + QUtil::int_to_string(offset);
  47 + }
  48 + result += ")";
  49 + }
  50 + if (! result.empty())
  51 + {
  52 + result += ": ";
  53 + }
  54 + result += message;
  55 + return result;
18 56 }
... ...
libqpdf/QPDFXRefEntry.cc
... ... @@ -16,7 +16,8 @@ QPDFXRefEntry::QPDFXRefEntry(int type, int field1, int field2) :
16 16 {
17 17 if ((type < 1) || (type > 2))
18 18 {
19   - throw QPDFExc("invalid xref type " + QUtil::int_to_string(type));
  19 + throw std::logic_error(
  20 + "invalid xref type " + QUtil::int_to_string(type));
20 21 }
21 22 }
22 23  
... ... @@ -31,7 +32,7 @@ QPDFXRefEntry::getOffset() const
31 32 {
32 33 if (this->type != 1)
33 34 {
34   - throw QPDFExc(
  35 + throw std::logic_error(
35 36 "getOffset called for xref entry of type != 1");
36 37 }
37 38 return this->field1;
... ... @@ -42,7 +43,7 @@ QPDFXRefEntry::getObjStreamNumber() const
42 43 {
43 44 if (this->type != 2)
44 45 {
45   - throw QPDFExc(
  46 + throw std::logic_error(
46 47 "getObjStreamNumber called for xref entry of type != 2");
47 48 }
48 49 return this->field1;
... ... @@ -53,7 +54,7 @@ QPDFXRefEntry::getObjStreamIndex() const
53 54 {
54 55 if (this->type != 2)
55 56 {
56   - throw QPDFExc(
  57 + throw std::logic_error(
57 58 "getObjStreamIndex called for xref entry of type != 2");
58 59 }
59 60 return this->field2;
... ...
libqpdf/QPDF_Stream.cc
... ... @@ -59,7 +59,7 @@ QPDF_Stream::getStreamData()
59 59 Pl_Buffer buf("stream data buffer");
60 60 if (! pipeStreamData(&buf, true, false, false))
61 61 {
62   - throw QPDFExc("getStreamData called on unfilterable stream");
  62 + throw std::logic_error("getStreamData called on unfilterable stream");
63 63 }
64 64 return buf.getBuffer();
65 65 }
... ... @@ -208,8 +208,9 @@ QPDF_Stream::filterable(std::vector&lt;std::string&gt;&amp; filters,
208 208 if (! filters_okay)
209 209 {
210 210 QTC::TC("qpdf", "QPDF_Stream invalid filter");
211   - throw QPDFExc(qpdf->getFilename(), this->offset,
212   - "invalid filter object type for this stream");
  211 + throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(),
  212 + "", this->offset,
  213 + "stream filter type is not name or array");
213 214 }
214 215  
215 216 // `filters' now contains a list of filters to be applied in
... ...
libqpdf/QPDF_encryption.cc
... ... @@ -337,14 +337,16 @@ QPDF::initializeEncryption()
337 337 (id_obj.getArrayNItems() == 2) &&
338 338 id_obj.getArrayItem(0).isString()))
339 339 {
340   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  340 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  341 + "trailer", this->file.getLastOffset(),
341 342 "invalid /ID in trailer dictionary");
342 343 }
343 344  
344 345 std::string id1 = id_obj.getArrayItem(0).getStringValue();
345 346 if (id1.length() != id_bytes)
346 347 {
347   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  348 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  349 + "trailer", this->file.getLastOffset(),
348 350 "first /ID string in trailer dictionary has "
349 351 "incorrect length");
350 352 }
... ... @@ -352,19 +354,23 @@ QPDF::initializeEncryption()
352 354 QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt");
353 355 if (! encryption_dict.isDictionary())
354 356 {
355   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  357 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  358 + this->last_object_description,
  359 + this->file.getLastOffset(),
356 360 "/Encrypt in trailer dictionary is not a dictionary");
357 361 }
358 362  
359 363 if (! (encryption_dict.getKey("/Filter").isName() &&
360 364 (encryption_dict.getKey("/Filter").getName() == "/Standard")))
361 365 {
362   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  366 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  367 + "encryption dictionary", this->file.getLastOffset(),
363 368 "unsupported encryption filter");
364 369 }
365 370 if (! encryption_dict.getKey("/SubFilter").isNull())
366 371 {
367   - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(),
  372 + warn(QPDFExc(qpdf_e_unsupported, this->file.getName(),
  373 + "encryption dictionary", this->file.getLastOffset(),
368 374 "file uses encryption SubFilters,"
369 375 " which qpdf does not support"));
370 376 }
... ... @@ -375,7 +381,8 @@ QPDF::initializeEncryption()
375 381 encryption_dict.getKey("/U").isString() &&
376 382 encryption_dict.getKey("/P").isInteger()))
377 383 {
378   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  384 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  385 + "encryption dictionary", this->file.getLastOffset(),
379 386 "some encryption dictionary parameters are missing "
380 387 "or the wrong type");
381 388 }
... ... @@ -389,7 +396,8 @@ QPDF::initializeEncryption()
389 396 if (! (((R == 2) || (R == 3) || (R == 4)) &&
390 397 ((V == 1) || (V == 2) || (V == 4))))
391 398 {
392   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  399 + throw QPDFExc(qpdf_e_unsupported, this->file.getName(),
  400 + "encryption dictionary", this->file.getLastOffset(),
393 401 "Unsupported /R or /V in encryption dictionary");
394 402 }
395 403  
... ... @@ -397,7 +405,8 @@ QPDF::initializeEncryption()
397 405  
398 406 if (! ((O.length() == key_bytes) && (U.length() == key_bytes)))
399 407 {
400   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  408 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  409 + "encryption dictionary", this->file.getLastOffset(),
401 410 "incorrect length for /O and/or /P in "
402 411 "encryption dictionary");
403 412 }
... ... @@ -408,7 +417,8 @@ QPDF::initializeEncryption()
408 417 Length = encryption_dict.getKey("/Length").getIntValue();
409 418 if ((Length % 8) || (Length < 40) || (Length > 128))
410 419 {
411   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  420 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  421 + "encryption dictionary", this->file.getLastOffset(),
412 422 "invalid /Length value in encryption dictionary");
413 423 }
414 424 }
... ... @@ -471,7 +481,8 @@ QPDF::initializeEncryption()
471 481 }
472 482 if (this->cf_file != this->cf_stream)
473 483 {
474   - throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  484 + throw QPDFExc(qpdf_e_unsupported, this->file.getName(),
  485 + "encryption dictionary", this->file.getLastOffset(),
475 486 "This document has embedded files that are"
476 487 " encrypted differently from the rest of the file."
477 488 " qpdf does not presently support this due to"
... ... @@ -492,7 +503,8 @@ QPDF::initializeEncryption()
492 503 }
493 504 else
494 505 {
495   - throw QPDFExc(this->file.getName() + ": invalid password");
  506 + throw QPDFExc(qpdf_e_password, this->file.getName(),
  507 + "", 0, "invalid password");
496 508 }
497 509  
498 510 this->encryption_key = compute_encryption_key(this->user_password, data);
... ... @@ -542,7 +554,9 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation)
542 554 break;
543 555  
544 556 default:
545   - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(),
  557 + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  558 + this->last_object_description,
  559 + this->file.getLastOffset(),
546 560 "unknown encryption filter for strings"
547 561 " (check /StrF in /Encrypt dictionary);"
548 562 " strings may be decrypted improperly"));
... ... @@ -554,28 +568,47 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation)
554 568 }
555 569  
556 570 std::string key = getKeyForObject(objid, generation, use_aes);
557   - if (use_aes)
  571 + try
558 572 {
559   - QTC::TC("qpdf", "QPDF_encryption aes decode string");
560   - assert(key.length() == Pl_AES_PDF::key_size);
561   - Pl_Buffer bufpl("decrypted string");
562   - Pl_AES_PDF pl("aes decrypt string", &bufpl, false,
563   - (unsigned char const*)key.c_str());
564   - pl.write((unsigned char*)str.c_str(), str.length());
565   - pl.finish();
566   - Buffer* buf = bufpl.getBuffer();
567   - str = std::string((char*)buf->getBuffer(), (size_t)buf->getSize());
568   - delete buf;
  573 + if (use_aes)
  574 + {
  575 + QTC::TC("qpdf", "QPDF_encryption aes decode string");
  576 + assert(key.length() == Pl_AES_PDF::key_size);
  577 + Pl_Buffer bufpl("decrypted string");
  578 + Pl_AES_PDF pl("aes decrypt string", &bufpl, false,
  579 + (unsigned char const*)key.c_str());
  580 + pl.write((unsigned char*)str.c_str(), str.length());
  581 + pl.finish();
  582 + PointerHolder<Buffer> buf = bufpl.getBuffer();
  583 + str = std::string((char*)buf.getPointer()->getBuffer(),
  584 + (size_t)buf.getPointer()->getSize());
  585 + }
  586 + else
  587 + {
  588 + QTC::TC("qpdf", "QPDF_encryption rc4 decode string");
  589 + unsigned int vlen = str.length();
  590 + // Using PointerHolder will cause a new char[] to be deleted
  591 + // with delete instead of delete [], but it's okay since the
  592 + // array is of a fundamental type, so there is no destructor
  593 + // to be called. Using PointerHolder guarantees that tmp will
  594 + // be freed even if rc4.process throws an exception.
  595 + PointerHolder<char> tmp = QUtil::copy_string(str);
  596 + RC4 rc4((unsigned char const*)key.c_str(), key.length());
  597 + rc4.process((unsigned char*)tmp.getPointer(), vlen);
  598 + str = std::string(tmp.getPointer(), vlen);
  599 + }
569 600 }
570   - else
  601 + catch (QPDFExc& e)
  602 + {
  603 + throw;
  604 + }
  605 + catch (std::runtime_error& e)
571 606 {
572   - QTC::TC("qpdf", "QPDF_encryption rc4 decode string");
573   - unsigned int vlen = str.length();
574   - char* tmp = QUtil::copy_string(str);
575   - RC4 rc4((unsigned char const*)key.c_str(), key.length());
576   - rc4.process((unsigned char*)tmp, vlen);
577   - str = std::string(tmp, vlen);
578   - delete [] tmp;
  607 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  608 + this->last_object_description, this->file.getLastOffset(),
  609 + "error decrypting string for object " +
  610 + QUtil::int_to_string(objid) + " " +
  611 + QUtil::int_to_string(generation) + ": " + e.what());
579 612 }
580 613 }
581 614  
... ... @@ -645,7 +678,9 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
645 678  
646 679 default:
647 680 // filter local to this stream.
648   - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(),
  681 + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  682 + this->last_object_description,
  683 + this->file.getLastOffset(),
649 684 "unknown encryption filter for streams"
650 685 " (check " + method_source + ");"
651 686 " streams may be decrypted improperly"));
... ...
libqpdf/QPDF_linearization.cc
... ... @@ -175,7 +175,8 @@ QPDF::readLinearizationData()
175 175  
176 176 if (! isLinearized())
177 177 {
178   - throw QPDFExc(this->file.getName() + " is not linearized");
  178 + throw std::logic_error("called readLinearizationData for file"
  179 + " that is not linearized");
179 180 }
180 181  
181 182 // /L is read and stored in linp by isLinearized()
... ... @@ -193,7 +194,10 @@ QPDF::readLinearizationData()
193 194 T.isInteger() &&
194 195 (P.isInteger() || P.isNull())))
195 196 {
196   - throw QPDFExc("some keys in linearization dictionary are of "
  197 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  198 + "linearization dictionary",
  199 + this->file.getLastOffset(),
  200 + "some keys in linearization dictionary are of "
197 201 "the wrong type");
198 202 }
199 203  
... ... @@ -201,7 +205,10 @@ QPDF::readLinearizationData()
201 205 unsigned int n_H_items = H.getArrayNItems();
202 206 if (! ((n_H_items == 2) || (n_H_items == 4)))
203 207 {
204   - throw QPDFExc("H has the wrong number of items");
  208 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  209 + "linearization dictionary",
  210 + this->file.getLastOffset(),
  211 + "H has the wrong number of items");
205 212 }
206 213  
207 214 std::vector<int> H_items;
... ... @@ -214,7 +221,10 @@ QPDF::readLinearizationData()
214 221 }
215 222 else
216 223 {
217   - throw QPDFExc("some H items are of the wrong type");
  224 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  225 + "linearization dictionary",
  226 + this->file.getLastOffset(),
  227 + "some H items are of the wrong type");
218 228 }
219 229 }
220 230  
... ... @@ -301,13 +311,17 @@ QPDF::readHintStream(Pipeline&amp; pl, off_t offset, size_t length)
301 311 {
302 312 int obj;
303 313 int gen;
304   - QPDFObjectHandle H = readObjectAtOffset(offset, 0, 0, obj, gen);
  314 + QPDFObjectHandle H = readObjectAtOffset(
  315 + false, offset, "linearization hint stream", 0, 0, obj, gen);
305 316 ObjCache& oc = this->obj_cache[ObjGen(obj, gen)];
306 317 off_t min_end_offset = oc.end_before_space;
307 318 off_t max_end_offset = oc.end_after_space;
308 319 if (! H.isStream())
309 320 {
310   - throw QPDFExc("hint table is not a stream");
  321 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  322 + "linearization dictionary",
  323 + this->file.getLastOffset(),
  324 + "hint table is not a stream");
311 325 }
312 326  
313 327 QPDFObjectHandle Hdict = H.getDict();
... ... @@ -340,7 +354,10 @@ QPDF::readHintStream(Pipeline&amp; pl, off_t offset, size_t length)
340 354 std::cout << "expected = " << computed_end
341 355 << "; actual = " << min_end_offset << ".."
342 356 << max_end_offset << std::endl;
343   - throw QPDFExc("hint table length mismatch");
  357 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  358 + "linearization dictionary",
  359 + this->file.getLastOffset(),
  360 + "hint table length mismatch");
344 361 }
345 362 H.pipeStreamData(&pl, true, false, false);
346 363 return Hdict;
... ... @@ -651,8 +668,7 @@ QPDF::getLinearizationOffset(ObjGen const&amp; og)
651 668 break;
652 669  
653 670 default:
654   - throw QPDFExc(
655   - this->file.getName(), 0,
  671 + throw std::logic_error(
656 672 "getLinearizationOffset called for xref entry not of type 1 or 2");
657 673 break;
658 674 }
... ...
libqpdf/QPDF_optimization.cc
... ... @@ -233,9 +233,12 @@ QPDF::optimizePagesTree(
233 233 {
234 234 if (! allow_changes)
235 235 {
236   - throw QPDFExc(this->file.getName() +
237   - ": optimize detected an "
238   - "inheritable resource");
  236 + throw QPDFExc(qpdf_e_internal, this->file.getName(),
  237 + this->last_object_description,
  238 + this->file.getLastOffset(),
  239 + "optimize detected an "
  240 + "inheritable resource when called "
  241 + "in no-change mode");
239 242 }
240 243  
241 244 // This is an inheritable resource
... ... @@ -338,7 +341,10 @@ QPDF::optimizePagesTree(
338 341 }
339 342 else
340 343 {
341   - throw QPDFExc(this->file.getName() + ": invalid Type in page tree");
  344 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
  345 + this->last_object_description,
  346 + this->file.getLastOffset(),
  347 + "invalid Type in page tree");
342 348 }
343 349 }
344 350  
... ...
qpdf/qpdf.cc
... ... @@ -2,6 +2,7 @@
2 2 #include <string.h>
3 3 #include <stdlib.h>
4 4 #include <fcntl.h>
  5 +#include <stdio.h>
5 6  
6 7 #include <qpdf/QUtil.hh>
7 8 #include <qpdf/QTC.hh>
... ... @@ -533,6 +534,7 @@ parse_encrypt_options(
533 534 int main(int argc, char* argv[])
534 535 {
535 536 whoami = QUtil::getWhoami(argv[0]);
  537 + setlinebuf(stdout);
536 538  
537 539 // For libtool's sake....
538 540 if (strncmp(whoami, "lt-", 3) == 0)
... ... @@ -892,7 +894,15 @@ int main(int argc, char* argv[])
892 894 }
893 895 if (show_linearization)
894 896 {
895   - pdf.showLinearizationData();
  897 + if (pdf.isLinearized())
  898 + {
  899 + pdf.showLinearizationData();
  900 + }
  901 + else
  902 + {
  903 + std::cout << infilename << " is not linearized"
  904 + << std::endl;
  905 + }
896 906 }
897 907 if (show_xref)
898 908 {
... ...
qpdf/qpdf.testcov
... ... @@ -171,3 +171,5 @@ QPDFWriter forced version disabled encryption 0
171 171 qpdf-c called qpdf_set_r4_encryption_parameters 0
172 172 qpdf-c called qpdf_set_static_aes_IV 0
173 173 QPDF_encryption stream crypt filter 0
  174 +QPDF ERR object stream with wrong type 0
  175 +QPDF object gone after xref reconstruction 0
... ...
qpdf/qtest/qpdf.test
... ... @@ -191,6 +191,8 @@ my @badfiles = (&quot;not a PDF file&quot;, # 1
191 191 "unknown stream /Filter", # 31
192 192 "obj/gen mismatch", # 32
193 193 "invalid stream /Filter and xref", # 33
  194 + "obj/gen in wrong place", # 34
  195 + "object stream of wrong type", # 35
194 196 );
195 197  
196 198 $n_tests += @badfiles + 5;
... ... @@ -249,7 +251,7 @@ $n_tests += @badfiles + 8;
249 251 # though in some cases it may. Acrobat Reader would not be able to
250 252 # recover any of these files any better.
251 253 my %recover_failures = ();
252   -for (1, 7, 13..21, 24..27, 29..30, 33)
  254 +for (1, 7, 13..21, 24, 29..30, 33, 35)
253 255 {
254 256 $recover_failures{$_} = 1;
255 257 }
... ...
qpdf/qtest/qpdf/append-page-content-damaged-c-check.out
1   -WARNING: append-page-content-damaged.pdf: offset 0: file is damaged
  1 +WARNING: append-page-content-damaged.pdf: file is damaged
2 2 WARNING: append-page-content-damaged.pdf: can't find startxref
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 version: 1.3
5 5 linearized: 0
6 6 encrypted: 0
7   -warning: append-page-content-damaged.pdf: offset 0: file is damaged
  7 +warning: append-page-content-damaged.pdf: file is damaged
8 8 warning: append-page-content-damaged.pdf: can't find startxref
9 9 warning: Attempting to reconstruct cross-reference table
... ...
qpdf/qtest/qpdf/append-page-content-damaged-check.out
1   -WARNING: append-page-content-damaged.pdf: offset 0: file is damaged
  1 +WARNING: append-page-content-damaged.pdf: file is damaged
2 2 WARNING: append-page-content-damaged.pdf: can't find startxref
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 checking append-page-content-damaged.pdf
... ...
qpdf/qtest/qpdf/append-page-content-damaged.out
1   -WARNING: append-page-content-damaged.pdf: offset 0: file is damaged
  1 +WARNING: append-page-content-damaged.pdf: file is damaged
2 2 WARNING: append-page-content-damaged.pdf: can't find startxref
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 qpdf: operation succeeded with warnings; resulting file may have some problems
... ...
qpdf/qtest/qpdf/bad1-recover.out
1   -bad1.pdf: offset 0: not a PDF file
  1 +bad1.pdf: not a PDF file
... ...
qpdf/qtest/qpdf/bad1.out
1   -bad1.pdf: offset 0: not a PDF file
  1 +bad1.pdf: not a PDF file
... ...
qpdf/qtest/qpdf/bad10-recover.out
1   -WARNING: bad10.pdf: offset 0: file is damaged
2   -WARNING: bad10.pdf: offset 712: /Size key in trailer dictionary is not an integer
  1 +WARNING: bad10.pdf: file is damaged
  2 +WARNING: bad10.pdf (trailer, file position 712): /Size key in trailer dictionary is not an integer
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 /QTest is implicit
5 5 /QTest is direct
... ...
qpdf/qtest/qpdf/bad10.out
1   -bad10.pdf: offset 712: /Size key in trailer dictionary is not an integer
  1 +bad10.pdf (trailer, file position 712): /Size key in trailer dictionary is not an integer
... ...
qpdf/qtest/qpdf/bad11-recover.out
1   -WARNING: bad11.pdf: offset 0: file is damaged
2   -WARNING: bad11.pdf: offset 905: /Prev key in trailer dictionary is not an integer
  1 +WARNING: bad11.pdf: file is damaged
  2 +WARNING: bad11.pdf (trailer, file position 905): /Prev key in trailer dictionary is not an integer
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 /QTest is implicit
5 5 /QTest is direct
... ...
qpdf/qtest/qpdf/bad11.out
1   -bad11.pdf: offset 905: /Prev key in trailer dictionary is not an integer
  1 +bad11.pdf (trailer, file position 905): /Prev key in trailer dictionary is not an integer
... ...
qpdf/qtest/qpdf/bad13-recover.out
1   -WARNING: bad13.pdf: offset 0: file is damaged
2   -WARNING: bad13.pdf: offset 753: unexpected brace token
  1 +WARNING: bad13.pdf: file is damaged
  2 +WARNING: bad13.pdf (trailer, file position 753): unexpected brace token
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad13.pdf: offset 753: unexpected brace token
  4 +bad13.pdf (trailer, file position 753): unexpected brace token
... ...
qpdf/qtest/qpdf/bad13.out
1   -bad13.pdf: offset 753: unexpected brace token
  1 +bad13.pdf (trailer, file position 753): unexpected brace token
... ...
qpdf/qtest/qpdf/bad14-recover.out
1   -WARNING: bad14.pdf: offset 0: file is damaged
2   -WARNING: bad14.pdf: offset 753: unexpected brace token
  1 +WARNING: bad14.pdf: file is damaged
  2 +WARNING: bad14.pdf (trailer, file position 753): unexpected brace token
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad14.pdf: offset 753: unexpected brace token
  4 +bad14.pdf (trailer, file position 753): unexpected brace token
... ...
qpdf/qtest/qpdf/bad14.out
1   -bad14.pdf: offset 753: unexpected brace token
  1 +bad14.pdf (trailer, file position 753): unexpected brace token
... ...
qpdf/qtest/qpdf/bad15-recover.out
1   -WARNING: bad15.pdf: offset 0: file is damaged
2   -WARNING: bad15.pdf: offset 753: unexpected array close token
  1 +WARNING: bad15.pdf: file is damaged
  2 +WARNING: bad15.pdf (trailer, file position 753): unexpected array close token
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad15.pdf: offset 753: unexpected array close token
  4 +bad15.pdf (trailer, file position 753): unexpected array close token
... ...
qpdf/qtest/qpdf/bad15.out
1   -bad15.pdf: offset 753: unexpected array close token
  1 +bad15.pdf (trailer, file position 753): unexpected array close token
... ...
qpdf/qtest/qpdf/bad16-recover.out
1   -WARNING: bad16.pdf: offset 0: file is damaged
2   -WARNING: bad16.pdf: offset 753: unexpected dictionary close token
  1 +WARNING: bad16.pdf: file is damaged
  2 +WARNING: bad16.pdf (trailer, file position 753): unexpected dictionary close token
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad16.pdf: offset 753: unexpected dictionary close token
  4 +bad16.pdf (trailer, file position 753): unexpected dictionary close token
... ...
qpdf/qtest/qpdf/bad16.out
1   -bad16.pdf: offset 753: unexpected dictionary close token
  1 +bad16.pdf (trailer, file position 753): unexpected dictionary close token
... ...
qpdf/qtest/qpdf/bad17-recover.out
1   -WARNING: bad17.pdf: offset 0: file is damaged
2   -WARNING: bad17.pdf: offset 753: dictionary ending here has an odd number of elements
  1 +WARNING: bad17.pdf: file is damaged
  2 +WARNING: bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad17.pdf: offset 753: dictionary ending here has an odd number of elements
  4 +bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements
... ...
qpdf/qtest/qpdf/bad17.out
1   -bad17.pdf: offset 753: dictionary ending here has an odd number of elements
  1 +bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements
... ...
qpdf/qtest/qpdf/bad18-recover.out
1   -WARNING: bad18.pdf: offset 0: file is damaged
2   -WARNING: bad18.pdf: offset 753: unexpected )
  1 +WARNING: bad18.pdf: file is damaged
  2 +WARNING: bad18.pdf (trailer, file position 753): unexpected )
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad18.pdf: offset 753: unexpected )
  4 +bad18.pdf (trailer, file position 753): unexpected )
... ...
qpdf/qtest/qpdf/bad18.out
1   -bad18.pdf: offset 753: unexpected )
  1 +bad18.pdf (trailer, file position 753): unexpected )
... ...
qpdf/qtest/qpdf/bad19-recover.out
1   -WARNING: bad19.pdf: offset 0: file is damaged
2   -WARNING: bad19.pdf: offset 753: unexpected >
  1 +WARNING: bad19.pdf: file is damaged
  2 +WARNING: bad19.pdf (trailer, file position 753): unexpected >
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad19.pdf: offset 753: unexpected >
  4 +bad19.pdf (trailer, file position 753): unexpected >
... ...
qpdf/qtest/qpdf/bad19.out
1   -bad19.pdf: offset 753: unexpected >
  1 +bad19.pdf (trailer, file position 753): unexpected >
... ...
qpdf/qtest/qpdf/bad2-recover.out
1   -WARNING: bad2.pdf: offset 0: file is damaged
  1 +WARNING: bad2.pdf: file is damaged
2 2 WARNING: bad2.pdf: can't find startxref
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 /QTest is implicit
... ...
qpdf/qtest/qpdf/bad20-recover.out
1   -WARNING: bad20.pdf: offset 0: file is damaged
2   -WARNING: bad20.pdf: offset 753: invalid character (q) in hexstring
  1 +WARNING: bad20.pdf: file is damaged
  2 +WARNING: bad20.pdf (trailer, file position 753): invalid character (q) in hexstring
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad20.pdf: offset 753: invalid character (q) in hexstring
  4 +bad20.pdf (trailer, file position 753): invalid character (q) in hexstring
... ...
qpdf/qtest/qpdf/bad20.out
1   -bad20.pdf: offset 753: invalid character (q) in hexstring
  1 +bad20.pdf (trailer, file position 753): invalid character (q) in hexstring
... ...
qpdf/qtest/qpdf/bad21-recover.out
1   -WARNING: bad21.pdf: offset 0: file is damaged
2   -WARNING: bad21.pdf: offset 742: invalid name token
  1 +WARNING: bad21.pdf: file is damaged
  2 +WARNING: bad21.pdf (trailer, file position 742): invalid name token
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad21.pdf: offset 742: invalid name token
  4 +bad21.pdf (trailer, file position 742): invalid name token
... ...
qpdf/qtest/qpdf/bad21.out
1   -bad21.pdf: offset 742: invalid name token
  1 +bad21.pdf (trailer, file position 742): invalid name token
... ...
qpdf/qtest/qpdf/bad22-recover.out
1   -WARNING: bad22.pdf: offset 341: attempting to recover stream length
  1 +WARNING: bad22.pdf (object 4 0, file position 341): attempting to recover stream length
2 2 /QTest is indirect
3 3 /QTest is a stream. Dictionary: << /Qength 44 >>
4 4 Raw stream data:
... ...
qpdf/qtest/qpdf/bad22.out
1   -bad22.pdf: offset 317: stream dictionary lacks /Length key
  1 +bad22.pdf (object 4 0, file position 317): stream dictionary lacks /Length key
... ...
qpdf/qtest/qpdf/bad23-recover.out
1   -WARNING: bad23.pdf: offset 341: attempting to recover stream length
  1 +WARNING: bad23.pdf (object 4 0, file position 341): attempting to recover stream length
2 2 /QTest is indirect
3 3 /QTest is a stream. Dictionary: << /Length () >>
4 4 Raw stream data:
... ...
qpdf/qtest/qpdf/bad23.out
1   -bad23.pdf: offset 317: /Length key in stream dictionary is not an integer
  1 +bad23.pdf (object 4 0, file position 317): /Length key in stream dictionary is not an integer
... ...
qpdf/qtest/qpdf/bad24-recover.out
1   -WARNING: bad24.pdf: offset 341: attempting to recover stream length
2   -bad24.pdf: offset 341: unable to recover stream data
  1 +WARNING: bad24.pdf (object 4 0, file position 341): attempting to recover stream length
  2 +bad24.pdf (object 4 0, file position 341): unable to recover stream data
... ...
qpdf/qtest/qpdf/bad24.out
1   -bad24.pdf: offset 385: expected endstream
  1 +bad24.pdf (object 4 0, file position 385): expected endstream
... ...
qpdf/qtest/qpdf/bad25-recover.out
1   -WARNING: bad25.pdf: offset 0: file is damaged
2   -WARNING: bad25.pdf: offset 307: expected n n obj
  1 +WARNING: bad25.pdf: file is damaged
  2 +WARNING: bad25.pdf (object 4 0, file position 307): expected n n obj
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad25.pdf: offset 307: expected n n obj
  4 +WARNING: bad25.pdf: object 4 0 not found in file after regenerating cross reference table
  5 +/QTest is implicit
  6 +/QTest is indirect
  7 +/QTest is null
  8 +unparse: 4 0 R
  9 +unparseResolved: null
  10 +test 1 done
... ...
qpdf/qtest/qpdf/bad25.out
1   -bad25.pdf: offset 307: expected n n obj
  1 +bad25.pdf (object 4 0, file position 307): expected n n obj
... ...
qpdf/qtest/qpdf/bad26-recover.out
1   -WARNING: bad26.pdf: offset 0: file is damaged
2   -WARNING: bad26.pdf: offset 307: expected n n obj
  1 +WARNING: bad26.pdf: file is damaged
  2 +WARNING: bad26.pdf (object 4 0, file position 307): expected n n obj
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad26.pdf: offset 307: expected n n obj
  4 +WARNING: bad26.pdf: object 4 0 not found in file after regenerating cross reference table
  5 +/QTest is implicit
  6 +/QTest is indirect
  7 +/QTest is null
  8 +unparse: 4 0 R
  9 +unparseResolved: null
  10 +test 1 done
... ...
qpdf/qtest/qpdf/bad26.out
1   -bad26.pdf: offset 307: expected n n obj
  1 +bad26.pdf (object 4 0, file position 307): expected n n obj
... ...
qpdf/qtest/qpdf/bad27-recover.out
1   -WARNING: bad27.pdf: offset 0: file is damaged
2   -WARNING: bad27.pdf: offset 307: expected n n obj
  1 +WARNING: bad27.pdf: file is damaged
  2 +WARNING: bad27.pdf (object 4 0, file position 307): expected n n obj
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad27.pdf: offset 307: expected n n obj
  4 +WARNING: bad27.pdf: object 4 0 not found in file after regenerating cross reference table
  5 +/QTest is implicit
  6 +/QTest is indirect
  7 +/QTest is null
  8 +unparse: 4 0 R
  9 +unparseResolved: null
  10 +test 1 done
... ...
qpdf/qtest/qpdf/bad27.out
1   -bad27.pdf: offset 307: expected n n obj
  1 +bad27.pdf (object 4 0, file position 307): expected n n obj
... ...
qpdf/qtest/qpdf/bad28-recover.out
1   -WARNING: bad28.pdf: offset 395: expected endobj
  1 +WARNING: bad28.pdf (object 4 0, file position 395): expected endobj
2 2 /QTest is indirect
3 3 /QTest is a stream. Dictionary: << /Length 44 >>
4 4 Raw stream data:
... ...
qpdf/qtest/qpdf/bad28.out
1   -WARNING: bad28.pdf: offset 395: expected endobj
  1 +WARNING: bad28.pdf (object 4 0, file position 395): expected endobj
2 2 /QTest is indirect
3 3 /QTest is a stream. Dictionary: << /Length 44 >>
4 4 Raw stream data:
... ...
qpdf/qtest/qpdf/bad29-recover.out
1   -WARNING: bad29.pdf: offset 0: file is damaged
2   -WARNING: bad29.pdf: offset 742: null character not allowed in name token
  1 +WARNING: bad29.pdf: file is damaged
  2 +WARNING: bad29.pdf (trailer, file position 742): null character not allowed in name token
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -bad29.pdf: offset 742: null character not allowed in name token
  4 +bad29.pdf (trailer, file position 742): null character not allowed in name token
... ...
qpdf/qtest/qpdf/bad29.out
1   -bad29.pdf: offset 742: null character not allowed in name token
  1 +bad29.pdf (trailer, file position 742): null character not allowed in name token
... ...
qpdf/qtest/qpdf/bad3-recover.out
1   -WARNING: bad3.pdf: offset 0: file is damaged
2   -WARNING: bad3.pdf: offset 542: xref not found
  1 +WARNING: bad3.pdf: file is damaged
  2 +WARNING: bad3.pdf (file position 542): xref not found
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 /QTest is implicit
5 5 /QTest is direct
... ...
qpdf/qtest/qpdf/bad3.out
1   -bad3.pdf: offset 542: xref not found
  1 +bad3.pdf (file position 542): xref not found
... ...
qpdf/qtest/qpdf/bad30-recover.out
No preview for this file type
qpdf/qtest/qpdf/bad30.out
No preview for this file type
qpdf/qtest/qpdf/bad32-recover.out
1   -WARNING: bad32.pdf: offset 0: file is damaged
2   -WARNING: bad32.pdf: offset 307: expected 4 0 obj
  1 +WARNING: bad32.pdf: file is damaged
  2 +WARNING: bad32.pdf (object 4 0, file position 307): expected 4 0 obj
3 3 WARNING: Attempting to reconstruct cross-reference table
  4 +WARNING: bad32.pdf: object 4 0 not found in file after regenerating cross reference table
4 5 /QTest is implicit
5 6 /QTest is indirect
6 7 /QTest is null
... ...
qpdf/qtest/qpdf/bad32.out
1   -bad32.pdf: offset 307: expected 4 0 obj
  1 +bad32.pdf (object 4 0, file position 307): expected 4 0 obj
... ...
qpdf/qtest/qpdf/bad33-recover.out
No preview for this file type
qpdf/qtest/qpdf/bad33.out
1   -bad33.pdf: offset 1771: xref not found
  1 +bad33.pdf (file position 1771): xref not found
... ...
qpdf/qtest/qpdf/bad34-recover.out 0 โ†’ 100644
  1 +WARNING: bad34.pdf: file is damaged
  2 +WARNING: bad34.pdf (object 4 0, file position 322): expected n n obj
  3 +WARNING: Attempting to reconstruct cross-reference table
  4 +/QTest is indirect
  5 +/QTest is a stream. Dictionary: << /Length 44 /Quack 9 0 R >>
  6 +Raw stream data:
  7 +BT
  8 + /F1 24 Tf
  9 + 72 720 Td
  10 + (Potato) Tj
  11 +ET
  12 +
  13 +Uncompressed stream data:
  14 +BT
  15 + /F1 24 Tf
  16 + 72 720 Td
  17 + (Potato) Tj
  18 +ET
  19 +
  20 +End of stream data
  21 +unparse: 4 0 R
  22 +unparseResolved: 4 0 R
  23 +test 1 done
... ...
qpdf/qtest/qpdf/bad34.out 0 โ†’ 100644
  1 +bad34.pdf (object 4 0, file position 322): expected n n obj
... ...
qpdf/qtest/qpdf/bad34.pdf 0 โ†’ 100644
  1 +%PDF-1.3
  2 +1 0 obj
  3 +<<
  4 + /Type /Catalog
  5 + /Pages 2 0 R
  6 +>>
  7 +endobj
  8 +
  9 +2 0 obj
  10 +<<
  11 + /Type /Pages
  12 + /Kids [
  13 + 3 0 R
  14 + ]
  15 + /Count 1
  16 +>>
  17 +endobj
  18 +
  19 +4 0 obj
  20 +<<
  21 + /Length 44
  22 + /Quack 9 0 R
  23 +>>
  24 +stream
  25 +BT
  26 + /F1 24 Tf
  27 + 72 720 Td
  28 + (Potato) Tj
  29 +ET
  30 +endstream
  31 +endobj
  32 +
  33 +3 0 obj
  34 +<<
  35 + /Type /Page
  36 + /Parent 2 0 R
  37 + /MediaBox [0 0 612 792]
  38 + /Contents 4 0 R
  39 + /Resources <<
  40 + /ProcSet 5 0 R
  41 + /Font <<
  42 + /F1 6 0 R
  43 + >>
  44 + >>
  45 +>>
  46 +endobj
  47 +
  48 +5 0 obj
  49 +[
  50 + /PDF
  51 + /Text
  52 +]
  53 +endobj
  54 +
  55 +6 0 obj
  56 +<<
  57 + /Type /Font
  58 + /Subtype /Type1
  59 + /Name /F1
  60 + /BaseFont /Helvetica
  61 + /Encoding /WinAnsiEncoding
  62 +>>
  63 +endobj
  64 +
  65 +xref
  66 +0 7
  67 +0000000000 65535 f
  68 +0000000009 00000 n
  69 +0000000063 00000 n
  70 +0000000135 00000 n
  71 +0000000322 00000 n
  72 +0000000418 00000 n
  73 +0000000453 00000 n
  74 +trailer <<
  75 + /Size 7
  76 + /Root 1 0 R
  77 + /QTest 4 0 R
  78 +>>
  79 +startxref
  80 +571
  81 +%%EOF
... ...
qpdf/qtest/qpdf/bad35-recover.out 0 โ†’ 100644
  1 +bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type
... ...
qpdf/qtest/qpdf/bad35.out 0 โ†’ 100644
  1 +bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type
... ...
qpdf/qtest/qpdf/bad35.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/bad4-recover.out
1   -WARNING: bad4.pdf: offset 0: file is damaged
2   -WARNING: bad4.pdf: offset 547: xref syntax invalid
  1 +WARNING: bad4.pdf: file is damaged
  2 +WARNING: bad4.pdf (xref table, file position 547): xref syntax invalid
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 /QTest is implicit
5 5 /QTest is direct
... ...
qpdf/qtest/qpdf/bad4.out
1   -bad4.pdf: offset 547: xref syntax invalid
  1 +bad4.pdf (xref table, file position 547): xref syntax invalid
... ...
qpdf/qtest/qpdf/bad5-recover.out
1   -WARNING: bad5.pdf: offset 0: file is damaged
2   -WARNING: bad5.pdf: offset 591: invalid xref entry (obj=2)
  1 +WARNING: bad5.pdf: file is damaged
  2 +WARNING: bad5.pdf (xref table, file position 591): invalid xref entry (obj=2)
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 /QTest is implicit
5 5 /QTest is direct
... ...
qpdf/qtest/qpdf/bad5.out
1   -bad5.pdf: offset 591: invalid xref entry (obj=2)
  1 +bad5.pdf (xref table, file position 591): invalid xref entry (obj=2)
... ...
qpdf/qtest/qpdf/bad7-recover.out
1   -WARNING: bad7.pdf: offset 0: file is damaged
2   -WARNING: bad7.pdf: offset 698: expected trailer dictionary
  1 +WARNING: bad7.pdf: file is damaged
  2 +WARNING: bad7.pdf (file position 698): expected trailer dictionary
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 bad7.pdf: unable to find trailer dictionary while recovering damaged file
... ...
qpdf/qtest/qpdf/bad7.out
1   -bad7.pdf: offset 698: expected trailer dictionary
  1 +bad7.pdf (file position 698): expected trailer dictionary
... ...
qpdf/qtest/qpdf/bad8-recover.out
1   -WARNING: bad8.pdf: offset 0: file is damaged
2   -WARNING: bad8.pdf: offset 543: xref not found
  1 +WARNING: bad8.pdf: file is damaged
  2 +WARNING: bad8.pdf (file position 543): xref not found
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 /QTest is implicit
5 5 /QTest is direct
... ...
qpdf/qtest/qpdf/bad8.out
1   -bad8.pdf: offset 543: xref not found
  1 +bad8.pdf (file position 543): xref not found
... ...
qpdf/qtest/qpdf/bad9-recover.out
1   -WARNING: bad9.pdf: offset 0: file is damaged
2   -WARNING: bad9.pdf: offset 712: trailer dictionary lacks /Size key
  1 +WARNING: bad9.pdf: file is damaged
  2 +WARNING: bad9.pdf (trailer, file position 712): trailer dictionary lacks /Size key
3 3 WARNING: Attempting to reconstruct cross-reference table
4 4 /QTest is implicit
5 5 /QTest is direct
... ...
qpdf/qtest/qpdf/bad9.out
1   -bad9.pdf: offset 712: trailer dictionary lacks /Size key
  1 +bad9.pdf (trailer, file position 712): trailer dictionary lacks /Size key
... ...
qpdf/qtest/qpdf/c-no-recovery.out
1   -error: bad33.pdf: offset 1771: xref not found
  1 +error: bad33.pdf (file position 1771): xref not found
... ...
qpdf/qtest/qpdf/c-read-errors.out
1   -error: bad1.pdf: offset 0: not a PDF file
  1 +error: bad1.pdf: not a PDF file
... ...
qpdf/qtest/qpdf/c-read-warnings-and-errors.out
1   -warning: bad17.pdf: offset 0: file is damaged
2   -warning: bad17.pdf: offset 753: dictionary ending here has an odd number of elements
  1 +warning: bad17.pdf: file is damaged
  2 +warning: bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements
3 3 warning: Attempting to reconstruct cross-reference table
4   -error: bad17.pdf: offset 753: dictionary ending here has an odd number of elements
  4 +error: bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements
... ...
qpdf/qtest/qpdf/c-write-damaged.out
1   -warning: append-page-content-damaged.pdf: offset 0: file is damaged
  1 +warning: append-page-content-damaged.pdf: file is damaged
2 2 warning: append-page-content-damaged.pdf: can't find startxref
3 3 warning: Attempting to reconstruct cross-reference table
... ...
qpdf/qtest/qpdf/c-write-errors.out
1   -error: bad30.pdf: offset 629: invalid filter object type for this stream
  1 +error: bad30.pdf (file position 629): stream filter type is not name or array
... ...
qpdf/qtest/qpdf/c-write-warnings-and-errors.out
1   -warning: bad33.pdf: offset 0: file is damaged
2   -warning: bad33.pdf: offset 1771: xref not found
  1 +warning: bad33.pdf: file is damaged
  2 +warning: bad33.pdf (file position 1771): xref not found
3 3 warning: Attempting to reconstruct cross-reference table
4   -error: bad33.pdf: offset 629: invalid filter object type for this stream
  4 +error: bad33.pdf (file position 629): stream filter type is not name or array
... ...
qpdf/qtest/qpdf/damaged-stream.out
... ... @@ -2,4 +2,4 @@ checking damaged-stream.pdf
2 2 PDF Version: 1.3
3 3 File is not encrypted
4 4 File is not linearized
5   -WARNING: damaged-stream.pdf: offset 426: error decoding stream data for object 5 0: LZWDecoder: bad code received
  5 +WARNING: damaged-stream.pdf (file position 426): error decoding stream data for object 5 0: LZWDecoder: bad code received
... ...
qpdf/qtest/qpdf/heifer.out
1   -WARNING: heifer.pdf: offset 0: file is damaged
2   -WARNING: heifer.pdf: offset 92741: xref not found
  1 +WARNING: heifer.pdf: file is damaged
  2 +WARNING: heifer.pdf (file position 92741): xref not found
3 3 WARNING: Attempting to reconstruct cross-reference table
4   -WARNING: heifer.pdf: offset 51: attempting to recover stream length
  4 +WARNING: heifer.pdf (object 2 0, file position 51): attempting to recover stream length
5 5 qpdf: operation succeeded with warnings; resulting file may have some problems
... ...
qpdf/qtest/qpdf/xref-with-short-size-recover.out
1   -WARNING: xref-with-short-size.pdf: offset 16227: Cross-reference stream data has the wrong size; expected = 52; actual = 56
  1 +WARNING: xref-with-short-size.pdf (xref stream, file position 16227): Cross-reference stream data has the wrong size; expected = 52; actual = 56
2 2 qpdf: operation succeeded with warnings; resulting file may have some problems
... ...
qpdf/qtest/qpdf/xref-with-short-size.out
1   -WARNING: xref-with-short-size.pdf: offset 16227: Cross-reference stream data has the wrong size; expected = 52; actual = 56
  1 +WARNING: xref-with-short-size.pdf (xref stream, file position 16227): Cross-reference stream data has the wrong size; expected = 52; actual = 56
2 2 1/0: compressed; stream = 5, index = 1
3 3 2/0: compressed; stream = 5, index = 0
4 4 3/0: uncompressed; offset = 15
... ...
qpdf/test_driver.cc
... ... @@ -9,6 +9,7 @@
9 9 #include <qpdf/Pl_Buffer.hh>
10 10 #include <qpdf/QPDFWriter.hh>
11 11 #include <iostream>
  12 +#include <stdio.h>
12 13 #include <string.h>
13 14 #include <stdlib.h>
14 15 #include <map>
... ... @@ -319,6 +320,7 @@ void runtest(int n, char const* filename)
319 320  
320 321 int main(int argc, char* argv[])
321 322 {
  323 + setlinebuf(stdout);
322 324 if ((whoami = strrchr(argv[0], '/')) == NULL)
323 325 {
324 326 whoami = argv[0];
... ...