2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 02:49:00 +00:00

Add new inner class to QPDF::Objects

This commit is contained in:
m-holger 2024-10-07 10:22:10 +01:00
parent 83897e8789
commit 2015f71c7d
5 changed files with 60 additions and 30 deletions

View File

@ -730,6 +730,7 @@ class QPDF
class Writer; class Writer;
class Resolver; class Resolver;
class StreamCopier; class StreamCopier;
class Objects;
class ParseGuard; class ParseGuard;
class Pipe; class Pipe;
class JobSetter; class JobSetter;
@ -757,6 +758,7 @@ class QPDF
class ResolveRecorder; class ResolveRecorder;
class JSONReactor; class JSONReactor;
inline Objects& objects();
void parse(char const* password); void parse(char const* password);
void inParse(bool); void inParse(bool);
void setLastObjectDescription(std::string const& description, QPDFObjGen const& og); void setLastObjectDescription(std::string const& description, QPDFObjGen const& og);

View File

@ -185,7 +185,8 @@ QPDF::Members::Members(QPDF& qpdf) :
file_sp(new InvalidInputSource(no_input_name)), file_sp(new InvalidInputSource(no_input_name)),
file(file_sp.get()), file(file_sp.get()),
encp(new EncryptionParameters), 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 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. // 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(); iter.second.object->disconnect();
if (iter.second.object->getTypeCode() != ::ot_null) { if (iter.second.object->getTypeCode() != ::ot_null) {
iter.second.object->destroy(); iter.second.object->destroy();
@ -494,8 +495,8 @@ QPDF::getObjectCount()
// be in obj_cache. // be in obj_cache.
fixDanglingReferences(); fixDanglingReferences();
QPDFObjGen og; QPDFObjGen og;
if (!m->obj_cache.empty()) { if (!m->objects.obj_cache.empty()) {
og = (*(m->obj_cache.rbegin())).first; og = (*(m->objects.obj_cache.rbegin())).first;
} }
return toS(og.getObj()); return toS(og.getObj());
} }
@ -575,12 +576,12 @@ QPDF::newStream(std::string const& data)
QPDFObjectHandle QPDFObjectHandle
QPDF::getObject(QPDFObjGen const& og) 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}; return {it->second.object};
} else if (m->xref_table.initialized() && !m->xref_table.type(og)) { } else if (m->xref_table.initialized() && !m->xref_table.type(og)) {
return QPDF_Null::create(); return QPDF_Null::create();
} else { } 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}; return {result.first->second.object};
} }
} }

View File

