aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2009-10-20 01:09:19 +0200
committerJay Berkenbilt <ejb@ql.org>2009-10-20 01:09:19 +0200
commit3f8c4c273649c857f5a607dcbb422729fce3a166 (patch)
tree9a71fbfc838cf876f7982f7213ad994c92c0a3fc /libqpdf
parentb67a3c15e768ed88ea3cdf7525c1ddc649aec2fe (diff)
downloadqpdf-3f8c4c273649c857f5a607dcbb422729fce3a166.tar.zst
categorize all error messages and include object information if available
git-svn-id: svn+q:///qpdf/trunk@829 71b93d88-0707-0410-a8cf-f5a4172ac649
Diffstat (limited to 'libqpdf')
-rw-r--r--libqpdf/QPDF.cc250
-rw-r--r--libqpdf/QPDFExc.cc52
-rw-r--r--libqpdf/QPDFXRefEntry.cc9
-rw-r--r--libqpdf/QPDF_Stream.cc7
-rw-r--r--libqpdf/QPDF_encryption.cc99
-rw-r--r--libqpdf/QPDF_linearization.cc34
-rw-r--r--libqpdf/QPDF_optimization.cc14
7 files changed, 344 insertions, 121 deletions
diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc
index c7ec873f..ebd78a9d 100644
--- a/libqpdf/QPDF.cc
+++ b/libqpdf/QPDF.cc
@@ -140,7 +140,9 @@ QPDF::FileInputSource::read(char* buffer, int length)
size_t len = fread(buffer, 1, length, this->file);
if ((len == 0) && ferror(this->file))
{
- throw QPDFExc(this->filename, this->last_offset,
+ throw QPDFExc(qpdf_e_system,
+ this->filename, "",
+ this->last_offset,
std::string("read ") +
QUtil::int_to_string(length) + " bytes");
}
@@ -325,7 +327,8 @@ QPDF::parse()
else
{
QTC::TC("qpdf", "QPDF not a pdf file");
- throw QPDFExc(this->file.getName(), 0, "not a PDF file");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "", 0, "not a PDF file");
}
// PDF spec says %%EOF must be found within the last 1024 bytes of
@@ -369,7 +372,8 @@ QPDF::parse()
if (! m2)
{
QTC::TC("qpdf", "QPDF can't find startxref");
- throw QPDFExc(this->file.getName() + ": can't find startxref");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
+ "can't find startxref");
}
off_t xref_offset = atoi(m2.getMatch(1).c_str());
read_xref(xref_offset);
@@ -417,9 +421,28 @@ QPDF::reconstruct_xref(QPDFExc& e)
static PCRE endobj_re("^endobj\\b");
static PCRE trailer_re("^trailer\\b");
- warn(QPDFExc(this->file.getName(), 0, "file is damaged"));
+ warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
+ "file is damaged"));
warn(e);
- warn(QPDFExc("Attempting to reconstruct cross-reference table"));
+ warn(QPDFExc(qpdf_e_damaged_pdf, "", "", 0,
+ "Attempting to reconstruct cross-reference table"));
+
+ // Delete all references to type 1 (uncompressed) objects
+ std::set<ObjGen> to_delete;
+ for (std::map<ObjGen, QPDFXRefEntry>::iterator iter =
+ this->xref_table.begin();
+ iter != this->xref_table.end(); ++iter)
+ {
+ if (((*iter).second).getType() == 1)
+ {
+ to_delete.insert((*iter).first);
+ }
+ }
+ for (std::set<ObjGen>::iterator iter = to_delete.begin();
+ iter != to_delete.end(); ++iter)
+ {
+ this->xref_table.erase(*iter);
+ }
this->file.seek(0, SEEK_END);
off_t eof = this->file.tell();
@@ -452,7 +475,8 @@ QPDF::reconstruct_xref(QPDFExc& e)
// read "trailer"
this->file.seek(this->file.getLastOffset(), SEEK_SET);
readToken(&this->file);
- QPDFObjectHandle t = readObject(&this->file, 0, 0, false);
+ QPDFObjectHandle t =
+ readObject(&this->file, "trailer", 0, 0, false);
if (! t.isDictionary())
{
// Oh well. It was worth a try.
@@ -473,7 +497,8 @@ QPDF::reconstruct_xref(QPDFExc& e)
// with bad startxref pointers even when they have object
// streams.
- throw QPDFExc(this->file.getName() + ": unable to find trailer "
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
+ "unable to find trailer "
"dictionary while recovering damaged file");
}
@@ -513,8 +538,8 @@ QPDF::read_xref(off_t xref_offset)
if (size != max_obj + 1)
{
QTC::TC("qpdf", "QPDF xref size mismatch");
- warn(QPDFExc(this->file.getName() +
- std::string(": reported number of objects (") +
+ warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
+ std::string("reported number of objects (") +
QUtil::int_to_string(size) +
") inconsistent with actual number of objects (" +
QUtil::int_to_string(max_obj + 1) + ")"));
@@ -542,7 +567,8 @@ QPDF::read_xrefTable(off_t xref_offset)
if (! m1)
{
QTC::TC("qpdf", "QPDF invalid xref");
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "xref table", this->file.getLastOffset(),
"xref syntax invalid");
}
int obj = atoi(m1.getMatch(1).c_str());
@@ -563,7 +589,8 @@ QPDF::read_xrefTable(off_t xref_offset)
{
QTC::TC("qpdf", "QPDF invalid xref entry");
throw QPDFExc(
- this->file.getName(), this->file.getLastOffset(),
+ qpdf_e_damaged_pdf, this->file.getName(),
+ "xref table", this->file.getLastOffset(),
"invalid xref entry (obj=" +
QUtil::int_to_string(i) + ")");
}
@@ -595,11 +622,13 @@ QPDF::read_xrefTable(off_t xref_offset)
}
// Set offset to previous xref table if any
- QPDFObjectHandle cur_trailer = readObject(&this->file, 0, 0, false);
+ QPDFObjectHandle cur_trailer =
+ readObject(&this->file, "trailer", 0, 0, false);
if (! cur_trailer.isDictionary())
{
QTC::TC("qpdf", "QPDF missing trailer");
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "", this->file.getLastOffset(),
"expected trailer dictionary");
}
@@ -610,13 +639,15 @@ QPDF::read_xrefTable(off_t xref_offset)
if (! this->trailer.hasKey("/Size"))
{
QTC::TC("qpdf", "QPDF trailer lacks size");
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "trailer", this->file.getLastOffset(),
"trailer dictionary lacks /Size key");
}
if (! this->trailer.getKey("/Size").isInteger())
{
QTC::TC("qpdf", "QPDF trailer size not integer");
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "trailer", this->file.getLastOffset(),
"/Size key in trailer dictionary is not "
"an integer");
}
@@ -640,7 +671,8 @@ QPDF::read_xrefTable(off_t xref_offset)
}
else
{
- throw QPDFExc(this->file.getName(), xref_offset,
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "xref stream", xref_offset,
"invalid /XRefStm");
}
}
@@ -659,7 +691,8 @@ QPDF::read_xrefTable(off_t xref_offset)
if (! cur_trailer.getKey("/Prev").isInteger())
{
QTC::TC("qpdf", "QPDF trailer prev not integer");
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "trailer", this->file.getLastOffset(),
"/Prev key in trailer dictionary is not "
"an integer");
}
@@ -685,7 +718,8 @@ QPDF::read_xrefStream(off_t xref_offset)
QPDFObjectHandle xref_obj;
try
{
- xref_obj = readObjectAtOffset(xref_offset, 0, 0, xobj, xgen);
+ xref_obj = readObjectAtOffset(
+ false, xref_offset, "xref stream", 0, 0, xobj, xgen);
}
catch (QPDFExc& e)
{
@@ -705,7 +739,8 @@ QPDF::read_xrefStream(off_t xref_offset)
if (! found)
{
QTC::TC("qpdf", "QPDF can't find xref");
- throw QPDFExc(this->file.getName(), xref_offset, "xref not found");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "", xref_offset, "xref not found");
}
return xref_offset;
@@ -725,7 +760,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj)
dict.getKey("/Size").isInteger() &&
(Index_obj.isArray() || Index_obj.isNull())))
{
- throw QPDFExc(this->file.getName(), xref_offset,
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "xref stream", xref_offset,
"Cross-reference stream does not have"
" proper /W and /Index keys");
}
@@ -735,7 +771,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj)
int n_index = Index_obj.getArrayNItems();
if ((n_index % 2) || (n_index < 2))
{
- throw QPDFExc(this->file.getName(), xref_offset,
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "xref stream", xref_offset,
"Cross-reference stream's /Index has an"
" invalid number of values");
}
@@ -747,7 +784,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj)
}
else
{
- throw QPDFExc(this->file.getName(), xref_offset,
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "xref stream", xref_offset,
"Cross-reference stream's /Index's item " +
QUtil::int_to_string(i) +
" is not an integer");
@@ -785,7 +823,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj)
if (expected_size != actual_size)
{
- QPDFExc x(this->file.getName(), xref_offset,
+ QPDFExc x(qpdf_e_damaged_pdf, this->file.getName(),
+ "xref stream", xref_offset,
"Cross-reference stream data has the wrong size;"
" expected = " + QUtil::int_to_string(expected_size) +
"; actual = " + QUtil::int_to_string(actual_size));
@@ -866,7 +905,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj)
{
if (! dict.getKey("/Prev").isInteger())
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "xref stream", this->file.getLastOffset(),
"/Prev key in xref stream dictionary is not "
"an integer");
}
@@ -935,7 +975,8 @@ QPDF::insertXrefEntry(int obj, int f0, int f1, int f2, bool overwrite)
break;
default:
- throw QPDFExc(this->file.getName(), 0,
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "xref stream", this->file.getLastOffset(),
"unknown xref stream entry type " +
QUtil::int_to_string(f0));
break;
@@ -972,10 +1013,32 @@ QPDF::showXRefTable()
}
}
+void
+QPDF::setLastObjectDescription(std::string const& description,
+ int objid, int generation)
+{
+ this->last_object_description.clear();
+ if (! description.empty())
+ {
+ this->last_object_description += description;
+ if (objid > 0)
+ {
+ this->last_object_description += ": ";
+ }
+ }
+ if (objid > 0)
+ {
+ this->last_object_description += "object " +
+ QUtil::int_to_string(objid) + " " +
+ QUtil::int_to_string(generation);
+ }
+}
+
QPDFObjectHandle
-QPDF::readObject(InputSource* input, int objid, int generation,
- bool in_object_stream)
+QPDF::readObject(InputSource* input, std::string const& description,
+ int objid, int generation, bool in_object_stream)
{
+ setLastObjectDescription(description, objid, generation);
off_t offset = input->tell();
QPDFObjectHandle object = readObjectInternal(
input, objid, generation, in_object_stream, false, false);
@@ -1017,7 +1080,9 @@ QPDF::readObjectInternal(InputSource* input,
case QPDFTokenizer::tt_brace_close:
// Don't know what to do with these for now
QTC::TC("qpdf", "QPDF bad brace");
- throw QPDFExc(input->getName(), input->getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description,
+ input->getLastOffset(),
"unexpected brace token");
break;
@@ -1029,7 +1094,9 @@ QPDF::readObjectInternal(InputSource* input,
else
{
QTC::TC("qpdf", "QPDF bad array close");
- throw QPDFExc(input->getName(), input->getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description,
+ input->getLastOffset(),
"unexpected array close token");
}
break;
@@ -1042,7 +1109,9 @@ QPDF::readObjectInternal(InputSource* input,
else
{
QTC::TC("qpdf", "QPDF bad dictionary close");
- throw QPDFExc(input->getName(), input->getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description,
+ input->getLastOffset(),
"unexpected dictionary close token");
}
break;
@@ -1097,7 +1166,9 @@ QPDF::readObjectInternal(InputSource* input,
}
else
{
- throw QPDFExc(input->getName(), input->getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description,
+ input->getLastOffset(),
"unknown token while reading object (" +
value + ")");
}
@@ -1116,7 +1187,9 @@ QPDF::readObjectInternal(InputSource* input,
break;
default:
- throw QPDFExc(input->getName(), input->getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description,
+ input->getLastOffset(),
"unknown token type while reading object");
break;
}
@@ -1153,7 +1226,8 @@ QPDF::readObjectInternal(InputSource* input,
{
QTC::TC("qpdf", "QPDF dictionary odd number of elements");
throw QPDFExc(
- input->getName(), input->getLastOffset(),
+ qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description, input->getLastOffset(),
"dictionary ending here has an odd number of elements");
}
for (unsigned int i = 0; i < olist.size(); i += 2)
@@ -1163,7 +1237,8 @@ QPDF::readObjectInternal(InputSource* input,
if (! key_obj.isName())
{
throw QPDFExc(
- input->getName(), offset,
+ qpdf_e_damaged_pdf,
+ input->getName(), this->last_object_description, offset,
std::string("dictionary key not name (") +
key_obj.unparse() + ")");
}
@@ -1209,7 +1284,8 @@ QPDF::readObjectInternal(InputSource* input,
if (dict.count("/Length") == 0)
{
QTC::TC("qpdf", "QPDF stream without length");
- throw QPDFExc(input->getName(), offset,
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description, offset,
"stream dictionary lacks /Length key");
}
@@ -1217,7 +1293,8 @@ QPDF::readObjectInternal(InputSource* input,
if (! length_obj.isInteger())
{
QTC::TC("qpdf", "QPDF stream length not integer");
- throw QPDFExc(input->getName(), offset,
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description, offset,
"/Length key in stream dictionary is not "
"an integer");
}
@@ -1229,7 +1306,9 @@ QPDF::readObjectInternal(InputSource* input,
QPDFTokenizer::tt_word, "endstream")))
{
QTC::TC("qpdf", "QPDF missing endstream");
- throw QPDFExc(input->getName(), input->getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description,
+ input->getLastOffset(),
"expected endstream");
}
}
@@ -1267,7 +1346,8 @@ QPDF::recoverStreamLength(InputSource* input,
// Try to reconstruct stream length by looking for
// endstream(\r\n?|\n)endobj
- warn(QPDFExc(input->getName(), stream_offset,
+ warn(QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description, stream_offset,
"attempting to recover stream length"));
input->seek(0, SEEK_END);
@@ -1336,7 +1416,8 @@ QPDF::recoverStreamLength(InputSource* input,
if (length == 0)
{
- throw QPDFExc(input->getName(), stream_offset,
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description, stream_offset,
"unable to recover stream data");
}
@@ -1356,7 +1437,9 @@ QPDF::readToken(InputSource* input)
char ch;
if (input->read(&ch, 1) == 0)
{
- throw QPDFExc(input->getName(), offset, "EOF while reading token");
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description, offset,
+ "EOF while reading token");
}
else
{
@@ -1376,7 +1459,9 @@ QPDF::readToken(InputSource* input)
if (token.getType() == QPDFTokenizer::tt_bad)
{
- throw QPDFExc(input->getName(), offset, token.getErrorMessage());
+ throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ this->last_object_description, offset,
+ token.getErrorMessage());
}
input->setLastOffset(offset);
@@ -1385,9 +1470,12 @@ QPDF::readToken(InputSource* input)
}
QPDFObjectHandle
-QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
+QPDF::readObjectAtOffset(bool try_recovery,
+ off_t offset, std::string const& description,
+ int exp_objid, int exp_generation,
int& objid, int& generation)
{
+ setLastObjectDescription(description, exp_objid, exp_generation);
this->file.seek(offset, SEEK_SET);
QPDFTokenizer::Token tobjid = readToken(&this->file);
@@ -1407,7 +1495,9 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
if (! (objidok && genok && objok))
{
QTC::TC("qpdf", "QPDF expected n n obj");
- throw QPDFExc(this->file.getName(), offset, "expected n n obj");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description, offset,
+ "expected n n obj");
}
objid = atoi(tobjid.getValue().c_str());
generation = atoi(tgen.getValue().c_str());
@@ -1416,7 +1506,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
(! ((objid == exp_objid) && (generation == exp_generation))))
{
QTC::TC("qpdf", "QPDF err wrong objid/generation");
- throw QPDFExc(this->file.getName(), offset,
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description, offset,
std::string("expected ") +
QUtil::int_to_string(exp_objid) + " " +
QUtil::int_to_string(exp_generation) + " obj");
@@ -1424,7 +1515,7 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
}
catch (QPDFExc& e)
{
- if (exp_objid && this->attempt_recovery)
+ if (exp_objid && try_recovery && this->attempt_recovery)
{
// Try again after reconstructing xref table
reconstruct_xref(e);
@@ -1433,13 +1524,27 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
(this->xref_table[og].getType() == 1))
{
off_t new_offset = this->xref_table[og].getOffset();
- // Call readObjectAtOffset with 0 for exp_objid to
- // avoid an infinite loop.
- QPDFObjectHandle result =
- readObjectAtOffset(new_offset, 0, 0, objid, generation);
+ QPDFObjectHandle result = readObjectAtOffset(
+ false, new_offset, description,
+ exp_objid, exp_generation, objid, generation);
QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset");
return result;
}
+ else
+ {
+ QTC::TC("qpdf", "QPDF object gone after xref reconstruction");
+ warn(QPDFExc(
+ qpdf_e_damaged_pdf, this->file.getName(),
+ "", 0,
+ std::string(
+ "object " +
+ QUtil::int_to_string(exp_objid) +
+ " " +
+ QUtil::int_to_string(exp_generation) +
+ " not found in file after regenerating"
+ " cross reference table")));
+ return QPDFObjectHandle::newNull();
+ }
}
else
{
@@ -1448,13 +1553,14 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
}
QPDFObjectHandle oh = readObject(
- &this->file, objid, generation, false);
+ &this->file, description, objid, generation, false);
if (! (readToken(&this->file) ==
QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj")))
{
QTC::TC("qpdf", "QPDF err expected endobj");
- warn(QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description, this->file.getLastOffset(),
"expected endobj"));
}
@@ -1487,7 +1593,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
}
else
{
- throw QPDFExc(this->file.getName(), offset,
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description, offset,
"EOF after endobj");
}
}
@@ -1526,7 +1633,7 @@ QPDF::resolve(int objid, int generation)
int aobjid;
int ageneration;
QPDFObjectHandle oh =
- readObjectAtOffset(offset, objid, generation,
+ readObjectAtOffset(true, offset, "", objid, generation,
aobjid, ageneration);
}
break;
@@ -1536,7 +1643,7 @@ QPDF::resolve(int objid, int generation)
break;
default:
- throw QPDFExc(this->file.getName(), 0,
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
"object " +
QUtil::int_to_string(objid) + "/" +
QUtil::int_to_string(generation) +
@@ -1554,7 +1661,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0);
if (! obj_stream.isStream())
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description,
+ this->file.getLastOffset(),
"supposed object stream " +
QUtil::int_to_string(obj_stream_number) +
" is not a stream");
@@ -1570,7 +1679,10 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
if (! (dict.getKey("/Type").isName() &&
dict.getKey("/Type").getName() == "/ObjStm"))
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ QTC::TC("qpdf", "QPDF ERR object stream with wrong type");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description,
+ this->file.getLastOffset(),
"supposed object stream " +
QUtil::int_to_string(obj_stream_number) +
" has wrong type");
@@ -1579,7 +1691,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
if (! (dict.getKey("/N").isInteger() &&
dict.getKey("/First").isInteger()))
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description,
+ this->file.getLastOffset(),
"object stream " +
QUtil::int_to_string(obj_stream_number) +
" has incorrect keys");
@@ -1602,7 +1716,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
if (! ((tnum.getType() == QPDFTokenizer::tt_integer) &&
(toffset.getType() == QPDFTokenizer::tt_integer)))
{
- throw QPDFExc(input.getName(), input.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, input.getName(),
+ this->last_object_description, input.getLastOffset(),
"expected integer in object stream header");
}
@@ -1617,7 +1732,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
int obj = (*iter).first;
int offset = (*iter).second;
input.seek(offset, SEEK_SET);
- QPDFObjectHandle oh = readObject(&input, obj, 0, true);
+ QPDFObjectHandle oh = readObject(&input, "", obj, 0, true);
// Store in cache
ObjGen og(obj, 0);
@@ -1830,17 +1945,25 @@ QPDF::pipeStreamData(int objid, int generation,
size_t len = this->file.read(buf, to_read);
if (len == 0)
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf,
+ this->file.getName(),
+ this->last_object_description,
+ this->file.getLastOffset(),
"unexpected EOF reading stream data");
}
length -= len;
pipeline->write((unsigned char*)buf, len);
}
}
+ catch (QPDFExc& e)
+ {
+ warn(e);
+ }
catch (std::runtime_error& e)
{
QTC::TC("qpdf", "QPDF decoding error warning");
- warn(QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "", this->file.getLastOffset(),
"error decoding stream data for object " +
QUtil::int_to_string(objid) + " " +
QUtil::int_to_string(generation) + ": " + e.what()));
@@ -1896,6 +2019,9 @@ QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages,
}
else
{
- throw QPDFExc(this->file.getName() + ": invalid Type in page tree");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description,
+ this->file.getLastOffset(),
+ + ": invalid Type in page tree");
}
}
diff --git a/libqpdf/QPDFExc.cc b/libqpdf/QPDFExc.cc
index 006a96ac..614990a9 100644
--- a/libqpdf/QPDFExc.cc
+++ b/libqpdf/QPDFExc.cc
@@ -1,18 +1,56 @@
#include <qpdf/QPDFExc.hh>
#include <qpdf/QUtil.hh>
-QPDFExc::QPDFExc(std::string const& message) :
- std::runtime_error(message)
+QPDFExc::QPDFExc(qpdf_error_code_e error_code,
+ std::string const& filename,
+ std::string const& object,
+ off_t offset,
+ std::string const& message) :
+ std::runtime_error(createWhat(filename, object, offset, message)),
+ error_code(error_code),
+ filename(filename),
+ object(object),
+ offset(offset),
+ message(message)
{
}
-QPDFExc::QPDFExc(std::string const& filename, int offset,
- std::string const& message) :
- std::runtime_error(filename + ": offset " + QUtil::int_to_string(offset) +
- ": " + message)
+QPDFExc::~QPDFExc() throw ()
{
}
-QPDFExc::~QPDFExc() throw ()
+std::string
+QPDFExc::createWhat(std::string const& filename,
+ std::string const& object,
+ off_t offset,
+ std::string const& message)
{
+ std::string result;
+ if (! filename.empty())
+ {
+ result += filename;
+ }
+ if (! (object.empty() && offset == 0))
+ {
+ result += " (";
+ if (! object.empty())
+ {
+ result += object;
+ if (offset > 0)
+ {
+ result += ", ";
+ }
+ }
+ if (offset > 0)
+ {
+ result += "file position " + QUtil::int_to_string(offset);
+ }
+ result += ")";
+ }
+ if (! result.empty())
+ {
+ result += ": ";
+ }
+ result += message;
+ return result;
}
diff --git a/libqpdf/QPDFXRefEntry.cc b/libqpdf/QPDFXRefEntry.cc
index b6d48d97..9a08c829 100644
--- a/libqpdf/QPDFXRefEntry.cc
+++ b/libqpdf/QPDFXRefEntry.cc
@@ -16,7 +16,8 @@ QPDFXRefEntry::QPDFXRefEntry(int type, int field1, int field2) :
{
if ((type < 1) || (type > 2))
{
- throw QPDFExc("invalid xref type " + QUtil::int_to_string(type));
+ throw std::logic_error(
+ "invalid xref type " + QUtil::int_to_string(type));
}
}
@@ -31,7 +32,7 @@ QPDFXRefEntry::getOffset() const
{
if (this->type != 1)
{
- throw QPDFExc(
+ throw std::logic_error(
"getOffset called for xref entry of type != 1");
}
return this->field1;
@@ -42,7 +43,7 @@ QPDFXRefEntry::getObjStreamNumber() const
{
if (this->type != 2)
{
- throw QPDFExc(
+ throw std::logic_error(
"getObjStreamNumber called for xref entry of type != 2");
}
return this->field1;
@@ -53,7 +54,7 @@ QPDFXRefEntry::getObjStreamIndex() const
{
if (this->type != 2)
{
- throw QPDFExc(
+ throw std::logic_error(
"getObjStreamIndex called for xref entry of type != 2");
}
return this->field2;
diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc
index 6642a06e..d0dd2e5f 100644
--- a/libqpdf/QPDF_Stream.cc
+++ b/libqpdf/QPDF_Stream.cc
@@ -59,7 +59,7 @@ QPDF_Stream::getStreamData()
Pl_Buffer buf("stream data buffer");
if (! pipeStreamData(&buf, true, false, false))
{
- throw QPDFExc("getStreamData called on unfilterable stream");
+ throw std::logic_error("getStreamData called on unfilterable stream");
}
return buf.getBuffer();
}
@@ -208,8 +208,9 @@ QPDF_Stream::filterable(std::vector<std::string>& filters,
if (! filters_okay)
{
QTC::TC("qpdf", "QPDF_Stream invalid filter");
- throw QPDFExc(qpdf->getFilename(), this->offset,
- "invalid filter object type for this stream");
+ throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(),
+ "", this->offset,
+ "stream filter type is not name or array");
}
// `filters' now contains a list of filters to be applied in
diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc
index 8962624d..d6b0ae23 100644
--- a/libqpdf/QPDF_encryption.cc
+++ b/libqpdf/QPDF_encryption.cc
@@ -337,14 +337,16 @@ QPDF::initializeEncryption()
(id_obj.getArrayNItems() == 2) &&
id_obj.getArrayItem(0).isString()))
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "trailer", this->file.getLastOffset(),
"invalid /ID in trailer dictionary");
}
std::string id1 = id_obj.getArrayItem(0).getStringValue();
if (id1.length() != id_bytes)
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "trailer", this->file.getLastOffset(),
"first /ID string in trailer dictionary has "
"incorrect length");
}
@@ -352,19 +354,23 @@ QPDF::initializeEncryption()
QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt");
if (! encryption_dict.isDictionary())
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description,
+ this->file.getLastOffset(),
"/Encrypt in trailer dictionary is not a dictionary");
}
if (! (encryption_dict.getKey("/Filter").isName() &&
(encryption_dict.getKey("/Filter").getName() == "/Standard")))
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "encryption dictionary", this->file.getLastOffset(),
"unsupported encryption filter");
}
if (! encryption_dict.getKey("/SubFilter").isNull())
{
- warn(QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ warn(QPDFExc(qpdf_e_unsupported, this->file.getName(),
+ "encryption dictionary", this->file.getLastOffset(),
"file uses encryption SubFilters,"
" which qpdf does not support"));
}
@@ -375,7 +381,8 @@ QPDF::initializeEncryption()
encryption_dict.getKey("/U").isString() &&
encryption_dict.getKey("/P").isInteger()))
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "encryption dictionary", this->file.getLastOffset(),
"some encryption dictionary parameters are missing "
"or the wrong type");
}
@@ -389,7 +396,8 @@ QPDF::initializeEncryption()
if (! (((R == 2) || (R == 3) || (R == 4)) &&
((V == 1) || (V == 2) || (V == 4))))
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_unsupported, this->file.getName(),
+ "encryption dictionary", this->file.getLastOffset(),
"Unsupported /R or /V in encryption dictionary");
}
@@ -397,7 +405,8 @@ QPDF::initializeEncryption()
if (! ((O.length() == key_bytes) && (U.length() == key_bytes)))
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "encryption dictionary", this->file.getLastOffset(),
"incorrect length for /O and/or /P in "
"encryption dictionary");
}
@@ -408,7 +417,8 @@ QPDF::initializeEncryption()
Length = encryption_dict.getKey("/Length").getIntValue();
if ((Length % 8) || (Length < 40) || (Length > 128))
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "encryption dictionary", this->file.getLastOffset(),
"invalid /Length value in encryption dictionary");
}
}
@@ -471,7 +481,8 @@ QPDF::initializeEncryption()
}
if (this->cf_file != this->cf_stream)
{
- throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ throw QPDFExc(qpdf_e_unsupported, this->file.getName(),
+ "encryption dictionary", this->file.getLastOffset(),
"This document has embedded files that are"
" encrypted differently from the rest of the file."
" qpdf does not presently support this due to"
@@ -492,7 +503,8 @@ QPDF::initializeEncryption()
}
else
{
- throw QPDFExc(this->file.getName() + ": invalid password");
+ throw QPDFExc(qpdf_e_password, this->file.getName(),
+ "", 0, "invalid password");
}
this->encryption_key = compute_encryption_key(this->user_password, data);
@@ -542,7 +554,9 @@ QPDF::decryptString(std::string& str, int objid, int generation)
break;
default:
- warn(QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description,
+ this->file.getLastOffset(),
"unknown encryption filter for strings"
" (check /StrF in /Encrypt dictionary);"
" strings may be decrypted improperly"));
@@ -554,28 +568,47 @@ QPDF::decryptString(std::string& str, int objid, int generation)
}
std::string key = getKeyForObject(objid, generation, use_aes);
- if (use_aes)
+ try
{
- QTC::TC("qpdf", "QPDF_encryption aes decode string");
- assert(key.length() == Pl_AES_PDF::key_size);
- Pl_Buffer bufpl("decrypted string");
- Pl_AES_PDF pl("aes decrypt string", &bufpl, false,
- (unsigned char const*)key.c_str());
- pl.write((unsigned char*)str.c_str(), str.length());
- pl.finish();
- Buffer* buf = bufpl.getBuffer();
- str = std::string((char*)buf->getBuffer(), (size_t)buf->getSize());
- delete buf;
+ if (use_aes)
+ {
+ QTC::TC("qpdf", "QPDF_encryption aes decode string");
+ assert(key.length() == Pl_AES_PDF::key_size);
+ Pl_Buffer bufpl("decrypted string");
+ Pl_AES_PDF pl("aes decrypt string", &bufpl, false,
+ (unsigned char const*)key.c_str());
+ pl.write((unsigned char*)str.c_str(), str.length());
+ pl.finish();
+ PointerHolder<Buffer> buf = bufpl.getBuffer();
+ str = std::string((char*)buf.getPointer()->getBuffer(),
+ (size_t)buf.getPointer()->getSize());
+ }
+ else
+ {
+ QTC::TC("qpdf", "QPDF_encryption rc4 decode string");
+ unsigned int vlen = str.length();
+ // Using PointerHolder will cause a new char[] to be deleted
+ // with delete instead of delete [], but it's okay since the
+ // array is of a fundamental type, so there is no destructor
+ // to be called. Using PointerHolder guarantees that tmp will
+ // be freed even if rc4.process throws an exception.
+ PointerHolder<char> tmp = QUtil::copy_string(str);
+ RC4 rc4((unsigned char const*)key.c_str(), key.length());
+ rc4.process((unsigned char*)tmp.getPointer(), vlen);
+ str = std::string(tmp.getPointer(), vlen);
+ }
}
- else
+ catch (QPDFExc& e)
+ {
+ throw;
+ }
+ catch (std::runtime_error& e)
{
- QTC::TC("qpdf", "QPDF_encryption rc4 decode string");
- unsigned int vlen = str.length();
- char* tmp = QUtil::copy_string(str);
- RC4 rc4((unsigned char const*)key.c_str(), key.length());
- rc4.process((unsigned char*)tmp, vlen);
- str = std::string(tmp, vlen);
- delete [] tmp;
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description, this->file.getLastOffset(),
+ "error decrypting string for object " +
+ QUtil::int_to_string(objid) + " " +
+ QUtil::int_to_string(generation) + ": " + e.what());
}
}
@@ -645,7 +678,9 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
default:
// filter local to this stream.
- warn(QPDFExc(this->file.getName(), this->file.getLastOffset(),
+ warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description,
+ this->file.getLastOffset(),
"unknown encryption filter for streams"
" (check " + method_source + ");"
" streams may be decrypted improperly"));
diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc
index 5bb4190e..51b74855 100644
--- a/libqpdf/QPDF_linearization.cc
+++ b/libqpdf/QPDF_linearization.cc
@@ -175,7 +175,8 @@ QPDF::readLinearizationData()
if (! isLinearized())
{
- throw QPDFExc(this->file.getName() + " is not linearized");
+ throw std::logic_error("called readLinearizationData for file"
+ " that is not linearized");
}
// /L is read and stored in linp by isLinearized()
@@ -193,7 +194,10 @@ QPDF::readLinearizationData()
T.isInteger() &&
(P.isInteger() || P.isNull())))
{
- throw QPDFExc("some keys in linearization dictionary are of "
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "linearization dictionary",
+ this->file.getLastOffset(),
+ "some keys in linearization dictionary are of "
"the wrong type");
}
@@ -201,7 +205,10 @@ QPDF::readLinearizationData()
unsigned int n_H_items = H.getArrayNItems();
if (! ((n_H_items == 2) || (n_H_items == 4)))
{
- throw QPDFExc("H has the wrong number of items");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "linearization dictionary",
+ this->file.getLastOffset(),
+ "H has the wrong number of items");
}
std::vector<int> H_items;
@@ -214,7 +221,10 @@ QPDF::readLinearizationData()
}
else
{
- throw QPDFExc("some H items are of the wrong type");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "linearization dictionary",
+ this->file.getLastOffset(),
+ "some H items are of the wrong type");
}
}
@@ -301,13 +311,17 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length)
{
int obj;
int gen;
- QPDFObjectHandle H = readObjectAtOffset(offset, 0, 0, obj, gen);
+ QPDFObjectHandle H = readObjectAtOffset(
+ false, offset, "linearization hint stream", 0, 0, obj, gen);
ObjCache& oc = this->obj_cache[ObjGen(obj, gen)];
off_t min_end_offset = oc.end_before_space;
off_t max_end_offset = oc.end_after_space;
if (! H.isStream())
{
- throw QPDFExc("hint table is not a stream");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "linearization dictionary",
+ this->file.getLastOffset(),
+ "hint table is not a stream");
}
QPDFObjectHandle Hdict = H.getDict();
@@ -340,7 +354,10 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length)
std::cout << "expected = " << computed_end
<< "; actual = " << min_end_offset << ".."
<< max_end_offset << std::endl;
- throw QPDFExc("hint table length mismatch");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ "linearization dictionary",
+ this->file.getLastOffset(),
+ "hint table length mismatch");
}
H.pipeStreamData(&pl, true, false, false);
return Hdict;
@@ -651,8 +668,7 @@ QPDF::getLinearizationOffset(ObjGen const& og)
break;
default:
- throw QPDFExc(
- this->file.getName(), 0,
+ throw std::logic_error(
"getLinearizationOffset called for xref entry not of type 1 or 2");
break;
}
diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc
index f3833efd..e8d20fd5 100644
--- a/libqpdf/QPDF_optimization.cc
+++ b/libqpdf/QPDF_optimization.cc
@@ -233,9 +233,12 @@ QPDF::optimizePagesTree(
{
if (! allow_changes)
{
- throw QPDFExc(this->file.getName() +
- ": optimize detected an "
- "inheritable resource");
+ throw QPDFExc(qpdf_e_internal, this->file.getName(),
+ this->last_object_description,
+ this->file.getLastOffset(),
+ "optimize detected an "
+ "inheritable resource when called "
+ "in no-change mode");
}
// This is an inheritable resource
@@ -338,7 +341,10 @@ QPDF::optimizePagesTree(
}
else
{
- throw QPDFExc(this->file.getName() + ": invalid Type in page tree");
+ throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(),
+ this->last_object_description,
+ this->file.getLastOffset(),
+ "invalid Type in page tree");
}
}