2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-05-31 01:10:51 +00:00

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> 2009-10-18 Jay Berkenbilt <ejb@ql.org>
* If forcing version, disable object stream creation and/or * 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 * Add comments for the security functions that map them back to the
items in Adobe's products. items in Adobe's products.
* Add error codes to QPDFException. Change the error interface so * Change the C error interface so that warnings and errors are
that warnings and errors are pointers that can be queried using pointers that can be queried using more C API functions. We need a
more C API functions. We need a way to get a full string as well way to get a full string as well as an error code, file name,
as an error code, file name, offset, and message. We should go offset, and message.
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.
* "Delphi wrapper unit 'qpdf.pas' created by Zarko Gajic * "Delphi wrapper unit 'qpdf.pas' created by Zarko Gajic
(http://delphi.about.com). .. use at your own risk and for whatever (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->runtest("bad",
{$td->COMMAND => "pdf-bookmarks 3.pdf"}, {$td->COMMAND => "pdf-bookmarks 3.pdf"},
{$td->STRING => "pdf-bookmarks processing file 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->EXIT_STATUS => 2},
$td->NORMALIZE_NEWLINES); $td->NORMALIZE_NEWLINES);

View File

@ -16,7 +16,7 @@ $td->runtest("normal",
$td->runtest("error", $td->runtest("error",
{$td->COMMAND => "pdf-npages bad"}, {$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->EXIT_STATUS => 2},
$td->NORMALIZE_NEWLINES); $td->NORMALIZE_NEWLINES);

View File

@ -371,9 +371,11 @@ class DLL_EXPORT QPDF
int processXRefStream(off_t offset, QPDFObjectHandle& xref_stream); int processXRefStream(off_t offset, QPDFObjectHandle& xref_stream);
void insertXrefEntry(int obj, int f0, int f1, int f2, void insertXrefEntry(int obj, int f0, int f1, int f2,
bool overwrite = false); bool overwrite = false);
void setLastObjectDescription(std::string const& description,
int objid, int generation);
QPDFObjectHandle readObject( QPDFObjectHandle readObject(
InputSource*, int objid, int generation, InputSource*, std::string const& description,
bool in_object_stream); int objid, int generation, bool in_object_stream);
QPDFObjectHandle readObjectInternal( QPDFObjectHandle readObjectInternal(
InputSource* input, int objid, int generation, InputSource* input, int objid, int generation,
bool in_object_stream, bool in_object_stream,
@ -383,7 +385,8 @@ class DLL_EXPORT QPDF
QPDFTokenizer::Token readToken(InputSource*); QPDFTokenizer::Token readToken(InputSource*);
QPDFObjectHandle readObjectAtOffset( QPDFObjectHandle readObjectAtOffset(
off_t offset, bool attempt_recovery,
off_t offset, std::string const& description,
int exp_objid, int exp_generation, int exp_objid, int exp_generation,
int& act_objid, int& act_generation); int& act_objid, int& act_generation);
PointerHolder<QPDFObject> resolve(int objid, int generation); PointerHolder<QPDFObject> resolve(int objid, int generation);
@ -734,6 +737,7 @@ class DLL_EXPORT QPDF
QPDFTokenizer tokenizer; QPDFTokenizer tokenizer;
FileInputSource file; FileInputSource file;
std::string last_object_description;
bool encrypted; bool encrypted;
bool encryption_initialized; bool encryption_initialized;
bool ignore_xref_streams; bool ignore_xref_streams;

View File

@ -9,15 +9,47 @@
#define __QPDFEXC_HH__ #define __QPDFEXC_HH__
#include <qpdf/DLL.h> #include <qpdf/DLL.h>
#include <qpdf/Constants.h>
#include <stdexcept> #include <stdexcept>
#include <stddef.h>
class DLL_EXPORT QPDFExc: public std::runtime_error class DLL_EXPORT QPDFExc: public std::runtime_error
{ {
public: public:
QPDFExc(std::string const& message); QPDFExc(qpdf_error_code_e error_code,
QPDFExc(std::string const& filename, int offset, std::string const& filename,
std::string const& object,
off_t offset,
std::string const& message); std::string const& message);
virtual ~QPDFExc() throw (); 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__ #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); size_t len = fread(buffer, 1, length, this->file);
if ((len == 0) && ferror(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 ") + std::string("read ") +
QUtil::int_to_string(length) + " bytes"); QUtil::int_to_string(length) + " bytes");
} }
@ -325,7 +327,8 @@ QPDF::parse()
else else
{ {
QTC::TC("qpdf", "QPDF not a pdf file"); 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 // PDF spec says %%EOF must be found within the last 1024 bytes of
@ -369,7 +372,8 @@ QPDF::parse()
if (! m2) if (! m2)
{ {
QTC::TC("qpdf", "QPDF can't find startxref"); 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()); off_t xref_offset = atoi(m2.getMatch(1).c_str());
read_xref(xref_offset); read_xref(xref_offset);
@ -417,9 +421,28 @@ QPDF::reconstruct_xref(QPDFExc& e)
static PCRE endobj_re("^endobj\\b"); static PCRE endobj_re("^endobj\\b");
static PCRE trailer_re("^trailer\\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(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); this->file.seek(0, SEEK_END);
off_t eof = this->file.tell(); off_t eof = this->file.tell();
@ -452,7 +475,8 @@ QPDF::reconstruct_xref(QPDFExc& e)
// read "trailer" // read "trailer"
this->file.seek(this->file.getLastOffset(), SEEK_SET); this->file.seek(this->file.getLastOffset(), SEEK_SET);
readToken(&this->file); readToken(&this->file);
QPDFObjectHandle t = readObject(&this->file, 0, 0, false); QPDFObjectHandle t =
readObject(&this->file, "trailer", 0, 0, false);
if (! t.isDictionary()) if (! t.isDictionary())
{ {
// Oh well. It was worth a try. // 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 // with bad startxref pointers even when they have object
// streams. // 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"); "dictionary while recovering damaged file");
} }
@ -513,8 +538,8 @@ QPDF::read_xref(off_t xref_offset)
if (size != max_obj + 1) if (size != max_obj + 1)
{ {
QTC::TC("qpdf", "QPDF xref size mismatch"); QTC::TC("qpdf", "QPDF xref size mismatch");
warn(QPDFExc(this->file.getName() + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
std::string(": reported number of objects (") + std::string("reported number of objects (") +
QUtil::int_to_string(size) + QUtil::int_to_string(size) +
") inconsistent with actual number of objects (" + ") inconsistent with actual number of objects (" +
QUtil::int_to_string(max_obj + 1) + ")")); QUtil::int_to_string(max_obj + 1) + ")"));
@ -542,7 +567,8 @@ QPDF::read_xrefTable(off_t xref_offset)
if (! m1) if (! m1)
{ {
QTC::TC("qpdf", "QPDF invalid xref"); 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"); "xref syntax invalid");
} }
int obj = atoi(m1.getMatch(1).c_str()); 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"); QTC::TC("qpdf", "QPDF invalid xref entry");
throw QPDFExc( throw QPDFExc(
this->file.getName(), this->file.getLastOffset(), qpdf_e_damaged_pdf, this->file.getName(),
"xref table", this->file.getLastOffset(),
"invalid xref entry (obj=" + "invalid xref entry (obj=" +
QUtil::int_to_string(i) + ")"); QUtil::int_to_string(i) + ")");
} }
@ -595,11 +622,13 @@ QPDF::read_xrefTable(off_t xref_offset)
} }
// Set offset to previous xref table if any // 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()) if (! cur_trailer.isDictionary())
{ {
QTC::TC("qpdf", "QPDF missing trailer"); 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"); "expected trailer dictionary");
} }
@ -610,13 +639,15 @@ QPDF::read_xrefTable(off_t xref_offset)
if (! this->trailer.hasKey("/Size")) if (! this->trailer.hasKey("/Size"))
{ {
QTC::TC("qpdf", "QPDF trailer lacks 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"); "trailer dictionary lacks /Size key");
} }
if (! this->trailer.getKey("/Size").isInteger()) if (! this->trailer.getKey("/Size").isInteger())
{ {
QTC::TC("qpdf", "QPDF trailer size not integer"); 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 " "/Size key in trailer dictionary is not "
"an integer"); "an integer");
} }
@ -640,7 +671,8 @@ QPDF::read_xrefTable(off_t xref_offset)
} }
else else
{ {
throw QPDFExc(this->file.getName(), xref_offset, throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
"xref stream", xref_offset,
"invalid /XRefStm"); "invalid /XRefStm");
} }
} }
@ -659,7 +691,8 @@ QPDF::read_xrefTable(off_t xref_offset)
if (! cur_trailer.getKey("/Prev").isInteger()) if (! cur_trailer.getKey("/Prev").isInteger())
{ {
QTC::TC("qpdf", "QPDF trailer prev not integer"); 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 " "/Prev key in trailer dictionary is not "
"an integer"); "an integer");
} }
@ -685,7 +718,8 @@ QPDF::read_xrefStream(off_t xref_offset)
QPDFObjectHandle xref_obj; QPDFObjectHandle xref_obj;
try 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) catch (QPDFExc& e)
{ {
@ -705,7 +739,8 @@ QPDF::read_xrefStream(off_t xref_offset)
if (! found) if (! found)
{ {
QTC::TC("qpdf", "QPDF can't find xref"); 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; return xref_offset;
@ -725,7 +760,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj)
dict.getKey("/Size").isInteger() && dict.getKey("/Size").isInteger() &&
(Index_obj.isArray() || Index_obj.isNull()))) (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" "Cross-reference stream does not have"
" proper /W and /Index keys"); " proper /W and /Index keys");
} }
@ -735,7 +771,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj)
int n_index = Index_obj.getArrayNItems(); int n_index = Index_obj.getArrayNItems();
if ((n_index % 2) || (n_index < 2)) 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" "Cross-reference stream's /Index has an"
" invalid number of values"); " invalid number of values");
} }
@ -747,7 +784,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj)
} }
else 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 " + "Cross-reference stream's /Index's item " +
QUtil::int_to_string(i) + QUtil::int_to_string(i) +
" is not an integer"); " is not an integer");
@ -785,7 +823,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj)
if (expected_size != actual_size) 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;" "Cross-reference stream data has the wrong size;"
" expected = " + QUtil::int_to_string(expected_size) + " expected = " + QUtil::int_to_string(expected_size) +
"; actual = " + QUtil::int_to_string(actual_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()) 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 " "/Prev key in xref stream dictionary is not "
"an integer"); "an integer");
} }
@ -935,7 +975,8 @@ QPDF::insertXrefEntry(int obj, int f0, int f1, int f2, bool overwrite)
break; break;
default: 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 " + "unknown xref stream entry type " +
QUtil::int_to_string(f0)); QUtil::int_to_string(f0));
break; break;
@ -972,10 +1013,32 @@ QPDF::showXRefTable()
} }
} }
QPDFObjectHandle void
QPDF::readObject(InputSource* input, int objid, int generation, QPDF::setLastObjectDescription(std::string const& description,
bool in_object_stream) 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(); off_t offset = input->tell();
QPDFObjectHandle object = readObjectInternal( QPDFObjectHandle object = readObjectInternal(
input, objid, generation, in_object_stream, false, false); input, objid, generation, in_object_stream, false, false);
@ -1017,7 +1080,9 @@ QPDF::readObjectInternal(InputSource* input,
case QPDFTokenizer::tt_brace_close: case QPDFTokenizer::tt_brace_close:
// Don't know what to do with these for now // Don't know what to do with these for now
QTC::TC("qpdf", "QPDF bad brace"); 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"); "unexpected brace token");
break; break;
@ -1029,7 +1094,9 @@ QPDF::readObjectInternal(InputSource* input,
else else
{ {
QTC::TC("qpdf", "QPDF bad array close"); 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"); "unexpected array close token");
} }
break; break;
@ -1042,7 +1109,9 @@ QPDF::readObjectInternal(InputSource* input,
else else
{ {
QTC::TC("qpdf", "QPDF bad dictionary close"); 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"); "unexpected dictionary close token");
} }
break; break;
@ -1097,7 +1166,9 @@ QPDF::readObjectInternal(InputSource* input,
} }
else 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 (" + "unknown token while reading object (" +
value + ")"); value + ")");
} }
@ -1116,7 +1187,9 @@ QPDF::readObjectInternal(InputSource* input,
break; break;
default: 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"); "unknown token type while reading object");
break; break;
} }
@ -1153,7 +1226,8 @@ QPDF::readObjectInternal(InputSource* input,
{ {
QTC::TC("qpdf", "QPDF dictionary odd number of elements"); QTC::TC("qpdf", "QPDF dictionary odd number of elements");
throw QPDFExc( 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"); "dictionary ending here has an odd number of elements");
} }
for (unsigned int i = 0; i < olist.size(); i += 2) for (unsigned int i = 0; i < olist.size(); i += 2)
@ -1163,7 +1237,8 @@ QPDF::readObjectInternal(InputSource* input,
if (! key_obj.isName()) if (! key_obj.isName())
{ {
throw QPDFExc( throw QPDFExc(
input->getName(), offset, qpdf_e_damaged_pdf,
input->getName(), this->last_object_description, offset,
std::string("dictionary key not name (") + std::string("dictionary key not name (") +
key_obj.unparse() + ")"); key_obj.unparse() + ")");
} }
@ -1209,7 +1284,8 @@ QPDF::readObjectInternal(InputSource* input,
if (dict.count("/Length") == 0) if (dict.count("/Length") == 0)
{ {
QTC::TC("qpdf", "QPDF stream without length"); 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"); "stream dictionary lacks /Length key");
} }
@ -1217,7 +1293,8 @@ QPDF::readObjectInternal(InputSource* input,
if (! length_obj.isInteger()) if (! length_obj.isInteger())
{ {
QTC::TC("qpdf", "QPDF stream length not integer"); 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 " "/Length key in stream dictionary is not "
"an integer"); "an integer");
} }
@ -1229,7 +1306,9 @@ QPDF::readObjectInternal(InputSource* input,
QPDFTokenizer::tt_word, "endstream"))) QPDFTokenizer::tt_word, "endstream")))
{ {
QTC::TC("qpdf", "QPDF missing 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"); "expected endstream");
} }
} }
@ -1267,7 +1346,8 @@ QPDF::recoverStreamLength(InputSource* input,
// Try to reconstruct stream length by looking for // Try to reconstruct stream length by looking for
// endstream(\r\n?|\n)endobj // 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")); "attempting to recover stream length"));
input->seek(0, SEEK_END); input->seek(0, SEEK_END);
@ -1336,7 +1416,8 @@ QPDF::recoverStreamLength(InputSource* input,
if (length == 0) 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"); "unable to recover stream data");
} }
@ -1356,7 +1437,9 @@ QPDF::readToken(InputSource* input)
char ch; char ch;
if (input->read(&ch, 1) == 0) 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 else
{ {
@ -1376,7 +1459,9 @@ QPDF::readToken(InputSource* input)
if (token.getType() == QPDFTokenizer::tt_bad) 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); input->setLastOffset(offset);
@ -1385,9 +1470,12 @@ QPDF::readToken(InputSource* input)
} }
QPDFObjectHandle 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) int& objid, int& generation)
{ {
setLastObjectDescription(description, exp_objid, exp_generation);
this->file.seek(offset, SEEK_SET); this->file.seek(offset, SEEK_SET);
QPDFTokenizer::Token tobjid = readToken(&this->file); 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)) if (! (objidok && genok && objok))
{ {
QTC::TC("qpdf", "QPDF expected n n obj"); 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()); objid = atoi(tobjid.getValue().c_str());
generation = atoi(tgen.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)))) (! ((objid == exp_objid) && (generation == exp_generation))))
{ {
QTC::TC("qpdf", "QPDF err wrong objid/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 ") + std::string("expected ") +
QUtil::int_to_string(exp_objid) + " " + QUtil::int_to_string(exp_objid) + " " +
QUtil::int_to_string(exp_generation) + " obj"); 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) catch (QPDFExc& e)
{ {
if (exp_objid && this->attempt_recovery) if (exp_objid && try_recovery && this->attempt_recovery)
{ {
// Try again after reconstructing xref table // Try again after reconstructing xref table
reconstruct_xref(e); reconstruct_xref(e);
@ -1433,13 +1524,27 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
(this->xref_table[og].getType() == 1)) (this->xref_table[og].getType() == 1))
{ {
off_t new_offset = this->xref_table[og].getOffset(); off_t new_offset = this->xref_table[og].getOffset();
// Call readObjectAtOffset with 0 for exp_objid to QPDFObjectHandle result = readObjectAtOffset(
// avoid an infinite loop. false, new_offset, description,
QPDFObjectHandle result = exp_objid, exp_generation, objid, generation);
readObjectAtOffset(new_offset, 0, 0, objid, generation);
QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset"); QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset");
return result; 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 else
{ {
@ -1448,13 +1553,14 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
} }
QPDFObjectHandle oh = readObject( QPDFObjectHandle oh = readObject(
&this->file, objid, generation, false); &this->file, description, objid, generation, false);
if (! (readToken(&this->file) == if (! (readToken(&this->file) ==
QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj"))) QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj")))
{ {
QTC::TC("qpdf", "QPDF err expected 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")); "expected endobj"));
} }
@ -1487,7 +1593,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
} }
else else
{ {
throw QPDFExc(this->file.getName(), offset, throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
this->last_object_description, offset,
"EOF after endobj"); "EOF after endobj");
} }
} }
@ -1526,7 +1633,7 @@ QPDF::resolve(int objid, int generation)
int aobjid; int aobjid;
int ageneration; int ageneration;
QPDFObjectHandle oh = QPDFObjectHandle oh =
readObjectAtOffset(offset, objid, generation, readObjectAtOffset(true, offset, "", objid, generation,
aobjid, ageneration); aobjid, ageneration);
} }
break; break;
@ -1536,7 +1643,7 @@ QPDF::resolve(int objid, int generation)
break; break;
default: default:
throw QPDFExc(this->file.getName(), 0, throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
"object " + "object " +
QUtil::int_to_string(objid) + "/" + QUtil::int_to_string(objid) + "/" +
QUtil::int_to_string(generation) + QUtil::int_to_string(generation) +
@ -1554,7 +1661,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0); QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0);
if (! obj_stream.isStream()) 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 " + "supposed object stream " +
QUtil::int_to_string(obj_stream_number) + QUtil::int_to_string(obj_stream_number) +
" is not a stream"); " is not a stream");
@ -1570,7 +1679,10 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
if (! (dict.getKey("/Type").isName() && if (! (dict.getKey("/Type").isName() &&
dict.getKey("/Type").getName() == "/ObjStm")) 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 " + "supposed object stream " +
QUtil::int_to_string(obj_stream_number) + QUtil::int_to_string(obj_stream_number) +
" has wrong type"); " has wrong type");
@ -1579,7 +1691,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
if (! (dict.getKey("/N").isInteger() && if (! (dict.getKey("/N").isInteger() &&
dict.getKey("/First").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 " + "object stream " +
QUtil::int_to_string(obj_stream_number) + QUtil::int_to_string(obj_stream_number) +
" has incorrect keys"); " has incorrect keys");
@ -1602,7 +1716,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
if (! ((tnum.getType() == QPDFTokenizer::tt_integer) && if (! ((tnum.getType() == QPDFTokenizer::tt_integer) &&
(toffset.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"); "expected integer in object stream header");
} }
@ -1617,7 +1732,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
int obj = (*iter).first; int obj = (*iter).first;
int offset = (*iter).second; int offset = (*iter).second;
input.seek(offset, SEEK_SET); input.seek(offset, SEEK_SET);
QPDFObjectHandle oh = readObject(&input, obj, 0, true); QPDFObjectHandle oh = readObject(&input, "", obj, 0, true);
// Store in cache // Store in cache
ObjGen og(obj, 0); ObjGen og(obj, 0);
@ -1830,17 +1945,25 @@ QPDF::pipeStreamData(int objid, int generation,
size_t len = this->file.read(buf, to_read); size_t len = this->file.read(buf, to_read);
if (len == 0) 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"); "unexpected EOF reading stream data");
} }
length -= len; length -= len;
pipeline->write((unsigned char*)buf, len); pipeline->write((unsigned char*)buf, len);
} }
} }
catch (QPDFExc& e)
{
warn(e);
}
catch (std::runtime_error& e) catch (std::runtime_error& e)
{ {
QTC::TC("qpdf", "QPDF decoding error warning"); 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 " + "error decoding stream data for object " +
QUtil::int_to_string(objid) + " " + QUtil::int_to_string(objid) + " " +
QUtil::int_to_string(generation) + ": " + e.what())); QUtil::int_to_string(generation) + ": " + e.what()));
@ -1896,6 +2019,9 @@ QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages,
} }
else 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/QPDFExc.hh>
#include <qpdf/QUtil.hh> #include <qpdf/QUtil.hh>
QPDFExc::QPDFExc(std::string const& message) : QPDFExc::QPDFExc(qpdf_error_code_e error_code,
std::runtime_error(message) std::string const& filename,
{ std::string const& object,
} off_t offset,
QPDFExc::QPDFExc(std::string const& filename, int offset,
std::string const& message) : std::string const& message) :
std::runtime_error(filename + ": offset " + QUtil::int_to_string(offset) + std::runtime_error(createWhat(filename, object, offset, message)),
": " + message) error_code(error_code),
filename(filename),
object(object),
offset(offset),
message(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;
}