@ -1152,7 +1152,7 @@ QPDF::getAllObjects()
// After fixDanglingReferences is called, all objects are in the object cache. // After fixDanglingReferences is called, all objects are in the object cache.
fixDanglingReferences(); fixDanglingReferences();
std::vector<QPDFObjectHandle> result; std::vector<QPDFObjectHandle> 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)); result.push_back(newIndirect(iter.first, iter.second.object));
} }
return result; return result;
@ -1537,7 +1537,7 @@ QPDFObject*
QPDF::resolve(QPDFObjGen og) QPDF::resolve(QPDFObjGen og)
{ {
if (!isUnresolved(og)) { if (!isUnresolved(og)) {
return m->obj_cache[og].object.get(); return m->objects.obj_cache[og].object.get();
} }
if (m->resolving.count(og)) { if (m->resolving.count(og)) {
@ -1546,7 +1546,7 @@ QPDF::resolve(QPDFObjGen og)
QTC::TC("qpdf", "QPDF recursion loop in resolve"); QTC::TC("qpdf", "QPDF recursion loop in resolve");
warn(damagedPDF("", "loop detected resolving object " + og.unparse(' '))); warn(damagedPDF("", "loop detected resolving object " + og.unparse(' ')));
updateCache(og, QPDF_Null::create()); updateCache(og, QPDF_Null::create());
return m->obj_cache[og].object.get(); return m->objects.obj_cache[og].object.get();
} }
ResolveRecorder rr(this, og); ResolveRecorder rr(this, og);
@ -1584,7 +1584,7 @@ QPDF::resolve(QPDFObjGen og)
updateCache(og, QPDF_Null::create()); updateCache(og, QPDF_Null::create());
} }
auto result(m->obj_cache[og].object); auto result(m->objects.obj_cache[og].object);
result->setDefaultDescription(this, og); result->setDefaultDescription(this, og);
return result.get(); return result.get();
} }
@ -1690,23 +1690,23 @@ QPDF::updateCache(QPDFObjGen const& og, std::shared_ptr<QPDFObject> const& objec
{ {
object->setObjGen(this, og); object->setObjGen(this, og);
if (isCached(og)) { if (isCached(og)) {
auto& cache = m->obj_cache[og]; auto& cache = m->objects.obj_cache[og];
cache.object->assign(object); cache.object->assign(object);
} else { } else {
m->obj_cache[og] = ObjCache(object); m->objects.obj_cache[og] = ObjCache(object);
} }
} }
bool bool
QPDF::isCached(QPDFObjGen const& og) QPDF::isCached(QPDFObjGen const& og)
{ {
return m->obj_cache.count(og) != 0; return m->objects.obj_cache.count(og) != 0;
} }
bool bool
QPDF::isUnresolved(QPDFObjGen const& og) 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 QPDFObjGen
@ -1723,8 +1723,8 @@ QPDFObjectHandle
QPDF::makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj) QPDF::makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj)
{ {
QPDFObjGen next{nextObjGen()}; QPDFObjGen next{nextObjGen()};
m->obj_cache[next] = ObjCache(obj); m->objects.obj_cache[next] = ObjCache(obj);
return newIndirect(next, m->obj_cache[next].object); return newIndirect(next, m->objects.obj_cache[next].object);
} }
std::shared_ptr<QPDFObject> std::shared_ptr<QPDFObject>
@ -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. // This method is called by the parser and therefore must not resolve any objects.
auto og = QPDFObjGen(id, gen); 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; return iter->second.object;
} }
if (m->xref_table.type(og) || !m->xref_table.initialized()) { 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) { if (parse_pdf) {
return QPDF_Null::create(); 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<QPDFObject> std::shared_ptr<QPDFObject>
QPDF::getObjectForJSON(int id, int gen) QPDF::getObjectForJSON(int id, int gen)
{ {
auto og = QPDFObjGen(id, 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; auto& obj = it->second.object;
if (inserted) { if (inserted) {
obj = (m->xref_table.initialized() && !m->xref_table.type(og)) obj = (m->xref_table.initialized() && !m->xref_table.type(og))
@ -1771,11 +1772,11 @@ QPDF::replaceObject(QPDFObjGen const& og, QPDFObjectHandle oh)
void void
QPDF::removeObject(QPDFObjGen og) 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. // Take care of any object handles that may be floating around.
cached->second.object->assign(QPDF_Null::create()); cached->second.object->assign(QPDF_Null::create());
cached->second.object->setObjGen(nullptr, QPDFObjGen()); 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. // Force objects to be read from the input source if needed, then swap them in the cache.
resolve(og1); resolve(og1);
resolve(og2); 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 size_t
@ -1797,7 +1798,7 @@ QPDF::tableSize()
if (max_xref > 0) { if (max_xref > 0) {
--max_xref; --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<int>::max() - 1; auto max_id = std::numeric_limits<int>::max() - 1;
if (max_obj >= max_id || max_xref >= max_id) { if (max_obj >= max_id || max_xref >= max_id) {
// Temporary fix. Long-term solution is // 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 // - xref table and obj cache to protect against insertion of impossibly large obj ids
stopOnError("Impossibly large object id encountered."); 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_obj);
} }
return toS(++max_xref); return toS(++max_xref);
@ -1844,7 +1845,7 @@ QPDF::getCompressibleObjGens()
queue.push_back(m->xref_table.trailer()); queue.push_back(m->xref_table.trailer());
std::vector<T> result; std::vector<T> result;
if constexpr (std::is_same_v<T, QPDFObjGen>) { if constexpr (std::is_same_v<T, QPDFObjGen>) {
result.reserve(m->obj_cache.size()); result.reserve(m->objects.obj_cache.size());
} else if constexpr (std::is_same_v<T, bool>) { } else if constexpr (std::is_same_v<T, bool>) {
result.resize(max_obj + 1U, false); result.resize(max_obj + 1U, false);
} else { } else {
@ -1868,8 +1869,8 @@ QPDF::getCompressibleObjGens()
// Check whether this is the current object. If not, remove it (which changes it into a // 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 // direct null and therefore stops us from revisiting it) and move on to the next object
// in the queue. // in the queue.
auto upper = m->obj_cache.upper_bound(og); auto upper = m->objects.obj_cache.upper_bound(og);
if (upper != m->obj_cache.end() && upper->first.getObj() == og.getObj()) { if (upper != m->objects.obj_cache.end() && upper->first.getObj() == og.getObj()) {
removeObject(og); removeObject(og);
continue; continue;
} }

View File

@ -0,0 +1,18 @@
#ifndef QPDF_OBJECTS_HH
#define QPDF_OBJECTS_HH
#include <qpdf/QPDF.hh>
// 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<QPDFObjGen, ObjCache> obj_cache;
}; // Objects
#endif // QPDF_OBJECTS_HH

View File

@ -3,13 +3,15 @@
#include <qpdf/QPDF.hh> #include <qpdf/QPDF.hh>
#include <qpdf/QPDF_objects.hh>
#include <variant> #include <variant>
// Xref_table encapsulates the pdf's xref table and trailer. // Xref_table encapsulates the pdf's xref table and trailer.
class QPDF::Xref_table class QPDF::Xref_table
{ {
public: public:
Xref_table(QPDF& qpdf, InputSource* const& file) : Xref_table(QPDF& qpdf, QPDF::Objects& objects, InputSource* const& file) :
qpdf(qpdf), qpdf(qpdf),
file(file) file(file)
{ {
@ -750,8 +752,8 @@ class QPDF::Members
bool check_mode{false}; bool check_mode{false};
std::shared_ptr<EncryptionParameters> encp; std::shared_ptr<EncryptionParameters> encp;
std::string pdf_version; std::string pdf_version;
Objects objects;
Xref_table xref_table; Xref_table xref_table;
std::map<QPDFObjGen, ObjCache> obj_cache;
std::set<QPDFObjGen> resolving; std::set<QPDFObjGen> resolving;
std::vector<QPDFObjectHandle> all_pages; std::vector<QPDFObjectHandle> all_pages;
bool invalid_page_found{false}; bool invalid_page_found{false};
@ -800,6 +802,12 @@ class QPDF::Members
std::map<QPDFObjGen, std::set<ObjUser>> object_to_obj_users; std::map<QPDFObjGen, std::set<ObjUser>> object_to_obj_users;
}; };
inline QPDF::Objects&
QPDF::objects()
{
return m->objects;
}
// JobSetter class is restricted to QPDFJob. // JobSetter class is restricted to QPDFJob.
class QPDF::JobSetter class QPDF::JobSetter
{ {