aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--TODO23
-rw-r--r--examples/qtest/bookmarks.test2
-rw-r--r--examples/qtest/npages.test2
-rw-r--r--include/qpdf/QPDF.hh10
-rw-r--r--include/qpdf/QPDFExc.hh36
-rw-r--r--libqpdf/QPDF.cc250
-rw-r--r--libqpdf/QPDFExc.cc52
-rw-r--r--libqpdf/QPDFXRefEntry.cc9
-rw-r--r--libqpdf/QPDF_Stream.cc7
-rw-r--r--libqpdf/QPDF_encryption.cc99
-rw-r--r--libqpdf/QPDF_linearization.cc34
-rw-r--r--libqpdf/QPDF_optimization.cc14
-rw-r--r--qpdf/qpdf.cc12
-rw-r--r--qpdf/qpdf.testcov2
-rw-r--r--qpdf/qtest/qpdf.test4
-rw-r--r--qpdf/qtest/qpdf/append-page-content-damaged-c-check.out4
-rw-r--r--qpdf/qtest/qpdf/append-page-content-damaged-check.out2
-rw-r--r--qpdf/qtest/qpdf/append-page-content-damaged.out2
-rw-r--r--qpdf/qtest/qpdf/bad1-recover.out2
-rw-r--r--qpdf/qtest/qpdf/bad1.out2
-rw-r--r--qpdf/qtest/qpdf/bad10-recover.out4
-rw-r--r--qpdf/qtest/qpdf/bad10.out2
-rw-r--r--qpdf/qtest/qpdf/bad11-recover.out4
-rw-r--r--qpdf/qtest/qpdf/bad11.out2
-rw-r--r--qpdf/qtest/qpdf/bad13-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad13.out2
-rw-r--r--qpdf/qtest/qpdf/bad14-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad14.out2
-rw-r--r--qpdf/qtest/qpdf/bad15-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad15.out2
-rw-r--r--qpdf/qtest/qpdf/bad16-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad16.out2
-rw-r--r--qpdf/qtest/qpdf/bad17-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad17.out2
-rw-r--r--qpdf/qtest/qpdf/bad18-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad18.out2
-rw-r--r--qpdf/qtest/qpdf/bad19-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad19.out2
-rw-r--r--qpdf/qtest/qpdf/bad2-recover.out2
-rw-r--r--qpdf/qtest/qpdf/bad20-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad20.out2
-rw-r--r--qpdf/qtest/qpdf/bad21-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad21.out2
-rw-r--r--qpdf/qtest/qpdf/bad22-recover.out2
-rw-r--r--qpdf/qtest/qpdf/bad22.out2
-rw-r--r--qpdf/qtest/qpdf/bad23-recover.out2
-rw-r--r--qpdf/qtest/qpdf/bad23.out2
-rw-r--r--qpdf/qtest/qpdf/bad24-recover.out4
-rw-r--r--qpdf/qtest/qpdf/bad24.out2
-rw-r--r--qpdf/qtest/qpdf/bad25-recover.out12
-rw-r--r--qpdf/qtest/qpdf/bad25.out2
-rw-r--r--qpdf/qtest/qpdf/bad26-recover.out12
-rw-r--r--qpdf/qtest/qpdf/bad26.out2
-rw-r--r--qpdf/qtest/qpdf/bad27-recover.out12
-rw-r--r--qpdf/qtest/qpdf/bad27.out2
-rw-r--r--qpdf/qtest/qpdf/bad28-recover.out2
-rw-r--r--qpdf/qtest/qpdf/bad28.out2
-rw-r--r--qpdf/qtest/qpdf/bad29-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad29.out2
-rw-r--r--qpdf/qtest/qpdf/bad3-recover.out4
-rw-r--r--qpdf/qtest/qpdf/bad3.out2
-rw-r--r--qpdf/qtest/qpdf/bad30-recover.out2
-rw-r--r--qpdf/qtest/qpdf/bad30.out2
-rw-r--r--qpdf/qtest/qpdf/bad32-recover.out5
-rw-r--r--qpdf/qtest/qpdf/bad32.out2
-rw-r--r--qpdf/qtest/qpdf/bad33-recover.out6
-rw-r--r--qpdf/qtest/qpdf/bad33.out2
-rw-r--r--qpdf/qtest/qpdf/bad34-recover.out23
-rw-r--r--qpdf/qtest/qpdf/bad34.out1
-rw-r--r--qpdf/qtest/qpdf/bad34.pdf81
-rw-r--r--qpdf/qtest/qpdf/bad35-recover.out1
-rw-r--r--qpdf/qtest/qpdf/bad35.out1
-rw-r--r--qpdf/qtest/qpdf/bad35.pdfbin0 -> 807 bytes
-rw-r--r--qpdf/qtest/qpdf/bad4-recover.out4
-rw-r--r--qpdf/qtest/qpdf/bad4.out2
-rw-r--r--qpdf/qtest/qpdf/bad5-recover.out4
-rw-r--r--qpdf/qtest/qpdf/bad5.out2
-rw-r--r--qpdf/qtest/qpdf/bad7-recover.out4
-rw-r--r--qpdf/qtest/qpdf/bad7.out2
-rw-r--r--qpdf/qtest/qpdf/bad8-recover.out4
-rw-r--r--qpdf/qtest/qpdf/bad8.out2
-rw-r--r--qpdf/qtest/qpdf/bad9-recover.out4
-rw-r--r--qpdf/qtest/qpdf/bad9.out2
-rw-r--r--qpdf/qtest/qpdf/c-no-recovery.out2
-rw-r--r--qpdf/qtest/qpdf/c-read-errors.out2
-rw-r--r--qpdf/qtest/qpdf/c-read-warnings-and-errors.out6
-rw-r--r--qpdf/qtest/qpdf/c-write-damaged.out2
-rw-r--r--qpdf/qtest/qpdf/c-write-errors.out2
-rw-r--r--qpdf/qtest/qpdf/c-write-warnings-and-errors.out6
-rw-r--r--qpdf/qtest/qpdf/damaged-stream.out2
-rw-r--r--qpdf/qtest/qpdf/heifer.out6
-rw-r--r--qpdf/qtest/qpdf/xref-with-short-size-recover.out2
-rw-r--r--qpdf/qtest/qpdf/xref-with-short-size.out2
-rw-r--r--qpdf/test_driver.cc2
95 files changed, 660 insertions, 266 deletions
diff --git a/ChangeLog b/ChangeLog
index 13badadb..0a7aab28 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2009-10-19 Jay Berkenbilt <jberkenb@argonst.com>
+
+ * 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 <ejb@ql.org>
* 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<QPDFObject> 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 <qpdf/DLL.h>
+#include <qpdf/Constants.h>
#include <stdexcept>
+#include <stddef.h>
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<ObjGen> to_delete;
+ for (std::map<ObjGen, QPDFXRefEntry>::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<ObjGen>::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()
}
}
+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, int objid, int generation,
- bool in_object_stream)
+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 <qpdf/QPDFExc.hh>
#include <qpdf/QUtil.hh>
-QPDFExc::QPDFExc(std::string const& message) :
- std::runtime_error(message)
+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(createWhat(filename, object, offset, message)),
+ error_code(error_code),
+ filename(filename),
+ object(object),
+ offset(offset),
+ message(message)
{
}
-QPDFExc::QPDFExc(std::string const& filename, int offset,
- std::string const& message) :
- std::runtime_error(filename + ": offset " + QUtil::int_to_string(offset) +
- ": " + message)
+QPDFExc::~QPDFExc() throw ()
{
}
-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<std::string>& 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<Buffer> 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<char> 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)
+ {
+ throw;
+ }
+ catch (std::runtime_error& 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 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<int> 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 <string.h>
#include <stdlib.h>
#include <fcntl.h>
+#include <stdio.h>
#include <qpdf/QUtil.hh>
#include <qpdf/QTC.hh>
@@ -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 00000000..799cb09b
--- /dev/null
+++ b/qpdf/qtest/qpdf/bad35.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/bad4-recover.out b/qpdf/qtest/qpdf/bad4-recover.out
index f7c56522..12512e98 100644
--- a/qpdf/qtest/qpdf/bad4-recover.out
+++ b/qpdf/qtest/qpdf/bad4-recover.out
@@ -1,5 +1,5 @@
-WARNING: bad4.pdf: offset 0: file is damaged
-WARNING: bad4.pdf: offset 547: xref syntax invalid
+WARNING: bad4.pdf: file is damaged
+WARNING: bad4.pdf (xref table, file position 547): xref syntax invalid
WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit
/QTest is direct
diff --git a/qpdf/qtest/qpdf/bad4.out b/qpdf/qtest/qpdf/bad4.out
index c29db5f4..fbfde227 100644
--- a/qpdf/qtest/qpdf/bad4.out
+++ b/qpdf/qtest/qpdf/bad4.out
@@ -1 +1 @@
-bad4.pdf: offset 547: xref syntax invalid
+bad4.pdf (xref table, file position 547): xref syntax invalid
diff --git a/qpdf/qtest/qpdf/bad5-recover.out b/qpdf/qtest/qpdf/bad5-recover.out
index 44f76aed..cdb207ee 100644
--- a/qpdf/qtest/qpdf/bad5-recover.out
+++ b/qpdf/qtest/qpdf/bad5-recover.out
@@ -1,5 +1,5 @@
-WARNING: bad5.pdf: offset 0: file is damaged
-WARNING: bad5.pdf: offset 591: invalid xref entry (obj=2)
+WARNING: bad5.pdf: file is damaged
+WARNING: bad5.pdf (xref table, file position 591): invalid xref entry (obj=2)
WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit
/QTest is direct
diff --git a/qpdf/qtest/qpdf/bad5.out b/qpdf/qtest/qpdf/bad5.out
index 5a9d8a13..29556195 100644
--- a/qpdf/qtest/qpdf/bad5.out
+++ b/qpdf/qtest/qpdf/bad5.out
@@ -1 +1 @@
-bad5.pdf: offset 591: invalid xref entry (obj=2)
+bad5.pdf (xref table, file position 591): invalid xref entry (obj=2)
diff --git a/qpdf/qtest/qpdf/bad7-recover.out b/qpdf/qtest/qpdf/bad7-recover.out
index bd694b65..139835f8 100644
--- a/qpdf/qtest/qpdf/bad7-recover.out
+++ b/qpdf/qtest/qpdf/bad7-recover.out
@@ -1,4 +1,4 @@
-WARNING: bad7.pdf: offset 0: file is damaged
-WARNING: bad7.pdf: offset 698: expected trailer dictionary
+WARNING: bad7.pdf: file is damaged
+WARNING: bad7.pdf (file position 698): expected trailer dictionary
WARNING: Attempting to reconstruct cross-reference table
bad7.pdf: unable to find trailer dictionary while recovering damaged file
diff --git a/qpdf/qtest/qpdf/bad7.out b/qpdf/qtest/qpdf/bad7.out
index aac77151..ae3da54a 100644
--- a/qpdf/qtest/qpdf/bad7.out
+++ b/qpdf/qtest/qpdf/bad7.out
@@ -1 +1 @@
-bad7.pdf: offset 698: expected trailer dictionary
+bad7.pdf (file position 698): expected trailer dictionary
diff --git a/qpdf/qtest/qpdf/bad8-recover.out b/qpdf/qtest/qpdf/bad8-recover.out
index e9144d42..389b8304 100644
--- a/qpdf/qtest/qpdf/bad8-recover.out
+++ b/qpdf/qtest/qpdf/bad8-recover.out
@@ -1,5 +1,5 @@
-WARNING: bad8.pdf: offset 0: file is damaged
-WARNING: bad8.pdf: offset 543: xref not found
+WARNING: bad8.pdf: file is damaged
+WARNING: bad8.pdf (file position 543): xref not found
WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit
/QTest is direct
diff --git a/qpdf/qtest/qpdf/bad8.out b/qpdf/qtest/qpdf/bad8.out
index a3338ed4..ace464d4 100644
--- a/qpdf/qtest/qpdf/bad8.out
+++ b/qpdf/qtest/qpdf/bad8.out
@@ -1 +1 @@
-bad8.pdf: offset 543: xref not found
+bad8.pdf (file position 543): xref not found
diff --git a/qpdf/qtest/qpdf/bad9-recover.out b/qpdf/qtest/qpdf/bad9-recover.out
index 424b47cf..4eb923f8 100644
--- a/qpdf/qtest/qpdf/bad9-recover.out
+++ b/qpdf/qtest/qpdf/bad9-recover.out
@@ -1,5 +1,5 @@
-WARNING: bad9.pdf: offset 0: file is damaged
-WARNING: bad9.pdf: offset 712: trailer dictionary lacks /Size key
+WARNING: bad9.pdf: file is damaged
+WARNING: bad9.pdf (trailer, file position 712): trailer dictionary lacks /Size key
WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit
/QTest is direct
diff --git a/qpdf/qtest/qpdf/bad9.out b/qpdf/qtest/qpdf/bad9.out
index 6799ccd9..45524ff2 100644
--- a/qpdf/qtest/qpdf/bad9.out
+++ b/qpdf/qtest/qpdf/bad9.out
@@ -1 +1 @@
-bad9.pdf: offset 712: trailer dictionary lacks /Size key
+bad9.pdf (trailer, file position 712): trailer dictionary lacks /Size key
diff --git a/qpdf/qtest/qpdf/c-no-recovery.out b/qpdf/qtest/qpdf/c-no-recovery.out
index 4fae9ebf..e6bc0956 100644
--- a/qpdf/qtest/qpdf/c-no-recovery.out
+++ b/qpdf/qtest/qpdf/c-no-recovery.out
@@ -1 +1 @@
-error: bad33.pdf: offset 1771: xref not found
+error: bad33.pdf (file position 1771): xref not found
diff --git a/qpdf/qtest/qpdf/c-read-errors.out b/qpdf/qtest/qpdf/c-read-errors.out
index 087d5343..1d74816e 100644
--- a/qpdf/qtest/qpdf/c-read-errors.out
+++ b/qpdf/qtest/qpdf/c-read-errors.out
@@ -1 +1 @@
-error: bad1.pdf: offset 0: not a PDF file
+error: bad1.pdf: not a PDF file
diff --git a/qpdf/qtest/qpdf/c-read-warnings-and-errors.out b/qpdf/qtest/qpdf/c-read-warnings-and-errors.out
index e0114a45..16674487 100644
--- a/qpdf/qtest/qpdf/c-read-warnings-and-errors.out
+++ b/qpdf/qtest/qpdf/c-read-warnings-and-errors.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
-error: bad17.pdf: offset 753: dictionary ending here has an odd number of elements
+error: bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements
diff --git a/qpdf/qtest/qpdf/c-write-damaged.out b/qpdf/qtest/qpdf/c-write-damaged.out
index 783c651b..f2b68ddd 100644
--- a/qpdf/qtest/qpdf/c-write-damaged.out
+++ b/qpdf/qtest/qpdf/c-write-damaged.out
@@ -1,3 +1,3 @@
-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/c-write-errors.out b/qpdf/qtest/qpdf/c-write-errors.out
index 6d8ffa77..031c6bf8 100644
--- a/qpdf/qtest/qpdf/c-write-errors.out
+++ b/qpdf/qtest/qpdf/c-write-errors.out
@@ -1 +1 @@
-error: bad30.pdf: offset 629: invalid filter object type for this stream
+error: bad30.pdf (file position 629): stream filter type is not name or array
diff --git a/qpdf/qtest/qpdf/c-write-warnings-and-errors.out b/qpdf/qtest/qpdf/c-write-warnings-and-errors.out
index 2aa9cbb2..e0833410 100644
--- a/qpdf/qtest/qpdf/c-write-warnings-and-errors.out
+++ b/qpdf/qtest/qpdf/c-write-warnings-and-errors.out
@@ -1,4 +1,4 @@
-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
-error: bad33.pdf: offset 629: invalid filter object type for this stream
+error: bad33.pdf (file position 629): stream filter type is not name or array
diff --git a/qpdf/qtest/qpdf/damaged-stream.out b/qpdf/qtest/qpdf/damaged-stream.out
index 002c89e6..39e7260b 100644
--- a/qpdf/qtest/qpdf/damaged-stream.out
+++ b/qpdf/qtest/qpdf/damaged-stream.out
@@ -2,4 +2,4 @@ checking damaged-stream.pdf
PDF Version: 1.3
File is not encrypted
File is not linearized
-WARNING: damaged-stream.pdf: offset 426: error decoding stream data for object 5 0: LZWDecoder: bad code received
+WARNING: damaged-stream.pdf (file position 426): error decoding stream data for object 5 0: LZWDecoder: bad code received
diff --git a/qpdf/qtest/qpdf/heifer.out b/qpdf/qtest/qpdf/heifer.out
index 75012a18..a3d365a2 100644
--- a/qpdf/qtest/qpdf/heifer.out
+++ b/qpdf/qtest/qpdf/heifer.out
@@ -1,5 +1,5 @@
-WARNING: heifer.pdf: offset 0: file is damaged
-WARNING: heifer.pdf: offset 92741: xref not found
+WARNING: heifer.pdf: file is damaged
+WARNING: heifer.pdf (file position 92741): xref not found
WARNING: Attempting to reconstruct cross-reference table
-WARNING: heifer.pdf: offset 51: attempting to recover stream length
+WARNING: heifer.pdf (object 2 0, file position 51): attempting to recover stream length
qpdf: operation succeeded with warnings; resulting file may have some problems
diff --git a/qpdf/qtest/qpdf/xref-with-short-size-recover.out b/qpdf/qtest/qpdf/xref-with-short-size-recover.out
index 52c58986..b57333b1 100644
--- a/qpdf/qtest/qpdf/xref-with-short-size-recover.out
+++ b/qpdf/qtest/qpdf/xref-with-short-size-recover.out
@@ -1,2 +1,2 @@
-WARNING: xref-with-short-size.pdf: offset 16227: Cross-reference stream data has the wrong size; expected = 52; actual = 56
+WARNING: xref-with-short-size.pdf (xref stream, file position 16227): Cross-reference stream data has the wrong size; expected = 52; actual = 56
qpdf: operation succeeded with warnings; resulting file may have some problems
diff --git a/qpdf/qtest/qpdf/xref-with-short-size.out b/qpdf/qtest/qpdf/xref-with-short-size.out
index 19f9963e..12f20e6a 100644
--- a/qpdf/qtest/qpdf/xref-with-short-size.out
+++ b/qpdf/qtest/qpdf/xref-with-short-size.out
@@ -1,4 +1,4 @@
-WARNING: xref-with-short-size.pdf: offset 16227: Cross-reference stream data has the wrong size; expected = 52; actual = 56
+WARNING: xref-with-short-size.pdf (xref stream, file position 16227): Cross-reference stream data has the wrong size; expected = 52; actual = 56
1/0: compressed; stream = 5, index = 1
2/0: compressed; stream = 5, index = 0
3/0: uncompressed; offset = 15
diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc
index 16859ad2..f5ea46bd 100644
--- a/qpdf/test_driver.cc
+++ b/qpdf/test_driver.cc
@@ -9,6 +9,7 @@
#include <qpdf/Pl_Buffer.hh>
#include <qpdf/QPDFWriter.hh>
#include <iostream>
+#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <map>
@@ -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];