View File

@ -16,7 +16,8 @@ QPDFXRefEntry::QPDFXRefEntry(int type, int field1, int field2) :
{ {
if ((type < 1) || (type > 2)) 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) if (this->type != 1)
{ {
throw QPDFExc( throw std::logic_error(
"getOffset called for xref entry of type != 1"); "getOffset called for xref entry of type != 1");
} }
return this->field1; return this->field1;
@ -42,7 +43,7 @@ QPDFXRefEntry::getObjStreamNumber() const
{ {
if (this->type != 2) if (this->type != 2)
{ {
throw QPDFExc( throw std::logic_error(
"getObjStreamNumber called for xref entry of type != 2"); "getObjStreamNumber called for xref entry of type != 2");
} }
return this->field1; return this->field1;
@ -53,7 +54,7 @@ QPDFXRefEntry::getObjStreamIndex() const
{ {
if (this->type != 2) if (this->type != 2)
{ {
throw QPDFExc( throw std::logic_error(
"getObjStreamIndex called for xref entry of type != 2"); "getObjStreamIndex called for xref entry of type != 2");
} }
return this->field2; return this->field2;

View File

@ -59,7 +59,7 @@ QPDF_Stream::getStreamData()
Pl_Buffer buf("stream data buffer"); Pl_Buffer buf("stream data buffer");
if (! pipeStreamData(&buf, true, false, false)) 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(); return buf.getBuffer();
} }
@ -208,8 +208,9 @@ QPDF_Stream::filterable(std::vector<std::string>& filters,
if (! filters_okay) if (! filters_okay)
{ {
QTC::TC("qpdf", "QPDF_Stream invalid filter"); QTC::TC("qpdf", "QPDF_Stream invalid filter");
throw QPDFExc(qpdf->getFilename(), this->offset, throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(),
"invalid filter object type for this stream"); "", this->offset,
"stream filter type is not name or array");
} }
// `filters' now contains a list of filters to be applied in // `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.getArrayNItems() == 2) &&
id_obj.getArrayItem(0).isString())) 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"); "invalid /ID in trailer dictionary");
} }
std::string id1 = id_obj.getArrayItem(0).getStringValue(); std::string id1 = id_obj.getArrayItem(0).getStringValue();
if (id1.length() != id_bytes) 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 " "first /ID string in trailer dictionary has "
"incorrect length"); "incorrect length");
} }
@ -352,19 +354,23 @@ QPDF::initializeEncryption()
QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt"); QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt");
if (! encryption_dict.isDictionary()) 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"); "/Encrypt in trailer dictionary is not a dictionary");
} }
if (! (encryption_dict.getKey("/Filter").isName() && if (! (encryption_dict.getKey("/Filter").isName() &&
(encryption_dict.getKey("/Filter").getName() == "/Standard"))) (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"); "unsupported encryption filter");
} }
if (! encryption_dict.getKey("/SubFilter").isNull()) 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," "file uses encryption SubFilters,"
" which qpdf does not support")); " which qpdf does not support"));
} }
@ -375,7 +381,8 @@ QPDF::initializeEncryption()
encryption_dict.getKey("/U").isString() && encryption_dict.getKey("/U").isString() &&
encryption_dict.getKey("/P").isInteger())) 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 " "some encryption dictionary parameters are missing "
"or the wrong type"); "or the wrong type");
} }
@ -389,7 +396,8 @@ QPDF::initializeEncryption()
if (! (((R == 2) || (R == 3) || (R == 4)) && if (! (((R == 2) || (R == 3) || (R == 4)) &&
((V == 1) || (V == 2) || (V == 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"); "Unsupported /R or /V in encryption dictionary");
} }
@ -397,7 +405,8 @@ QPDF::initializeEncryption()
if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) 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 " "incorrect length for /O and/or /P in "
"encryption dictionary"); "encryption dictionary");
} }
@ -408,7 +417,8 @@ QPDF::initializeEncryption()
Length = encryption_dict.getKey("/Length").getIntValue(); Length = encryption_dict.getKey("/Length").getIntValue();
if ((Length % 8) || (Length < 40) || (Length > 128)) 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"); "invalid /Length value in encryption dictionary");
} }
} }
@ -471,7 +481,8 @@ QPDF::initializeEncryption()
} }
if (this->cf_file != this->cf_stream) 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" "This document has embedded files that are"
" encrypted differently from the rest of the file." " encrypted differently from the rest of the file."
" qpdf does not presently support this due to" " qpdf does not presently support this due to"
@ -492,7 +503,8 @@ QPDF::initializeEncryption()
} }
else 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); this->encryption_key = compute_encryption_key(this->user_password, data);
@ -542,7 +554,9 @@ QPDF::decryptString(std::string& str, int objid, int generation)
break; break;
default: 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" "unknown encryption filter for strings"
" (check /StrF in /Encrypt dictionary);" " (check /StrF in /Encrypt dictionary);"
" strings may be decrypted improperly")); " 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); std::string key = getKeyForObject(objid, generation, use_aes);
if (use_aes) try
{ {
QTC::TC("qpdf", "QPDF_encryption aes decode string"); if (use_aes)
assert(key.length() == Pl_AES_PDF::key_size); {
Pl_Buffer bufpl("decrypted string"); QTC::TC("qpdf", "QPDF_encryption aes decode string");
Pl_AES_PDF pl("aes decrypt string", &bufpl, false, assert(key.length() == Pl_AES_PDF::key_size);
(unsigned char const*)key.c_str()); Pl_Buffer bufpl("decrypted string");
pl.write((unsigned char*)str.c_str(), str.length()); Pl_AES_PDF pl("aes decrypt string", &bufpl, false,
pl.finish(); (unsigned char const*)key.c_str());
Buffer* buf = bufpl.getBuffer(); pl.write((unsigned char*)str.c_str(), str.length());
str = std::string((char*)buf->getBuffer(), (size_t)buf->getSize()); pl.finish();
delete buf; 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"); throw;
unsigned int vlen = str.length(); }
char* tmp = QUtil::copy_string(str); catch (std::runtime_error& e)
RC4 rc4((unsigned char const*)key.c_str(), key.length()); {
rc4.process((unsigned char*)tmp, vlen); throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
str = std::string(tmp, vlen); this->last_object_description, this->file.getLastOffset(),
delete [] tmp; "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: default:
// filter local to this stream. // 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" "unknown encryption filter for streams"
" (check " + method_source + ");" " (check " + method_source + ");"
" streams may be decrypted improperly")); " streams may be decrypted improperly"));

