From d0e99f195a987c483bbb6c5449cf39bee34e08a1 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Fri, 16 Feb 2018 17:25:27 -0500 Subject: 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. --- libqpdf/QPDF.cc | 53 ++--- libqpdf/QPDFExc.cc | 10 +- libqpdf/QPDFObject.cc | 35 +++ libqpdf/QPDFObjectHandle.cc | 484 +++++++++++++++++++++++++++++++++------- libqpdf/QPDFTokenizer.cc | 9 +- libqpdf/QPDF_Array.cc | 7 + libqpdf/QPDF_Dictionary.cc | 19 +- libqpdf/QPDF_Stream.cc | 31 +++ libqpdf/QPDF_linearization.cc | 8 +- libqpdf/qpdf/QPDF_Array.hh | 1 + libqpdf/qpdf/QPDF_Dictionary.hh | 3 +- libqpdf/qpdf/QPDF_Stream.hh | 3 + 12 files changed, 545 insertions(+), 118 deletions(-) (limited to 'libqpdf') 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 input, } QPDFTokenizer::Token -QPDF::readToken(PointerHolder input, - bool allow_bad, size_t max_len) +QPDF::readToken(PointerHolder 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 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 + +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 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(m->obj.getPointer())->getVal(); + if (isBool()) + { + return dynamic_cast(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(m->obj.getPointer())->getVal(); + if (isInteger()) + { + return dynamic_cast(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(m->obj.getPointer())->getVal(); + if (isReal()) + { + return dynamic_cast(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(m->obj.getPointer())->getName(); + if (isName()) + { + return dynamic_cast(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(m->obj.getPointer())->getVal(); + if (isString()) + { + return dynamic_cast(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(m->obj.getPointer())->getUTF8Val(); + if (isString()) + { + return dynamic_cast(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(m->obj.getPointer())->getVal(); + if (isOperator()) + { + return dynamic_cast(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(m->obj.getPointer())->getVal(); + if (isInlineImage()) + { + return dynamic_cast(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(m->obj.getPointer())->getNItems(); + if (isArray()) + { + return dynamic_cast(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(m->obj.getPointer())->getItem(n); + QPDFObjectHandle result; + if (isArray() && (n < getArrayNItems()) && (n >= 0)) + { + result = dynamic_cast(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::getArrayAsVector() { - assertArray(); - return dynamic_cast(m->obj.getPointer())->getAsVector(); + std::vector result; + if (isArray()) + { + result = dynamic_cast(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(m->obj.getPointer())->setItem(n, item); + if (isArray()) + { + dynamic_cast(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 const& items) { - assertArray(); - return dynamic_cast(m->obj.getPointer())->setFromVector(items); + if (isArray()) + { + dynamic_cast(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(m->obj.getPointer())->insertItem(at, item); + if (isArray()) + { + dynamic_cast(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(m->obj.getPointer())->appendItem(item); + if (isArray()) + { + dynamic_cast(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(m->obj.getPointer())->eraseItem(at); + if (isArray() && (at < getArrayNItems()) && (at >= 0)) + { + dynamic_cast(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(m->obj.getPointer())->hasKey(key); + if (isDictionary()) + { + return dynamic_cast(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(m->obj.getPointer())->getKey(key); + QPDFObjectHandle result; + if (isDictionary()) + { + result = dynamic_cast( + 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 QPDFObjectHandle::getKeys() { - assertDictionary(); - return dynamic_cast(m->obj.getPointer())->getKeys(); + std::set result; + if (isDictionary()) + { + result = dynamic_cast(m->obj.getPointer())->getKeys(); + } + else + { + typeWarning("dictionary", "treating as empty"); + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys"); + } + return result; } std::map QPDFObjectHandle::getDictAsMap() { - assertDictionary(); - return dynamic_cast(m->obj.getPointer())->getAsMap(); + std::map result; + if (isDictionary()) + { + result = dynamic_cast( + 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( - m->obj.getPointer())->replaceKey(key, value); + if (isDictionary()) + { + dynamic_cast( + 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(m->obj.getPointer())->removeKey(key); + if (isDictionary()) + { + dynamic_cast(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( - m->obj.getPointer())->replaceOrRemoveKey(key, value); + if (isDictionary()) + { + dynamic_cast( + 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 input, std::vector state_stack; state_stack.push_back(st_top); std::vector 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& 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 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 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 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 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 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(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 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 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 +#include #include QPDF_Array::QPDF_Array(std::vector 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 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 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; -- cgit v1.2.3-54-g00ecf