categorize all error messages and include object information if available

git-svn-id: svn+q:///qpdf/trunk@829 71b93d88-0707-0410-a8cf-f5a4172ac649
This commit is contained in:
Jay Berkenbilt 2009-10-19 23:09:19 +00:00
parent b67a3c15e7
commit 3f8c4c2736
95 changed files with 662 additions and 268 deletions

View File

@ -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

23
TODO
View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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__

View File

@ -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()
}
}
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");
}
}

View File

@ -1,18 +1,56 @@
#include <qpdf/QPDFExc.hh>
#include <qpdf/QUtil.hh>
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;
}

View File

@ -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;

View File

@ -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

View File

@ -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)
{
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"));

View File

@ -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;
}

View File

@ -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");
}
}

View File

@ -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)
{

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1 +1 @@
bad1.pdf: offset 0: not a PDF file
bad1.pdf: not a PDF file

View File

@ -1 +1 @@
bad1.pdf: offset 0: not a PDF file
bad1.pdf: not a PDF file

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1 +1 @@
bad13.pdf: offset 753: unexpected brace token
bad13.pdf (trailer, file position 753): unexpected brace token

View File

@ -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

View File

@ -1 +1 @@
bad14.pdf: offset 753: unexpected brace token
bad14.pdf (trailer, file position 753): unexpected brace token

View File

@ -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

View File

@ -1 +1 @@
bad15.pdf: offset 753: unexpected array close token
bad15.pdf (trailer, file position 753): unexpected array close token

View File

@ -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

View File

@ -1 +1 @@
bad16.pdf: offset 753: unexpected dictionary close token
bad16.pdf (trailer, file position 753): unexpected dictionary close token

View File

@ -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

View File

@ -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

View File

@ -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 )

View File

@ -1 +1 @@
bad18.pdf: offset 753: unexpected )
bad18.pdf (trailer, file position 753): unexpected )

View File

@ -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 >

View File

@ -1 +1 @@
bad19.pdf: offset 753: unexpected >
bad19.pdf (trailer, file position 753): unexpected >

View File

@ -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

View File

@ -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

View File

@ -1 +1 @@
bad20.pdf: offset 753: invalid character (q) in hexstring
bad20.pdf (trailer, file position 753): invalid character (q) in hexstring

View File

@ -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

View File

@ -1 +1 @@
bad21.pdf: offset 742: invalid name token
bad21.pdf (trailer, file position 742): invalid name token

View File

@ -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:

View File

@ -1 +1 @@
bad22.pdf: offset 317: stream dictionary lacks /Length key
bad22.pdf (object 4 0, file position 317): stream dictionary lacks /Length key

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -1 +1 @@
bad24.pdf: offset 385: expected endstream
bad24.pdf (object 4 0, file position 385): expected endstream

View File

@ -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

View File

@ -1 +1 @@
bad25.pdf: offset 307: expected n n obj
bad25.pdf (object 4 0, file position 307): expected n n obj

View File

@ -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

View File

@ -1 +1 @@
bad26.pdf: offset 307: expected n n obj
bad26.pdf (object 4 0, file position 307): expected n n obj

View File

@ -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

View File

@ -1 +1 @@
bad27.pdf: offset 307: expected n n obj
bad27.pdf (object 4 0, file position 307): expected n n obj

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1 +1 @@
bad3.pdf: offset 542: xref not found
bad3.pdf (file position 542): xref not found

View File

@ -3,4 +3,4 @@
Raw stream data:
xœ%<25>11 û¼b;t<>à4| wXIDì Øå÷8G·«Í>rQ¨uŠ êEŒ:©IWìÃPlíµII)Ãr´p4~;§ÎAs/òÒ…jcúú¾÷Žs§åözû»ž<C2BB>T.?®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

View File

@ -3,4 +3,4 @@
Raw stream data:
xœ%<25>11 û¼b;t<>à4| wXIDì Øå÷8G·«Í>rQ¨uŠ êEŒ:©IWìÃPlíµII)Ãr´p4~;§ÎAs/òÒ…jcúú¾÷Žs§åözû»ž<C2BB>T.?®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

View File

@ -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

View File

@ -1 +1 @@
bad32.pdf: offset 307: expected 4 0 obj
bad32.pdf (object 4 0, file position 307): expected 4 0 obj

View File

@ -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œ%<25>11 û¼b;t<>à4| wXIDì Øå÷8G·«Í>rQ¨uŠ êEŒ:©IWìÃPlíµII)Ãr´p4~;§ÎAs/òÒ…jcúú¾÷Žs§åözû»ž<C2BB>T.?®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

View File

@ -1 +1 @@
bad33.pdf: offset 1771: xref not found
bad33.pdf (file position 1771): xref not found

View File

@ -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

View File

@ -0,0 +1 @@
bad34.pdf (object 4 0, file position 322): expected n n obj

81
qpdf/qtest/qpdf/bad34.pdf Normal file
View File

@ -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

View File

@ -0,0 +1 @@
bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type

View File

@ -0,0 +1 @@
bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type

BIN
qpdf/qtest/qpdf/bad35.pdf Normal file

Binary file not shown.

View File

@ -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

View File

@ -1 +1 @@
bad4.pdf: offset 547: xref syntax invalid
bad4.pdf (xref table, file position 547): xref syntax invalid

View File

@ -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

View File

@ -1 +1 @@
bad5.pdf: offset 591: invalid xref entry (obj=2)
bad5.pdf (xref table, file position 591): invalid xref entry (obj=2)

View File

@ -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

View File

@ -1 +1 @@
bad7.pdf: offset 698: expected trailer dictionary
bad7.pdf (file position 698): expected trailer dictionary

View File

@ -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

View File

@ -1 +1 @@
bad8.pdf: offset 543: xref not found
bad8.pdf (file position 543): xref not found

View File

@ -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

View File

@ -1 +1 @@
bad9.pdf: offset 712: trailer dictionary lacks /Size key
bad9.pdf (trailer, file position 712): trailer dictionary lacks /Size key

View File

@ -1 +1 @@
error: bad33.pdf: offset 1771: xref not found
error: bad33.pdf (file position 1771): xref not found

View File

@ -1 +1 @@
error: bad1.pdf: offset 0: not a PDF file
error: bad1.pdf: not a PDF file

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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];