View File

@ -175,7 +175,8 @@ QPDF::readLinearizationData()
if (! isLinearized()) 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() // /L is read and stored in linp by isLinearized()
@ -193,7 +194,10 @@ QPDF::readLinearizationData()
T.isInteger() && T.isInteger() &&
(P.isInteger() || P.isNull()))) (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"); "the wrong type");
} }
@ -201,7 +205,10 @@ QPDF::readLinearizationData()
unsigned int n_H_items = H.getArrayNItems(); unsigned int n_H_items = H.getArrayNItems();
if (! ((n_H_items == 2) || (n_H_items == 4))) 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; std::vector<int> H_items;
@ -214,7 +221,10 @@ QPDF::readLinearizationData()
} }
else 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 obj;
int gen; 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)]; ObjCache& oc = this->obj_cache[ObjGen(obj, gen)];
off_t min_end_offset = oc.end_before_space; off_t min_end_offset = oc.end_before_space;
off_t max_end_offset = oc.end_after_space; off_t max_end_offset = oc.end_after_space;
if (! H.isStream()) 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(); QPDFObjectHandle Hdict = H.getDict();
@ -340,7 +354,10 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length)
std::cout << "expected = " << computed_end std::cout << "expected = " << computed_end
<< "; actual = " << min_end_offset << ".." << "; actual = " << min_end_offset << ".."
<< max_end_offset << std::endl; << 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); H.pipeStreamData(&pl, true, false, false);
return Hdict; return Hdict;
@ -651,8 +668,7 @@ QPDF::getLinearizationOffset(ObjGen const& og)
break; break;
default: default:
throw QPDFExc( throw std::logic_error(
this->file.getName(), 0,
"getLinearizationOffset called for xref entry not of type 1 or 2"); "getLinearizationOffset called for xref entry not of type 1 or 2");
break; break;
} }

