aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2018-02-16 23:25:27 +0100
committerJay Berkenbilt <ejb@ql.org>2018-02-19 03:06:27 +0100
commitd0e99f195a987c483bbb6c5449cf39bee34e08a1 (patch)
treecead8acd60cd14fd5d904ed380c750540cb361f3 /libqpdf
parentc2e16827b69f3d3ac3721cfcd608b87f28e2a13f (diff)
downloadqpdf-d0e99f195a987c483bbb6c5449cf39bee34e08a1.tar.zst
More robust handling of type errors
Give objects descriptions and context so it is possible to issue warnings instead of fatal errors for attempts to access objects of the wrong type.
Diffstat (limited to 'libqpdf')
-rw-r--r--libqpdf/QPDF.cc53
-rw-r--r--libqpdf/QPDFExc.cc10
-rw-r--r--libqpdf/QPDFObject.cc35
-rw-r--r--libqpdf/QPDFObjectHandle.cc484
-rw-r--r--libqpdf/QPDFTokenizer.cc9
-rw-r--r--libqpdf/QPDF_Array.cc7
-rw-r--r--libqpdf/QPDF_Dictionary.cc19
-rw-r--r--libqpdf/QPDF_Stream.cc31
-rw-r--r--libqpdf/QPDF_linearization.cc8
-rw-r--r--libqpdf/qpdf/QPDF_Array.hh1
-rw-r--r--libqpdf/qpdf/QPDF_Dictionary.hh3
-rw-r--r--libqpdf/qpdf/QPDF_Stream.hh3
12 files changed, 545 insertions, 118 deletions
diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc
index 31c8d8e2..31f13118 100644
--- a/libqpdf/QPDF.cc
+++ b/libqpdf/QPDF.cc
@@ -106,6 +106,7 @@ QPDF::Members::~Members()
QPDF::QPDF() :
m(new Members())
{
+ m->tokenizer.allowEOF();
}
QPDF::~QPDF()
@@ -272,10 +273,10 @@ QPDF::findHeader()
bool
QPDF::findStartxref()
{
- QPDFTokenizer::Token t = readToken(this->m->file, true);
+ QPDFTokenizer::Token t = readToken(this->m->file);
if (t == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "startxref"))
{
- t = readToken(this->m->file, true);
+ t = readToken(this->m->file);
if (t.getType() == QPDFTokenizer::tt_integer)
{
// Position in front of offset token
@@ -421,7 +422,7 @@ QPDF::reconstruct_xref(QPDFExc& e)
this->m->file->findAndSkipNextEOL();
qpdf_offset_t next_line_start = this->m->file->tell();
this->m->file->seek(line_start, SEEK_SET);
- QPDFTokenizer::Token t1 = readToken(this->m->file, true, MAX_LEN);
+ QPDFTokenizer::Token t1 = readToken(this->m->file, MAX_LEN);
qpdf_offset_t token_start =
this->m->file->tell() - t1.getValue().length();
if (token_start >= next_line_start)
@@ -440,9 +441,9 @@ QPDF::reconstruct_xref(QPDFExc& e)
if (t1.getType() == QPDFTokenizer::tt_integer)
{
QPDFTokenizer::Token t2 =
- readToken(this->m->file, true, MAX_LEN);
+ readToken(this->m->file, MAX_LEN);
QPDFTokenizer::Token t3 =
- readToken(this->m->file, true, MAX_LEN);
+ readToken(this->m->file, MAX_LEN);
if ((t2.getType() == QPDFTokenizer::tt_integer) &&
(t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj")))
{
@@ -1429,7 +1430,7 @@ bool
QPDF::findEndstream()
{
// Find endstream or endobj. Position the input at that token.
- QPDFTokenizer::Token t = readToken(this->m->file, true, 20);
+ QPDFTokenizer::Token t = readToken(this->m->file, 20);
if ((t.getType() == QPDFTokenizer::tt_word) &&
((t.getValue() == "endobj") ||
(t.getValue() == "endstream")))
@@ -1522,11 +1523,10 @@ QPDF::recoverStreamLength(PointerHolder<InputSource> input,
}
QPDFTokenizer::Token
-QPDF::readToken(PointerHolder<InputSource> input,
- bool allow_bad, size_t max_len)
+QPDF::readToken(PointerHolder<InputSource> input, size_t max_len)
{
return this->m->tokenizer.readToken(
- input, this->m->last_object_description, allow_bad, max_len);
+ input, this->m->last_object_description, true, max_len);
}
QPDFObjectHandle
@@ -1730,16 +1730,10 @@ QPDF::resolve(int objid, int generation)
}
ResolveRecorder rr(this, og);
- if (! this->m->obj_cache.count(og))
+ // PDF spec says unknown objects resolve to the null object.
+ if ((! this->m->obj_cache.count(og)) && this->m->xref_table.count(og))
{
- if (! this->m->xref_table.count(og))
- {
- // PDF spec says unknown objects resolve to the null object.
- return new QPDF_Null;
- }
-
QPDFXRefEntry const& entry = this->m->xref_table[og];
- bool success = false;
try
{
switch (entry.getType())
@@ -1768,7 +1762,6 @@ QPDF::resolve(int objid, int generation)
QUtil::int_to_string(generation) +
" has unexpected xref entry type");
}
- success = true;
}
catch (QPDFExc& e)
{
@@ -1782,16 +1775,24 @@ QPDF::resolve(int objid, int generation)
QUtil::int_to_string(generation) +
": error reading object: " + e.what()));
}
- if (! success)
- {
- QTC::TC("qpdf", "QPDF resolve failure to null");
- QPDFObjectHandle oh = QPDFObjectHandle::newNull();
- this->m->obj_cache[og] =
- ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
- }
+ }
+ if (this->m->obj_cache.count(og) == 0)
+ {
+ QTC::TC("qpdf", "QPDF resolve failure to null");
+ QPDFObjectHandle oh = QPDFObjectHandle::newNull();
+ this->m->obj_cache[og] =
+ ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
}
- return this->m->obj_cache[og].object;
+ PointerHolder<QPDFObject> result(this->m->obj_cache[og].object);
+ if (! result->hasDescription())
+ {
+ result->setDescription(
+ this,
+ "object " + QUtil::int_to_string(objid) + " " +
+ QUtil::int_to_string(generation));
+ }
+ return result;
}
void
diff --git a/libqpdf/QPDFExc.cc b/libqpdf/QPDFExc.cc
index 728d4ce8..b816e913 100644
--- a/libqpdf/QPDFExc.cc
+++ b/libqpdf/QPDFExc.cc
@@ -32,7 +32,10 @@ QPDFExc::createWhat(std::string const& filename,
}
if (! (object.empty() && offset == 0))
{
- result += " (";
+ if (! filename.empty())
+ {
+ result += " (";
+ }
if (! object.empty())
{
result += object;
@@ -45,7 +48,10 @@ QPDFExc::createWhat(std::string const& filename,
{
result += "offset " + QUtil::int_to_string(offset);
}
- result += ")";
+ if (! filename.empty())
+ {
+ result += ")";
+ }
}
if (! result.empty())
{
diff --git a/libqpdf/QPDFObject.cc b/libqpdf/QPDFObject.cc
index 8df2b480..cffb8a56 100644
--- a/libqpdf/QPDFObject.cc
+++ b/libqpdf/QPDFObject.cc
@@ -1 +1,36 @@
#include <qpdf/QPDFObject.hh>
+
+QPDFObject::Members::Members() :
+ owning_qpdf(0)
+{
+}
+
+QPDFObject::Members::~Members()
+{
+}
+
+QPDFObject::QPDFObject() :
+ m(new Members)
+{
+}
+
+void
+QPDFObject::setDescription(QPDF* qpdf, std::string const& description)
+{
+ this->m->owning_qpdf = qpdf;
+ this->m->object_description = description;
+}
+
+bool
+QPDFObject::getDescription(QPDF*& qpdf, std::string& description)
+{
+ qpdf = this->m->owning_qpdf;
+ description = this->m->object_description;
+ return this->m->owning_qpdf;
+}
+
+bool
+QPDFObject::hasDescription()
+{
+ return this->m->owning_qpdf;
+}
diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc
index d48461bf..2e9cc996 100644
--- a/libqpdf/QPDFObjectHandle.cc
+++ b/libqpdf/QPDFObjectHandle.cc
@@ -190,6 +190,18 @@ QPDFObjectHandle::releaseResolved()
}
}
+void
+QPDFObjectHandle::setObjectDescriptionFromInput(
+ QPDFObjectHandle object, QPDF* context,
+ std::string const& description, PointerHolder<InputSource> input,
+ qpdf_offset_t offset)
+{
+ object.setObjectDescription(
+ context,
+ input->getName() + ", " + description +
+ " at offset " + QUtil::int_to_string(offset));
+}
+
bool
QPDFObjectHandle::isInitialized() const
{
@@ -282,7 +294,8 @@ QPDFObjectHandle::getNumericValue()
}
else
{
- throw std::logic_error("getNumericValue called for non-numeric object");
+ typeWarning("number", "returning 0");
+ QTC::TC("qpdf", "QPDFObjectHandle numeric non-numeric");
}
return result;
}
@@ -363,8 +376,16 @@ QPDFObjectHandle::isScalar()
bool
QPDFObjectHandle::getBoolValue()
{
- assertBool();
- return dynamic_cast<QPDF_Bool*>(m->obj.getPointer())->getVal();
+ if (isBool())
+ {
+ return dynamic_cast<QPDF_Bool*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("boolean", "returning false");
+ QTC::TC("qpdf", "QPDFObjectHandle boolean returning false");
+ return false;
+ }
}
// Integer accessors
@@ -372,8 +393,16 @@ QPDFObjectHandle::getBoolValue()
long long
QPDFObjectHandle::getIntValue()
{
- assertInteger();
- return dynamic_cast<QPDF_Integer*>(m->obj.getPointer())->getVal();
+ if (isInteger())
+ {
+ return dynamic_cast<QPDF_Integer*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("integer", "returning 0");
+ QTC::TC("qpdf", "QPDFObjectHandle integer returning 0");
+ return 0;
+ }
}
// Real accessors
@@ -381,8 +410,16 @@ QPDFObjectHandle::getIntValue()
std::string
QPDFObjectHandle::getRealValue()
{
- assertReal();
- return dynamic_cast<QPDF_Real*>(m->obj.getPointer())->getVal();
+ if (isReal())
+ {
+ return dynamic_cast<QPDF_Real*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("real", "returning 0.0");
+ QTC::TC("qpdf", "QPDFObjectHandle real returning 0.0");
+ return "0.0";
+ }
}
// Name accessors
@@ -390,8 +427,16 @@ QPDFObjectHandle::getRealValue()
std::string
QPDFObjectHandle::getName()
{
- assertName();
- return dynamic_cast<QPDF_Name*>(m->obj.getPointer())->getName();
+ if (isName())
+ {
+ return dynamic_cast<QPDF_Name*>(m->obj.getPointer())->getName();
+ }
+ else
+ {
+ typeWarning("name", "returning dummy name");
+ QTC::TC("qpdf", "QPDFObjectHandle name returning dummy name");
+ return "/QPDFFakeName";
+ }
}
// String accessors
@@ -399,15 +444,31 @@ QPDFObjectHandle::getName()
std::string
QPDFObjectHandle::getStringValue()
{
- assertString();
- return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getVal();
+ if (isString())
+ {
+ return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("string", "returning empty string");
+ QTC::TC("qpdf", "QPDFObjectHandle string returning empty string");
+ return "";
+ }
}
std::string
QPDFObjectHandle::getUTF8Value()
{
- assertString();
- return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getUTF8Val();
+ if (isString())
+ {
+ return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getUTF8Val();
+ }
+ else
+ {
+ typeWarning("string", "returning empty string");
+ QTC::TC("qpdf", "QPDFObjectHandle string returning empty utf8");
+ return "";
+ }
}
// Operator and Inline Image accessors
@@ -415,15 +476,31 @@ QPDFObjectHandle::getUTF8Value()
std::string
QPDFObjectHandle::getOperatorValue()
{
- assertOperator();
- return dynamic_cast<QPDF_Operator*>(m->obj.getPointer())->getVal();
+ if (isOperator())
+ {
+ return dynamic_cast<QPDF_Operator*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("operator", "returning fake value");
+ QTC::TC("qpdf", "QPDFObjectHandle operator returning fake value");
+ return "QPDFFAKE";
+ }
}
std::string
QPDFObjectHandle::getInlineImageValue()
{
- assertInlineImage();
- return dynamic_cast<QPDF_InlineImage*>(m->obj.getPointer())->getVal();
+ if (isInlineImage())
+ {
+ return dynamic_cast<QPDF_InlineImage*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("inlineimage", "returning empty data");
+ QTC::TC("qpdf", "QPDFObjectHandle inlineimage returning empty data");
+ return "";
+ }
}
// Array accessors
@@ -431,22 +508,66 @@ QPDFObjectHandle::getInlineImageValue()
int
QPDFObjectHandle::getArrayNItems()
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getNItems();
+ if (isArray())
+ {
+ return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getNItems();
+ }
+ else
+ {
+ typeWarning("array", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
+ return 0;
+ }
}
QPDFObjectHandle
QPDFObjectHandle::getArrayItem(int n)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getItem(n);
+ QPDFObjectHandle result;
+ if (isArray() && (n < getArrayNItems()) && (n >= 0))
+ {
+ result = dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getItem(n);
+ }
+ else
+ {
+ result = newNull();
+ if (isArray())
+ {
+ objectWarning("returning null for out of bounds array access");
+ QTC::TC("qpdf", "QPDFObjectHandle array bounds");
+ }
+ else
+ {
+ typeWarning("array", "returning null");
+ QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");
+ }
+ QPDF* context = 0;
+ std::string description;
+ if (this->m->obj->getDescription(context, description))
+ {
+ result.setObjectDescription(
+ context,
+ description +
+ " -> null returned from invalid array access");
+ }
+ }
+ return result;
}
std::vector<QPDFObjectHandle>
QPDFObjectHandle::getArrayAsVector()
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getAsVector();
+ std::vector<QPDFObjectHandle> result;
+ if (isArray())
+ {
+ result = dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getAsVector();
+ }
+ else
+ {
+ typeWarning("array", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector");
+ }
+ return result;
}
// Array mutators
@@ -454,36 +575,79 @@ QPDFObjectHandle::getArrayAsVector()
void
QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setItem(n, item);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setItem(n, item);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to set item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");
+ }
}
void
QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setFromVector(items);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setFromVector(items);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to replace items");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items");
+ }
}
void
QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->insertItem(at, item);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->insertItem(at, item);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to insert item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item");
+ }
}
void
QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->appendItem(item);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->appendItem(item);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to append item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item");
+ }
}
void
QPDFObjectHandle::eraseItem(int at)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->eraseItem(at);
+ if (isArray() && (at < getArrayNItems()) && (at >= 0))
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->eraseItem(at);
+ }
+ else
+ {
+ if (isArray())
+ {
+ objectWarning("ignoring attempt to erase out of bounds array item");
+ QTC::TC("qpdf", "QPDFObjectHandle erase array bounds");
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to erase item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item");
+ }
+ }
}
// Dictionary accessors
@@ -491,29 +655,79 @@ QPDFObjectHandle::eraseItem(int at)
bool
QPDFObjectHandle::hasKey(std::string const& key)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->hasKey(key);
+ if (isDictionary())
+ {
+ return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->hasKey(key);
+ }
+ else
+ {
+ typeWarning("dictionary",
+ "returning false for a key containment request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
+ return false;
+ }
}
QPDFObjectHandle
QPDFObjectHandle::getKey(std::string const& key)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKey(key);
+ QPDFObjectHandle result;
+ if (isDictionary())
+ {
+ result = dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->getKey(key);
+ }
+ else
+ {
+ typeWarning(
+ "dictionary", "returning null for attempted key retrieval");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
+ result = newNull();
+ QPDF* qpdf = 0;
+ std::string description;
+ if (this->m->obj->getDescription(qpdf, description))
+ {
+ result.setObjectDescription(
+ qpdf,
+ description +
+ " -> null returned from getting key " +
+ key + " from non-Dictionary");
+ }
+ }
+ return result;
}
std::set<std::string>
QPDFObjectHandle::getKeys()
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKeys();
+ std::set<std::string> result;
+ if (isDictionary())
+ {
+ result = dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKeys();
+ }
+ else
+ {
+ typeWarning("dictionary", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
+ }
+ return result;
}
std::map<std::string, QPDFObjectHandle>
QPDFObjectHandle::getDictAsMap()
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getAsMap();
+ std::map<std::string, QPDFObjectHandle> result;
+ if (isDictionary())
+ {
+ result = dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->getAsMap();
+ }
+ else
+ {
+ typeWarning("dictionary", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
+ }
+ return result;
}
// Array and Name accessors
@@ -551,27 +765,48 @@ QPDFObjectHandle::getOwningQPDF()
void
QPDFObjectHandle::replaceKey(std::string const& key,
- QPDFObjectHandle const& value)
+ QPDFObjectHandle value)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(
- m->obj.getPointer())->replaceKey(key, value);
+ if (isDictionary())
+ {
+ dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->replaceKey(key, value);
+ }
+ else
+ {
+ typeWarning("dictionary", "ignoring key replacement request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
+ }
}
void
QPDFObjectHandle::removeKey(std::string const& key)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->removeKey(key);
+ if (isDictionary())
+ {
+ dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->removeKey(key);
+ }
+ else
+ {
+ typeWarning("dictionary", "ignoring key removal request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
+ }
}
void
QPDFObjectHandle::replaceOrRemoveKey(std::string const& key,
QPDFObjectHandle value)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(
- m->obj.getPointer())->replaceOrRemoveKey(key, value);
+ if (isDictionary())
+ {
+ dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->replaceOrRemoveKey(key, value);
+ }
+ else
+ {
+ typeWarning("dictionary", "ignoring key removal/replacement request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removereplace");
+ }
}
// Stream accessors
@@ -1173,35 +1408,45 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
std::vector<parser_state_e> state_stack;
state_stack.push_back(st_top);
std::vector<qpdf_offset_t> offset_stack;
- offset_stack.push_back(input->tell());
+ qpdf_offset_t offset = input->tell();
+ offset_stack.push_back(offset);
bool done = false;
while (! done)
{
std::vector<QPDFObjectHandle>& olist = olist_stack.back();
parser_state_e state = state_stack.back();
- qpdf_offset_t offset = offset_stack.back();
+ offset = offset_stack.back();
object = QPDFObjectHandle();
QPDFTokenizer::Token token =
- tokenizer.readToken(input, object_description);
+ tokenizer.readToken(input, object_description, true);
switch (token.getType())
{
case QPDFTokenizer::tt_eof:
- if (content_stream)
+ if (! content_stream)
{
- state = st_eof;
- }
- else
- {
- // When not in content stream mode, EOF is tt_bad and
- // throws an exception before we get here.
- throw std::logic_error(
- "EOF received while not in content stream mode");
+ QTC::TC("qpdf", "QPDFObjectHandle eof in parseInternal");
+ warn(context,
+ QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ object_description,
+ input->getLastOffset(),
+ "unexpected EOF"));
}
+ state = st_eof;
break;
+ case QPDFTokenizer::tt_bad:
+ QTC::TC("qpdf", "QPDFObjectHandle bad token in parse");
+ warn(context,
+ QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ object_description,
+ input->getLastOffset(),
+ token.getErrorMessage()));
+ object = newNull();
+ break;
+
case QPDFTokenizer::tt_brace_open:
case QPDFTokenizer::tt_brace_close:
QTC::TC("qpdf", "QPDFObjectHandle bad brace");
@@ -1375,11 +1620,19 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
"parse error while reading object"));
}
done = true;
- // Leave object uninitialized to indicate EOF
+ // In content stream mode, leave object uninitialized to
+ // indicate EOF
+ if (! content_stream)
+ {
+ object = newNull();
+ }
break;
case st_dictionary:
case st_array:
+ setObjectDescriptionFromInput(
+ object, context, object_description, input,
+ input->getLastOffset());
olist.push_back(object);
break;
@@ -1402,6 +1655,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
if (old_state == st_array)
{
object = newArray(olist);
+ setObjectDescriptionFromInput(
+ object, context, object_description, input, offset);
}
else if (old_state == st_dictionary)
{
@@ -1458,6 +1713,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
"dictionary ended prematurely; "
"using null as value for last key"));
val = newNull();
+ setObjectDescriptionFromInput(
+ val, context, object_description, input, offset);
}
else
{
@@ -1466,6 +1723,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
dict[key_obj.getName()] = val;
}
object = newDictionary(dict);
+ setObjectDescriptionFromInput(
+ object, context, object_description, input, offset);
}
olist_stack.pop_back();
offset_stack.pop_back();
@@ -1480,6 +1739,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
}
}
+ setObjectDescriptionFromInput(
+ object, context, object_description, input, offset);
return object;
}
@@ -1635,6 +1896,26 @@ QPDFObjectHandle::newReserved(QPDF* qpdf)
return result;
}
+void
+QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf,
+ std::string const& object_description)
+{
+ if (isInitialized() && this->m->obj.getPointer())
+ {
+ this->m->obj->setDescription(owning_qpdf, object_description);
+ }
+}
+
+bool
+QPDFObjectHandle::hasObjectDescription()
+{
+ if (isInitialized() && this->m->obj.getPointer())
+ {
+ return this->m->obj->hasDescription();
+ }
+ return false;
+}
+
QPDFObjectHandle
QPDFObjectHandle::shallowCopy()
{
@@ -1793,85 +2074,127 @@ QPDFObjectHandle::assertInitialized() const
}
void
-QPDFObjectHandle::assertType(char const* type_name, bool istype) const
+QPDFObjectHandle::typeWarning(char const* expected_type,
+ std::string const& warning)
+{
+ QPDF* context = 0;
+ std::string description;
+ if (this->m->obj->getDescription(context, description))
+ {
+ warn(context,
+ QPDFExc(
+ qpdf_e_damaged_pdf,
+ "", description, 0,
+ std::string("operation for ") + expected_type +
+ " attempted on object of type " +
+ getTypeName() + ": " + warning));
+ }
+ else
+ {
+ assertType(expected_type, false);
+ }
+}
+
+void
+QPDFObjectHandle::objectWarning(std::string const& warning)
+{
+ QPDF* context = 0;
+ std::string description;
+ if (this->m->obj->getDescription(context, description))
+ {
+ warn(context,
+ QPDFExc(
+ qpdf_e_damaged_pdf,
+ "", description, 0,
+ warning));
+ }
+ else
+ {
+ throw std::logic_error(warning);
+ }
+}
+
+void
+QPDFObjectHandle::assertType(char const* type_name, bool istype)
{
if (! istype)
{
throw std::logic_error(std::string("operation for ") + type_name +
- " object attempted on object of wrong type");
+ " attempted on object of type " +
+ getTypeName());
}
}
void
QPDFObjectHandle::assertNull()
{
- assertType("Null", isNull());
+ assertType("null", isNull());
}
void
QPDFObjectHandle::assertBool()
{
- assertType("Boolean", isBool());
+ assertType("boolean", isBool());
}
void
QPDFObjectHandle::assertInteger()
{
- assertType("Integer", isInteger());
+ assertType("integer", isInteger());
}
void
QPDFObjectHandle::assertReal()
{
- assertType("Real", isReal());
+ assertType("real", isReal());
}
void
QPDFObjectHandle::assertName()
{
- assertType("Name", isName());
+ assertType("name", isName());
}
void
QPDFObjectHandle::assertString()
{
- assertType("String", isString());
+ assertType("string", isString());
}
void
QPDFObjectHandle::assertOperator()
{
- assertType("Operator", isOperator());
+ assertType("operator", isOperator());
}
void
QPDFObjectHandle::assertInlineImage()
{
- assertType("InlineImage", isInlineImage());
+ assertType("inlineimage", isInlineImage());
}
void
QPDFObjectHandle::assertArray()
{
- assertType("Array", isArray());
+ assertType("array", isArray());
}
void
QPDFObjectHandle::assertDictionary()
{
- assertType("Dictionary", isDictionary());
+ assertType("dictionary", isDictionary());
}
void
QPDFObjectHandle::assertStream()
{
- assertType("Stream", isStream());
+ assertType("stream", isStream());
}
void
QPDFObjectHandle::assertReserved()
{
- assertType("Reserved", isReserved());
+ assertType("reserved", isReserved());
}
void
@@ -1887,13 +2210,13 @@ QPDFObjectHandle::assertIndirect()
void
QPDFObjectHandle::assertScalar()
{
- assertType("Scalar", isScalar());
+ assertType("scalar", isScalar());
}
void
QPDFObjectHandle::assertNumber()
{
- assertType("Number", isNumber());
+ assertType("number", isNumber());
}
bool
@@ -1928,7 +2251,8 @@ QPDFObjectHandle::dereference()
this->m->qpdf, this->m->objid, this->m->generation);
if (obj.getPointer() == 0)
{
- QTC::TC("qpdf", "QPDFObjectHandle indirect to unknown");
+ // QPDF::resolve never returns an uninitialized object, but
+ // check just in case.
this->m->obj = new QPDF_Null();
}
else if (dynamic_cast<QPDF_Reserved*>(obj.getPointer()))
diff --git a/libqpdf/QPDFTokenizer.cc b/libqpdf/QPDFTokenizer.cc
index c3a017d0..95551e7c 100644
--- a/libqpdf/QPDFTokenizer.cc
+++ b/libqpdf/QPDFTokenizer.cc
@@ -640,7 +640,9 @@ QPDFTokenizer::readToken(PointerHolder<InputSource> input,
presented_eof = true;
if ((this->m->type == tt_eof) && (! this->m->allow_eof))
{
- QTC::TC("qpdf", "QPDFTokenizer EOF when not allowed");
+ // Nothing in the qpdf library calls readToken
+ // without allowEOF anymore, so this case is not
+ // exercised.
this->m->type = tt_bad;
this->m->error_message = "unexpected EOF";
offset = input->getLastOffset();
@@ -677,7 +679,10 @@ QPDFTokenizer::readToken(PointerHolder<InputSource> input,
input->unreadCh(char_to_unread);
}
- input->setLastOffset(offset);
+ if (token.getType() != tt_eof)
+ {
+ input->setLastOffset(offset);
+ }
if (token.getType() == tt_bad)
{
diff --git a/libqpdf/QPDF_Array.cc b/libqpdf/QPDF_Array.cc
index c526174f..1a4ba61d 100644
--- a/libqpdf/QPDF_Array.cc
+++ b/libqpdf/QPDF_Array.cc
@@ -1,4 +1,5 @@
#include <qpdf/QPDF_Array.hh>
+#include <qpdf/QUtil.hh>
#include <stdexcept>
QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& items) :
@@ -46,6 +47,12 @@ QPDF_Array::getTypeName() const
return "array";
}
+void
+QPDF_Array::setDescription(QPDF* qpdf, std::string const& description)
+{
+ this->QPDFObject::setDescription(qpdf, description);
+}
+
int
QPDF_Array::getNItems() const
{
diff --git a/libqpdf/QPDF_Dictionary.cc b/libqpdf/QPDF_Dictionary.cc
index 0af2f4bf..df640354 100644
--- a/libqpdf/QPDF_Dictionary.cc
+++ b/libqpdf/QPDF_Dictionary.cc
@@ -51,6 +51,12 @@ QPDF_Dictionary::getTypeName() const
return "dictionary";
}
+void
+QPDF_Dictionary::setDescription(QPDF* qpdf, std::string const& description)
+{
+ this->QPDFObject::setDescription(qpdf, description);
+}
+
bool
QPDF_Dictionary::hasKey(std::string const& key)
{
@@ -70,7 +76,15 @@ QPDF_Dictionary::getKey(std::string const& key)
}
else
{
- return QPDFObjectHandle::newNull();
+ QPDFObjectHandle null = QPDFObjectHandle::newNull();
+ QPDF* qpdf = 0;
+ std::string description;
+ if (getDescription(qpdf, description))
+ {
+ null.setObjectDescription(
+ qpdf, description + " -> dictionary key " + key);
+ }
+ return null;
}
}
@@ -93,13 +107,12 @@ QPDF_Dictionary::getKeys()
std::map<std::string, QPDFObjectHandle> const&
QPDF_Dictionary::getAsMap() const
{
-
return this->items;
}
void
QPDF_Dictionary::replaceKey(std::string const& key,
- QPDFObjectHandle const& value)
+ QPDFObjectHandle value)
{
// add or replace value
this->items[key] = value;
diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc
index 7b84d10c..384652e2 100644
--- a/libqpdf/QPDF_Stream.cc
+++ b/libqpdf/QPDF_Stream.cc
@@ -39,6 +39,7 @@ QPDF_Stream::QPDF_Stream(QPDF* qpdf, int objid, int generation,
"stream object instantiated with non-dictionary "
"object for dictionary");
}
+ setStreamDescription();
}
QPDF_Stream::~QPDF_Stream()
@@ -85,6 +86,35 @@ QPDF_Stream::getTypeName() const
return "stream";
}
+void
+QPDF_Stream::setDescription(QPDF* qpdf, std::string const& description)
+{
+ this->QPDFObject::setDescription(qpdf, description);
+ setDictDescription();
+}
+
+void
+QPDF_Stream::setStreamDescription()
+{
+ setDescription(
+ this->qpdf,
+ "stream object " + QUtil::int_to_string(this->objid) + " " +
+ QUtil::int_to_string(this->generation));
+}
+
+void
+QPDF_Stream::setDictDescription()
+{
+ QPDF* qpdf = 0;
+ std::string description;
+ if ((! this->stream_dict.hasObjectDescription()) &&
+ getDescription(qpdf, description))
+ {
+ this->stream_dict.setObjectDescription(
+ qpdf, description + " -> stream dictionary");
+ }
+}
+
QPDFObjectHandle
QPDF_Stream::getDict() const
{
@@ -688,6 +718,7 @@ void
QPDF_Stream::replaceDict(QPDFObjectHandle new_dict)
{
this->stream_dict = new_dict;
+ setDictDescription();
QPDFObjectHandle length_obj = new_dict.getKey("/Length");
if (length_obj.isInteger())
{
diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc
index 3d04ab90..ecf81bee 100644
--- a/libqpdf/QPDF_linearization.cc
+++ b/libqpdf/QPDF_linearization.cc
@@ -121,10 +121,10 @@ QPDF::isLinearized()
++p;
}
- QPDFTokenizer::Token t1 = readToken(this->m->file, true);
- QPDFTokenizer::Token t2 = readToken(this->m->file, true);
- QPDFTokenizer::Token t3 = readToken(this->m->file, true);
- QPDFTokenizer::Token t4 = readToken(this->m->file, true);
+ QPDFTokenizer::Token t1 = readToken(this->m->file);
+ QPDFTokenizer::Token t2 = readToken(this->m->file);
+ QPDFTokenizer::Token t3 = readToken(this->m->file);
+ QPDFTokenizer::Token t4 = readToken(this->m->file);
if ((t1.getType() == QPDFTokenizer::tt_integer) &&
(t2.getType() == QPDFTokenizer::tt_integer) &&
(t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj")) &&
diff --git a/libqpdf/qpdf/QPDF_Array.hh b/libqpdf/qpdf/QPDF_Array.hh
index e81f8664..8a23da35 100644
--- a/libqpdf/qpdf/QPDF_Array.hh
+++ b/libqpdf/qpdf/QPDF_Array.hh
@@ -14,6 +14,7 @@ class QPDF_Array: public QPDFObject
virtual std::string unparse();
virtual QPDFObject::object_type_e getTypeCode() const;
virtual char const* getTypeName() const;
+ virtual void setDescription(QPDF*, std::string const&);
int getNItems() const;
QPDFObjectHandle getItem(int n) const;
diff --git a/libqpdf/qpdf/QPDF_Dictionary.hh b/libqpdf/qpdf/QPDF_Dictionary.hh
index 5b5630cf..cea63835 100644
--- a/libqpdf/qpdf/QPDF_Dictionary.hh
+++ b/libqpdf/qpdf/QPDF_Dictionary.hh
@@ -16,6 +16,7 @@ class QPDF_Dictionary: public QPDFObject
virtual std::string unparse();
virtual QPDFObject::object_type_e getTypeCode() const;
virtual char const* getTypeName() const;
+ virtual void setDescription(QPDF*, std::string const&);
// hasKey() and getKeys() treat keys with null values as if they
// aren't there. getKey() returns null for the value of a
@@ -26,7 +27,7 @@ class QPDF_Dictionary: public QPDFObject
std::map<std::string, QPDFObjectHandle> const& getAsMap() const;
// Replace value of key, adding it if it does not exist
- void replaceKey(std::string const& key, QPDFObjectHandle const&);
+ void replaceKey(std::string const& key, QPDFObjectHandle);
// Remove key, doing nothing if key does not exist
void removeKey(std::string const& key);
// If object is null, replace key; otherwise, remove key
diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh
index 86b796cf..98b8c11f 100644
--- a/libqpdf/qpdf/QPDF_Stream.hh
+++ b/libqpdf/qpdf/QPDF_Stream.hh
@@ -19,6 +19,7 @@ class QPDF_Stream: public QPDFObject
virtual std::string unparse();
virtual QPDFObject::object_type_e getTypeCode() const;
virtual char const* getTypeName() const;
+ virtual void setDescription(QPDF*, std::string const&);
QPDFObjectHandle getDict() const;
bool isDataModified() const;
@@ -66,6 +67,8 @@ class QPDF_Stream: public QPDFObject
int& colors, int& bits_per_component,
bool& early_code_change);
void warn(QPDFExc const& e);
+ void setDictDescription();
+ void setStreamDescription();
QPDF* qpdf;
int objid;