diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 5dc3f671..c0369e36 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -730,6 +730,7 @@ class QPDF class Writer; class Resolver; class StreamCopier; + class Objects; class ParseGuard; class Pipe; class JobSetter; @@ -757,6 +758,7 @@ class QPDF class ResolveRecorder; class JSONReactor; + inline Objects& objects(); void parse(char const* password); void inParse(bool); void setLastObjectDescription(std::string const& description, QPDFObjGen const& og); diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 02ee2fa0..d9715e7e 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -185,7 +185,8 @@ QPDF::Members::Members(QPDF& qpdf) : file_sp(new InvalidInputSource(no_input_name)), file(file_sp.get()), encp(new EncryptionParameters), - xref_table(qpdf, file) + objects(qpdf, this), + xref_table(qpdf, objects, file) { } @@ -211,7 +212,7 @@ QPDF::~QPDF() // are reachable from this object to release their association with this QPDF. Direct objects // are not destroyed since they can be moved to other QPDF objects safely. - for (auto const& iter: m->obj_cache) { + for (auto const& iter: m->objects.obj_cache) { iter.second.object->disconnect(); if (iter.second.object->getTypeCode() != ::ot_null) { iter.second.object->destroy(); @@ -494,8 +495,8 @@ QPDF::getObjectCount() // be in obj_cache. fixDanglingReferences(); QPDFObjGen og; - if (!m->obj_cache.empty()) { - og = (*(m->obj_cache.rbegin())).first; + if (!m->objects.obj_cache.empty()) { + og = (*(m->objects.obj_cache.rbegin())).first; } return toS(og.getObj()); } @@ -575,12 +576,12 @@ QPDF::newStream(std::string const& data) QPDFObjectHandle QPDF::getObject(QPDFObjGen const& og) { - if (auto it = m->obj_cache.find(og); it != m->obj_cache.end()) { + if (auto it = m->objects.obj_cache.find(og); it != m->objects.obj_cache.end()) { return {it->second.object}; } else if (m->xref_table.initialized() && !m->xref_table.type(og)) { return QPDF_Null::create(); } else { - auto result = m->obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og)); + auto result = m->objects.obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og)); return {result.first->second.object}; } } diff --git a/libqpdf/QPDF_objects.cc b/libqpdf/QPDF_objects.cc index 75e2be4c..32f5f50d 100644 --- a/libqpdf/QPDF_objects.cc +++ b/libqpdf/QPDF_objects.cc @@ -1152,7 +1152,7 @@ QPDF::getAllObjects() // After fixDanglingReferences is called, all objects are in the object cache. fixDanglingReferences(); std::vector result; - for (auto const& iter: m->obj_cache) { + for (auto const& iter: m->objects.obj_cache) { result.push_back(newIndirect(iter.first, iter.second.object)); } return result; @@ -1537,7 +1537,7 @@ QPDFObject* QPDF::resolve(QPDFObjGen og) { if (!isUnresolved(og)) { - return m->obj_cache[og].object.get(); + return m->objects.obj_cache[og].object.get(); } if (m->resolving.count(og)) { @@ -1546,7 +1546,7 @@ QPDF::resolve(QPDFObjGen og) QTC::TC("qpdf", "QPDF recursion loop in resolve"); warn(damagedPDF("", "loop detected resolving object " + og.unparse(' '))); updateCache(og, QPDF_Null::create()); - return m->obj_cache[og].object.get(); + return m->objects.obj_cache[og].object.get(); } ResolveRecorder rr(this, og); @@ -1584,7 +1584,7 @@ QPDF::resolve(QPDFObjGen og) updateCache(og, QPDF_Null::create()); } - auto result(m->obj_cache[og].object); + auto result(m->objects.obj_cache[og].object); result->setDefaultDescription(this, og); return result.get(); } @@ -1690,23 +1690,23 @@ QPDF::updateCache(QPDFObjGen const& og, std::shared_ptr const& objec { object->setObjGen(this, og); if (isCached(og)) { - auto& cache = m->obj_cache[og]; + auto& cache = m->objects.obj_cache[og]; cache.object->assign(object); } else { - m->obj_cache[og] = ObjCache(object); + m->objects.obj_cache[og] = ObjCache(object); } } bool QPDF::isCached(QPDFObjGen const& og) { - return m->obj_cache.count(og) != 0; + return m->objects.obj_cache.count(og) != 0; } bool QPDF::isUnresolved(QPDFObjGen const& og) { - return !isCached(og) || m->obj_cache[og].object->isUnresolved(); + return !isCached(og) || m->objects.obj_cache[og].object->isUnresolved(); } QPDFObjGen @@ -1723,8 +1723,8 @@ QPDFObjectHandle QPDF::makeIndirectFromQPDFObject(std::shared_ptr const& obj) { QPDFObjGen next{nextObjGen()}; - m->obj_cache[next] = ObjCache(obj); - return newIndirect(next, m->obj_cache[next].object); + m->objects.obj_cache[next] = ObjCache(obj); + return newIndirect(next, m->objects.obj_cache[next].object); } std::shared_ptr @@ -1732,23 +1732,24 @@ QPDF::getObjectForParser(int id, int gen, bool parse_pdf) { // This method is called by the parser and therefore must not resolve any objects. auto og = QPDFObjGen(id, gen); - if (auto iter = m->obj_cache.find(og); iter != m->obj_cache.end()) { + if (auto iter = m->objects.obj_cache.find(og); iter != m->objects.obj_cache.end()) { return iter->second.object; } if (m->xref_table.type(og) || !m->xref_table.initialized()) { - return m->obj_cache.insert({og, QPDF_Unresolved::create(this, og)}).first->second.object; + return m->objects.obj_cache.insert({og, QPDF_Unresolved::create(this, og)}) + .first->second.object; } if (parse_pdf) { return QPDF_Null::create(); } - return m->obj_cache.insert({og, QPDF_Null::create(this, og)}).first->second.object; + return m->objects.obj_cache.insert({og, QPDF_Null::create(this, og)}).first->second.object; } std::shared_ptr QPDF::getObjectForJSON(int id, int gen) { auto og = QPDFObjGen(id, gen); - auto [it, inserted] = m->obj_cache.try_emplace(og); + auto [it, inserted] = m->objects.obj_cache.try_emplace(og); auto& obj = it->second.object; if (inserted) { obj = (m->xref_table.initialized() && !m->xref_table.type(og)) @@ -1771,11 +1772,11 @@ QPDF::replaceObject(QPDFObjGen const& og, QPDFObjectHandle oh) void QPDF::removeObject(QPDFObjGen og) { - if (auto cached = m->obj_cache.find(og); cached != m->obj_cache.end()) { + if (auto cached = m->objects.obj_cache.find(og); cached != m->objects.obj_cache.end()) { // Take care of any object handles that may be floating around. cached->second.object->assign(QPDF_Null::create()); cached->second.object->setObjGen(nullptr, QPDFObjGen()); - m->obj_cache.erase(cached); + m->objects.obj_cache.erase(cached); } } @@ -1785,7 +1786,7 @@ QPDF::swapObjects(QPDFObjGen const& og1, QPDFObjGen const& og2) // Force objects to be read from the input source if needed, then swap them in the cache. resolve(og1); resolve(og2); - m->obj_cache[og1].object->swapWith(m->obj_cache[og2].object); + m->objects.obj_cache[og1].object->swapWith(m->objects.obj_cache[og2].object); } size_t @@ -1797,7 +1798,7 @@ QPDF::tableSize() if (max_xref > 0) { --max_xref; } - auto max_obj = m->obj_cache.size() ? m->obj_cache.crbegin()->first.getObj() : 0; + auto max_obj = m->objects.obj_cache.size() ? m->objects.obj_cache.crbegin()->first.getObj() : 0; auto max_id = std::numeric_limits::max() - 1; if (max_obj >= max_id || max_xref >= max_id) { // Temporary fix. Long-term solution is @@ -1805,7 +1806,7 @@ QPDF::tableSize() // - xref table and obj cache to protect against insertion of impossibly large obj ids stopOnError("Impossibly large object id encountered."); } - if (max_obj < 1.1 * std::max(toI(m->obj_cache.size()), max_xref)) { + if (max_obj < 1.1 * std::max(toI(m->objects.obj_cache.size()), max_xref)) { return toS(++max_obj); } return toS(++max_xref); @@ -1844,7 +1845,7 @@ QPDF::getCompressibleObjGens() queue.push_back(m->xref_table.trailer()); std::vector result; if constexpr (std::is_same_v) { - result.reserve(m->obj_cache.size()); + result.reserve(m->objects.obj_cache.size()); } else if constexpr (std::is_same_v) { result.resize(max_obj + 1U, false); } else { @@ -1868,8 +1869,8 @@ QPDF::getCompressibleObjGens() // Check whether this is the current object. If not, remove it (which changes it into a // direct null and therefore stops us from revisiting it) and move on to the next object // in the queue. - auto upper = m->obj_cache.upper_bound(og); - if (upper != m->obj_cache.end() && upper->first.getObj() == og.getObj()) { + auto upper = m->objects.obj_cache.upper_bound(og); + if (upper != m->objects.obj_cache.end() && upper->first.getObj() == og.getObj()) { removeObject(og); continue; } diff --git a/libqpdf/qpdf/QPDF_objects.hh b/libqpdf/qpdf/QPDF_objects.hh new file mode 100644 index 00000000..02162d85 --- /dev/null +++ b/libqpdf/qpdf/QPDF_objects.hh @@ -0,0 +1,18 @@ +#ifndef QPDF_OBJECTS_HH +#define QPDF_OBJECTS_HH + +#include + +// The Objects class is responsible for keeping track of all objects belonging to a QPDF instance, +// including loading it from an input source when required. +class QPDF::Objects +{ + public: + Objects(QPDF& qpdf, QPDF::Members* m) + { + } + + std::map obj_cache; +}; // Objects + +#endif // QPDF_OBJECTS_HH diff --git a/libqpdf/qpdf/QPDF_private.hh b/libqpdf/qpdf/QPDF_private.hh index fa14fdc3..b658992f 100644 --- a/libqpdf/qpdf/QPDF_private.hh +++ b/libqpdf/qpdf/QPDF_private.hh @@ -3,13 +3,15 @@ #include +#include + #include // Xref_table encapsulates the pdf's xref table and trailer. class QPDF::Xref_table { public: - Xref_table(QPDF& qpdf, InputSource* const& file) : + Xref_table(QPDF& qpdf, QPDF::Objects& objects, InputSource* const& file) : qpdf(qpdf), file(file) { @@ -750,8 +752,8 @@ class QPDF::Members bool check_mode{false}; std::shared_ptr encp; std::string pdf_version; + Objects objects; Xref_table xref_table; - std::map obj_cache; std::set resolving; std::vector all_pages; bool invalid_page_found{false}; @@ -800,6 +802,12 @@ class QPDF::Members std::map> object_to_obj_users; }; +inline QPDF::Objects& +QPDF::objects() +{ + return m->objects; +} + // JobSetter class is restricted to QPDFJob. class QPDF::JobSetter {