From 3f8c4c273649c857f5a607dcbb422729fce3a166 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Mon, 19 Oct 2009 23:09:19 +0000 Subject: [PATCH] categorize all error messages and include object information if available git-svn-id: svn+q:///qpdf/trunk@829 71b93d88-0707-0410-a8cf-f5a4172ac649 --- ChangeLog | 8 + TODO | 23 +- examples/qtest/bookmarks.test | 2 +- examples/qtest/npages.test | 2 +- include/qpdf/QPDF.hh | 10 +- include/qpdf/QPDFExc.hh | 36 ++- libqpdf/QPDF.cc | 252 +++++++++++++----- libqpdf/QPDFExc.cc | 54 +++- libqpdf/QPDFXRefEntry.cc | 9 +- libqpdf/QPDF_Stream.cc | 7 +- libqpdf/QPDF_encryption.cc | 99 ++++--- libqpdf/QPDF_linearization.cc | 34 ++- libqpdf/QPDF_optimization.cc | 14 +- qpdf/qpdf.cc | 12 +- qpdf/qpdf.testcov | 2 + qpdf/qtest/qpdf.test | 4 +- .../append-page-content-damaged-c-check.out | 4 +- .../append-page-content-damaged-check.out | 2 +- .../qpdf/append-page-content-damaged.out | 2 +- qpdf/qtest/qpdf/bad1-recover.out | 2 +- qpdf/qtest/qpdf/bad1.out | 2 +- qpdf/qtest/qpdf/bad10-recover.out | 4 +- qpdf/qtest/qpdf/bad10.out | 2 +- qpdf/qtest/qpdf/bad11-recover.out | 4 +- qpdf/qtest/qpdf/bad11.out | 2 +- qpdf/qtest/qpdf/bad13-recover.out | 6 +- qpdf/qtest/qpdf/bad13.out | 2 +- qpdf/qtest/qpdf/bad14-recover.out | 6 +- qpdf/qtest/qpdf/bad14.out | 2 +- qpdf/qtest/qpdf/bad15-recover.out | 6 +- qpdf/qtest/qpdf/bad15.out | 2 +- qpdf/qtest/qpdf/bad16-recover.out | 6 +- qpdf/qtest/qpdf/bad16.out | 2 +- qpdf/qtest/qpdf/bad17-recover.out | 6 +- qpdf/qtest/qpdf/bad17.out | 2 +- qpdf/qtest/qpdf/bad18-recover.out | 6 +- qpdf/qtest/qpdf/bad18.out | 2 +- qpdf/qtest/qpdf/bad19-recover.out | 6 +- qpdf/qtest/qpdf/bad19.out | 2 +- qpdf/qtest/qpdf/bad2-recover.out | 2 +- qpdf/qtest/qpdf/bad20-recover.out | 6 +- qpdf/qtest/qpdf/bad20.out | 2 +- qpdf/qtest/qpdf/bad21-recover.out | 6 +- qpdf/qtest/qpdf/bad21.out | 2 +- qpdf/qtest/qpdf/bad22-recover.out | 2 +- qpdf/qtest/qpdf/bad22.out | 2 +- qpdf/qtest/qpdf/bad23-recover.out | 2 +- qpdf/qtest/qpdf/bad23.out | 2 +- qpdf/qtest/qpdf/bad24-recover.out | 4 +- qpdf/qtest/qpdf/bad24.out | 2 +- qpdf/qtest/qpdf/bad25-recover.out | 12 +- qpdf/qtest/qpdf/bad25.out | 2 +- qpdf/qtest/qpdf/bad26-recover.out | 12 +- qpdf/qtest/qpdf/bad26.out | 2 +- qpdf/qtest/qpdf/bad27-recover.out | 12 +- qpdf/qtest/qpdf/bad27.out | 2 +- qpdf/qtest/qpdf/bad28-recover.out | 2 +- qpdf/qtest/qpdf/bad28.out | 2 +- qpdf/qtest/qpdf/bad29-recover.out | 6 +- qpdf/qtest/qpdf/bad29.out | 2 +- qpdf/qtest/qpdf/bad3-recover.out | 4 +- qpdf/qtest/qpdf/bad3.out | 2 +- qpdf/qtest/qpdf/bad30-recover.out | 2 +- qpdf/qtest/qpdf/bad30.out | 2 +- qpdf/qtest/qpdf/bad32-recover.out | 5 +- qpdf/qtest/qpdf/bad32.out | 2 +- qpdf/qtest/qpdf/bad33-recover.out | 6 +- qpdf/qtest/qpdf/bad33.out | 2 +- qpdf/qtest/qpdf/bad34-recover.out | 23 ++ qpdf/qtest/qpdf/bad34.out | 1 + qpdf/qtest/qpdf/bad34.pdf | 81 ++++++ qpdf/qtest/qpdf/bad35-recover.out | 1 + qpdf/qtest/qpdf/bad35.out | 1 + qpdf/qtest/qpdf/bad35.pdf | Bin 0 -> 807 bytes qpdf/qtest/qpdf/bad4-recover.out | 4 +- qpdf/qtest/qpdf/bad4.out | 2 +- qpdf/qtest/qpdf/bad5-recover.out | 4 +- qpdf/qtest/qpdf/bad5.out | 2 +- qpdf/qtest/qpdf/bad7-recover.out | 4 +- qpdf/qtest/qpdf/bad7.out | 2 +- qpdf/qtest/qpdf/bad8-recover.out | 4 +- qpdf/qtest/qpdf/bad8.out | 2 +- qpdf/qtest/qpdf/bad9-recover.out | 4 +- qpdf/qtest/qpdf/bad9.out | 2 +- qpdf/qtest/qpdf/c-no-recovery.out | 2 +- qpdf/qtest/qpdf/c-read-errors.out | 2 +- .../qtest/qpdf/c-read-warnings-and-errors.out | 6 +- qpdf/qtest/qpdf/c-write-damaged.out | 2 +- qpdf/qtest/qpdf/c-write-errors.out | 2 +- .../qpdf/c-write-warnings-and-errors.out | 6 +- qpdf/qtest/qpdf/damaged-stream.out | 2 +- qpdf/qtest/qpdf/heifer.out | 6 +- .../qpdf/xref-with-short-size-recover.out | 2 +- qpdf/qtest/qpdf/xref-with-short-size.out | 2 +- qpdf/test_driver.cc | 2 + 95 files changed, 662 insertions(+), 268 deletions(-) create mode 100644 qpdf/qtest/qpdf/bad34-recover.out create mode 100644 qpdf/qtest/qpdf/bad34.out create mode 100644 qpdf/qtest/qpdf/bad34.pdf create mode 100644 qpdf/qtest/qpdf/bad35-recover.out create mode 100644 qpdf/qtest/qpdf/bad35.out create mode 100644 qpdf/qtest/qpdf/bad35.pdf diff --git a/ChangeLog b/ChangeLog index 13badadb..0a7aab28 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-10-19 Jay Berkenbilt + + * Include information about the last object read in most error + messages. Most of the time, this will provide a good hint as to + which object contains the error, but it's possible that the last + object read may not necessarily be the one that has the error if + the erroneous object was previously read and cached. + 2009-10-18 Jay Berkenbilt * If forcing version, disable object stream creation and/or diff --git a/TODO b/TODO index 1f63c646..2027fb2e 100644 --- a/TODO +++ b/TODO @@ -16,25 +16,10 @@ * Add comments for the security functions that map them back to the items in Adobe's products. - * Add error codes to QPDFException. Change the error interface so - that warnings and errors are pointers that can be queried using - more C API functions. We need a way to get a full string as well - as an error code, file name, offset, and message. We should go - through all error messages to try to include all these fields as - appropriate. Make sure invalid password is specifically - detectable. I/O errors and so forth should also be - distinguishable. Make sure all errors include information about - the most recent read location including byte offset and - object/generation number. - - * It might be nice to be able to trap I/O errors separately from - other errors; especially be able to separate errors that the user - can fix (like permission errors) from errors that they probably - can't fix like corrupted PDF files, unsupported filters, or - internal errors. However, only QPDF::processFile(), which does the - initial read, and QPDFWriter::QPDFWriter(), which does the initial - write, are at all likely to generate such errors for a case other - than a catastrophic failure. + * Change the C error interface so that warnings and errors are + pointers that can be queried using more C API functions. We need a + way to get a full string as well as an error code, file name, + offset, and message. * "Delphi wrapper unit 'qpdf.pas' created by Zarko Gajic (http://delphi.about.com). .. use at your own risk and for whatever diff --git a/examples/qtest/bookmarks.test b/examples/qtest/bookmarks.test index 4cf4abcf..ba8be25f 100644 --- a/examples/qtest/bookmarks.test +++ b/examples/qtest/bookmarks.test @@ -30,7 +30,7 @@ $td->runtest("no bookmarks", $td->runtest("bad", {$td->COMMAND => "pdf-bookmarks 3.pdf"}, {$td->STRING => "pdf-bookmarks processing file 3.pdf: " . - "3.pdf: offset 0: not a PDF file\n", + "3.pdf: not a PDF file\n", $td->EXIT_STATUS => 2}, $td->NORMALIZE_NEWLINES); diff --git a/examples/qtest/npages.test b/examples/qtest/npages.test index cd81e6b8..b20f254c 100644 --- a/examples/qtest/npages.test +++ b/examples/qtest/npages.test @@ -16,7 +16,7 @@ $td->runtest("normal", $td->runtest("error", {$td->COMMAND => "pdf-npages bad"}, - {$td->STRING => "pdf-npages: bad: offset 0: not a PDF file\n", + {$td->STRING => "pdf-npages: bad: not a PDF file\n", $td->EXIT_STATUS => 2}, $td->NORMALIZE_NEWLINES); diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 9770f46d..178d71c7 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -371,9 +371,11 @@ class DLL_EXPORT QPDF int processXRefStream(off_t offset, QPDFObjectHandle& xref_stream); void insertXrefEntry(int obj, int f0, int f1, int f2, bool overwrite = false); + void setLastObjectDescription(std::string const& description, + int objid, int generation); QPDFObjectHandle readObject( - InputSource*, int objid, int generation, - bool in_object_stream); + InputSource*, std::string const& description, + int objid, int generation, bool in_object_stream); QPDFObjectHandle readObjectInternal( InputSource* input, int objid, int generation, bool in_object_stream, @@ -383,7 +385,8 @@ class DLL_EXPORT QPDF QPDFTokenizer::Token readToken(InputSource*); QPDFObjectHandle readObjectAtOffset( - off_t offset, + bool attempt_recovery, + off_t offset, std::string const& description, int exp_objid, int exp_generation, int& act_objid, int& act_generation); PointerHolder resolve(int objid, int generation); @@ -734,6 +737,7 @@ class DLL_EXPORT QPDF QPDFTokenizer tokenizer; FileInputSource file; + std::string last_object_description; bool encrypted; bool encryption_initialized; bool ignore_xref_streams; diff --git a/include/qpdf/QPDFExc.hh b/include/qpdf/QPDFExc.hh index d9a61bf3..6b9a11e4 100644 --- a/include/qpdf/QPDFExc.hh +++ b/include/qpdf/QPDFExc.hh @@ -9,15 +9,47 @@ #define __QPDFEXC_HH__ #include +#include #include +#include class DLL_EXPORT QPDFExc: public std::runtime_error { public: - QPDFExc(std::string const& message); - QPDFExc(std::string const& filename, int offset, + QPDFExc(qpdf_error_code_e error_code, + std::string const& filename, + std::string const& object, + off_t offset, std::string const& message); virtual ~QPDFExc() throw (); + + // To get a complete error string, call what(), provided by + // std::exception. The accessors below return the original values + // used to create the exception. Only the error code and message + // are guaranteed to have non-zero/empty values. + + // There is no lookup code that maps numeric error codes into + // strings. The numeric error code is just another way to get at + // the underlying issue, but it is more programmer-friendly than + // trying to parse a string that is subject to change. + + qpdf_error_code_e getErrorCode() const; + std::string const& getFilename() const; + std::string const& getObject() const; + off_t getOffset() const; + std::string const& getMessage() const; + + private: + static std::string createWhat(std::string const& filename, + std::string const& object, + off_t offset, + std::string const& message); + + qpdf_error_code_e error_code; + std::string filename; + std::string object; + off_t offset; + std::string message; }; #endif // __QPDFEXC_HH__ diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index c7ec873f..ebd78a9d 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -140,7 +140,9 @@ QPDF::FileInputSource::read(char* buffer, int length) size_t len = fread(buffer, 1, length, this->file); if ((len == 0) && ferror(this->file)) { - throw QPDFExc(this->filename, this->last_offset, + throw QPDFExc(qpdf_e_system, + this->filename, "", + this->last_offset, std::string("read ") + QUtil::int_to_string(length) + " bytes"); } @@ -325,7 +327,8 @@ QPDF::parse() else { QTC::TC("qpdf", "QPDF not a pdf file"); - throw QPDFExc(this->file.getName(), 0, "not a PDF file"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "", 0, "not a PDF file"); } // PDF spec says %%EOF must be found within the last 1024 bytes of @@ -369,7 +372,8 @@ QPDF::parse() if (! m2) { QTC::TC("qpdf", "QPDF can't find startxref"); - throw QPDFExc(this->file.getName() + ": can't find startxref"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, + "can't find startxref"); } off_t xref_offset = atoi(m2.getMatch(1).c_str()); read_xref(xref_offset); @@ -417,9 +421,28 @@ QPDF::reconstruct_xref(QPDFExc& e) static PCRE endobj_re("^endobj\\b"); static PCRE trailer_re("^trailer\\b"); - warn(QPDFExc(this->file.getName(), 0, "file is damaged")); + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, + "file is damaged")); warn(e); - warn(QPDFExc("Attempting to reconstruct cross-reference table")); + warn(QPDFExc(qpdf_e_damaged_pdf, "", "", 0, + "Attempting to reconstruct cross-reference table")); + + // Delete all references to type 1 (uncompressed) objects + std::set to_delete; + for (std::map::iterator iter = + this->xref_table.begin(); + iter != this->xref_table.end(); ++iter) + { + if (((*iter).second).getType() == 1) + { + to_delete.insert((*iter).first); + } + } + for (std::set::iterator iter = to_delete.begin(); + iter != to_delete.end(); ++iter) + { + this->xref_table.erase(*iter); + } this->file.seek(0, SEEK_END); off_t eof = this->file.tell(); @@ -452,7 +475,8 @@ QPDF::reconstruct_xref(QPDFExc& e) // read "trailer" this->file.seek(this->file.getLastOffset(), SEEK_SET); readToken(&this->file); - QPDFObjectHandle t = readObject(&this->file, 0, 0, false); + QPDFObjectHandle t = + readObject(&this->file, "trailer", 0, 0, false); if (! t.isDictionary()) { // Oh well. It was worth a try. @@ -473,7 +497,8 @@ QPDF::reconstruct_xref(QPDFExc& e) // with bad startxref pointers even when they have object // streams. - throw QPDFExc(this->file.getName() + ": unable to find trailer " + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, + "unable to find trailer " "dictionary while recovering damaged file"); } @@ -513,8 +538,8 @@ QPDF::read_xref(off_t xref_offset) if (size != max_obj + 1) { QTC::TC("qpdf", "QPDF xref size mismatch"); - warn(QPDFExc(this->file.getName() + - std::string(": reported number of objects (") + + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, + std::string("reported number of objects (") + QUtil::int_to_string(size) + ") inconsistent with actual number of objects (" + QUtil::int_to_string(max_obj + 1) + ")")); @@ -542,7 +567,8 @@ QPDF::read_xrefTable(off_t xref_offset) if (! m1) { QTC::TC("qpdf", "QPDF invalid xref"); - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref table", this->file.getLastOffset(), "xref syntax invalid"); } int obj = atoi(m1.getMatch(1).c_str()); @@ -563,7 +589,8 @@ QPDF::read_xrefTable(off_t xref_offset) { QTC::TC("qpdf", "QPDF invalid xref entry"); throw QPDFExc( - this->file.getName(), this->file.getLastOffset(), + qpdf_e_damaged_pdf, this->file.getName(), + "xref table", this->file.getLastOffset(), "invalid xref entry (obj=" + QUtil::int_to_string(i) + ")"); } @@ -595,11 +622,13 @@ QPDF::read_xrefTable(off_t xref_offset) } // Set offset to previous xref table if any - QPDFObjectHandle cur_trailer = readObject(&this->file, 0, 0, false); + QPDFObjectHandle cur_trailer = + readObject(&this->file, "trailer", 0, 0, false); if (! cur_trailer.isDictionary()) { QTC::TC("qpdf", "QPDF missing trailer"); - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "", this->file.getLastOffset(), "expected trailer dictionary"); } @@ -610,13 +639,15 @@ QPDF::read_xrefTable(off_t xref_offset) if (! this->trailer.hasKey("/Size")) { QTC::TC("qpdf", "QPDF trailer lacks size"); - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "trailer", this->file.getLastOffset(), "trailer dictionary lacks /Size key"); } if (! this->trailer.getKey("/Size").isInteger()) { QTC::TC("qpdf", "QPDF trailer size not integer"); - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "trailer", this->file.getLastOffset(), "/Size key in trailer dictionary is not " "an integer"); } @@ -640,7 +671,8 @@ QPDF::read_xrefTable(off_t xref_offset) } else { - throw QPDFExc(this->file.getName(), xref_offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", xref_offset, "invalid /XRefStm"); } } @@ -659,7 +691,8 @@ QPDF::read_xrefTable(off_t xref_offset) if (! cur_trailer.getKey("/Prev").isInteger()) { QTC::TC("qpdf", "QPDF trailer prev not integer"); - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "trailer", this->file.getLastOffset(), "/Prev key in trailer dictionary is not " "an integer"); } @@ -685,7 +718,8 @@ QPDF::read_xrefStream(off_t xref_offset) QPDFObjectHandle xref_obj; try { - xref_obj = readObjectAtOffset(xref_offset, 0, 0, xobj, xgen); + xref_obj = readObjectAtOffset( + false, xref_offset, "xref stream", 0, 0, xobj, xgen); } catch (QPDFExc& e) { @@ -705,7 +739,8 @@ QPDF::read_xrefStream(off_t xref_offset) if (! found) { QTC::TC("qpdf", "QPDF can't find xref"); - throw QPDFExc(this->file.getName(), xref_offset, "xref not found"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "", xref_offset, "xref not found"); } return xref_offset; @@ -725,7 +760,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) dict.getKey("/Size").isInteger() && (Index_obj.isArray() || Index_obj.isNull()))) { - throw QPDFExc(this->file.getName(), xref_offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", xref_offset, "Cross-reference stream does not have" " proper /W and /Index keys"); } @@ -735,7 +771,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) int n_index = Index_obj.getArrayNItems(); if ((n_index % 2) || (n_index < 2)) { - throw QPDFExc(this->file.getName(), xref_offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", xref_offset, "Cross-reference stream's /Index has an" " invalid number of values"); } @@ -747,7 +784,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) } else { - throw QPDFExc(this->file.getName(), xref_offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", xref_offset, "Cross-reference stream's /Index's item " + QUtil::int_to_string(i) + " is not an integer"); @@ -785,7 +823,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) if (expected_size != actual_size) { - QPDFExc x(this->file.getName(), xref_offset, + QPDFExc x(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", xref_offset, "Cross-reference stream data has the wrong size;" " expected = " + QUtil::int_to_string(expected_size) + "; actual = " + QUtil::int_to_string(actual_size)); @@ -866,7 +905,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) { if (! dict.getKey("/Prev").isInteger()) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", this->file.getLastOffset(), "/Prev key in xref stream dictionary is not " "an integer"); } @@ -935,7 +975,8 @@ QPDF::insertXrefEntry(int obj, int f0, int f1, int f2, bool overwrite) break; default: - throw QPDFExc(this->file.getName(), 0, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", this->file.getLastOffset(), "unknown xref stream entry type " + QUtil::int_to_string(f0)); break; @@ -972,10 +1013,32 @@ QPDF::showXRefTable() } } -QPDFObjectHandle -QPDF::readObject(InputSource* input, int objid, int generation, - bool in_object_stream) +void +QPDF::setLastObjectDescription(std::string const& description, + int objid, int generation) { + this->last_object_description.clear(); + if (! description.empty()) + { + this->last_object_description += description; + if (objid > 0) + { + this->last_object_description += ": "; + } + } + if (objid > 0) + { + this->last_object_description += "object " + + QUtil::int_to_string(objid) + " " + + QUtil::int_to_string(generation); + } +} + +QPDFObjectHandle +QPDF::readObject(InputSource* input, std::string const& description, + int objid, int generation, bool in_object_stream) +{ + setLastObjectDescription(description, objid, generation); off_t offset = input->tell(); QPDFObjectHandle object = readObjectInternal( input, objid, generation, in_object_stream, false, false); @@ -1017,7 +1080,9 @@ QPDF::readObjectInternal(InputSource* input, case QPDFTokenizer::tt_brace_close: // Don't know what to do with these for now QTC::TC("qpdf", "QPDF bad brace"); - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "unexpected brace token"); break; @@ -1029,7 +1094,9 @@ QPDF::readObjectInternal(InputSource* input, else { QTC::TC("qpdf", "QPDF bad array close"); - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "unexpected array close token"); } break; @@ -1042,7 +1109,9 @@ QPDF::readObjectInternal(InputSource* input, else { QTC::TC("qpdf", "QPDF bad dictionary close"); - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "unexpected dictionary close token"); } break; @@ -1097,7 +1166,9 @@ QPDF::readObjectInternal(InputSource* input, } else { - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "unknown token while reading object (" + value + ")"); } @@ -1116,7 +1187,9 @@ QPDF::readObjectInternal(InputSource* input, break; default: - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "unknown token type while reading object"); break; } @@ -1153,7 +1226,8 @@ QPDF::readObjectInternal(InputSource* input, { QTC::TC("qpdf", "QPDF dictionary odd number of elements"); throw QPDFExc( - input->getName(), input->getLastOffset(), + qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, input->getLastOffset(), "dictionary ending here has an odd number of elements"); } for (unsigned int i = 0; i < olist.size(); i += 2) @@ -1163,7 +1237,8 @@ QPDF::readObjectInternal(InputSource* input, if (! key_obj.isName()) { throw QPDFExc( - input->getName(), offset, + qpdf_e_damaged_pdf, + input->getName(), this->last_object_description, offset, std::string("dictionary key not name (") + key_obj.unparse() + ")"); } @@ -1209,7 +1284,8 @@ QPDF::readObjectInternal(InputSource* input, if (dict.count("/Length") == 0) { QTC::TC("qpdf", "QPDF stream without length"); - throw QPDFExc(input->getName(), offset, + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, offset, "stream dictionary lacks /Length key"); } @@ -1217,7 +1293,8 @@ QPDF::readObjectInternal(InputSource* input, if (! length_obj.isInteger()) { QTC::TC("qpdf", "QPDF stream length not integer"); - throw QPDFExc(input->getName(), offset, + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, offset, "/Length key in stream dictionary is not " "an integer"); } @@ -1229,7 +1306,9 @@ QPDF::readObjectInternal(InputSource* input, QPDFTokenizer::tt_word, "endstream"))) { QTC::TC("qpdf", "QPDF missing endstream"); - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "expected endstream"); } } @@ -1267,7 +1346,8 @@ QPDF::recoverStreamLength(InputSource* input, // Try to reconstruct stream length by looking for // endstream(\r\n?|\n)endobj - warn(QPDFExc(input->getName(), stream_offset, + warn(QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, stream_offset, "attempting to recover stream length")); input->seek(0, SEEK_END); @@ -1336,7 +1416,8 @@ QPDF::recoverStreamLength(InputSource* input, if (length == 0) { - throw QPDFExc(input->getName(), stream_offset, + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, stream_offset, "unable to recover stream data"); } @@ -1356,7 +1437,9 @@ QPDF::readToken(InputSource* input) char ch; if (input->read(&ch, 1) == 0) { - throw QPDFExc(input->getName(), offset, "EOF while reading token"); + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, offset, + "EOF while reading token"); } else { @@ -1376,7 +1459,9 @@ QPDF::readToken(InputSource* input) if (token.getType() == QPDFTokenizer::tt_bad) { - throw QPDFExc(input->getName(), offset, token.getErrorMessage()); + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, offset, + token.getErrorMessage()); } input->setLastOffset(offset); @@ -1385,9 +1470,12 @@ QPDF::readToken(InputSource* input) } QPDFObjectHandle -QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, +QPDF::readObjectAtOffset(bool try_recovery, + off_t offset, std::string const& description, + int exp_objid, int exp_generation, int& objid, int& generation) { + setLastObjectDescription(description, exp_objid, exp_generation); this->file.seek(offset, SEEK_SET); QPDFTokenizer::Token tobjid = readToken(&this->file); @@ -1407,7 +1495,9 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, if (! (objidok && genok && objok)) { QTC::TC("qpdf", "QPDF expected n n obj"); - throw QPDFExc(this->file.getName(), offset, "expected n n obj"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, offset, + "expected n n obj"); } objid = atoi(tobjid.getValue().c_str()); generation = atoi(tgen.getValue().c_str()); @@ -1416,7 +1506,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, (! ((objid == exp_objid) && (generation == exp_generation)))) { QTC::TC("qpdf", "QPDF err wrong objid/generation"); - throw QPDFExc(this->file.getName(), offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, offset, std::string("expected ") + QUtil::int_to_string(exp_objid) + " " + QUtil::int_to_string(exp_generation) + " obj"); @@ -1424,7 +1515,7 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, } catch (QPDFExc& e) { - if (exp_objid && this->attempt_recovery) + if (exp_objid && try_recovery && this->attempt_recovery) { // Try again after reconstructing xref table reconstruct_xref(e); @@ -1433,13 +1524,27 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, (this->xref_table[og].getType() == 1)) { off_t new_offset = this->xref_table[og].getOffset(); - // Call readObjectAtOffset with 0 for exp_objid to - // avoid an infinite loop. - QPDFObjectHandle result = - readObjectAtOffset(new_offset, 0, 0, objid, generation); + QPDFObjectHandle result = readObjectAtOffset( + false, new_offset, description, + exp_objid, exp_generation, objid, generation); QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset"); return result; } + else + { + QTC::TC("qpdf", "QPDF object gone after xref reconstruction"); + warn(QPDFExc( + qpdf_e_damaged_pdf, this->file.getName(), + "", 0, + std::string( + "object " + + QUtil::int_to_string(exp_objid) + + " " + + QUtil::int_to_string(exp_generation) + + " not found in file after regenerating" + " cross reference table"))); + return QPDFObjectHandle::newNull(); + } } else { @@ -1448,13 +1553,14 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, } QPDFObjectHandle oh = readObject( - &this->file, objid, generation, false); + &this->file, description, objid, generation, false); if (! (readToken(&this->file) == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj"))) { QTC::TC("qpdf", "QPDF err expected endobj"); - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, this->file.getLastOffset(), "expected endobj")); } @@ -1487,7 +1593,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, } else { - throw QPDFExc(this->file.getName(), offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, offset, "EOF after endobj"); } } @@ -1526,7 +1633,7 @@ QPDF::resolve(int objid, int generation) int aobjid; int ageneration; QPDFObjectHandle oh = - readObjectAtOffset(offset, objid, generation, + readObjectAtOffset(true, offset, "", objid, generation, aobjid, ageneration); } break; @@ -1536,7 +1643,7 @@ QPDF::resolve(int objid, int generation) break; default: - throw QPDFExc(this->file.getName(), 0, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, "object " + QUtil::int_to_string(objid) + "/" + QUtil::int_to_string(generation) + @@ -1554,7 +1661,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number) QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0); if (! obj_stream.isStream()) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "supposed object stream " + QUtil::int_to_string(obj_stream_number) + " is not a stream"); @@ -1570,7 +1679,10 @@ QPDF::resolveObjectsInStream(int obj_stream_number) if (! (dict.getKey("/Type").isName() && dict.getKey("/Type").getName() == "/ObjStm")) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + QTC::TC("qpdf", "QPDF ERR object stream with wrong type"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "supposed object stream " + QUtil::int_to_string(obj_stream_number) + " has wrong type"); @@ -1579,7 +1691,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number) if (! (dict.getKey("/N").isInteger() && dict.getKey("/First").isInteger())) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "object stream " + QUtil::int_to_string(obj_stream_number) + " has incorrect keys"); @@ -1602,7 +1716,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number) if (! ((tnum.getType() == QPDFTokenizer::tt_integer) && (toffset.getType() == QPDFTokenizer::tt_integer))) { - throw QPDFExc(input.getName(), input.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input.getName(), + this->last_object_description, input.getLastOffset(), "expected integer in object stream header"); } @@ -1617,7 +1732,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) int obj = (*iter).first; int offset = (*iter).second; input.seek(offset, SEEK_SET); - QPDFObjectHandle oh = readObject(&input, obj, 0, true); + QPDFObjectHandle oh = readObject(&input, "", obj, 0, true); // Store in cache ObjGen og(obj, 0); @@ -1830,17 +1945,25 @@ QPDF::pipeStreamData(int objid, int generation, size_t len = this->file.read(buf, to_read); if (len == 0) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, + this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "unexpected EOF reading stream data"); } length -= len; pipeline->write((unsigned char*)buf, len); } } + catch (QPDFExc& e) + { + warn(e); + } catch (std::runtime_error& e) { QTC::TC("qpdf", "QPDF decoding error warning"); - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "", this->file.getLastOffset(), "error decoding stream data for object " + QUtil::int_to_string(objid) + " " + QUtil::int_to_string(generation) + ": " + e.what())); @@ -1896,6 +2019,9 @@ QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages, } else { - throw QPDFExc(this->file.getName() + ": invalid Type in page tree"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), + + ": invalid Type in page tree"); } } diff --git a/libqpdf/QPDFExc.cc b/libqpdf/QPDFExc.cc index 006a96ac..614990a9 100644 --- a/libqpdf/QPDFExc.cc +++ b/libqpdf/QPDFExc.cc @@ -1,18 +1,56 @@ #include #include -QPDFExc::QPDFExc(std::string const& message) : - std::runtime_error(message) -{ -} - -QPDFExc::QPDFExc(std::string const& filename, int offset, +QPDFExc::QPDFExc(qpdf_error_code_e error_code, + std::string const& filename, + std::string const& object, + off_t offset, std::string const& message) : - std::runtime_error(filename + ": offset " + QUtil::int_to_string(offset) + - ": " + message) + std::runtime_error(createWhat(filename, object, offset, message)), + error_code(error_code), + filename(filename), + object(object), + offset(offset), + message(message) { } QPDFExc::~QPDFExc() throw () { } + +std::string +QPDFExc::createWhat(std::string const& filename, + std::string const& object, + off_t offset, + std::string const& message) +{ + std::string result; + if (! filename.empty()) + { + result += filename; + } + if (! (object.empty() && offset == 0)) + { + result += " ("; + if (! object.empty()) + { + result += object; + if (offset > 0) + { + result += ", "; + } + } + if (offset > 0) + { + result += "file position " + QUtil::int_to_string(offset); + } + result += ")"; + } + if (! result.empty()) + { + result += ": "; + } + result += message; + return result; +} diff --git a/libqpdf/QPDFXRefEntry.cc b/libqpdf/QPDFXRefEntry.cc index b6d48d97..9a08c829 100644 --- a/libqpdf/QPDFXRefEntry.cc +++ b/libqpdf/QPDFXRefEntry.cc @@ -16,7 +16,8 @@ QPDFXRefEntry::QPDFXRefEntry(int type, int field1, int field2) : { if ((type < 1) || (type > 2)) { - throw QPDFExc("invalid xref type " + QUtil::int_to_string(type)); + throw std::logic_error( + "invalid xref type " + QUtil::int_to_string(type)); } } @@ -31,7 +32,7 @@ QPDFXRefEntry::getOffset() const { if (this->type != 1) { - throw QPDFExc( + throw std::logic_error( "getOffset called for xref entry of type != 1"); } return this->field1; @@ -42,7 +43,7 @@ QPDFXRefEntry::getObjStreamNumber() const { if (this->type != 2) { - throw QPDFExc( + throw std::logic_error( "getObjStreamNumber called for xref entry of type != 2"); } return this->field1; @@ -53,7 +54,7 @@ QPDFXRefEntry::getObjStreamIndex() const { if (this->type != 2) { - throw QPDFExc( + throw std::logic_error( "getObjStreamIndex called for xref entry of type != 2"); } return this->field2; diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 6642a06e..d0dd2e5f 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -59,7 +59,7 @@ QPDF_Stream::getStreamData() Pl_Buffer buf("stream data buffer"); if (! pipeStreamData(&buf, true, false, false)) { - throw QPDFExc("getStreamData called on unfilterable stream"); + throw std::logic_error("getStreamData called on unfilterable stream"); } return buf.getBuffer(); } @@ -208,8 +208,9 @@ QPDF_Stream::filterable(std::vector& filters, if (! filters_okay) { QTC::TC("qpdf", "QPDF_Stream invalid filter"); - throw QPDFExc(qpdf->getFilename(), this->offset, - "invalid filter object type for this stream"); + throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(), + "", this->offset, + "stream filter type is not name or array"); } // `filters' now contains a list of filters to be applied in diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc index 8962624d..d6b0ae23 100644 --- a/libqpdf/QPDF_encryption.cc +++ b/libqpdf/QPDF_encryption.cc @@ -337,14 +337,16 @@ QPDF::initializeEncryption() (id_obj.getArrayNItems() == 2) && id_obj.getArrayItem(0).isString())) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "trailer", this->file.getLastOffset(), "invalid /ID in trailer dictionary"); } std::string id1 = id_obj.getArrayItem(0).getStringValue(); if (id1.length() != id_bytes) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "trailer", this->file.getLastOffset(), "first /ID string in trailer dictionary has " "incorrect length"); } @@ -352,19 +354,23 @@ QPDF::initializeEncryption() QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt"); if (! encryption_dict.isDictionary()) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "/Encrypt in trailer dictionary is not a dictionary"); } if (! (encryption_dict.getKey("/Filter").isName() && (encryption_dict.getKey("/Filter").getName() == "/Standard"))) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "unsupported encryption filter"); } if (! encryption_dict.getKey("/SubFilter").isNull()) { - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), + warn(QPDFExc(qpdf_e_unsupported, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "file uses encryption SubFilters," " which qpdf does not support")); } @@ -375,7 +381,8 @@ QPDF::initializeEncryption() encryption_dict.getKey("/U").isString() && encryption_dict.getKey("/P").isInteger())) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "some encryption dictionary parameters are missing " "or the wrong type"); } @@ -389,7 +396,8 @@ QPDF::initializeEncryption() if (! (((R == 2) || (R == 3) || (R == 4)) && ((V == 1) || (V == 2) || (V == 4)))) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_unsupported, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "Unsupported /R or /V in encryption dictionary"); } @@ -397,7 +405,8 @@ QPDF::initializeEncryption() if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "incorrect length for /O and/or /P in " "encryption dictionary"); } @@ -408,7 +417,8 @@ QPDF::initializeEncryption() Length = encryption_dict.getKey("/Length").getIntValue(); if ((Length % 8) || (Length < 40) || (Length > 128)) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "invalid /Length value in encryption dictionary"); } } @@ -471,7 +481,8 @@ QPDF::initializeEncryption() } if (this->cf_file != this->cf_stream) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_unsupported, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "This document has embedded files that are" " encrypted differently from the rest of the file." " qpdf does not presently support this due to" @@ -492,7 +503,8 @@ QPDF::initializeEncryption() } else { - throw QPDFExc(this->file.getName() + ": invalid password"); + throw QPDFExc(qpdf_e_password, this->file.getName(), + "", 0, "invalid password"); } this->encryption_key = compute_encryption_key(this->user_password, data); @@ -542,7 +554,9 @@ QPDF::decryptString(std::string& str, int objid, int generation) break; default: - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "unknown encryption filter for strings" " (check /StrF in /Encrypt dictionary);" " strings may be decrypted improperly")); @@ -554,28 +568,47 @@ QPDF::decryptString(std::string& str, int objid, int generation) } std::string key = getKeyForObject(objid, generation, use_aes); - if (use_aes) + try { - QTC::TC("qpdf", "QPDF_encryption aes decode string"); - assert(key.length() == Pl_AES_PDF::key_size); - Pl_Buffer bufpl("decrypted string"); - Pl_AES_PDF pl("aes decrypt string", &bufpl, false, - (unsigned char const*)key.c_str()); - pl.write((unsigned char*)str.c_str(), str.length()); - pl.finish(); - Buffer* buf = bufpl.getBuffer(); - str = std::string((char*)buf->getBuffer(), (size_t)buf->getSize()); - delete buf; + if (use_aes) + { + QTC::TC("qpdf", "QPDF_encryption aes decode string"); + assert(key.length() == Pl_AES_PDF::key_size); + Pl_Buffer bufpl("decrypted string"); + Pl_AES_PDF pl("aes decrypt string", &bufpl, false, + (unsigned char const*)key.c_str()); + pl.write((unsigned char*)str.c_str(), str.length()); + pl.finish(); + PointerHolder buf = bufpl.getBuffer(); + str = std::string((char*)buf.getPointer()->getBuffer(), + (size_t)buf.getPointer()->getSize()); + } + else + { + QTC::TC("qpdf", "QPDF_encryption rc4 decode string"); + unsigned int vlen = str.length(); + // Using PointerHolder will cause a new char[] to be deleted + // with delete instead of delete [], but it's okay since the + // array is of a fundamental type, so there is no destructor + // to be called. Using PointerHolder guarantees that tmp will + // be freed even if rc4.process throws an exception. + PointerHolder tmp = QUtil::copy_string(str); + RC4 rc4((unsigned char const*)key.c_str(), key.length()); + rc4.process((unsigned char*)tmp.getPointer(), vlen); + str = std::string(tmp.getPointer(), vlen); + } } - else + catch (QPDFExc& e) { - QTC::TC("qpdf", "QPDF_encryption rc4 decode string"); - unsigned int vlen = str.length(); - char* tmp = QUtil::copy_string(str); - RC4 rc4((unsigned char const*)key.c_str(), key.length()); - rc4.process((unsigned char*)tmp, vlen); - str = std::string(tmp, vlen); - delete [] tmp; + throw; + } + catch (std::runtime_error& e) + { + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, this->file.getLastOffset(), + "error decrypting string for object " + + QUtil::int_to_string(objid) + " " + + QUtil::int_to_string(generation) + ": " + e.what()); } } @@ -645,7 +678,9 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, default: // filter local to this stream. - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "unknown encryption filter for streams" " (check " + method_source + ");" " streams may be decrypted improperly")); diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc index 5bb4190e..51b74855 100644 --- a/libqpdf/QPDF_linearization.cc +++ b/libqpdf/QPDF_linearization.cc @@ -175,7 +175,8 @@ QPDF::readLinearizationData() if (! isLinearized()) { - throw QPDFExc(this->file.getName() + " is not linearized"); + throw std::logic_error("called readLinearizationData for file" + " that is not linearized"); } // /L is read and stored in linp by isLinearized() @@ -193,7 +194,10 @@ QPDF::readLinearizationData() T.isInteger() && (P.isInteger() || P.isNull()))) { - throw QPDFExc("some keys in linearization dictionary are of " + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "linearization dictionary", + this->file.getLastOffset(), + "some keys in linearization dictionary are of " "the wrong type"); } @@ -201,7 +205,10 @@ QPDF::readLinearizationData() unsigned int n_H_items = H.getArrayNItems(); if (! ((n_H_items == 2) || (n_H_items == 4))) { - throw QPDFExc("H has the wrong number of items"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "linearization dictionary", + this->file.getLastOffset(), + "H has the wrong number of items"); } std::vector H_items; @@ -214,7 +221,10 @@ QPDF::readLinearizationData() } else { - throw QPDFExc("some H items are of the wrong type"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "linearization dictionary", + this->file.getLastOffset(), + "some H items are of the wrong type"); } } @@ -301,13 +311,17 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length) { int obj; int gen; - QPDFObjectHandle H = readObjectAtOffset(offset, 0, 0, obj, gen); + QPDFObjectHandle H = readObjectAtOffset( + false, offset, "linearization hint stream", 0, 0, obj, gen); ObjCache& oc = this->obj_cache[ObjGen(obj, gen)]; off_t min_end_offset = oc.end_before_space; off_t max_end_offset = oc.end_after_space; if (! H.isStream()) { - throw QPDFExc("hint table is not a stream"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "linearization dictionary", + this->file.getLastOffset(), + "hint table is not a stream"); } QPDFObjectHandle Hdict = H.getDict(); @@ -340,7 +354,10 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length) std::cout << "expected = " << computed_end << "; actual = " << min_end_offset << ".." << max_end_offset << std::endl; - throw QPDFExc("hint table length mismatch"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "linearization dictionary", + this->file.getLastOffset(), + "hint table length mismatch"); } H.pipeStreamData(&pl, true, false, false); return Hdict; @@ -651,8 +668,7 @@ QPDF::getLinearizationOffset(ObjGen const& og) break; default: - throw QPDFExc( - this->file.getName(), 0, + throw std::logic_error( "getLinearizationOffset called for xref entry not of type 1 or 2"); break; } diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index f3833efd..e8d20fd5 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -233,9 +233,12 @@ QPDF::optimizePagesTree( { if (! allow_changes) { - throw QPDFExc(this->file.getName() + - ": optimize detected an " - "inheritable resource"); + throw QPDFExc(qpdf_e_internal, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), + "optimize detected an " + "inheritable resource when called " + "in no-change mode"); } // This is an inheritable resource @@ -338,7 +341,10 @@ QPDF::optimizePagesTree( } else { - throw QPDFExc(this->file.getName() + ": invalid Type in page tree"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), + "invalid Type in page tree"); } } diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc index bfce0555..7f7751c8 100644 --- a/qpdf/qpdf.cc +++ b/qpdf/qpdf.cc @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -533,6 +534,7 @@ parse_encrypt_options( int main(int argc, char* argv[]) { whoami = QUtil::getWhoami(argv[0]); + setlinebuf(stdout); // For libtool's sake.... if (strncmp(whoami, "lt-", 3) == 0) @@ -892,7 +894,15 @@ int main(int argc, char* argv[]) } if (show_linearization) { - pdf.showLinearizationData(); + if (pdf.isLinearized()) + { + pdf.showLinearizationData(); + } + else + { + std::cout << infilename << " is not linearized" + << std::endl; + } } if (show_xref) { diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index 6246e8e1..ac715259 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -171,3 +171,5 @@ QPDFWriter forced version disabled encryption 0 qpdf-c called qpdf_set_r4_encryption_parameters 0 qpdf-c called qpdf_set_static_aes_IV 0 QPDF_encryption stream crypt filter 0 +QPDF ERR object stream with wrong type 0 +QPDF object gone after xref reconstruction 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 0c05121b..fedbeb23 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -191,6 +191,8 @@ my @badfiles = ("not a PDF file", # 1 "unknown stream /Filter", # 31 "obj/gen mismatch", # 32 "invalid stream /Filter and xref", # 33 + "obj/gen in wrong place", # 34 + "object stream of wrong type", # 35 ); $n_tests += @badfiles + 5; @@ -249,7 +251,7 @@ $n_tests += @badfiles + 8; # though in some cases it may. Acrobat Reader would not be able to # recover any of these files any better. my %recover_failures = (); -for (1, 7, 13..21, 24..27, 29..30, 33) +for (1, 7, 13..21, 24, 29..30, 33, 35) { $recover_failures{$_} = 1; } diff --git a/qpdf/qtest/qpdf/append-page-content-damaged-c-check.out b/qpdf/qtest/qpdf/append-page-content-damaged-c-check.out index 892f61e4..f1ef65b1 100644 --- a/qpdf/qtest/qpdf/append-page-content-damaged-c-check.out +++ b/qpdf/qtest/qpdf/append-page-content-damaged-c-check.out @@ -1,9 +1,9 @@ -WARNING: append-page-content-damaged.pdf: offset 0: file is damaged +WARNING: append-page-content-damaged.pdf: file is damaged WARNING: append-page-content-damaged.pdf: can't find startxref WARNING: Attempting to reconstruct cross-reference table version: 1.3 linearized: 0 encrypted: 0 -warning: append-page-content-damaged.pdf: offset 0: file is damaged +warning: append-page-content-damaged.pdf: file is damaged warning: append-page-content-damaged.pdf: can't find startxref warning: Attempting to reconstruct cross-reference table diff --git a/qpdf/qtest/qpdf/append-page-content-damaged-check.out b/qpdf/qtest/qpdf/append-page-content-damaged-check.out index a50b32e6..2b04bd9a 100644 --- a/qpdf/qtest/qpdf/append-page-content-damaged-check.out +++ b/qpdf/qtest/qpdf/append-page-content-damaged-check.out @@ -1,4 +1,4 @@ -WARNING: append-page-content-damaged.pdf: offset 0: file is damaged +WARNING: append-page-content-damaged.pdf: file is damaged WARNING: append-page-content-damaged.pdf: can't find startxref WARNING: Attempting to reconstruct cross-reference table checking append-page-content-damaged.pdf diff --git a/qpdf/qtest/qpdf/append-page-content-damaged.out b/qpdf/qtest/qpdf/append-page-content-damaged.out index a3916a1f..7835d627 100644 --- a/qpdf/qtest/qpdf/append-page-content-damaged.out +++ b/qpdf/qtest/qpdf/append-page-content-damaged.out @@ -1,4 +1,4 @@ -WARNING: append-page-content-damaged.pdf: offset 0: file is damaged +WARNING: append-page-content-damaged.pdf: file is damaged WARNING: append-page-content-damaged.pdf: can't find startxref WARNING: Attempting to reconstruct cross-reference table qpdf: operation succeeded with warnings; resulting file may have some problems diff --git a/qpdf/qtest/qpdf/bad1-recover.out b/qpdf/qtest/qpdf/bad1-recover.out index 1cd5b015..f89eeb6d 100644 --- a/qpdf/qtest/qpdf/bad1-recover.out +++ b/qpdf/qtest/qpdf/bad1-recover.out @@ -1 +1 @@ -bad1.pdf: offset 0: not a PDF file +bad1.pdf: not a PDF file diff --git a/qpdf/qtest/qpdf/bad1.out b/qpdf/qtest/qpdf/bad1.out index 1cd5b015..f89eeb6d 100644 --- a/qpdf/qtest/qpdf/bad1.out +++ b/qpdf/qtest/qpdf/bad1.out @@ -1 +1 @@ -bad1.pdf: offset 0: not a PDF file +bad1.pdf: not a PDF file diff --git a/qpdf/qtest/qpdf/bad10-recover.out b/qpdf/qtest/qpdf/bad10-recover.out index 905ca597..ca726ca9 100644 --- a/qpdf/qtest/qpdf/bad10-recover.out +++ b/qpdf/qtest/qpdf/bad10-recover.out @@ -1,5 +1,5 @@ -WARNING: bad10.pdf: offset 0: file is damaged -WARNING: bad10.pdf: offset 712: /Size key in trailer dictionary is not an integer +WARNING: bad10.pdf: file is damaged +WARNING: bad10.pdf (trailer, file position 712): /Size key in trailer dictionary is not an integer WARNING: Attempting to reconstruct cross-reference table /QTest is implicit /QTest is direct diff --git a/qpdf/qtest/qpdf/bad10.out b/qpdf/qtest/qpdf/bad10.out index 935f5b24..2545a721 100644 --- a/qpdf/qtest/qpdf/bad10.out +++ b/qpdf/qtest/qpdf/bad10.out @@ -1 +1 @@ -bad10.pdf: offset 712: /Size key in trailer dictionary is not an integer +bad10.pdf (trailer, file position 712): /Size key in trailer dictionary is not an integer diff --git a/qpdf/qtest/qpdf/bad11-recover.out b/qpdf/qtest/qpdf/bad11-recover.out index 7dfac209..f7825327 100644 --- a/qpdf/qtest/qpdf/bad11-recover.out +++ b/qpdf/qtest/qpdf/bad11-recover.out @@ -1,5 +1,5 @@ -WARNING: bad11.pdf: offset 0: file is damaged -WARNING: bad11.pdf: offset 905: /Prev key in trailer dictionary is not an integer +WARNING: bad11.pdf: file is damaged +WARNING: bad11.pdf (trailer, file position 905): /Prev key in trailer dictionary is not an integer WARNING: Attempting to reconstruct cross-reference table /QTest is implicit /QTest is direct diff --git a/qpdf/qtest/qpdf/bad11.out b/qpdf/qtest/qpdf/bad11.out index 55705b73..78ad060a 100644 --- a/qpdf/qtest/qpdf/bad11.out +++ b/qpdf/qtest/qpdf/bad11.out @@ -1 +1 @@ -bad11.pdf: offset 905: /Prev key in trailer dictionary is not an integer +bad11.pdf (trailer, file position 905): /Prev key in trailer dictionary is not an integer diff --git a/qpdf/qtest/qpdf/bad13-recover.out b/qpdf/qtest/qpdf/bad13-recover.out index 5ea29917..06f1a002 100644 --- a/qpdf/qtest/qpdf/bad13-recover.out +++ b/qpdf/qtest/qpdf/bad13-recover.out @@ -1,4 +1,4 @@ -WARNING: bad13.pdf: offset 0: file is damaged -WARNING: bad13.pdf: offset 753: unexpected brace token +WARNING: bad13.pdf: file is damaged +WARNING: bad13.pdf (trailer, file position 753): unexpected brace token WARNING: Attempting to reconstruct cross-reference table -bad13.pdf: offset 753: unexpected brace token +bad13.pdf (trailer, file position 753): unexpected brace token diff --git a/qpdf/qtest/qpdf/bad13.out b/qpdf/qtest/qpdf/bad13.out index 12eedf08..04c34e36 100644 --- a/qpdf/qtest/qpdf/bad13.out +++ b/qpdf/qtest/qpdf/bad13.out @@ -1 +1 @@ -bad13.pdf: offset 753: unexpected brace token +bad13.pdf (trailer, file position 753): unexpected brace token diff --git a/qpdf/qtest/qpdf/bad14-recover.out b/qpdf/qtest/qpdf/bad14-recover.out index 26ee2ef6..cd97fd5f 100644 --- a/qpdf/qtest/qpdf/bad14-recover.out +++ b/qpdf/qtest/qpdf/bad14-recover.out @@ -1,4 +1,4 @@ -WARNING: bad14.pdf: offset 0: file is damaged -WARNING: bad14.pdf: offset 753: unexpected brace token +WARNING: bad14.pdf: file is damaged +WARNING: bad14.pdf (trailer, file position 753): unexpected brace token WARNING: Attempting to reconstruct cross-reference table -bad14.pdf: offset 753: unexpected brace token +bad14.pdf (trailer, file position 753): unexpected brace token diff --git a/qpdf/qtest/qpdf/bad14.out b/qpdf/qtest/qpdf/bad14.out index 82c58c22..6d32b70c 100644 --- a/qpdf/qtest/qpdf/bad14.out +++ b/qpdf/qtest/qpdf/bad14.out @@ -1 +1 @@ -bad14.pdf: offset 753: unexpected brace token +bad14.pdf (trailer, file position 753): unexpected brace token diff --git a/qpdf/qtest/qpdf/bad15-recover.out b/qpdf/qtest/qpdf/bad15-recover.out index 2d6d0cf1..333717cd 100644 --- a/qpdf/qtest/qpdf/bad15-recover.out +++ b/qpdf/qtest/qpdf/bad15-recover.out @@ -1,4 +1,4 @@ -WARNING: bad15.pdf: offset 0: file is damaged -WARNING: bad15.pdf: offset 753: unexpected array close token +WARNING: bad15.pdf: file is damaged +WARNING: bad15.pdf (trailer, file position 753): unexpected array close token WARNING: Attempting to reconstruct cross-reference table -bad15.pdf: offset 753: unexpected array close token +bad15.pdf (trailer, file position 753): unexpected array close token diff --git a/qpdf/qtest/qpdf/bad15.out b/qpdf/qtest/qpdf/bad15.out index 19ffbfcc..54e799df 100644 --- a/qpdf/qtest/qpdf/bad15.out +++ b/qpdf/qtest/qpdf/bad15.out @@ -1 +1 @@ -bad15.pdf: offset 753: unexpected array close token +bad15.pdf (trailer, file position 753): unexpected array close token diff --git a/qpdf/qtest/qpdf/bad16-recover.out b/qpdf/qtest/qpdf/bad16-recover.out index 22a49ee9..6c5f745c 100644 --- a/qpdf/qtest/qpdf/bad16-recover.out +++ b/qpdf/qtest/qpdf/bad16-recover.out @@ -1,4 +1,4 @@ -WARNING: bad16.pdf: offset 0: file is damaged -WARNING: bad16.pdf: offset 753: unexpected dictionary close token +WARNING: bad16.pdf: file is damaged +WARNING: bad16.pdf (trailer, file position 753): unexpected dictionary close token WARNING: Attempting to reconstruct cross-reference table -bad16.pdf: offset 753: unexpected dictionary close token +bad16.pdf (trailer, file position 753): unexpected dictionary close token diff --git a/qpdf/qtest/qpdf/bad16.out b/qpdf/qtest/qpdf/bad16.out index 315f5203..d3a72218 100644 --- a/qpdf/qtest/qpdf/bad16.out +++ b/qpdf/qtest/qpdf/bad16.out @@ -1 +1 @@ -bad16.pdf: offset 753: unexpected dictionary close token +bad16.pdf (trailer, file position 753): unexpected dictionary close token diff --git a/qpdf/qtest/qpdf/bad17-recover.out b/qpdf/qtest/qpdf/bad17-recover.out index bcaa948f..60d28961 100644 --- a/qpdf/qtest/qpdf/bad17-recover.out +++ b/qpdf/qtest/qpdf/bad17-recover.out @@ -1,4 +1,4 @@ -WARNING: bad17.pdf: offset 0: file is damaged -WARNING: bad17.pdf: offset 753: dictionary ending here has an odd number of elements +WARNING: bad17.pdf: file is damaged +WARNING: bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements WARNING: Attempting to reconstruct cross-reference table -bad17.pdf: offset 753: dictionary ending here has an odd number of elements +bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements diff --git a/qpdf/qtest/qpdf/bad17.out b/qpdf/qtest/qpdf/bad17.out index 36c059d6..7285b0ae 100644 --- a/qpdf/qtest/qpdf/bad17.out +++ b/qpdf/qtest/qpdf/bad17.out @@ -1 +1 @@ -bad17.pdf: offset 753: dictionary ending here has an odd number of elements +bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements diff --git a/qpdf/qtest/qpdf/bad18-recover.out b/qpdf/qtest/qpdf/bad18-recover.out index 2a4d2a06..b81c6d7a 100644 --- a/qpdf/qtest/qpdf/bad18-recover.out +++ b/qpdf/qtest/qpdf/bad18-recover.out @@ -1,4 +1,4 @@ -WARNING: bad18.pdf: offset 0: file is damaged -WARNING: bad18.pdf: offset 753: unexpected ) +WARNING: bad18.pdf: file is damaged +WARNING: bad18.pdf (trailer, file position 753): unexpected ) WARNING: Attempting to reconstruct cross-reference table -bad18.pdf: offset 753: unexpected ) +bad18.pdf (trailer, file position 753): unexpected ) diff --git a/qpdf/qtest/qpdf/bad18.out b/qpdf/qtest/qpdf/bad18.out index 5e319d9b..b5ea0907 100644 --- a/qpdf/qtest/qpdf/bad18.out +++ b/qpdf/qtest/qpdf/bad18.out @@ -1 +1 @@ -bad18.pdf: offset 753: unexpected ) +bad18.pdf (trailer, file position 753): unexpected ) diff --git a/qpdf/qtest/qpdf/bad19-recover.out b/qpdf/qtest/qpdf/bad19-recover.out index 4fa46af8..7e99befd 100644 --- a/qpdf/qtest/qpdf/bad19-recover.out +++ b/qpdf/qtest/qpdf/bad19-recover.out @@ -1,4 +1,4 @@ -WARNING: bad19.pdf: offset 0: file is damaged -WARNING: bad19.pdf: offset 753: unexpected > +WARNING: bad19.pdf: file is damaged +WARNING: bad19.pdf (trailer, file position 753): unexpected > WARNING: Attempting to reconstruct cross-reference table -bad19.pdf: offset 753: unexpected > +bad19.pdf (trailer, file position 753): unexpected > diff --git a/qpdf/qtest/qpdf/bad19.out b/qpdf/qtest/qpdf/bad19.out index 57ffd7d2..3ff479a1 100644 --- a/qpdf/qtest/qpdf/bad19.out +++ b/qpdf/qtest/qpdf/bad19.out @@ -1 +1 @@ -bad19.pdf: offset 753: unexpected > +bad19.pdf (trailer, file position 753): unexpected > diff --git a/qpdf/qtest/qpdf/bad2-recover.out b/qpdf/qtest/qpdf/bad2-recover.out index 142f10ff..277913ef 100644 --- a/qpdf/qtest/qpdf/bad2-recover.out +++ b/qpdf/qtest/qpdf/bad2-recover.out @@ -1,4 +1,4 @@ -WARNING: bad2.pdf: offset 0: file is damaged +WARNING: bad2.pdf: file is damaged WARNING: bad2.pdf: can't find startxref WARNING: Attempting to reconstruct cross-reference table /QTest is implicit diff --git a/qpdf/qtest/qpdf/bad20-recover.out b/qpdf/qtest/qpdf/bad20-recover.out index 95fc3ca6..2924a563 100644 --- a/qpdf/qtest/qpdf/bad20-recover.out +++ b/qpdf/qtest/qpdf/bad20-recover.out @@ -1,4 +1,4 @@ -WARNING: bad20.pdf: offset 0: file is damaged -WARNING: bad20.pdf: offset 753: invalid character (q) in hexstring +WARNING: bad20.pdf: file is damaged +WARNING: bad20.pdf (trailer, file position 753): invalid character (q) in hexstring WARNING: Attempting to reconstruct cross-reference table -bad20.pdf: offset 753: invalid character (q) in hexstring +bad20.pdf (trailer, file position 753): invalid character (q) in hexstring diff --git a/qpdf/qtest/qpdf/bad20.out b/qpdf/qtest/qpdf/bad20.out index f70b938a..0b70ae18 100644 --- a/qpdf/qtest/qpdf/bad20.out +++ b/qpdf/qtest/qpdf/bad20.out @@ -1 +1 @@ -bad20.pdf: offset 753: invalid character (q) in hexstring +bad20.pdf (trailer, file position 753): invalid character (q) in hexstring diff --git a/qpdf/qtest/qpdf/bad21-recover.out b/qpdf/qtest/qpdf/bad21-recover.out index 7423fe4f..b45319ee 100644 --- a/qpdf/qtest/qpdf/bad21-recover.out +++ b/qpdf/qtest/qpdf/bad21-recover.out @@ -1,4 +1,4 @@ -WARNING: bad21.pdf: offset 0: file is damaged -WARNING: bad21.pdf: offset 742: invalid name token +WARNING: bad21.pdf: file is damaged +WARNING: bad21.pdf (trailer, file position 742): invalid name token WARNING: Attempting to reconstruct cross-reference table -bad21.pdf: offset 742: invalid name token +bad21.pdf (trailer, file position 742): invalid name token diff --git a/qpdf/qtest/qpdf/bad21.out b/qpdf/qtest/qpdf/bad21.out index fb71b5ac..055484cf 100644 --- a/qpdf/qtest/qpdf/bad21.out +++ b/qpdf/qtest/qpdf/bad21.out @@ -1 +1 @@ -bad21.pdf: offset 742: invalid name token +bad21.pdf (trailer, file position 742): invalid name token diff --git a/qpdf/qtest/qpdf/bad22-recover.out b/qpdf/qtest/qpdf/bad22-recover.out index 823e8b96..41eb3e34 100644 --- a/qpdf/qtest/qpdf/bad22-recover.out +++ b/qpdf/qtest/qpdf/bad22-recover.out @@ -1,4 +1,4 @@ -WARNING: bad22.pdf: offset 341: attempting to recover stream length +WARNING: bad22.pdf (object 4 0, file position 341): attempting to recover stream length /QTest is indirect /QTest is a stream. Dictionary: << /Qength 44 >> Raw stream data: diff --git a/qpdf/qtest/qpdf/bad22.out b/qpdf/qtest/qpdf/bad22.out index 40d92a45..2ff4de23 100644 --- a/qpdf/qtest/qpdf/bad22.out +++ b/qpdf/qtest/qpdf/bad22.out @@ -1 +1 @@ -bad22.pdf: offset 317: stream dictionary lacks /Length key +bad22.pdf (object 4 0, file position 317): stream dictionary lacks /Length key diff --git a/qpdf/qtest/qpdf/bad23-recover.out b/qpdf/qtest/qpdf/bad23-recover.out index 981766cd..773205a9 100644 --- a/qpdf/qtest/qpdf/bad23-recover.out +++ b/qpdf/qtest/qpdf/bad23-recover.out @@ -1,4 +1,4 @@ -WARNING: bad23.pdf: offset 341: attempting to recover stream length +WARNING: bad23.pdf (object 4 0, file position 341): attempting to recover stream length /QTest is indirect /QTest is a stream. Dictionary: << /Length () >> Raw stream data: diff --git a/qpdf/qtest/qpdf/bad23.out b/qpdf/qtest/qpdf/bad23.out index 5cef84f9..9ff20de3 100644 --- a/qpdf/qtest/qpdf/bad23.out +++ b/qpdf/qtest/qpdf/bad23.out @@ -1 +1 @@ -bad23.pdf: offset 317: /Length key in stream dictionary is not an integer +bad23.pdf (object 4 0, file position 317): /Length key in stream dictionary is not an integer diff --git a/qpdf/qtest/qpdf/bad24-recover.out b/qpdf/qtest/qpdf/bad24-recover.out index 5e643eff..0af01e90 100644 --- a/qpdf/qtest/qpdf/bad24-recover.out +++ b/qpdf/qtest/qpdf/bad24-recover.out @@ -1,2 +1,2 @@ -WARNING: bad24.pdf: offset 341: attempting to recover stream length -bad24.pdf: offset 341: unable to recover stream data +WARNING: bad24.pdf (object 4 0, file position 341): attempting to recover stream length +bad24.pdf (object 4 0, file position 341): unable to recover stream data diff --git a/qpdf/qtest/qpdf/bad24.out b/qpdf/qtest/qpdf/bad24.out index 76baa51a..f503214f 100644 --- a/qpdf/qtest/qpdf/bad24.out +++ b/qpdf/qtest/qpdf/bad24.out @@ -1 +1 @@ -bad24.pdf: offset 385: expected endstream +bad24.pdf (object 4 0, file position 385): expected endstream diff --git a/qpdf/qtest/qpdf/bad25-recover.out b/qpdf/qtest/qpdf/bad25-recover.out index f8a18758..9528a504 100644 --- a/qpdf/qtest/qpdf/bad25-recover.out +++ b/qpdf/qtest/qpdf/bad25-recover.out @@ -1,4 +1,10 @@ -WARNING: bad25.pdf: offset 0: file is damaged -WARNING: bad25.pdf: offset 307: expected n n obj +WARNING: bad25.pdf: file is damaged +WARNING: bad25.pdf (object 4 0, file position 307): expected n n obj WARNING: Attempting to reconstruct cross-reference table -bad25.pdf: offset 307: expected n n obj +WARNING: bad25.pdf: object 4 0 not found in file after regenerating cross reference table +/QTest is implicit +/QTest is indirect +/QTest is null +unparse: 4 0 R +unparseResolved: null +test 1 done diff --git a/qpdf/qtest/qpdf/bad25.out b/qpdf/qtest/qpdf/bad25.out index 11e3899a..f336b343 100644 --- a/qpdf/qtest/qpdf/bad25.out +++ b/qpdf/qtest/qpdf/bad25.out @@ -1 +1 @@ -bad25.pdf: offset 307: expected n n obj +bad25.pdf (object 4 0, file position 307): expected n n obj diff --git a/qpdf/qtest/qpdf/bad26-recover.out b/qpdf/qtest/qpdf/bad26-recover.out index 64b6f610..b3da37ce 100644 --- a/qpdf/qtest/qpdf/bad26-recover.out +++ b/qpdf/qtest/qpdf/bad26-recover.out @@ -1,4 +1,10 @@ -WARNING: bad26.pdf: offset 0: file is damaged -WARNING: bad26.pdf: offset 307: expected n n obj +WARNING: bad26.pdf: file is damaged +WARNING: bad26.pdf (object 4 0, file position 307): expected n n obj WARNING: Attempting to reconstruct cross-reference table -bad26.pdf: offset 307: expected n n obj +WARNING: bad26.pdf: object 4 0 not found in file after regenerating cross reference table +/QTest is implicit +/QTest is indirect +/QTest is null +unparse: 4 0 R +unparseResolved: null +test 1 done diff --git a/qpdf/qtest/qpdf/bad26.out b/qpdf/qtest/qpdf/bad26.out index 2b1b01e2..30c3b723 100644 --- a/qpdf/qtest/qpdf/bad26.out +++ b/qpdf/qtest/qpdf/bad26.out @@ -1 +1 @@ -bad26.pdf: offset 307: expected n n obj +bad26.pdf (object 4 0, file position 307): expected n n obj diff --git a/qpdf/qtest/qpdf/bad27-recover.out b/qpdf/qtest/qpdf/bad27-recover.out index be980b95..18a133d0 100644 --- a/qpdf/qtest/qpdf/bad27-recover.out +++ b/qpdf/qtest/qpdf/bad27-recover.out @@ -1,4 +1,10 @@ -WARNING: bad27.pdf: offset 0: file is damaged -WARNING: bad27.pdf: offset 307: expected n n obj +WARNING: bad27.pdf: file is damaged +WARNING: bad27.pdf (object 4 0, file position 307): expected n n obj WARNING: Attempting to reconstruct cross-reference table -bad27.pdf: offset 307: expected n n obj +WARNING: bad27.pdf: object 4 0 not found in file after regenerating cross reference table +/QTest is implicit +/QTest is indirect +/QTest is null +unparse: 4 0 R +unparseResolved: null +test 1 done diff --git a/qpdf/qtest/qpdf/bad27.out b/qpdf/qtest/qpdf/bad27.out index a0c47a7c..2c494e4f 100644 --- a/qpdf/qtest/qpdf/bad27.out +++ b/qpdf/qtest/qpdf/bad27.out @@ -1 +1 @@ -bad27.pdf: offset 307: expected n n obj +bad27.pdf (object 4 0, file position 307): expected n n obj diff --git a/qpdf/qtest/qpdf/bad28-recover.out b/qpdf/qtest/qpdf/bad28-recover.out index 3bf944d5..62322d92 100644 --- a/qpdf/qtest/qpdf/bad28-recover.out +++ b/qpdf/qtest/qpdf/bad28-recover.out @@ -1,4 +1,4 @@ -WARNING: bad28.pdf: offset 395: expected endobj +WARNING: bad28.pdf (object 4 0, file position 395): expected endobj /QTest is indirect /QTest is a stream. Dictionary: << /Length 44 >> Raw stream data: diff --git a/qpdf/qtest/qpdf/bad28.out b/qpdf/qtest/qpdf/bad28.out index a0809eb9..11309bc5 100644 --- a/qpdf/qtest/qpdf/bad28.out +++ b/qpdf/qtest/qpdf/bad28.out @@ -1,4 +1,4 @@ -WARNING: bad28.pdf: offset 395: expected endobj +WARNING: bad28.pdf (object 4 0, file position 395): expected endobj /QTest is indirect /QTest is a stream. Dictionary: << /Length 44 >> Raw stream data: diff --git a/qpdf/qtest/qpdf/bad29-recover.out b/qpdf/qtest/qpdf/bad29-recover.out index bc6e38d5..a39e7eda 100644 --- a/qpdf/qtest/qpdf/bad29-recover.out +++ b/qpdf/qtest/qpdf/bad29-recover.out @@ -1,4 +1,4 @@ -WARNING: bad29.pdf: offset 0: file is damaged -WARNING: bad29.pdf: offset 742: null character not allowed in name token +WARNING: bad29.pdf: file is damaged +WARNING: bad29.pdf (trailer, file position 742): null character not allowed in name token WARNING: Attempting to reconstruct cross-reference table -bad29.pdf: offset 742: null character not allowed in name token +bad29.pdf (trailer, file position 742): null character not allowed in name token diff --git a/qpdf/qtest/qpdf/bad29.out b/qpdf/qtest/qpdf/bad29.out index 9f279743..7d4189ec 100644 --- a/qpdf/qtest/qpdf/bad29.out +++ b/qpdf/qtest/qpdf/bad29.out @@ -1 +1 @@ -bad29.pdf: offset 742: null character not allowed in name token +bad29.pdf (trailer, file position 742): null character not allowed in name token diff --git a/qpdf/qtest/qpdf/bad3-recover.out b/qpdf/qtest/qpdf/bad3-recover.out index d205398a..927c03bf 100644 --- a/qpdf/qtest/qpdf/bad3-recover.out +++ b/qpdf/qtest/qpdf/bad3-recover.out @@ -1,5 +1,5 @@ -WARNING: bad3.pdf: offset 0: file is damaged -WARNING: bad3.pdf: offset 542: xref not found +WARNING: bad3.pdf: file is damaged +WARNING: bad3.pdf (file position 542): xref not found WARNING: Attempting to reconstruct cross-reference table /QTest is implicit /QTest is direct diff --git a/qpdf/qtest/qpdf/bad3.out b/qpdf/qtest/qpdf/bad3.out index 22ee8fbd..ef99562b 100644 --- a/qpdf/qtest/qpdf/bad3.out +++ b/qpdf/qtest/qpdf/bad3.out @@ -1 +1 @@ -bad3.pdf: offset 542: xref not found +bad3.pdf (file position 542): xref not found diff --git a/qpdf/qtest/qpdf/bad30-recover.out b/qpdf/qtest/qpdf/bad30-recover.out index 95d194d4..515bfbd5 100644 --- a/qpdf/qtest/qpdf/bad30-recover.out +++ b/qpdf/qtest/qpdf/bad30-recover.out @@ -3,4 +3,4 @@ Raw stream data: x%11 b;t4| wXID8G>rQu O E:IWPlII)rp4~;As/҅jcszT.?u<*6 Uncompressed stream data: -bad30.pdf: offset 629: invalid filter object type for this stream +bad30.pdf (file position 629): stream filter type is not name or array diff --git a/qpdf/qtest/qpdf/bad30.out b/qpdf/qtest/qpdf/bad30.out index 95d194d4..515bfbd5 100644 --- a/qpdf/qtest/qpdf/bad30.out +++ b/qpdf/qtest/qpdf/bad30.out @@ -3,4 +3,4 @@ Raw stream data: x%11 b;t4| wXID8G>rQu O E:IWPlII)rp4~;As/҅jcszT.?u<*6 Uncompressed stream data: -bad30.pdf: offset 629: invalid filter object type for this stream +bad30.pdf (file position 629): stream filter type is not name or array diff --git a/qpdf/qtest/qpdf/bad32-recover.out b/qpdf/qtest/qpdf/bad32-recover.out index d0fe873b..dd002f5e 100644 --- a/qpdf/qtest/qpdf/bad32-recover.out +++ b/qpdf/qtest/qpdf/bad32-recover.out @@ -1,6 +1,7 @@ -WARNING: bad32.pdf: offset 0: file is damaged -WARNING: bad32.pdf: offset 307: expected 4 0 obj +WARNING: bad32.pdf: file is damaged +WARNING: bad32.pdf (object 4 0, file position 307): expected 4 0 obj WARNING: Attempting to reconstruct cross-reference table +WARNING: bad32.pdf: object 4 0 not found in file after regenerating cross reference table /QTest is implicit /QTest is indirect /QTest is null diff --git a/qpdf/qtest/qpdf/bad32.out b/qpdf/qtest/qpdf/bad32.out index 3212ea02..60727cc9 100644 --- a/qpdf/qtest/qpdf/bad32.out +++ b/qpdf/qtest/qpdf/bad32.out @@ -1 +1 @@ -bad32.pdf: offset 307: expected 4 0 obj +bad32.pdf (object 4 0, file position 307): expected 4 0 obj diff --git a/qpdf/qtest/qpdf/bad33-recover.out b/qpdf/qtest/qpdf/bad33-recover.out index 27425f8a..9132c2a7 100644 --- a/qpdf/qtest/qpdf/bad33-recover.out +++ b/qpdf/qtest/qpdf/bad33-recover.out @@ -1,9 +1,9 @@ -WARNING: bad33.pdf: offset 0: file is damaged -WARNING: bad33.pdf: offset 1771: xref not found +WARNING: bad33.pdf: file is damaged +WARNING: bad33.pdf (file position 1771): xref not found WARNING: Attempting to reconstruct cross-reference table /QTest is indirect /QTest is a stream. Dictionary: << /Filter (FlateDecode) /Length 123 >> Raw stream data: x%11 b;t4| wXID8G>rQu O E:IWPlII)rp4~;As/҅jcszT.?u<*6 Uncompressed stream data: -bad33.pdf: offset 629: invalid filter object type for this stream +bad33.pdf (file position 629): stream filter type is not name or array diff --git a/qpdf/qtest/qpdf/bad33.out b/qpdf/qtest/qpdf/bad33.out index d9cbad38..9057b06c 100644 --- a/qpdf/qtest/qpdf/bad33.out +++ b/qpdf/qtest/qpdf/bad33.out @@ -1 +1 @@ -bad33.pdf: offset 1771: xref not found +bad33.pdf (file position 1771): xref not found diff --git a/qpdf/qtest/qpdf/bad34-recover.out b/qpdf/qtest/qpdf/bad34-recover.out new file mode 100644 index 00000000..071ebb91 --- /dev/null +++ b/qpdf/qtest/qpdf/bad34-recover.out @@ -0,0 +1,23 @@ +WARNING: bad34.pdf: file is damaged +WARNING: bad34.pdf (object 4 0, file position 322): expected n n obj +WARNING: Attempting to reconstruct cross-reference table +/QTest is indirect +/QTest is a stream. Dictionary: << /Length 44 /Quack 9 0 R >> +Raw stream data: +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET + +Uncompressed stream data: +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET + +End of stream data +unparse: 4 0 R +unparseResolved: 4 0 R +test 1 done diff --git a/qpdf/qtest/qpdf/bad34.out b/qpdf/qtest/qpdf/bad34.out new file mode 100644 index 00000000..ee65e147 --- /dev/null +++ b/qpdf/qtest/qpdf/bad34.out @@ -0,0 +1 @@ +bad34.pdf (object 4 0, file position 322): expected n n obj diff --git a/qpdf/qtest/qpdf/bad34.pdf b/qpdf/qtest/qpdf/bad34.pdf new file mode 100644 index 00000000..6070a5d5 --- /dev/null +++ b/qpdf/qtest/qpdf/bad34.pdf @@ -0,0 +1,81 @@ +%PDF-1.3 +1 0 obj +<< + /Type /Catalog + /Pages 2 0 R +>> +endobj + +2 0 obj +<< + /Type /Pages + /Kids [ + 3 0 R + ] + /Count 1 +>> +endobj + +4 0 obj +<< + /Length 44 + /Quack 9 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +3 0 obj +<< + /Type /Page + /Parent 2 0 R + /MediaBox [0 0 612 792] + /Contents 4 0 R + /Resources << + /ProcSet 5 0 R + /Font << + /F1 6 0 R + >> + >> +>> +endobj + +5 0 obj +[ + /PDF + /Text +] +endobj + +6 0 obj +<< + /Type /Font + /Subtype /Type1 + /Name /F1 + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding +>> +endobj + +xref +0 7 +0000000000 65535 f +0000000009 00000 n +0000000063 00000 n +0000000135 00000 n +0000000322 00000 n +0000000418 00000 n +0000000453 00000 n +trailer << + /Size 7 + /Root 1 0 R + /QTest 4 0 R +>> +startxref +571 +%%EOF diff --git a/qpdf/qtest/qpdf/bad35-recover.out b/qpdf/qtest/qpdf/bad35-recover.out new file mode 100644 index 00000000..add1666e --- /dev/null +++ b/qpdf/qtest/qpdf/bad35-recover.out @@ -0,0 +1 @@ +bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type diff --git a/qpdf/qtest/qpdf/bad35.out b/qpdf/qtest/qpdf/bad35.out new file mode 100644 index 00000000..add1666e --- /dev/null +++ b/qpdf/qtest/qpdf/bad35.out @@ -0,0 +1 @@ +bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type diff --git a/qpdf/qtest/qpdf/bad35.pdf b/qpdf/qtest/qpdf/bad35.pdf new file mode 100644 index 0000000000000000000000000000000000000000..799cb09bf15547067b06a2ea5b9323224ae8f9a8 GIT binary patch literal 807 zcmZuvO>fgc5KRIX_QGFy7fWyed)FUHq^c4ru|S0q5=%vjdf3KGaMgM%dxHW$r+ z#SJmDcBvw2X}vQ$Z{N)9n_%%aeWE)xUfz;%l;Ws z-twwx9b(D$2UBm|HzaZ*j*ms_8l!K7LL_1&N+ghx5-C)sLJ6{?(Lawjn5mtGuPjZt2UZ1&FhUV zt9py@ysF>SZFPUZ1(>?*wIYiJ7u@HA^7pbP4DOM$f@!HGm7w@a_L!baMJP(LUvyLQ z62+C67mx5y4*0vt`m!|46RxVC22-})*QN1lOuY){SW`Mk@ANgr83UKrI*RbDkoUPT yG?CpjJQO^jZ#Z~97}1~Bkk*LS6IxG)qi5t*gGVZDoo?K|q0%xr7C|uol#9Q4IK+ #include #include +#include #include #include #include @@ -319,6 +320,7 @@ void runtest(int n, char const* filename) int main(int argc, char* argv[]) { + setlinebuf(stdout); if ((whoami = strrchr(argv[0], '/')) == NULL) { whoami = argv[0];