View File

@ -233,9 +233,12 @@ QPDF::optimizePagesTree(
{ {
if (! allow_changes) if (! allow_changes)
{ {
throw QPDFExc(this->file.getName() + throw QPDFExc(qpdf_e_internal, this->file.getName(),
": optimize detected an " this->last_object_description,
"inheritable resource"); this->file.getLastOffset(),
"optimize detected an "
"inheritable resource when called "
"in no-change mode");
} }
// This is an inheritable resource // This is an inheritable resource
@ -338,7 +341,10 @@ QPDF::optimizePagesTree(
} }
else 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 <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h>
#include <qpdf/QUtil.hh> #include <qpdf/QUtil.hh>
#include <qpdf/QTC.hh> #include <qpdf/QTC.hh>
@ -533,6 +534,7 @@ parse_encrypt_options(
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
whoami = QUtil::getWhoami(argv[0]); whoami = QUtil::getWhoami(argv[0]);
setlinebuf(stdout);
// For libtool's sake.... // For libtool's sake....
if (strncmp(whoami, "lt-", 3) == 0) if (strncmp(whoami, "lt-", 3) == 0)
@ -892,7 +894,15 @@ int main(int argc, char* argv[])
} }
if (show_linearization) if (show_linearization)
{ {
pdf.showLinearizationData(); if (pdf.isLinearized())
{
pdf.showLinearizationData();
}
else
{
std::cout << infilename << " is not linearized"
<< std::endl;
}
} }
if (show_xref) 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_r4_encryption_parameters 0
qpdf-c called qpdf_set_static_aes_IV 0 qpdf-c called qpdf_set_static_aes_IV 0
QPDF_encryption stream crypt filter 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 "unknown stream /Filter", # 31
"obj/gen mismatch", # 32 "obj/gen mismatch", # 32
"invalid stream /Filter and xref", # 33 "invalid stream /Filter and xref", # 33
"obj/gen in wrong place", # 34
"object stream of wrong type", # 35
); );
$n_tests += @badfiles + 5; $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 # though in some cases it may. Acrobat Reader would not be able to
# recover any of these files any better. # recover any of these files any better.
my %recover_failures = (); 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; $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: append-page-content-damaged.pdf: can't find startxref
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
version: 1.3 version: 1.3
linearized: 0 linearized: 0
encrypted: 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: append-page-content-damaged.pdf: can't find startxref
warning: Attempting to reconstruct cross-reference table 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: append-page-content-damaged.pdf: can't find startxref
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
checking append-page-content-damaged.pdf 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: append-page-content-damaged.pdf: can't find startxref
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
qpdf: operation succeeded with warnings; resulting file may have some problems 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: file is damaged
WARNING: bad10.pdf: offset 712: /Size key in trailer dictionary is not an integer WARNING: bad10.pdf (trailer, file position 712): /Size key in trailer dictionary is not an integer
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit /QTest is implicit
/QTest is direct /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: file is damaged
WARNING: bad11.pdf: offset 905: /Prev key in trailer dictionary is not an integer WARNING: bad11.pdf (trailer, file position 905): /Prev key in trailer dictionary is not an integer
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit /QTest is implicit
/QTest is direct /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: file is damaged
WARNING: bad13.pdf: offset 753: unexpected brace token WARNING: bad13.pdf (trailer, file position 753): unexpected brace token
WARNING: Attempting to reconstruct cross-reference table 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: file is damaged
WARNING: bad14.pdf: offset 753: unexpected brace token WARNING: bad14.pdf (trailer, file position 753): unexpected brace token
WARNING: Attempting to reconstruct cross-reference table 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: file is damaged
WARNING: bad15.pdf: offset 753: unexpected array close token WARNING: bad15.pdf (trailer, file position 753): unexpected array close token
WARNING: Attempting to reconstruct cross-reference table 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: file is damaged
WARNING: bad16.pdf: offset 753: unexpected dictionary close token WARNING: bad16.pdf (trailer, file position 753): unexpected dictionary close token
WARNING: Attempting to reconstruct cross-reference table 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: file is damaged
WARNING: bad17.pdf: offset 753: dictionary ending here has an odd number of elements WARNING: bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements
WARNING: Attempting to reconstruct cross-reference table 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: file is damaged
WARNING: bad18.pdf: offset 753: unexpected ) WARNING: bad18.pdf (trailer, file position 753): unexpected )
WARNING: Attempting to reconstruct cross-reference table 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: file is damaged
WARNING: bad19.pdf: offset 753: unexpected > WARNING: bad19.pdf (trailer, file position 753): unexpected >
WARNING: Attempting to reconstruct cross-reference table 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: bad2.pdf: can't find startxref
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit /QTest is implicit

