diff options
42 files changed, 825 insertions, 638 deletions
diff --git a/include/qpdf/Constants.h b/include/qpdf/Constants.h index 5d2113bd..cf6bdaef 100644 --- a/include/qpdf/Constants.h +++ b/include/qpdf/Constants.h @@ -82,6 +82,8 @@ enum qpdf_object_type_e { /* Additional object types that can occur in content streams */ ot_operator, ot_inlineimage, + /* Object types internal to qpdf */ + ot_unresolved, /* NOTE: if adding to this list, update QPDFObject.hh */ }; diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 12d41eff..bcd85cd2 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -843,19 +843,13 @@ class QPDF // it can resolve indirect references. class Resolver { - friend class QPDFObjectHandle; + friend class QPDFObject; private: - static std::shared_ptr<QPDFObject> + static void resolve(QPDF* qpdf, QPDFObjGen const& og) { - return qpdf->resolve(og); - } - static bool - objectChanged( - QPDF* qpdf, QPDFObjGen const& og, std::shared_ptr<QPDFObject>& oph) - { - return qpdf->objectChanged(og, oph); + qpdf->resolve(og); } }; friend class Resolver; @@ -1174,12 +1168,20 @@ class QPDF std::string const& description, QPDFObjGen const& exp_og, QPDFObjGen& og); - bool objectChanged(QPDFObjGen const& og, std::shared_ptr<QPDFObject>& oph); - std::shared_ptr<QPDFObject> resolve(QPDFObjGen const& og); + void resolve(QPDFObjGen const& og); void resolveObjectsInStream(int obj_stream_number); void stopOnError(std::string const& message); QPDFObjectHandle reserveObjectIfNotExists(QPDFObjGen const& og); QPDFObjectHandle reserveStream(QPDFObjGen const& og); + QPDFObjectHandle + newIndirect(QPDFObjGen const&, std::shared_ptr<QPDFObject> const&); + bool isCached(QPDFObjGen const& og); + bool isUnresolved(QPDFObjGen const& og); + void updateCache( + QPDFObjGen const& og, + std::shared_ptr<QPDFObject> const& object, + qpdf_offset_t end_before_space, + qpdf_offset_t end_after_space); // Calls finish() on the pipeline when done but does not delete it bool pipeStreamData( @@ -1727,7 +1729,6 @@ class QPDF bool in_parse; bool parsed; std::set<int> resolved_object_streams; - bool ever_replaced_objects; // Linearization data qpdf_offset_t first_xref_item_offset; // actual value from file diff --git a/include/qpdf/QPDFObject.hh b/include/qpdf/QPDFObject.hh index eb7c4b90..e6d1d18b 100644 --- a/include/qpdf/QPDFObject.hh +++ b/include/qpdf/QPDFObject.hh @@ -25,6 +25,7 @@ #include <qpdf/Constants.h> #include <qpdf/DLL.h> #include <qpdf/JSON.hh> +#include <qpdf/QPDFValue.hh> #include <qpdf/Types.h> #include <string> @@ -34,9 +35,9 @@ class QPDFObjectHandle; class QPDFObject { - public: - QPDFObject(); + friend class QPDFValue; + public: // Objects derived from QPDFObject are accessible through // QPDFObjectHandle. Each object returns a unique type code that // has one of the valid qpdf_object_type_e values. As new object @@ -61,18 +62,128 @@ class QPDFObject static constexpr object_type_e ot_stream = ::ot_stream; static constexpr object_type_e ot_operator = ::ot_operator; static constexpr object_type_e ot_inlineimage = ::ot_inlineimage; + static constexpr object_type_e ot_unresolved = ::ot_unresolved; + QPDFObject() = default; virtual ~QPDFObject() = default; - virtual std::shared_ptr<QPDFObject> shallowCopy() = 0; - virtual std::string unparse() = 0; - virtual JSON getJSON(int json_version) = 0; + + std::shared_ptr<QPDFObject> + shallowCopy() + { + return value->shallowCopy(); + } + std::string + unparse() + { + return value->unparse(); + } + JSON + getJSON(int json_version) + { + return value->getJSON(json_version); + } // Return a unique type code for the object - virtual object_type_e getTypeCode() const = 0; + object_type_e + getTypeCode() const + { + return value->type_code; + } // Return a string literal that describes the type, useful for // debugging and testing - virtual char const* getTypeName() const = 0; + char const* + getTypeName() const + { + return value->type_name; + } + // Returns nullptr for direct objects + QPDF* + getQPDF() const + { + return value->qpdf; + } + QPDFObjGen + getObjGen() const + { + return value->og; + } + + void + setDescription(QPDF* qpdf, std::string const& description) + { + return value->setDescription(qpdf, description); + } + bool + getDescription(QPDF*& qpdf, std::string& description) + { + return value->getDescription(qpdf, description); + } + bool + hasDescription() + { + return value->hasDescription(); + } + void + setParsedOffset(qpdf_offset_t offset) + { + value->setParsedOffset(offset); + } + qpdf_offset_t + getParsedOffset() + { + return value->getParsedOffset(); + } + void + assign(std::shared_ptr<QPDFObject> o) + { + value = o->value; + } + void + swapWith(std::shared_ptr<QPDFObject> o) + { + auto v = value; + value = o->value; + o->value = v; + auto og = value->og; + value->og = o->value->og; + o->value->og = og; + } + + // The following two methods are for use by class QPDF only + void + setObjGen(QPDF* qpdf, QPDFObjGen const& og) + { + value->qpdf = qpdf; + value->og = og; + } + void + resetObjGen() + { + value->qpdf = nullptr; + value->og = QPDFObjGen(); + } + + bool + isUnresolved() const + { + return value->type_code == ::ot_unresolved; + } + void + resolve() + { + if (isUnresolved()) { + doResolve(); + } + } + void doResolve(); + + template <typename T> + T* + as() + { + return dynamic_cast<T*>(value.get()); + } // Accessor to give specific access to non-public methods class ObjAccessor @@ -89,29 +200,20 @@ class QPDFObject } } }; - friend class ObjAccessor; - virtual void setDescription(QPDF*, std::string const&); - bool getDescription(QPDF*&, std::string&); - bool hasDescription(); - - void setParsedOffset(qpdf_offset_t offset); - qpdf_offset_t getParsedOffset(); + friend class ObjAccessor; protected: virtual void releaseResolved() { + value->releaseResolved(); } - static std::shared_ptr<QPDFObject> do_create(QPDFObject*); private: QPDFObject(QPDFObject const&) = delete; QPDFObject& operator=(QPDFObject const&) = delete; - - QPDF* owning_qpdf; - std::string object_description; - qpdf_offset_t parsed_offset; + std::shared_ptr<QPDFValue> value; }; #endif // QPDFOBJECT_HH diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index c1bed81f..16e8dc8b 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -43,8 +43,18 @@ class Pipeline; class QPDF; -class QPDF_Dictionary; class QPDF_Array; +class QPDF_Bool; +class QPDF_Dictionary; +class QPDF_InlineImage; +class QPDF_Integer; +class QPDF_Name; +class QPDF_Null; +class QPDF_Operator; +class QPDF_Real; +class QPDF_Reserved; +class QPDF_Stream; +class QPDF_String; class QPDFTokenizer; class QPDFExc; class Pl_QPDFTokenizer; @@ -316,7 +326,7 @@ class QPDFObjectHandle }; QPDF_DLL - QPDFObjectHandle(); + QPDFObjectHandle() = default; QPDF_DLL QPDFObjectHandle(QPDFObjectHandle const&) = default; QPDF_DLL @@ -963,8 +973,8 @@ class QPDFObjectHandle // null for a direct object if allow_nullptr is set to true or // throws a runtime error otherwise. QPDF_DLL - inline QPDF* - getOwningQPDF(bool allow_nullptr = true, std::string const& error_msg = ""); + inline QPDF* getOwningQPDF( + bool allow_nullptr = true, std::string const& error_msg = "") const; // Create a shallow copy of an object as a direct object, but do not // traverse across indirect object boundaries. That means that, @@ -1443,9 +1453,9 @@ class QPDFObjectHandle private: static QPDFObjectHandle - newIndirect(QPDF* qpdf, QPDFObjGen const& og) + newIndirect(std::shared_ptr<QPDFObject> const& obj) { - return QPDFObjectHandle::newIndirect(qpdf, og); + return QPDFObjectHandle(obj); } static QPDFObjectHandle newStream( @@ -1458,12 +1468,6 @@ class QPDFObjectHandle return QPDFObjectHandle::newStream( qpdf, og, stream_dict, offset, length); } - // Reserve an object with a specific ID - static QPDFObjectHandle - makeReserved() - { - return QPDFObjectHandle::makeReserved(); - } }; friend class Factory; @@ -1483,6 +1487,16 @@ class QPDFObjectHandle }; return o.obj; } + static QPDF_Array* + asArray(QPDFObjectHandle& oh) + { + return oh.asArray(); + } + static QPDF_Stream* + asStream(QPDFObjectHandle& oh) + { + return oh.asStream(); + } }; friend class ObjAccessor; @@ -1563,18 +1577,32 @@ class QPDFObjectHandle bool isImage(bool exclude_imagemask = true); private: - QPDFObjectHandle(QPDF*, QPDFObjGen const& og); - QPDFObjectHandle(std::shared_ptr<QPDFObject> const&); + QPDFObjectHandle(std::shared_ptr<QPDFObject> const& obj) : + obj(obj) + { + } // Private object factory methods - static QPDFObjectHandle newIndirect(QPDF*, QPDFObjGen const& og); static QPDFObjectHandle newStream( QPDF* qpdf, QPDFObjGen const& og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length); - static QPDFObjectHandle makeReserved(); + + QPDF_Array* asArray(); + QPDF_Bool* asBool(); + QPDF_Dictionary* asDictionary(); + QPDF_InlineImage* asInlineImage(); + QPDF_Integer* asInteger(); + QPDF_Name* asName(); + QPDF_Null* asNull(); + QPDF_Operator* asOperator(); + QPDF_Real* asReal(); + QPDF_Reserved* asReserved(); + QPDF_Stream* asStream(); + QPDF_Stream* asStreamWithAssert(); + QPDF_String* asString(); void typeWarning(char const* expected_type, std::string const& warning); void objectWarning(std::string const& warning); @@ -1601,15 +1629,10 @@ class QPDFObjectHandle static void warn(QPDF*, QPDFExc const&); void checkOwnership(QPDFObjectHandle const&) const; - bool initialized; - // Moving members of QPDFObjectHandle into a smart pointer incurs // a substantial performance penalty since QPDFObjectHandle // objects are copied around so frequently. - QPDF* qpdf; - QPDFObjGen og; std::shared_ptr<QPDFObject> obj; - bool reserved; }; #ifndef QPDF_NO_QPDF_STRING @@ -1832,44 +1855,45 @@ class QPDFObjectHandle::QPDFArrayItems inline QPDFObjGen QPDFObjectHandle::getObjGen() const { - return og; + return isInitialized() ? obj->getObjGen() : QPDFObjGen(); } inline int QPDFObjectHandle::getObjectID() const { - return og.getObj(); + return getObjGen().getObj(); } inline int QPDFObjectHandle::getGeneration() const { - return og.getGen(); + return getObjGen().getGen(); } inline bool QPDFObjectHandle::isIndirect() const { - return initialized && (getObjectID() != 0); + return (obj != nullptr) && (getObjectID() != 0); } inline bool QPDFObjectHandle::isInitialized() const { - return initialized; + return obj != nullptr; } // Indirect object accessors inline QPDF* QPDFObjectHandle::getOwningQPDF( - bool allow_nullptr, std::string const& error_msg) + bool allow_nullptr, std::string const& error_msg) const { // Will be null for direct objects - if (!allow_nullptr && (this->qpdf == nullptr)) { + auto result = isInitialized() ? this->obj->getQPDF() : nullptr; + if (!allow_nullptr && (result == nullptr)) { throw std::runtime_error( error_msg == "" ? "attempt to use a null qpdf object" : error_msg); } - return this->qpdf; + return result; } inline void @@ -1877,7 +1901,7 @@ QPDFObjectHandle::setParsedOffset(qpdf_offset_t offset) { // This is called during parsing on newly created direct objects, // so we can't call dereference() here. - if (this->obj.get()) { + if (isInitialized()) { this->obj->setParsedOffset(offset); } } diff --git a/include/qpdf/QPDFValue.hh b/include/qpdf/QPDFValue.hh new file mode 100644 index 00000000..8b4f53b5 --- /dev/null +++ b/include/qpdf/QPDFValue.hh @@ -0,0 +1,130 @@ +// Copyright (c) 2005-2022 Jay Berkenbilt +// +// This file is part of qpdf. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Versions of qpdf prior to version 7 were released under the terms +// of version 2.0 of the Artistic License. At your option, you may +// continue to consider qpdf to be licensed under those terms. Please +// see the manual for additional information. + +#ifndef QPDFVALUE_HH +#define QPDFVALUE_HH + +#include <qpdf/Constants.h> +#include <qpdf/DLL.h> +#include <qpdf/JSON.hh> +#include <qpdf/QPDFObjGen.hh> +#include <qpdf/Types.h> + +#include <string> + +class QPDF; +class QPDFObjectHandle; +class QPDFObject; + +class QPDFValue +{ + friend class QPDFObject; + + public: + virtual ~QPDFValue() = default; + + virtual std::shared_ptr<QPDFObject> shallowCopy() = 0; + virtual std::string unparse() = 0; + virtual JSON getJSON(int json_version) = 0; + virtual void + setDescription(QPDF* qpdf, std::string const& description) + { + owning_qpdf = qpdf; + object_description = description; + } + bool + getDescription(QPDF*& qpdf, std::string& description) + { + qpdf = owning_qpdf; + description = object_description; + return owning_qpdf != nullptr; + } + bool + hasDescription() + { + return owning_qpdf != nullptr; + } + void + setParsedOffset(qpdf_offset_t offset) + { + if (parsed_offset < 0) { + parsed_offset = offset; + } + } + qpdf_offset_t + getParsedOffset() + { + return parsed_offset; + } + QPDF* + getQPDF() + { + return qpdf; + } + QPDFObjGen + getObjGen() + { + return og; + } + + protected: + QPDFValue() : + type_code(::ot_uninitialized), + type_name("uninitialized") + { + } + QPDFValue(qpdf_object_type_e type_code, char const* type_name) : + type_code(type_code), + type_name(type_name) + { + } + QPDFValue( + qpdf_object_type_e type_code, + char const* type_name, + QPDF* qpdf, + QPDFObjGen const& og) : + type_code(type_code), + type_name(type_name), + qpdf(qpdf), + og(og) + { + } + virtual void + releaseResolved() + { + } + static std::shared_ptr<QPDFObject> do_create(QPDFValue*); + + private: + QPDFValue(QPDFValue const&) = delete; + QPDFValue& operator=(QPDFValue const&) = delete; + QPDF* owning_qpdf{nullptr}; + std::string object_description; + qpdf_offset_t parsed_offset{-1}; + const qpdf_object_type_e type_code; + char const* type_name; + + protected: + QPDF* qpdf{nullptr}; + QPDFObjGen og; +}; + +#endif // QPDFVALUE_HH diff --git a/libqpdf/CMakeLists.txt b/libqpdf/CMakeLists.txt index 51f7476d..686ea04a 100644 --- a/libqpdf/CMakeLists.txt +++ b/libqpdf/CMakeLists.txt @@ -85,6 +85,7 @@ set(libqpdf_SOURCES QPDFSystemError.cc QPDFTokenizer.cc QPDFUsage.cc + QPDFValue.cc QPDFWriter.cc QPDFXRefEntry.cc QPDF_Array.cc @@ -99,6 +100,7 @@ set(libqpdf_SOURCES QPDF_Reserved.cc QPDF_Stream.cc QPDF_String.cc + QPDF_Unresolved.cc QPDF_encryption.cc QPDF_json.cc QPDF_linearization.cc diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 6ae74b25..9593c44f 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -24,7 +24,9 @@ #include <qpdf/QPDF_Array.hh> #include <qpdf/QPDF_Dictionary.hh> #include <qpdf/QPDF_Null.hh> +#include <qpdf/QPDF_Reserved.hh> #include <qpdf/QPDF_Stream.hh> +#include <qpdf/QPDF_Unresolved.hh> #include <qpdf/QTC.hh> #include <qpdf/QUtil.hh> @@ -222,7 +224,6 @@ QPDF::Members::Members() : immediate_copy_from(false), in_parse(false), parsed(false), - ever_replaced_objects(false), first_xref_item_offset(0), uncompressed_after_compressed(false) { @@ -258,6 +259,7 @@ QPDF::~QPDF() this->m->xref_table.clear(); for (auto const& iter: this->m->obj_cache) { QPDFObject::ObjAccessor::releaseResolved(iter.second.object.get()); + iter.second.object->resetObjGen(); } } @@ -1397,7 +1399,7 @@ QPDF::fixDanglingReferences(bool force) std::list<QPDFObjectHandle> queue; queue.push_back(this->m->trailer); for (auto const& og: to_process) { - QPDFObjectHandle obj = QPDFObjectHandle::Factory::newIndirect(this, og); + auto obj = getObject(og); if (obj.isDictionary() || obj.isArray()) { queue.push_back(obj); } else if (obj.isStream()) { @@ -1419,18 +1421,15 @@ QPDF::fixDanglingReferences(bool force) to_check.push_back(iter.second); } } else if (obj.isArray()) { - QPDF_Array* arr = dynamic_cast<QPDF_Array*>( - QPDFObjectHandle::ObjAccessor::getObject(obj).get()); + auto arr = QPDFObjectHandle::ObjAccessor::asArray(obj); arr->addExplicitElementsToList(to_check); } for (auto sub: to_check) { if (sub.isIndirect()) { - if (sub.getOwningQPDF() == this) { - QPDFObjGen og(sub.getObjGen()); - if (this->m->obj_cache.count(og) == 0) { - QTC::TC("qpdf", "QPDF detected dangling ref"); - queue.push_back(sub); - } + if ((sub.getOwningQPDF() == this) && + isUnresolved(sub.getObjGen())) { + QTC::TC("qpdf", "QPDF detected dangling ref"); + queue.push_back(sub); } } else { queue.push_back(sub); @@ -1462,8 +1461,7 @@ QPDF::getAllObjects() fixDanglingReferences(true); std::vector<QPDFObjectHandle> result; for (auto const& iter: this->m->obj_cache) { - QPDFObjGen const& og = iter.first; - result.push_back(QPDFObjectHandle::Factory::newIndirect(this, og)); + result.push_back(newIndirect(iter.first, iter.second.object)); } return result; } @@ -1888,7 +1886,7 @@ QPDF::readObjectAtOffset( "expected endobj"); } - if (!this->m->obj_cache.count(og)) { + if (isUnresolved(og)) { // Store the object in the cache here so it gets cached // whether we first know the offset or whether we first know // the object ID and generation (in which we case we would get @@ -1919,8 +1917,8 @@ QPDF::readObjectAtOffset( } } qpdf_offset_t end_after_space = this->m->file->tell(); - - this->m->obj_cache[og] = ObjCache( + updateCache( + og, QPDFObjectHandle::ObjAccessor::getObject(oh), end_before_space, end_after_space); @@ -1929,31 +1927,14 @@ QPDF::readObjectAtOffset( return oh; } -bool -QPDF::objectChanged(QPDFObjGen const& og, std::shared_ptr<QPDFObject>& oph) -{ - // See if the object cached at og, if any, is the one passed in. - // QPDFObjectHandle uses this to detect outdated handles to - // replaced or swapped objects. This is a somewhat expensive check - // because it happens with every dereference of a - // QPDFObjectHandle. To reduce the hit somewhat, short-circuit the - // check if we never called a function that replaces an object - // already in cache. It is important for functions that do this to - // set ever_replaced_objects = true. - - if (!this->m->ever_replaced_objects) { - return false; - } - auto c = this->m->obj_cache.find(og); - if (c == this->m->obj_cache.end()) { - return true; - } - return (c->second.object.get() != oph.get()); -} - -std::shared_ptr<QPDFObject> +void QPDF::resolve(QPDFObjGen const& og) { + if (isCached(og) && !isUnresolved(og)) { + // We only need to resolve unresolved objects + return; + } + // Check object cache before checking xref table. This allows us // to insert things into the object cache that don't actually // exist in the file. @@ -1967,11 +1948,12 @@ QPDF::resolve(QPDFObjGen const& og) "", this->m->file->getLastOffset(), ("loop detected resolving object " + og.unparse(' '))); - return QPDF_Null::create(); + updateCache(og, QPDF_Null::create(), -1, -1); + return; } ResolveRecorder rr(this, og); - if ((!this->m->obj_cache.count(og)) && this->m->xref_table.count(og)) { + if (m->xref_table.count(og) != 0) { QPDFXRefEntry const& entry = this->m->xref_table[og]; try { switch (entry.getType()) { @@ -2009,19 +1991,17 @@ QPDF::resolve(QPDFObjGen const& og) ": error reading object: " + e.what())); } } - if (this->m->obj_cache.count(og) == 0) { + + if (isUnresolved(og)) { // PDF spec says unknown objects resolve to the null object. QTC::TC("qpdf", "QPDF resolve failure to null"); - QPDFObjectHandle oh = QPDFObjectHandle::newNull(); - this->m->obj_cache[og] = - ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1); + updateCache(og, QPDF_Null::create(), -1, -1); } - std::shared_ptr<QPDFObject> result(this->m->obj_cache[og].object); + auto result(this->m->obj_cache[og].object); if (!result->hasDescription()) { result->setDescription(this, ("object " + og.unparse(' '))); } - return result; } void @@ -2109,15 +2089,15 @@ QPDF::resolveObjectsInStream(int obj_stream_number) // objects appended to the file, so it is necessary to recheck the // xref table and only cache what would actually be resolved here. for (auto const& iter: offsets) { - int obj = iter.first; - QPDFObjGen og(obj, 0); + QPDFObjGen og(iter.first, 0); QPDFXRefEntry const& entry = this->m->xref_table[og]; if ((entry.getType() == 2) && (entry.getObjStreamNumber() == obj_stream_number)) { int offset = iter.second; input->seek(offset, SEEK_SET); QPDFObjectHandle oh = readObject(input, "", og, true); - this->m->obj_cache[og] = ObjCache( + updateCache( + og, QPDFObjectHandle::ObjAccessor::getObject(oh), end_before_space, end_after_space); @@ -2128,6 +2108,47 @@ QPDF::resolveObjectsInStream(int obj_stream_number) } QPDFObjectHandle +QPDF::newIndirect(QPDFObjGen const& og, std::shared_ptr<QPDFObject> const& obj) +{ + obj->setObjGen(this, og); + if (!obj->hasDescription()) { + obj->setDescription(this, "object " + og.unparse(' ')); + } + return QPDFObjectHandle::Factory::newIndirect(obj); +} + +void +QPDF::updateCache( + QPDFObjGen const& og, + std::shared_ptr<QPDFObject> const& object, + qpdf_offset_t end_before_space, + qpdf_offset_t end_after_space) +{ + object->setObjGen(this, og); + if (isCached(og)) { + auto& cache = m->obj_cache[og]; + cache.object->resetObjGen(); + cache.object->assign(object); + cache.end_before_space = end_before_space; + cache.end_after_space = end_after_space; + } else { + m->obj_cache[og] = ObjCache(object, end_before_space, end_after_space); + } +} + +bool +QPDF::isCached(QPDFObjGen const& og) +{ + return m->obj_cache.count(og) != 0; +} + +bool +QPDF::isUnresolved(QPDFObjGen const& og) +{ + return !isCached(og) || m->obj_cache[og].object->isUnresolved(); +} + +QPDFObjectHandle QPDF::makeIndirectObject(QPDFObjectHandle oh) { int max_objid = toI(getObjectCount()); @@ -2136,19 +2157,21 @@ QPDF::makeIndirectObject(QPDFObjectHandle oh) "max object id is too high to create new objects"); } QPDFObjGen next(max_objid + 1, 0); - this->m->obj_cache[next] = + m->obj_cache[next] = ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1); - return QPDFObjectHandle::Factory::newIndirect(this, next); + return newIndirect(next, m->obj_cache[next].object); } QPDFObjectHandle QPDF::reserveObjectIfNotExists(QPDFObjGen const& og) { - if ((!this->m->obj_cache.count(og)) && (!this->m->xref_table.count(og))) { + if (!isCached(og) && !m->xref_table.count(og)) { resolve(og); - replaceObject(og, QPDFObjectHandle::Factory::makeReserved()); + m->obj_cache[og].object = QPDF_Reserved::create(); + return newIndirect(og, m->obj_cache[og].object); + } else { + return getObject(og); } - return getObject(og); } QPDFObjectHandle @@ -2161,7 +2184,13 @@ QPDF::reserveStream(QPDFObjGen const& og) QPDFObjectHandle QPDF::getObject(QPDFObjGen const& og) { - return QPDFObjectHandle::Factory::newIndirect(this, og); + if (!og.isIndirect()) { + return QPDFObjectHandle::newNull(); + } + if (!isCached(og)) { + m->obj_cache[og] = ObjCache(QPDF_Unresolved::create(this, og), -1, -1); + } + return newIndirect(og, m->obj_cache[og].object); } QPDFObjectHandle @@ -2196,14 +2225,11 @@ QPDF::replaceObject(QPDFObjGen const& og, QPDFObjectHandle oh) throw std::logic_error( "QPDF::replaceObject called with indirect object handle"); } - // Force new object to appear in the cache resolve(og); // Replace the object in the object cache - this->m->ever_replaced_objects = true; - this->m->obj_cache[og] = - ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1); + updateCache(og, QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1); } void @@ -2456,12 +2482,12 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign) QPDFObjGen local_og(result.getObjGen()); // Copy information from the foreign stream so we can pipe its // data later without keeping the original QPDF object around. + QPDF* foreign_stream_qpdf = foreign.getOwningQPDF( false, "unable to retrieve owning qpdf from foreign stream"); - QPDF_Stream* stream = dynamic_cast<QPDF_Stream*>( - QPDFObjectHandle::ObjAccessor::getObject(foreign).get()); - if (!stream) { + auto stream = QPDFObjectHandle::ObjAccessor::asStream(foreign); + if (stream == nullptr) { throw std::logic_error("unable to retrieve underlying" " stream object from foreign stream"); } @@ -2525,10 +2551,7 @@ QPDF::swapObjects(QPDFObjGen const& og1, QPDFObjGen const& og2) // cache. resolve(og1); resolve(og2); - ObjCache t = this->m->obj_cache[og1]; - this->m->ever_replaced_objects = true; - this->m->obj_cache[og1] = this->m->obj_cache[og2]; - this->m->obj_cache[og2] = t; + m->obj_cache[og1].object->swapWith(m->obj_cache[og2].object); } unsigned long long diff --git a/libqpdf/QPDFObject.cc b/libqpdf/QPDFObject.cc index 382dd6c6..8b538021 100644 --- a/libqpdf/QPDFObject.cc +++ b/libqpdf/QPDFObject.cc @@ -1,47 +1,10 @@ #include <qpdf/QPDFObject.hh> -QPDFObject::QPDFObject() : - owning_qpdf(nullptr), - parsed_offset(-1) -{ -} - -std::shared_ptr<QPDFObject> -QPDFObject::do_create(QPDFObject* object) -{ - std::shared_ptr<QPDFObject> obj(object); - return obj; -} - -void -QPDFObject::setDescription(QPDF* qpdf, std::string const& description) -{ - this->owning_qpdf = qpdf; - this->object_description = description; -} - -bool -QPDFObject::getDescription(QPDF*& qpdf, std::string& description) -{ - qpdf = this->owning_qpdf; - description = this->object_description; - return this->owning_qpdf != nullptr; -} - -bool -QPDFObject::hasDescription() -{ - return this->owning_qpdf != nullptr; -} +#include <qpdf/QPDF.hh> void -QPDFObject::setParsedOffset(qpdf_offset_t offset) -{ - this->parsed_offset = offset; -} - -qpdf_offset_t -QPDFObject::getParsedOffset() +QPDFObject::doResolve() { - return this->parsed_offset; + auto og = value->og; + QPDF::Resolver::resolve(value->qpdf, og); } diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 2227a77b..19a85034 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -21,6 +21,7 @@ #include <qpdf/QPDF_Reserved.hh> #include <qpdf/QPDF_Stream.hh> #include <qpdf/QPDF_String.hh> +#include <qpdf/QPDF_Unresolved.hh> #include <qpdf/SparseOHArray.hh> #include <qpdf/QIntC.hh> @@ -234,29 +235,6 @@ LastChar::getLastChar() return this->last_char; } -QPDFObjectHandle::QPDFObjectHandle() : - initialized(false), - qpdf(nullptr), - reserved(false) -{ -} - -QPDFObjectHandle::QPDFObjectHandle(QPDF* qpdf, QPDFObjGen const& og) : - initialized(true), - qpdf(qpdf), - og(og), - reserved(false) -{ -} - -QPDFObjectHandle::QPDFObjectHandle(std::shared_ptr<QPDFObject> const& data) : - initialized(true), - qpdf(nullptr), - obj(data), - reserved(false) -{ -} - void QPDFObjectHandle::releaseResolved() { @@ -286,24 +264,90 @@ QPDFObjectHandle::getTypeName() return dereference() ? this->obj->getTypeName() : "uninitialized"; } -namespace +QPDF_Array* +QPDFObjectHandle::asArray() { - template <class T> - class QPDFObjectTypeAccessor - { - public: - static bool - check(std::shared_ptr<QPDFObject> const& o) - { - return (o && dynamic_cast<T const*>(o.get())); - } - }; -} // namespace + return dereference() ? obj->as<QPDF_Array>() : nullptr; +} + +QPDF_Bool* +QPDFObjectHandle::asBool() +{ + return dereference() ? obj->as<QPDF_Bool>() : nullptr; +} + +QPDF_Dictionary* +QPDFObjectHandle::asDictionary() +{ + return dereference() ? obj->as<QPDF_Dictionary>() : nullptr; +} + +QPDF_InlineImage* +QPDFObjectHandle::asInlineImage() +{ + return dereference() ? obj->as<QPDF_InlineImage>() : nullptr; +} + +QPDF_Integer* +QPDFObjectHandle::asInteger() +{ + return dereference() ? obj->as<QPDF_Integer>() : nullptr; +} + +QPDF_Name* +QPDFObjectHandle::asName() +{ + return dereference() ? obj->as<QPDF_Name>() : nullptr; +} + +QPDF_Null* +QPDFObjectHandle::asNull() +{ + return dereference() ? obj->as<QPDF_Null>() : nullptr; +} + +QPDF_Operator* +QPDFObjectHandle::asOperator() +{ + return dereference() ? obj->as<QPDF_Operator>() : nullptr; +} + +QPDF_Real* +QPDFObjectHandle::asReal() +{ + return dereference() ? obj->as<QPDF_Real>() : nullptr; +} + +QPDF_Reserved* +QPDFObjectHandle::asReserved() +{ + return dereference() ? obj->as<QPDF_Reserved>() : nullptr; +} + +QPDF_Stream* +QPDFObjectHandle::asStream() +{ + return dereference() ? obj->as<QPDF_Stream>() : nullptr; +} + +QPDF_Stream* +QPDFObjectHandle::asStreamWithAssert() +{ + auto stream = asStream(); + assertType("stream", stream); + return stream; +} + +QPDF_String* +QPDFObjectHandle::asString() +{ + return dereference() ? obj->as<QPDF_String>() : nullptr; +} bool QPDFObjectHandle::isBool() { - return dereference() && QPDFObjectTypeAccessor<QPDF_Bool>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_boolean); } bool @@ -312,26 +356,26 @@ QPDFObjectHandle::isDirectNull() const // Don't call dereference() -- this is a const method, and we know // objid == 0, so there's nothing to resolve. return ( - this->initialized && (getObjectID() == 0) && - QPDFObjectTypeAccessor<QPDF_Null>::check(obj)); + isInitialized() && (getObjectID() == 0) && + (obj->getTypeCode() == QPDFObject::ot_null)); } bool QPDFObjectHandle::isNull() { - return dereference() && QPDFObjectTypeAccessor<QPDF_Null>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_null); } bool QPDFObjectHandle::isInteger() { - return dereference() && QPDFObjectTypeAccessor<QPDF_Integer>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_integer); } bool QPDFObjectHandle::isReal() { - return dereference() && QPDFObjectTypeAccessor<QPDF_Real>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_real); } bool @@ -368,51 +412,49 @@ QPDFObjectHandle::getValueAsNumber(double& value) bool QPDFObjectHandle::isName() { - return dereference() && QPDFObjectTypeAccessor<QPDF_Name>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_name); } bool QPDFObjectHandle::isString() { - return dereference() && QPDFObjectTypeAccessor<QPDF_String>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_string); } bool QPDFObjectHandle::isOperator() { - return dereference() && QPDFObjectTypeAccessor<QPDF_Operator>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_operator); } bool QPDFObjectHandle::isInlineImage() { - return dereference() && - QPDFObjectTypeAccessor<QPDF_InlineImage>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_inlineimage); } bool QPDFObjectHandle::isArray() { - return dereference() && QPDFObjectTypeAccessor<QPDF_Array>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_array); } bool QPDFObjectHandle::isDictionary() { - return dereference() && QPDFObjectTypeAccessor<QPDF_Dictionary>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_dictionary); } bool QPDFObjectHandle::isStream() { - return dereference() && QPDFObjectTypeAccessor<QPDF_Stream>::check(obj); + return dereference() && (obj->getTypeCode() == QPDFObject::ot_stream); } bool QPDFObjectHandle::isReserved() { - // dereference will clear reserved if this has been replaced - return dereference() && this->reserved; + return dereference() && (obj->getTypeCode() == QPDFObject::ot_reserved); } bool @@ -450,8 +492,9 @@ QPDFObjectHandle::isStreamOfType( bool QPDFObjectHandle::getBoolValue() { - if (isBool()) { - return dynamic_cast<QPDF_Bool*>(obj.get())->getVal(); + auto boolean = asBool(); + if (boolean) { + return boolean->getVal(); } else { typeWarning("boolean", "returning false"); QTC::TC("qpdf", "QPDFObjectHandle boolean returning false"); @@ -462,10 +505,11 @@ QPDFObjectHandle::getBoolValue() bool QPDFObjectHandle::getValueAsBool(bool& value) { - if (!isBool()) { + auto boolean = asBool(); + if (boolean == nullptr) { return false; } - value = dynamic_cast<QPDF_Bool*>(obj.get())->getVal(); + value = boolean->getVal(); return true; } @@ -474,8 +518,9 @@ QPDFObjectHandle::getValueAsBool(bool& value) long long QPDFObjectHandle::getIntValue() { - if (isInteger()) { - return dynamic_cast<QPDF_Integer*>(obj.get())->getVal(); + auto integer = asInteger(); + if (integer) { + return integer->getVal(); } else { typeWarning("integer", "returning 0"); QTC::TC("qpdf", "QPDFObjectHandle integer returning 0"); @@ -486,10 +531,11 @@ QPDFObjectHandle::getIntValue() bool QPDFObjectHandle::getValueAsInt(long long& value) { - if (!isInteger()) { + auto integer = asInteger(); + if (integer == nullptr) { return false; } - value = dynamic_cast<QPDF_Integer*>(obj.get())->getVal(); + value = integer->getVal(); return true; } @@ -585,8 +631,9 @@ QPDFObjectHandle::getValueAsUInt(unsigned int& value) std::string QPDFObjectHandle::getRealValue() { - if (isReal()) { - return dynamic_cast<QPDF_Real*>(obj.get())->getVal(); + auto real = asReal(); + if (real) { + return real->getVal(); } else { typeWarning("real", "returning 0.0"); QTC::TC("qpdf", "QPDFObjectHandle real returning 0.0"); @@ -597,10 +644,11 @@ QPDFObjectHandle::getRealValue() bool QPDFObjectHandle::getValueAsReal(std::string& value) { - if (!isReal()) { + auto real = asReal(); + if (real == nullptr) { return false; } - value = dynamic_cast<QPDF_Real*>(obj.get())->getVal(); + value = real->getVal(); return true; } @@ -609,8 +657,9 @@ QPDFObjectHandle::getValueAsReal(std::string& value) std::string QPDFObjectHandle::getName() { - if (isName()) { - return dynamic_cast<QPDF_Name*>(obj.get())->getName(); + auto name = asName(); + if (name) { + return name->getName(); } else { typeWarning("name", "returning dummy name"); QTC::TC("qpdf", "QPDFObjectHandle name returning dummy name"); @@ -621,10 +670,11 @@ QPDFObjectHandle::getName() bool QPDFObjectHandle::getValueAsName(std::string& value) { - if (!isName()) { + auto name = asName(); + if (name == nullptr) { return false; } - value = dynamic_cast<QPDF_Name*>(obj.get())->getName(); + value = name->getName(); return true; } @@ -633,8 +683,9 @@ QPDFObjectHandle::getValueAsName(std::string& value) std::string QPDFObjectHandle::getStringValue() { - if (isString()) { - return dynamic_cast<QPDF_String*>(obj.get())->getVal(); + auto str = asString(); + if (str) { + return str->getVal(); } else { typeWarning("string", "returning empty string"); QTC::TC("qpdf", "QPDFObjectHandle string returning empty string"); @@ -645,18 +696,20 @@ QPDFObjectHandle::getStringValue() bool QPDFObjectHandle::getValueAsString(std::string& value) { - if (!isString()) { + auto str = asString(); + if (str == nullptr) { return false; } - value = dynamic_cast<QPDF_String*>(obj.get())->getVal(); + value = str->getVal(); return true; } std::string QPDFObjectHandle::getUTF8Value() { - if (isString()) { - return dynamic_cast<QPDF_String*>(obj.get())->getUTF8Val(); + auto str = asString(); + if (str) { + return str->getUTF8Val(); } else { typeWarning("string", "returning empty string"); QTC::TC("qpdf", "QPDFObjectHandle string returning empty utf8"); @@ -667,10 +720,11 @@ QPDFObjectHandle::getUTF8Value() bool QPDFObjectHandle::getValueAsUTF8(std::string& value) { - if (!isString()) { + auto str = asString(); + if (str == nullptr) { return false; } - value = dynamic_cast<QPDF_String*>(obj.get())->getUTF8Val(); + value = str->getUTF8Val(); return true; } @@ -679,8 +733,9 @@ QPDFObjectHandle::getValueAsUTF8(std::string& value) std::string QPDFObjectHandle::getOperatorValue() { - if (isOperator()) { - return dynamic_cast<QPDF_Operator*>(obj.get())->getVal(); + auto op = asOperator(); + if (op) { + return op->getVal(); } else { typeWarning("operator", "returning fake value"); QTC::TC("qpdf", "QPDFObjectHandle operator returning fake value"); @@ -691,18 +746,20 @@ QPDFObjectHandle::getOperatorValue() bool QPDFObjectHandle::getValueAsOperator(std::string& value) { - if (!isOperator()) { + auto op = asOperator(); + if (op == nullptr) { return false; } - value = dynamic_cast<QPDF_Operator*>(obj.get())->getVal(); + value = op->getVal(); return true; } std::string QPDFObjectHandle::getInlineImageValue() { - if (isInlineImage()) { - return dynamic_cast<QPDF_InlineImage*>(obj.get())->getVal(); + auto image = asInlineImage(); + if (image) { + return image->getVal(); } else { typeWarning("inlineimage", "returning empty data"); QTC::TC("qpdf", "QPDFObjectHandle inlineimage returning empty data"); @@ -713,10 +770,11 @@ QPDFObjectHandle::getInlineImageValue() bool QPDFObjectHandle::getValueAsInlineImage(std::string& value) { - if (!isInlineImage()) { + auto image = asInlineImage(); + if (image == nullptr) { return false; } - value = dynamic_cast<QPDF_InlineImage*>(obj.get())->getVal(); + value = image->getVal(); return true; } @@ -731,8 +789,9 @@ QPDFObjectHandle::aitems() int QPDFObjectHandle::getArrayNItems() { - if (isArray()) { - return dynamic_cast<QPDF_Array*>(obj.get())->getNItems(); + auto array = asArray(); + if (array) { + return array->getNItems(); } else { typeWarning("array", "treating as empty"); QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); @@ -744,11 +803,12 @@ QPDFObjectHandle QPDFObjectHandle::getArrayItem(int n) { QPDFObjectHandle result; - if (isArray() && (n < getArrayNItems()) && (n >= 0)) { - result = dynamic_cast<QPDF_Array*>(obj.get())->getItem(n); + auto array = asArray(); + if (array && (n < array->getNItems()) && (n >= 0)) { + result = array->getItem(n); } else { result = newNull(); - if (isArray()) { + if (array) { objectWarning("returning null for out of bounds array access"); QTC::TC("qpdf", "QPDFObjectHandle array bounds"); } else { @@ -757,7 +817,7 @@ QPDFObjectHandle::getArrayItem(int n) } QPDF* context = nullptr; std::string description; - if (this->obj->getDescription(context, description)) { + if (obj->getDescription(context, description)) { result.setObjectDescription( context, description + " -> null returned from invalid array access"); @@ -769,14 +829,12 @@ QPDFObjectHandle::getArrayItem(int n) bool QPDFObjectHandle::isRectangle() { - if (!isArray()) { - return false; - } - if (getArrayNItems() != 4) { + auto array = asArray(); + if ((array == nullptr) || (array->getNItems() != 4)) { return false; } for (int i = 0; i < 4; ++i) { - if (!getArrayItem(i).isNumber()) { + if (!array->getItem(i).isNumber()) { return false; } } @@ -786,14 +844,12 @@ QPDFObjectHandle::isRectangle() bool QPDFObjectHandle::isMatrix() { - if (!isArray()) { - return false; - } - if (getArrayNItems() != 6) { + auto array = asArray(); + if ((array == nullptr) || (array->getNItems() != 6)) { return false; } for (int i = 0; i < 6; ++i) { - if (!getArrayItem(i).isNumber()) { + if (!array->getItem(i).isNumber()) { return false; } } @@ -805,13 +861,14 @@ QPDFObjectHandle::getArrayAsRectangle() { Rectangle result; if (isRectangle()) { + auto array = asArray(); // Rectangle coordinates are always supposed to be llx, lly, // urx, ury, but files have been found in the wild where // llx > urx or lly > ury. - double i0 = getArrayItem(0).getNumericValue(); - double i1 = getArrayItem(1).getNumericValue(); - double i2 = getArrayItem(2).getNumericValue(); - double i3 = getArrayItem(3).getNumericValue(); + double i0 = array->getItem(0).getNumericValue(); + double i1 = array->getItem(1).getNumericValue(); + double i2 = array->getItem(2).getNumericValue(); + double i3 = array->getItem(3).getNumericValue(); result = Rectangle( std::min(i0, i2), std::min(i1, i3), @@ -826,13 +883,14 @@ QPDFObjectHandle::getArrayAsMatrix() { Matrix result; if (isMatrix()) { + auto array = asArray(); result = Matrix( - getArrayItem(0).getNumericValue(), - getArrayItem(1).getNumericValue(), - getArrayItem(2).getNumericValue(), - getArrayItem(3).getNumericValue(), - getArrayItem(4).getNumericValue(), - getArrayItem(5).getNumericValue()); + array->getItem(0).getNumericValue(), + array->getItem(1).getNumericValue(), + array->getItem(2).getNumericValue(), + array->getItem(3).getNumericValue(), + array->getItem(4).getNumericValue(), + array->getItem(5).getNumericValue()); } return result; } @@ -841,8 +899,9 @@ std::vector<QPDFObjectHandle> QPDFObjectHandle::getArrayAsVector() { std::vector<QPDFObjectHandle> result; - if (isArray()) { - dynamic_cast<QPDF_Array*>(obj.get())->getAsVector(result); + auto array = asArray(); + if (array) { + array->getAsVector(result); } else { typeWarning("array", "treating as empty"); QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector"); @@ -855,9 +914,10 @@ QPDFObjectHandle::getArrayAsVector() void QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) { - if (isArray()) { + auto array = asArray(); + if (array) { checkOwnership(item); - dynamic_cast<QPDF_Array*>(obj.get())->setItem(n, item); + array->setItem(n, item); } else { typeWarning("array", "ignoring attempt to set item"); QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item"); @@ -867,11 +927,12 @@ QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) void QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) { - if (isArray()) { + auto array = asArray(); + if (array) { for (auto const& item: items) { checkOwnership(item); } - dynamic_cast<QPDF_Array*>(obj.get())->setFromVector(items); + array->setFromVector(items); } else { typeWarning("array", "ignoring attempt to replace items"); QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items"); @@ -881,8 +942,9 @@ QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) void QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) { - if (isArray()) { - dynamic_cast<QPDF_Array*>(obj.get())->insertItem(at, item); + auto array = asArray(); + if (array) { + array->insertItem(at, item); } else { typeWarning("array", "ignoring attempt to insert item"); QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item"); @@ -899,9 +961,10 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const& item) void QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) { - if (isArray()) { + auto array = asArray(); + if (array) { checkOwnership(item); - dynamic_cast<QPDF_Array*>(obj.get())->appendItem(item); + array->appendItem(item); } else { typeWarning("array", "ignoring attempt to append item"); QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item"); @@ -918,10 +981,11 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const& item) void QPDFObjectHandle::eraseItem(int at) { - if (isArray() && (at < getArrayNItems()) && (at >= 0)) { - dynamic_cast<QPDF_Array*>(obj.get())->eraseItem(at); + auto array = asArray(); + if (array && (at < array->getNItems()) && (at >= 0)) { + array->eraseItem(at); } else { - if (isArray()) { + if (array) { objectWarning("ignoring attempt to erase out of bounds array item"); QTC::TC("qpdf", "QPDFObjectHandle erase array bounds"); } else { @@ -935,8 +999,9 @@ QPDFObjectHandle QPDFObjectHandle::eraseItemAndGetOld(int at) { auto result = QPDFObjectHandle::newNull(); - if (isArray() && (at < getArrayNItems()) && (at >= 0)) { - result = getArrayItem(at); + auto array = asArray(); + if (array && (at < array->getNItems()) && (at >= 0)) { + result = array->getItem(at); } eraseItem(at); return result; @@ -953,8 +1018,9 @@ QPDFObjectHandle::ditems() bool QPDFObjectHandle::hasKey(std::string const& key) { - if (isDictionary()) { - return dynamic_cast<QPDF_Dictionary*>(obj.get())->hasKey(key); + auto dict = asDictionary(); + if (dict) { + return dict->hasKey(key); } else { typeWarning( "dictionary", "returning false for a key containment request"); @@ -967,15 +1033,16 @@ QPDFObjectHandle QPDFObjectHandle::getKey(std::string const& key) { QPDFObjectHandle result; - if (isDictionary()) { - result = dynamic_cast<QPDF_Dictionary*>(obj.get())->getKey(key); + auto dict = asDictionary(); + if (dict) { + result = dict->getKey(key); } else { typeWarning("dictionary", "returning null for attempted key retrieval"); QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey"); result = newNull(); QPDF* qpdf = nullptr; std::string description; - if (this->obj->getDescription(qpdf, description)) { + if (obj->getDescription(qpdf, description)) { result.setObjectDescription( qpdf, (description + " -> null returned from getting key " + key + @@ -995,8 +1062,9 @@ std::set<std::string> QPDFObjectHandle::getKeys() { std::set<std::string> result; - if (isDictionary()) { - result = dynamic_cast<QPDF_Dictionary*>(obj.get())->getKeys(); + auto dict = asDictionary(); + if (dict) { + result = dict->getKeys(); } else { typeWarning("dictionary", "treating as empty"); QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys"); @@ -1008,8 +1076,9 @@ std::map<std::string, QPDFObjectHandle> QPDFObjectHandle::getDictAsMap() { std::map<std::string, QPDFObjectHandle> result; - if (isDictionary()) { - result = dynamic_cast<QPDF_Dictionary*>(obj.get())->getAsMap(); + auto dict = asDictionary(); + if (dict) { + result = dict->getAsMap(); } else { typeWarning("dictionary", "treating as empty"); QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap"); @@ -1200,9 +1269,10 @@ void QPDFObjectHandle::replaceKey( std::string const& key, QPDFObjectHandle const& value) { - if (isDictionary()) { + auto dict = asDictionary(); + if (dict) { checkOwnership(value); - dynamic_cast<QPDF_Dictionary*>(obj.get())->replaceKey(key, value); + dict->replaceKey(key, value); } else { typeWarning("dictionary", "ignoring key replacement request"); QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey"); @@ -1229,8 +1299,9 @@ QPDFObjectHandle::replaceKeyAndGetOld( void QPDFObjectHandle::removeKey(std::string const& key) { - if (isDictionary()) { - dynamic_cast<QPDF_Dictionary*>(obj.get())->removeKey(key); + auto dict = asDictionary(); + if (dict) { + dict->removeKey(key); } else { typeWarning("dictionary", "ignoring key removal request"); QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey"); @@ -1241,8 +1312,9 @@ QPDFObjectHandle QPDFObjectHandle::removeKeyAndGetOld(std::string const& key) { auto result = QPDFObjectHandle::newNull(); - if (isDictionary()) { - result = getKey(key); + auto dict = asDictionary(); + if (dict) { + result = dict->getKey(key); } removeKey(key); return result; @@ -1259,50 +1331,43 @@ QPDFObjectHandle::replaceOrRemoveKey( QPDFObjectHandle QPDFObjectHandle::getDict() { - assertStream(); - return dynamic_cast<QPDF_Stream*>(obj.get())->getDict(); + return asStreamWithAssert()->getDict(); } void QPDFObjectHandle::setFilterOnWrite(bool val) { - assertStream(); - dynamic_cast<QPDF_Stream*>(obj.get())->setFilterOnWrite(val); + asStreamWithAssert()->setFilterOnWrite(val); } bool QPDFObjectHandle::getFilterOnWrite() { - assertStream(); - return dynamic_cast<QPDF_Stream*>(obj.get())->getFilterOnWrite(); + return asStreamWithAssert()->getFilterOnWrite(); } bool QPDFObjectHandle::isDataModified() { - assertStream(); - return dynamic_cast<QPDF_Stream*>(obj.get())->isDataModified(); + return asStreamWithAssert()->isDataModified(); } void QPDFObjectHandle::replaceDict(QPDFObjectHandle const& new_dict) { - assertStream(); - dynamic_cast<QPDF_Stream*>(obj.get())->replaceDict(new_dict); + asStreamWithAssert()->replaceDict(new_dict); } std::shared_ptr<Buffer> QPDFObjectHandle::getStreamData(qpdf_stream_decode_level_e level) { - assertStream(); - return dynamic_cast<QPDF_Stream*>(obj.get())->getStreamData(level); + return asStreamWithAssert()->getStreamData(level); } std::shared_ptr<Buffer> QPDFObjectHandle::getRawStreamData() { - assertStream(); - return dynamic_cast<QPDF_Stream*>(obj.get())->getRawStreamData(); + return asStreamWithAssert()->getRawStreamData(); } bool @@ -1314,8 +1379,7 @@ QPDFObjectHandle::pipeStreamData( bool suppress_warnings, bool will_retry) { - assertStream(); - return dynamic_cast<QPDF_Stream*>(obj.get())->pipeStreamData( + return asStreamWithAssert()->pipeStreamData( p, filtering_attempted, encode_flags, @@ -1332,9 +1396,8 @@ QPDFObjectHandle::pipeStreamData( bool suppress_warnings, bool will_retry) { - assertStream(); bool filtering_attempted; - dynamic_cast<QPDF_Stream*>(obj.get())->pipeStreamData( + asStreamWithAssert()->pipeStreamData( p, &filtering_attempted, encode_flags, @@ -1368,9 +1431,7 @@ QPDFObjectHandle::replaceStreamData( QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms) { - assertStream(); - dynamic_cast<QPDF_Stream*>(obj.get())->replaceStreamData( - data, filter, decode_parms); + asStreamWithAssert()->replaceStreamData(data, filter, decode_parms); } void @@ -1379,14 +1440,12 @@ QPDFObjectHandle::replaceStreamData( QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms) { - assertStream(); auto b = std::make_shared<Buffer>(data.length()); unsigned char* bp = b->getBuffer(); if (bp) { memcpy(bp, data.c_str(), data.length()); } - dynamic_cast<QPDF_Stream*>(obj.get())->replaceStreamData( - b, filter, decode_parms); + asStreamWithAssert()->replaceStreamData(b, filter, decode_parms); } void @@ -1395,9 +1454,7 @@ QPDFObjectHandle::replaceStreamData( QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms) { - assertStream(); - dynamic_cast<QPDF_Stream*>(obj.get())->replaceStreamData( - provider, filter, decode_parms); + asStreamWithAssert()->replaceStreamData(provider, filter, decode_parms); } namespace @@ -1446,11 +1503,9 @@ QPDFObjectHandle::replaceStreamData( QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms) { - assertStream(); auto sdp = std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider)); - dynamic_cast<QPDF_Stream*>(obj.get())->replaceStreamData( - sdp, filter, decode_parms); + asStreamWithAssert()->replaceStreamData(sdp, filter, decode_parms); } void @@ -1459,11 +1514,9 @@ QPDFObjectHandle::replaceStreamData( QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms) { - assertStream(); auto sdp = std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider)); - dynamic_cast<QPDF_Stream*>(obj.get())->replaceStreamData( - sdp, filter, decode_parms); + asStreamWithAssert()->replaceStreamData(sdp, filter, decode_parms); } std::map<std::string, QPDFObjectHandle> @@ -1478,10 +1531,11 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( { all_description = description; std::vector<QPDFObjectHandle> result; - if (isArray()) { - int n_items = getArrayNItems(); + auto array = asArray(); + if (array) { + int n_items = array->getNItems(); for (int i = 0; i < n_items; ++i) { - QPDFObjectHandle item = getArrayItem(i); + QPDFObjectHandle item = array->getItem(i); if (item.isStream()) { result.push_back(item); } else { @@ -1648,18 +1702,16 @@ QPDFObjectHandle::unparseResolved() if (!dereference()) { throw std::logic_error( "attempted to dereference an uninitialized QPDFObjectHandle"); - } else if (this->reserved) { - throw std::logic_error( - "QPDFObjectHandle: attempting to unparse a reserved object"); } - return this->obj->unparse(); + return obj->unparse(); } std::string QPDFObjectHandle::unparseBinary() { - if (this->isString()) { - return dynamic_cast<QPDF_String*>(this->obj.get())->unparse(true); + auto str = asString(); + if (str) { + return str->unparse(true); } else { return unparse(); } @@ -1675,16 +1727,13 @@ QPDFObjectHandle::getJSON(bool dereference_indirect) JSON QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) { - if ((!dereference_indirect) && this->isIndirect()) { + if ((!dereference_indirect) && isIndirect()) { return JSON::makeString(unparse()); } else if (!dereference()) { throw std::logic_error( "attempted to dereference an uninitialized QPDFObjectHandle"); - } else if (this->reserved) { - throw std::logic_error( - "QPDFObjectHandle: attempting to unparse a reserved object"); } else { - return this->obj->getJSON(json_version); + return obj->getJSON(json_version); } } @@ -1696,8 +1745,7 @@ QPDFObjectHandle::getStreamJSON( Pipeline* p, std::string const& data_filename) { - assertStream(); - return dynamic_cast<QPDF_Stream*>(obj.get())->getStreamJSON( + return asStreamWithAssert()->getStreamJSON( json_version, json_data, decode_level, p, data_filename); } @@ -1917,8 +1965,7 @@ QPDFObjectHandle::addContentTokenFilter(std::shared_ptr<TokenFilter> filter) void QPDFObjectHandle::addTokenFilter(std::shared_ptr<TokenFilter> filter) { - assertStream(); - return dynamic_cast<QPDF_Stream*>(obj.get())->addTokenFilter(filter); + return asStreamWithAssert()->addTokenFilter(filter); } QPDFObjectHandle @@ -1945,21 +1992,6 @@ QPDFObjectHandle::getParsedOffset() } QPDFObjectHandle -QPDFObjectHandle::newIndirect(QPDF* qpdf, QPDFObjGen const& og) -{ - if (!og.isIndirect()) { - // Special case: QPDF uses objid 0 as a sentinel for direct - // objects, and the PDF specification doesn't allow for object - // 0. Treat indirect references to object 0 as null so that we - // never create an indirect object with objid 0. - QTC::TC("qpdf", "QPDFObjectHandle indirect with 0 objid"); - return newNull(); - } - - return QPDFObjectHandle(qpdf, og); -} - -QPDFObjectHandle QPDFObjectHandle::newBool(bool value) { return QPDFObjectHandle(QPDF_Bool::create(value)); @@ -2128,8 +2160,7 @@ QPDFObjectHandle::newStream(QPDF* qpdf) QPDFObjectHandle stream_dict = newDictionary(); QPDFObjectHandle result = qpdf->makeIndirectObject(QPDFObjectHandle( QPDF_Stream::create(qpdf, QPDFObjGen(), stream_dict, 0, 0))); - result.dereference(); - QPDF_Stream* stream = dynamic_cast<QPDF_Stream*>(result.obj.get()); + auto stream = result.asStream(); stream->setObjGen(result.getObjGen()); return result; } @@ -2155,18 +2186,7 @@ QPDFObjectHandle::newStream(QPDF* qpdf, std::string const& data) QPDFObjectHandle QPDFObjectHandle::newReserved(QPDF* qpdf) { - // Reserve a spot for this object by assigning it an object - // number, but then return an unresolved handle to the object. - QPDFObjectHandle reserved = qpdf->makeIndirectObject(makeReserved()); - QPDFObjectHandle result = newIndirect(qpdf, reserved.getObjGen()); - result.reserved = true; - return result; -} - -QPDFObjectHandle -QPDFObjectHandle::makeReserved() -{ - return QPDFObjectHandle(QPDF_Reserved::create()); + return qpdf->makeIndirectObject(QPDFObjectHandle(QPDF_Reserved::create())); } void @@ -2212,12 +2232,7 @@ QPDFObjectHandle::shallowCopyInternal( QTC::TC("qpdf", "QPDFObjectHandle ERR shallow copy stream"); throw std::runtime_error("attempt to make a shallow copy of a stream"); } - - if (isArray() || isDictionary()) { - new_obj = QPDFObjectHandle(obj->shallowCopy()); - } else { - new_obj = *this; - } + new_obj = QPDFObjectHandle(obj->shallowCopy()); std::set<QPDFObjGen> visited; new_obj.copyObject(visited, false, first_level_only, false); @@ -2258,9 +2273,6 @@ QPDFObjectHandle::copyObject( " reserved object handle direct"); } - qpdf = nullptr; - og = QPDFObjGen(); - std::shared_ptr<QPDFObject> new_obj; if (isBool() || isInteger() || isName() || isNull() || isReal() || @@ -2268,9 +2280,10 @@ QPDFObjectHandle::copyObject( new_obj = obj->shallowCopy(); } else if (isArray()) { std::vector<QPDFObjectHandle> items; - int n = getArrayNItems(); + auto array = asArray(); + int n = array->getNItems(); for (int i = 0; i < n; ++i) { - items.push_back(getArrayItem(i)); + items.push_back(array->getItem(i)); if ((!first_level_only) && (cross_indirect || (!items.back().isIndirect()))) { items.back().copyObject( @@ -2280,8 +2293,9 @@ QPDFObjectHandle::copyObject( new_obj = QPDF_Array::create(items); } else if (isDictionary()) { std::map<std::string, QPDFObjectHandle> items; + auto dict = asDictionary(); for (auto const& key: getKeys()) { - items[key] = getKey(key); + items[key] = dict->getKey(key); if ((!first_level_only) && (cross_indirect || (!items[key].isIndirect()))) { items[key].copyObject( @@ -2329,7 +2343,7 @@ QPDFObjectHandle::makeDirect(bool allow_streams) void QPDFObjectHandle::assertInitialized() const { - if (!this->initialized) { + if (!isInitialized()) { throw std::logic_error("operation attempted on uninitialized " "QPDFObjectHandle"); } @@ -2544,8 +2558,9 @@ QPDFObjectHandle::isImage(bool exclude_imagemask) void QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const { - if ((this->qpdf != nullptr) && (item.qpdf != nullptr) && - (this->qpdf != item.qpdf)) { + auto qpdf = getOwningQPDF(); + auto item_qpdf = item.getOwningQPDF(); + if ((qpdf != nullptr) && (item_qpdf != nullptr) && (qpdf != item_qpdf)) { QTC::TC("qpdf", "QPDFObjectHandle check ownership"); throw std::logic_error( "Attempting to add an object from a different QPDF." @@ -2564,28 +2579,10 @@ QPDFObjectHandle::assertPageObject() bool QPDFObjectHandle::dereference() { - if (!this->initialized) { + if (!isInitialized()) { return false; } - if (this->obj.get() && getObjectID() && - QPDF::Resolver::objectChanged(this->qpdf, getObjGen(), this->obj)) { - this->obj = nullptr; - } - if (this->obj == nullptr) { - std::shared_ptr<QPDFObject> obj = - QPDF::Resolver::resolve(this->qpdf, getObjGen()); - if (obj == nullptr) { - // QPDF::resolve never returns an uninitialized object, but - // check just in case. - this->obj = QPDF_Null::create(); - } else if (dynamic_cast<QPDF_Reserved*>(obj.get())) { - // Do not resolve - this->reserved = true; - } else { - this->reserved = false; - this->obj = obj; - } - } + this->obj->resolve(); return true; } diff --git a/libqpdf/QPDFParser.cc b/libqpdf/QPDFParser.cc index ccdd9db0..9aa1f426 100644 --- a/libqpdf/QPDFParser.cc +++ b/libqpdf/QPDFParser.cc @@ -1,8 +1,8 @@ #include <qpdf/QPDFParser.hh> #include <qpdf/QPDF.hh> +#include <qpdf/QPDFObjGen.hh> #include <qpdf/QPDFObjectHandle.hh> -#include <qpdf/QPDF_Array.hh> #include <qpdf/QTC.hh> #include <qpdf/QUtil.hh> @@ -55,6 +55,7 @@ QPDFParser::parse(bool& empty, bool content_stream) while (!done) { bool bad = false; + bool indirect_ref = false; is_null = false; auto& frame = stack.back(); auto& olist = frame.olist; @@ -185,12 +186,16 @@ QPDFParser::parse(bool& empty, bool content_stream) "QPDFObjectHandle::parse called without context" " on an object with indirect references"); } - // Try to resolve indirect objects - object = QPDFObjectHandle::newIndirect( - context, - QPDFObjGen( - olist.at(size - 2).getIntValueAsInt(), - olist.back().getIntValueAsInt())); + auto ref_og = QPDFObjGen( + olist.at(size - 2).getIntValueAsInt(), + olist.back().getIntValueAsInt()); + if (ref_og.isIndirect()) { + object = context->getObject(ref_og); + indirect_ref = true; + } else { + QTC::TC("qpdf", "QPDFParser indirect with 0 objid"); + is_null = true; + } olist.pop_back(); olist.pop_back(); } else if ((value == "endobj") && (state == st_top)) { @@ -274,8 +279,8 @@ QPDFParser::parse(bool& empty, bool content_stream) case st_dictionary: case st_array: - if (!object.isDirectNull()) { - // No need to set description for direct nulls- they will + if (!indirect_ref && !object.isDirectNull()) { + // No need to set description for direct nulls - they will // become implicit. setDescriptionFromInput(object, input->getLastOffset()); object.setParsedOffset(input->getLastOffset()); diff --git a/libqpdf/QPDFValue.cc b/libqpdf/QPDFValue.cc new file mode 100644 index 00000000..8a6222d2 --- /dev/null +++ b/libqpdf/QPDFValue.cc @@ -0,0 +1,11 @@ +#include <qpdf/QPDFValue.hh> + +#include <qpdf/QPDFObject.hh> + +std::shared_ptr<QPDFObject> +QPDFValue::do_create(QPDFValue* object) +{ + std::shared_ptr<QPDFObject> obj(new QPDFObject()); + obj->value = std::shared_ptr<QPDFValue>(object); + return obj; +} diff --git a/libqpdf/QPDF_Array.cc b/libqpdf/QPDF_Array.cc index 55e4d20a..63fe98d4 100644 --- a/libqpdf/QPDF_Array.cc +++ b/libqpdf/QPDF_Array.cc @@ -4,12 +4,14 @@ #include <qpdf/QUtil.hh> #include <stdexcept> -QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v) +QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v) : + QPDFValue(::ot_array, "array") { setFromVector(v); } QPDF_Array::QPDF_Array(SparseOHArray const& items) : + QPDFValue(::ot_array, "array"), elements(items) { } @@ -62,18 +64,6 @@ QPDF_Array::getJSON(int json_version) return j; } -QPDFObject::object_type_e -QPDF_Array::getTypeCode() const -{ - return QPDFObject::ot_array; -} - -char const* -QPDF_Array::getTypeName() const -{ - return "array"; -} - int QPDF_Array::getNItems() const { diff --git a/libqpdf/QPDF_Bool.cc b/libqpdf/QPDF_Bool.cc index f26325c3..efbfd6c9 100644 --- a/libqpdf/QPDF_Bool.cc +++ b/libqpdf/QPDF_Bool.cc @@ -1,6 +1,7 @@ #include <qpdf/QPDF_Bool.hh> QPDF_Bool::QPDF_Bool(bool val) : + QPDFValue(::ot_boolean, "boolean"), val(val) { } @@ -29,18 +30,6 @@ QPDF_Bool::getJSON(int json_version) return JSON::makeBool(this->val); } -QPDFObject::object_type_e -QPDF_Bool::getTypeCode() const -{ - return QPDFObject::ot_boolean; -} - -char const* -QPDF_Bool::getTypeName() const -{ - return "boolean"; -} - bool QPDF_Bool::getVal() const { diff --git a/libqpdf/QPDF_Dictionary.cc b/libqpdf/QPDF_Dictionary.cc index 60b2339f..845bcad8 100644 --- a/libqpdf/QPDF_Dictionary.cc +++ b/libqpdf/QPDF_Dictionary.cc @@ -1,10 +1,10 @@ #include <qpdf/QPDF_Dictionary.hh> #include <qpdf/QPDF_Name.hh> -#include <qpdf/QPDF_Null.hh> QPDF_Dictionary::QPDF_Dictionary( std::map<std::string, QPDFObjectHandle> const& items) : + QPDFValue(::ot_dictionary, "dictionary"), items(items) { } @@ -58,18 +58,6 @@ QPDF_Dictionary::getJSON(int json_version) return j; } -QPDFObject::object_type_e -QPDF_Dictionary::getTypeCode() const -{ - return QPDFObject::ot_dictionary; -} - -char const* -QPDF_Dictionary::getTypeName() const -{ - return "dictionary"; -} - bool QPDF_Dictionary::hasKey(std::string const& key) { diff --git a/libqpdf/QPDF_InlineImage.cc b/libqpdf/QPDF_InlineImage.cc index c3c656e0..76318196 100644 --- a/libqpdf/QPDF_InlineImage.cc +++ b/libqpdf/QPDF_InlineImage.cc @@ -1,6 +1,7 @@ #include <qpdf/QPDF_InlineImage.hh> QPDF_InlineImage::QPDF_InlineImage(std::string const& val) : + QPDFValue(::ot_inlineimage, "inline-image"), val(val) { } @@ -29,18 +30,6 @@ QPDF_InlineImage::getJSON(int json_version) return JSON::makeNull(); } -QPDFObject::object_type_e -QPDF_InlineImage::getTypeCode() const -{ - return QPDFObject::ot_inlineimage; -} - -char const* -QPDF_InlineImage::getTypeName() const -{ - return "inline-image"; -} - std::string QPDF_InlineImage::getVal() const { diff --git a/libqpdf/QPDF_Integer.cc b/libqpdf/QPDF_Integer.cc index e8d23e4a..24812573 100644 --- a/libqpdf/QPDF_Integer.cc +++ b/libqpdf/QPDF_Integer.cc @@ -3,6 +3,7 @@ #include <qpdf/QUtil.hh> QPDF_Integer::QPDF_Integer(long long val) : + QPDFValue(::ot_integer, "integer"), val(val) { } @@ -31,18 +32,6 @@ QPDF_Integer::getJSON(int json_version) return JSON::makeInt(this->val); } -QPDFObject::object_type_e -QPDF_Integer::getTypeCode() const -{ - return QPDFObject::ot_integer; -} - -char const* -QPDF_Integer::getTypeName() const -{ - return "integer"; -} - long long QPDF_Integer::getVal() const { diff --git a/libqpdf/QPDF_Name.cc b/libqpdf/QPDF_Name.cc index 73990775..c86d34b4 100644 --- a/libqpdf/QPDF_Name.cc +++ b/libqpdf/QPDF_Name.cc @@ -5,6 +5,7 @@ #include <string.h> QPDF_Name::QPDF_Name(std::string const& name) : + QPDFValue(::ot_name, "name"), name(name) { } @@ -61,18 +62,6 @@ QPDF_Name::getJSON(int json_version) } } -QPDFObject::object_type_e -QPDF_Name::getTypeCode() const -{ - return QPDFObject::ot_name; -} - -char const* -QPDF_Name::getTypeName() const -{ - return "name"; -} - std::string QPDF_Name::getName() const { diff --git a/libqpdf/QPDF_Null.cc b/libqpdf/QPDF_Null.cc index b015ed8b..f60dda1f 100644 --- a/libqpdf/QPDF_Null.cc +++ b/libqpdf/QPDF_Null.cc @@ -1,5 +1,10 @@ #include <qpdf/QPDF_Null.hh> +QPDF_Null::QPDF_Null() : + QPDFValue(::ot_null, "null") +{ +} + std::shared_ptr<QPDFObject> QPDF_Null::create() { @@ -23,15 +28,3 @@ QPDF_Null::getJSON(int json_version) { return JSON::makeNull(); } - -QPDFObject::object_type_e -QPDF_Null::getTypeCode() const -{ - return QPDFObject::ot_null; -} - -char const* -QPDF_Null::getTypeName() const -{ - return "null"; -} diff --git a/libqpdf/QPDF_Operator.cc b/libqpdf/QPDF_Operator.cc index cd5009ae..547ff40a 100644 --- a/libqpdf/QPDF_Operator.cc +++ b/libqpdf/QPDF_Operator.cc @@ -1,6 +1,7 @@ #include <qpdf/QPDF_Operator.hh> QPDF_Operator::QPDF_Operator(std::string const& val) : + QPDFValue(::ot_operator, "operator"), val(val) { } @@ -20,7 +21,7 @@ QPDF_Operator::shallowCopy() std::string QPDF_Operator::unparse() { - return this->val; + return val; } JSON @@ -29,18 +30,6 @@ QPDF_Operator::getJSON(int json_version) return JSON::makeNull(); } -QPDFObject::object_type_e -QPDF_Operator::getTypeCode() const -{ - return QPDFObject::ot_operator; -} - -char const* -QPDF_Operator::getTypeName() const -{ - return "operator"; -} - std::string QPDF_Operator::getVal() const { diff --git a/libqpdf/QPDF_Real.cc b/libqpdf/QPDF_Real.cc index 138bbb3c..85c9ceeb 100644 --- a/libqpdf/QPDF_Real.cc +++ b/libqpdf/QPDF_Real.cc @@ -3,12 +3,14 @@ #include <qpdf/QUtil.hh> QPDF_Real::QPDF_Real(std::string const& val) : + QPDFValue(::ot_real, "real"), val(val) { } QPDF_Real::QPDF_Real( double value, int decimal_places, bool trim_trailing_zeroes) : + QPDFValue(::ot_real, "real"), val(QUtil::double_to_string(value, decimal_places, trim_trailing_zeroes)) { } @@ -60,18 +62,6 @@ QPDF_Real::getJSON(int json_version) return JSON::makeNumber(result); } -QPDFObject::object_type_e -QPDF_Real::getTypeCode() const -{ - return QPDFObject::ot_real; -} - -char const* -QPDF_Real::getTypeName() const -{ - return "real"; -} - std::string QPDF_Real::getVal() { diff --git a/libqpdf/QPDF_Reserved.cc b/libqpdf/QPDF_Reserved.cc index 5808a369..f5af4688 100644 --- a/libqpdf/QPDF_Reserved.cc +++ b/libqpdf/QPDF_Reserved.cc @@ -2,6 +2,11 @@ #include <stdexcept> +QPDF_Reserved::QPDF_Reserved() : + QPDFValue(::ot_reserved, "reserved") +{ +} + std::shared_ptr<QPDFObject> QPDF_Reserved::create() { @@ -17,25 +22,15 @@ QPDF_Reserved::shallowCopy() std::string QPDF_Reserved::unparse() { - throw std::logic_error("attempt to unparse QPDF_Reserved"); + throw std::logic_error( + "QPDFObjectHandle: attempting to unparse a reserved object"); return ""; } JSON QPDF_Reserved::getJSON(int json_version) { - throw std::logic_error("attempt to generate JSON from QPDF_Reserved"); + throw std::logic_error( + "QPDFObjectHandle: attempting to unparse a reserved object"); return JSON::makeNull(); } - -QPDFObject::object_type_e -QPDF_Reserved::getTypeCode() const -{ - return QPDFObject::ot_reserved; -} - -char const* -QPDF_Reserved::getTypeName() const -{ - return "reserved"; -} diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 1b7f9461..9932c15d 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -114,6 +114,7 @@ QPDF_Stream::QPDF_Stream( QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length) : + QPDFValue(::ot_stream, "stream"), qpdf(qpdf), og(og), filter_on_write(true), @@ -291,22 +292,10 @@ QPDF_Stream::getStreamJSON( return result; } -QPDFObject::object_type_e -QPDF_Stream::getTypeCode() const -{ - return QPDFObject::ot_stream; -} - -char const* -QPDF_Stream::getTypeName() const -{ - return "stream"; -} - void QPDF_Stream::setDescription(QPDF* qpdf, std::string const& description) { - this->QPDFObject::setDescription(qpdf, description); + this->QPDFValue::setDescription(qpdf, description); setDictDescription(); } diff --git a/libqpdf/QPDF_String.cc b/libqpdf/QPDF_String.cc index b038366b..c6cb6c41 100644 --- a/libqpdf/QPDF_String.cc +++ b/libqpdf/QPDF_String.cc @@ -21,6 +21,7 @@ is_iso_latin1_printable(char ch) } QPDF_String::QPDF_String(std::string const& val) : + QPDFValue(::ot_string, "string"), val(val) { } @@ -84,18 +85,6 @@ QPDF_String::getJSON(int json_version) return JSON::makeString(result); } -QPDFObject::object_type_e -QPDF_String::getTypeCode() const -{ - return QPDFObject::ot_string; -} - -char const* -QPDF_String::getTypeName() const -{ - return "string"; -} - bool QPDF_String::useHexString() const { diff --git a/libqpdf/QPDF_Unresolved.cc b/libqpdf/QPDF_Unresolved.cc new file mode 100644 index 00000000..f824a9a6 --- /dev/null +++ b/libqpdf/QPDF_Unresolved.cc @@ -0,0 +1,36 @@ +#include <qpdf/QPDF_Unresolved.hh> + +#include <stdexcept> + +QPDF_Unresolved::QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og) : + QPDFValue(::ot_unresolved, "unresolved", qpdf, og) +{ +} + +std::shared_ptr<QPDFObject> +QPDF_Unresolved::create(QPDF* qpdf, QPDFObjGen const& og) +{ + return do_create(new QPDF_Unresolved(qpdf, og)); +} + +std::shared_ptr<QPDFObject> +QPDF_Unresolved::shallowCopy() +{ + throw std::logic_error( + "attempted to shallow copy unresolved QPDFObjectHandle"); + return create(qpdf, og); +} + +std::string +QPDF_Unresolved::unparse() +{ + throw std::logic_error( + "attempted to unparse an unresolved QPDFObjectHandle"); + return ""; +} + +JSON +QPDF_Unresolved::getJSON(int json_version) +{ + return JSON::makeNull(); +} diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc index e988092e..e09f7b68 100644 --- a/libqpdf/QPDF_linearization.cc +++ b/libqpdf/QPDF_linearization.cc @@ -137,8 +137,7 @@ QPDF::isLinearized() return false; } - QPDFObjectHandle candidate = QPDFObjectHandle::Factory::newIndirect( - this, QPDFObjGen(lindict_obj, 0)); + auto candidate = getObjectByID(lindict_obj, 0); if (!candidate.isDictionary()) { return false; } diff --git a/libqpdf/qpdf/QPDF_Array.hh b/libqpdf/qpdf/QPDF_Array.hh index 3e095637..426efe36 100644 --- a/libqpdf/qpdf/QPDF_Array.hh +++ b/libqpdf/qpdf/QPDF_Array.hh @@ -1,13 +1,13 @@ #ifndef QPDF_ARRAY_HH #define QPDF_ARRAY_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> #include <qpdf/SparseOHArray.hh> #include <list> #include <vector> -class QPDF_Array: public QPDFObject +class QPDF_Array: public QPDFValue { public: virtual ~QPDF_Array() = default; @@ -17,8 +17,6 @@ class QPDF_Array: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; int getNItems() const; QPDFObjectHandle getItem(int n) const; diff --git a/libqpdf/qpdf/QPDF_Bool.hh b/libqpdf/qpdf/QPDF_Bool.hh index dbedc70a..3e45cd8e 100644 --- a/libqpdf/qpdf/QPDF_Bool.hh +++ b/libqpdf/qpdf/QPDF_Bool.hh @@ -1,9 +1,9 @@ #ifndef QPDF_BOOL_HH #define QPDF_BOOL_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> -class QPDF_Bool: public QPDFObject +class QPDF_Bool: public QPDFValue { public: virtual ~QPDF_Bool() = default; @@ -11,8 +11,6 @@ class QPDF_Bool: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; bool getVal() const; private: diff --git a/libqpdf/qpdf/QPDF_Dictionary.hh b/libqpdf/qpdf/QPDF_Dictionary.hh index cacc8961..19ab8d9b 100644 --- a/libqpdf/qpdf/QPDF_Dictionary.hh +++ b/libqpdf/qpdf/QPDF_Dictionary.hh @@ -1,14 +1,14 @@ #ifndef QPDF_DICTIONARY_HH #define QPDF_DICTIONARY_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> #include <map> #include <set> #include <qpdf/QPDFObjectHandle.hh> -class QPDF_Dictionary: public QPDFObject +class QPDF_Dictionary: public QPDFValue { public: virtual ~QPDF_Dictionary() = default; @@ -17,8 +17,6 @@ class QPDF_Dictionary: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; // hasKey() and getKeys() treat keys with null values as if they // aren't there. getKey() returns null for the value of a diff --git a/libqpdf/qpdf/QPDF_InlineImage.hh b/libqpdf/qpdf/QPDF_InlineImage.hh index caaeaf87..b7bea9c7 100644 --- a/libqpdf/qpdf/QPDF_InlineImage.hh +++ b/libqpdf/qpdf/QPDF_InlineImage.hh @@ -1,9 +1,9 @@ #ifndef QPDF_INLINEIMAGE_HH #define QPDF_INLINEIMAGE_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> -class QPDF_InlineImage: public QPDFObject +class QPDF_InlineImage: public QPDFValue { public: virtual ~QPDF_InlineImage() = default; @@ -11,8 +11,6 @@ class QPDF_InlineImage: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; std::string getVal() const; private: diff --git a/libqpdf/qpdf/QPDF_Integer.hh b/libqpdf/qpdf/QPDF_Integer.hh index 2c17daf0..7e09673c 100644 --- a/libqpdf/qpdf/QPDF_Integer.hh +++ b/libqpdf/qpdf/QPDF_Integer.hh @@ -1,9 +1,9 @@ #ifndef QPDF_INTEGER_HH #define QPDF_INTEGER_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> -class QPDF_Integer: public QPDFObject +class QPDF_Integer: public QPDFValue { public: virtual ~QPDF_Integer() = default; @@ -11,8 +11,6 @@ class QPDF_Integer: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; long long getVal() const; private: diff --git a/libqpdf/qpdf/QPDF_Name.hh b/libqpdf/qpdf/QPDF_Name.hh index cf653b2e..74fc7e44 100644 --- a/libqpdf/qpdf/QPDF_Name.hh +++ b/libqpdf/qpdf/QPDF_Name.hh @@ -1,9 +1,9 @@ #ifndef QPDF_NAME_HH #define QPDF_NAME_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> -class QPDF_Name: public QPDFObject +class QPDF_Name: public QPDFValue { public: virtual ~QPDF_Name() = default; @@ -11,8 +11,6 @@ class QPDF_Name: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; std::string getName() const; // Put # into strings with characters unsuitable for name token diff --git a/libqpdf/qpdf/QPDF_Null.hh b/libqpdf/qpdf/QPDF_Null.hh index 16833424..68973de9 100644 --- a/libqpdf/qpdf/QPDF_Null.hh +++ b/libqpdf/qpdf/QPDF_Null.hh @@ -1,9 +1,9 @@ #ifndef QPDF_NULL_HH #define QPDF_NULL_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> -class QPDF_Null: public QPDFObject +class QPDF_Null: public QPDFValue { public: virtual ~QPDF_Null() = default; @@ -11,11 +11,9 @@ class QPDF_Null: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; private: - QPDF_Null() = default; + QPDF_Null(); }; #endif // QPDF_NULL_HH diff --git a/libqpdf/qpdf/QPDF_Operator.hh b/libqpdf/qpdf/QPDF_Operator.hh index 1da43d72..767c0ba0 100644 --- a/libqpdf/qpdf/QPDF_Operator.hh +++ b/libqpdf/qpdf/QPDF_Operator.hh @@ -1,9 +1,9 @@ #ifndef QPDF_OPERATOR_HH #define QPDF_OPERATOR_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> -class QPDF_Operator: public QPDFObject +class QPDF_Operator: public QPDFValue { public: virtual ~QPDF_Operator() = default; @@ -11,8 +11,6 @@ class QPDF_Operator: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; std::string getVal() const; private: diff --git a/libqpdf/qpdf/QPDF_Real.hh b/libqpdf/qpdf/QPDF_Real.hh index f5ab4bd6..dc0f3ff8 100644 --- a/libqpdf/qpdf/QPDF_Real.hh +++ b/libqpdf/qpdf/QPDF_Real.hh @@ -1,9 +1,9 @@ #ifndef QPDF_REAL_HH #define QPDF_REAL_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> -class QPDF_Real: public QPDFObject +class QPDF_Real: public QPDFValue { public: virtual ~QPDF_Real() = default; @@ -13,8 +13,6 @@ class QPDF_Real: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; std::string getVal(); private: diff --git a/libqpdf/qpdf/QPDF_Reserved.hh b/libqpdf/qpdf/QPDF_Reserved.hh index 243a1728..f90242a9 100644 --- a/libqpdf/qpdf/QPDF_Reserved.hh +++ b/libqpdf/qpdf/QPDF_Reserved.hh @@ -1,9 +1,9 @@ #ifndef QPDF_RESERVED_HH #define QPDF_RESERVED_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> -class QPDF_Reserved: public QPDFObject +class QPDF_Reserved: public QPDFValue { public: virtual ~QPDF_Reserved() = default; @@ -11,11 +11,9 @@ class QPDF_Reserved: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; private: - QPDF_Reserved() = default; + QPDF_Reserved(); }; #endif // QPDF_RESERVED_HH diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh index 8980c751..3a16160e 100644 --- a/libqpdf/qpdf/QPDF_Stream.hh +++ b/libqpdf/qpdf/QPDF_Stream.hh @@ -3,9 +3,9 @@ #include <qpdf/Types.h> -#include <qpdf/QPDFObject.hh> #include <qpdf/QPDFObjectHandle.hh> #include <qpdf/QPDFStreamFilter.hh> +#include <qpdf/QPDFValue.hh> #include <functional> #include <memory> @@ -13,7 +13,7 @@ class Pipeline; class QPDF; -class QPDF_Stream: public QPDFObject +class QPDF_Stream: public QPDFValue { public: virtual ~QPDF_Stream() = default; @@ -26,8 +26,6 @@ class QPDF_Stream: public QPDFObject virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); virtual JSON getJSON(int json_version); - 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; diff --git a/libqpdf/qpdf/QPDF_String.hh b/libqpdf/qpdf/QPDF_String.hh index b6d77637..a92427e3 100644 --- a/libqpdf/qpdf/QPDF_String.hh +++ b/libqpdf/qpdf/QPDF_String.hh @@ -1,11 +1,11 @@ #ifndef QPDF_STRING_HH #define QPDF_STRING_HH -#include <qpdf/QPDFObject.hh> +#include <qpdf/QPDFValue.hh> // QPDF_Strings may included embedded null characters. -class QPDF_String: public QPDFObject +class QPDF_String: public QPDFValue { friend class QPDFWriter; @@ -16,8 +16,6 @@ class QPDF_String: public QPDFObject create_utf16(std::string const& utf8_val); virtual std::shared_ptr<QPDFObject> shallowCopy(); virtual std::string unparse(); - virtual QPDFObject::object_type_e getTypeCode() const; - virtual char const* getTypeName() const; std::string unparse(bool force_binary); virtual JSON getJSON(int json_version); std::string getVal() const; diff --git a/libqpdf/qpdf/QPDF_Unresolved.hh b/libqpdf/qpdf/QPDF_Unresolved.hh new file mode 100644 index 00000000..efcf4e3d --- /dev/null +++ b/libqpdf/qpdf/QPDF_Unresolved.hh @@ -0,0 +1,19 @@ +#ifndef QPDF_UNRESOLVED_HH +#define QPDF_UNRESOLVED_HH + +#include <qpdf/QPDFValue.hh> + +class QPDF_Unresolved: public QPDFValue +{ + public: + virtual ~QPDF_Unresolved() = default; + static std::shared_ptr<QPDFObject> create(QPDF* qpdf, QPDFObjGen const& og); + virtual std::shared_ptr<QPDFObject> shallowCopy(); + virtual std::string unparse(); + virtual JSON getJSON(int json_version); + + private: + QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og); +}; + +#endif // QPDF_UNRESOLVED_HH diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index f13385e4..c3ab0a07 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -254,7 +254,7 @@ QPDFWriter standard deterministic ID 1 QPDFWriter linearized deterministic ID 1 QPDFWriter deterministic with no data 0 qpdf-c called qpdf_set_deterministic_ID 0 -QPDFObjectHandle indirect with 0 objid 0 +QPDFParser indirect with 0 objid 0 QPDF object id 0 0 QPDF recursion loop in resolve 0 QPDFParser treat word as string 0 diff --git a/qpdf/qtest/qpdf/issue-51.out b/qpdf/qtest/qpdf/issue-51.out index b4bd165c..feffea44 100644 --- a/qpdf/qtest/qpdf/issue-51.out +++ b/qpdf/qtest/qpdf/issue-51.out @@ -9,7 +9,6 @@ WARNING: issue-51.pdf (object 2 0, offset 26): /Length key in stream dictionary WARNING: issue-51.pdf (object 2 0, offset 71): attempting to recover stream length WARNING: issue-51.pdf (object 2 0, offset 71): unable to recover stream data; treating stream as empty WARNING: issue-51.pdf (object 2 0, offset 977): expected endobj -WARNING: issue-51.pdf (object 2 0, offset 977): EOF after endobj WARNING: issue-51.pdf (object 3 0): object has offset 0 WARNING: issue-51.pdf (object 4 0): object has offset 0 WARNING: issue-51.pdf (object 5 0): object has offset 0 diff --git a/qpdf/qtest/type-checks.test b/qpdf/qtest/type-checks.test index 03d75a6c..17b3c994 100644 --- a/qpdf/qtest/type-checks.test +++ b/qpdf/qtest/type-checks.test @@ -14,7 +14,7 @@ cleanup(); my $td = new TestDriver('type-checks'); -my $n_tests = 5; +my $n_tests = 6; # Whenever object-types.pdf is edited, object-types-os.pdf should be # regenerated. @@ -43,6 +43,10 @@ $td->runtest("compound type checks", {$td->COMMAND => "test_driver 82 object-types-os.pdf"}, {$td->STRING => "test 82 done\n", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); +$td->runtest("indirect objects belonging to destroyed QPDF", + {$td->COMMAND => "test_driver 92 -"}, + {$td->STRING => "test 92 done\n", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); cleanup(); $td->report($n_tests); diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index aa4f9ce5..5572e824 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -3258,6 +3258,20 @@ test_91(QPDF& pdf, char const* arg2) 2, &p, qpdf_dl_none, qpdf_sj_inline, "", std::set<std::string>()); } +static void +test_92(QPDF& pdf, char const* arg2) +{ + // Exercise indirect objects owned by destroyed QPDF object. + QPDF* qpdf = new QPDF(); + qpdf->emptyPDF(); + auto root = qpdf->getRoot(); + assert(root.getOwningQPDF() != nullptr); + assert(root.isIndirect()); + delete qpdf; + assert(root.getOwningQPDF() == nullptr); + assert(!root.isIndirect()); +} + void runtest(int n, char const* filename1, char const* arg2) { @@ -3265,7 +3279,7 @@ runtest(int n, char const* filename1, char const* arg2) // the test suite to see how the test is invoked to find the file // that the test is supposed to operate on. - std::set<int> ignore_filename = {61, 81, 83, 84, 85, 86, 87}; + std::set<int> ignore_filename = {61, 81, 83, 84, 85, 86, 87, 92}; if (n == 0) { // Throw in some random test cases that don't fit anywhere @@ -3362,7 +3376,8 @@ runtest(int n, char const* filename1, char const* arg2) {76, test_76}, {77, test_77}, {78, test_78}, {79, test_79}, {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83}, {84, test_84}, {85, test_85}, {86, test_86}, {87, test_87}, - {88, test_88}, {89, test_89}, {90, test_90}, {91, test_91}}; + {88, test_88}, {89, test_89}, {90, test_90}, {91, test_91}, + {92, test_92}}; auto fn = test_functions.find(n); if (fn == test_functions.end()) { |