diff options
Diffstat (limited to 'libqpdf/QPDF.cc')
-rw-r--r-- | libqpdf/QPDF.cc | 155 |
1 files changed, 89 insertions, 66 deletions
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 |