View File

@ -1,4 +1,4 @@
WARNING: bad20.pdf: offset 0: file is damaged WARNING: bad20.pdf: file is damaged
WARNING: bad20.pdf: offset 753: invalid character (q) in hexstring WARNING: bad20.pdf (trailer, file position 753): invalid character (q) in hexstring
WARNING: Attempting to reconstruct cross-reference table 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: file is damaged
WARNING: bad21.pdf: offset 742: invalid name token WARNING: bad21.pdf (trailer, file position 742): invalid name token
WARNING: Attempting to reconstruct cross-reference table 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 indirect
/QTest is a stream. Dictionary: << /Qength 44 >> /QTest is a stream. Dictionary: << /Qength 44 >>
Raw stream data: 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 indirect
/QTest is a stream. Dictionary: << /Length () >> /QTest is a stream. Dictionary: << /Length () >>
Raw stream data: 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 WARNING: bad24.pdf (object 4 0, file position 341): attempting to recover stream length
bad24.pdf: offset 341: unable to recover stream data 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: file is damaged
WARNING: bad25.pdf: offset 307: expected n n obj WARNING: bad25.pdf (object 4 0, file position 307): expected n n obj
WARNING: Attempting to reconstruct cross-reference table 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: file is damaged
WARNING: bad26.pdf: offset 307: expected n n obj WARNING: bad26.pdf (object 4 0, file position 307): expected n n obj
WARNING: Attempting to reconstruct cross-reference table 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: file is damaged
WARNING: bad27.pdf: offset 307: expected n n obj WARNING: bad27.pdf (object 4 0, file position 307): expected n n obj
WARNING: Attempting to reconstruct cross-reference table 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 indirect
/QTest is a stream. Dictionary: << /Length 44 >> /QTest is a stream. Dictionary: << /Length 44 >>
Raw stream data: 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 indirect
/QTest is a stream. Dictionary: << /Length 44 >> /QTest is a stream. Dictionary: << /Length 44 >>
Raw stream data: Raw stream data:

View File

@ -1,4 +1,4 @@
WARNING: bad29.pdf: offset 0: file is damaged WARNING: bad29.pdf: file is damaged
WARNING: bad29.pdf: offset 742: null character not allowed in name token WARNING: bad29.pdf (trailer, file position 742): null character not allowed in name token
WARNING: Attempting to reconstruct cross-reference table 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: file is damaged
WARNING: bad3.pdf: offset 542: xref not found WARNING: bad3.pdf (file position 542): xref not found
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit /QTest is implicit
/QTest is direct /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: 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ä 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: 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: 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ä 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: 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: file is damaged
WARNING: bad32.pdf: offset 307: expected 4 0 obj WARNING: bad32.pdf (object 4 0, file position 307): expected 4 0 obj
WARNING: Attempting to reconstruct cross-reference table 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 implicit
/QTest is indirect /QTest is indirect
/QTest is null /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: file is damaged
WARNING: bad33.pdf: offset 1771: xref not found WARNING: bad33.pdf (file position 1771): xref not found
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
/QTest is indirect /QTest is indirect
/QTest is a stream. Dictionary: << /Filter (FlateDecode) /Length 123 >> /QTest is a stream. Dictionary: << /Filter (FlateDecode) /Length 123 >>
Raw stream data: 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ä 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: 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: file is damaged
WARNING: bad4.pdf: offset 547: xref syntax invalid WARNING: bad4.pdf (xref table, file position 547): xref syntax invalid
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit /QTest is implicit
/QTest is direct /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: file is damaged
WARNING: bad5.pdf: offset 591: invalid xref entry (obj=2) WARNING: bad5.pdf (xref table, file position 591): invalid xref entry (obj=2)
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit /QTest is implicit
/QTest is direct /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: file is damaged
WARNING: bad7.pdf: offset 698: expected trailer dictionary WARNING: bad7.pdf (file position 698): expected trailer dictionary
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
bad7.pdf: unable to find trailer dictionary while recovering damaged file 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: file is damaged
WARNING: bad8.pdf: offset 543: xref not found WARNING: bad8.pdf (file position 543): xref not found
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit /QTest is implicit
/QTest is direct /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: file is damaged
WARNING: bad9.pdf: offset 712: trailer dictionary lacks /Size key WARNING: bad9.pdf (trailer, file position 712): trailer dictionary lacks /Size key
WARNING: Attempting to reconstruct cross-reference table WARNING: Attempting to reconstruct cross-reference table
/QTest is implicit /QTest is implicit
/QTest is direct /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: file is damaged
warning: bad17.pdf: offset 753: dictionary ending here has an odd number of elements warning: bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements
warning: Attempting to reconstruct cross-reference table 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: append-page-content-damaged.pdf: can't find startxref
warning: Attempting to reconstruct cross-reference table 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: file is damaged
warning: bad33.pdf: offset 1771: xref not found warning: bad33.pdf (file position 1771): xref not found
warning: Attempting to reconstruct cross-reference table 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 PDF Version: 1.3
File is not encrypted File is not encrypted
File is not linearized 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: file is damaged
WARNING: heifer.pdf: offset 92741: xref not found WARNING: heifer.pdf (file position 92741): xref not found
WARNING: Attempting to reconstruct cross-reference table 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 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 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 1/0: compressed; stream = 5, index = 1
2/0: compressed; stream = 5, index = 0 2/0: compressed; stream = 5, index = 0
3/0: uncompressed; offset = 15 3/0: uncompressed; offset = 15

View File

@ -9,6 +9,7 @@
#include <qpdf/Pl_Buffer.hh> #include <qpdf/Pl_Buffer.hh>
#include <qpdf/QPDFWriter.hh> #include <qpdf/QPDFWriter.hh>
#include <iostream> #include <iostream>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <map> #include <map>
@ -319,6 +320,7 @@ void runtest(int n, char const* filename)
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
setlinebuf(stdout);
if ((whoami = strrchr(argv[0], '/')) == NULL) if ((whoami = strrchr(argv[0], '/')) == NULL)
{ {
whoami = argv[0]; whoami = argv[0];