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

Move private methods in QPDF_objects to QPDF::Objects

This commit is contained in:
m-holger 2024-10-07 13:42:19 +01:00
parent 2015f71c7d
commit a3f693c8f9
7 changed files with 205 additions and 183 deletions

View File

@ -758,37 +758,15 @@ class QPDF
class ResolveRecorder;
class JSONReactor;
inline Objects& objects();
inline Objects& objects() noexcept;
inline Objects const& objects() const noexcept;
void parse(char const* password);
void inParse(bool);
void setLastObjectDescription(std::string const& description, QPDFObjGen const& og);
QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og);
void readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
void validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
QPDFObjectHandle readObjectInStream(std::shared_ptr<InputSource>& input, int obj);
size_t recoverStreamLength(
std::shared_ptr<InputSource> input, QPDFObjGen const& og, qpdf_offset_t stream_offset);
QPDFTokenizer::Token readToken(InputSource&, size_t max_len = 0);
QPDFObjectHandle readObjectAtOffset(
bool attempt_recovery,
qpdf_offset_t offset,
std::string const& description,
QPDFObjGen exp_og,
QPDFObjGen& og,
bool skip_cache_if_in_xref);
QPDFObject* resolve(QPDFObjGen og);
void resolveObjectsInStream(int obj_stream_number);
void stopOnError(std::string const& message);
QPDFObjGen nextObjGen();
QPDFObjectHandle newIndirect(QPDFObjGen const&, std::shared_ptr<QPDFObject> const&);
QPDFObjectHandle makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj);
bool isCached(QPDFObjGen const& og);
bool isUnresolved(QPDFObjGen const& og);
std::shared_ptr<QPDFObject> getObjectForParser(int id, int gen, bool parse_pdf);
std::shared_ptr<QPDFObject> getObjectForJSON(int id, int gen);
void removeObject(QPDFObjGen og);
void updateCache(QPDFObjGen const& og, std::shared_ptr<QPDFObject> const& object);
static QPDFExc damagedPDF(
InputSource& input,
std::string const& object,
@ -834,7 +812,6 @@ class QPDF
QPDFWriter::ObjTable const& obj,
std::function<int(QPDFObjectHandle&)> skip_stream_parameters);
void optimize(Xref_table const& obj);
size_t tableSize();
// Get lists of all objects in order according to the part of a linearized file that they belong
// to.
@ -854,12 +831,6 @@ class QPDF
int& O,
bool compressed);
// Get a list of objects that would be permitted in an object stream.
template <typename T>
std::vector<T> getCompressibleObjGens();
std::vector<QPDFObjGen> getCompressibleObjVector();
std::vector<bool> getCompressibleObjSet();
// methods to support page handling
void getAllPagesInternal(

View File

@ -535,26 +535,26 @@ QPDF::makeIndirectObject(QPDFObjectHandle oh)
if (!oh) {
throw std::logic_error("attempted to make an uninitialized QPDFObjectHandle indirect");
}
return makeIndirectFromQPDFObject(oh.getObj());
return m->objects.make_indirect(oh.getObj());
}
QPDFObjectHandle
QPDF::newReserved()
{
return makeIndirectFromQPDFObject(QPDF_Reserved::create());
return m->objects.make_indirect(QPDF_Reserved::create());
}
QPDFObjectHandle
QPDF::newIndirectNull()
{
return makeIndirectFromQPDFObject(QPDF_Null::create());
return m->objects.make_indirect(QPDF_Null::create());
}
QPDFObjectHandle
QPDF::newStream()
{
return makeIndirectFromQPDFObject(
QPDF_Stream::create(this, nextObjGen(), QPDFObjectHandle::newDictionary(), 0, 0));
return m->objects.make_indirect(
QPDF_Stream::create(this, m->objects.next_id(), QPDFObjectHandle::newDictionary(), 0, 0));
}
QPDFObjectHandle

View File

@ -536,7 +536,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value)
} else if (is_obj_key(key, obj, gen)) {
this->cur_object = key;
if (setNextStateIfDictionary(key, value, st_object_top)) {
next_obj = pdf.getObjectForJSON(obj, gen);
next_obj = pdf.objects().get_for_json(obj, gen);
}
} else {
QTC::TC("qpdf", "QPDF_json bad object key");
@ -740,7 +740,7 @@ QPDF::JSONReactor::makeObject(JSON const& value)
int gen = 0;
std::string str;
if (is_indirect_object(str_v, obj, gen)) {
result = pdf.getObjectForJSON(obj, gen);
result = pdf.objects().get_for_json(obj, gen);
} else if (is_unicode_string(str_v, str)) {
result = QPDFObjectHandle::newUnicodeString(str);
} else if (is_binary_string(str_v, str)) {

View File

@ -287,7 +287,7 @@ QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length)
{
QPDFObjGen og;
QPDFObjectHandle H =
readObjectAtOffset(false, offset, "linearization hint stream", QPDFObjGen(0, 0), og, false);
objects().read(false, offset, "linearization hint stream", QPDFObjGen(0, 0), og, false);
qpdf_offset_t min_end_offset = m->xref_table.end_before_space(og);
qpdf_offset_t max_end_offset = m->xref_table.end_after_space(og);
if (!H.isStream()) {

View File

@ -744,7 +744,7 @@ QPDF::Xref_table::read_stream(qpdf_offset_t xref_offset)
QPDFObjGen x_og;
QPDFObjectHandle xref_obj;
try {
xref_obj = qpdf.readObjectAtOffset(
xref_obj = qpdf.objects().read(
false, xref_offset, "xref stream", QPDFObjGen(0, 0), x_og, true);
} catch (QPDFExc&) {
// ignore -- report error below
@ -1135,8 +1135,8 @@ QPDF::Xref_table::resolve()
for (auto& item: table) {
++i;
if (item.type()) {
if (qpdf.isUnresolved(QPDFObjGen(i, item.gen()))) {
qpdf.resolve(QPDFObjGen(i, item.gen()));
if (objects.unresolved(QPDFObjGen(i, item.gen()))) {
objects.resolve(QPDFObjGen(i, item.gen()));
if (may_change && reconstructed_) {
return false;
}
@ -1177,40 +1177,41 @@ QPDF::Xref_table::read_trailer()
}
QPDFObjectHandle
QPDF::readObject(std::string const& description, QPDFObjGen og)
QPDF::Objects::read_object(std::string const& description, QPDFObjGen og)
{
setLastObjectDescription(description, og);
qpdf.setLastObjectDescription(description, og);
qpdf_offset_t offset = m->file->tell();
bool empty = false;
StringDecrypter decrypter{this, og};
StringDecrypter decrypter{&qpdf, og};
StringDecrypter* decrypter_ptr = m->encp->encrypted ? &decrypter : nullptr;
auto object =
QPDFParser(*m->file, m->last_object_description, m->tokenizer, decrypter_ptr, this, true)
QPDFParser(*m->file, m->last_object_description, m->tokenizer, decrypter_ptr, &qpdf, true)
.parse(empty, false);
if (empty) {
// Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
// actual PDF files and Adobe Reader appears to ignore them.
warn(damagedPDF(*m->file, m->file->getLastOffset(), "empty object treated as null"));
qpdf.warn(
qpdf.damagedPDF(*m->file, m->file->getLastOffset(), "empty object treated as null"));
return object;
}
auto token = readToken(*m->file);
auto token = qpdf.readToken(*m->file);
if (object.isDictionary() && token.isWord("stream")) {
readStream(object, og, offset);
token = readToken(*m->file);
read_stream(object, og, offset);
token = qpdf.readToken(*m->file);
}
if (!token.isWord("endobj")) {
QTC::TC("qpdf", "QPDF err expected endobj");
warn(damagedPDF("expected endobj"));
qpdf.warn(qpdf.damagedPDF("expected endobj"));
}
return object;
}
// After reading stream dictionary and stream keyword, read rest of stream.
void
QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset)
QPDF::Objects::read_stream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset)
{
validateStreamLineEnd(object, og, offset);
validate_stream_line_end(object, og, offset);
// Must get offset before accessing any additional objects since resolving a previously
// unresolved indirect object will change file position.
@ -1223,33 +1224,34 @@ QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset)
if (!length_obj.isInteger()) {
if (length_obj.isNull()) {
QTC::TC("qpdf", "QPDF stream without length");
throw damagedPDF(offset, "stream dictionary lacks /Length key");
throw qpdf.damagedPDF(offset, "stream dictionary lacks /Length key");
}
QTC::TC("qpdf", "QPDF stream length not integer");
throw damagedPDF(offset, "/Length key in stream dictionary is not an integer");
throw qpdf.damagedPDF(offset, "/Length key in stream dictionary is not an integer");
}
length = toS(length_obj.getUIntValue());
// Seek in two steps to avoid potential integer overflow
m->file->seek(stream_offset, SEEK_SET);
m->file->seek(toO(length), SEEK_CUR);
if (!readToken(*m->file).isWord("endstream")) {
if (!qpdf.readToken(*m->file).isWord("endstream")) {
QTC::TC("qpdf", "QPDF missing endstream");
throw damagedPDF("expected endstream");
throw qpdf.damagedPDF("expected endstream");
}
} catch (QPDFExc& e) {
if (m->attempt_recovery) {
warn(e);
length = recoverStreamLength(m->file_sp, og, stream_offset);
qpdf.warn(e);
length = recover_stream_length(m->file_sp, og, stream_offset);
} else {
throw;
}
}
object = {QPDF_Stream::create(this, og, object, stream_offset, length)};
object = {QPDF_Stream::create(&qpdf, og, object, stream_offset, length)};
}
void
QPDF::validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset)
QPDF::Objects::validate_stream_line_end(
QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset)
{
// The PDF specification states that the word "stream" should be followed by either a carriage
// return and a newline or by a newline alone. It specifically disallowed following it by a
@ -1281,7 +1283,7 @@ QPDF::validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset
// stream data in spite of not having seen a newline.
QTC::TC("qpdf", "QPDF stream with CR only");
m->file->unreadCh(ch);
warn(damagedPDF(
qpdf.warn(qpdf.damagedPDF(
m->file->tell(), "stream keyword followed by carriage return only"));
}
}
@ -1290,28 +1292,29 @@ QPDF::validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset
if (!QUtil::is_space(ch)) {
QTC::TC("qpdf", "QPDF stream without newline");
m->file->unreadCh(ch);
warn(damagedPDF(
qpdf.warn(qpdf.damagedPDF(
m->file->tell(), "stream keyword not followed by proper line terminator"));
return;
}
warn(damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace"));
qpdf.warn(
qpdf.damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace"));
}
}
QPDFObjectHandle
QPDF::readObjectInStream(std::shared_ptr<InputSource>& input, int obj)
QPDF::Objects::readObjectInStream(std::shared_ptr<InputSource>& input, int obj)
{
m->last_object_description.erase(7); // last_object_description starts with "object "
m->last_object_description += std::to_string(obj);
m->last_object_description += " 0";
bool empty = false;
auto object = QPDFParser(*input, m->last_object_description, m->tokenizer, nullptr, this, true)
auto object = QPDFParser(*input, m->last_object_description, m->tokenizer, nullptr, &qpdf, true)
.parse(empty, false);
if (empty) {
// Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
// actual PDF files and Adobe Reader appears to ignore them.
warn(damagedPDF(*input, input->getLastOffset(), "empty object treated as null"));
qpdf.warn(qpdf.damagedPDF(*input, input->getLastOffset(), "empty object treated as null"));
}
return object;
}
@ -1329,18 +1332,18 @@ QPDF::findEndstream()
}
size_t
QPDF::recoverStreamLength(
std::shared_ptr<InputSource> input, QPDFObjGen const& og, qpdf_offset_t stream_offset)
QPDF::Objects::recover_stream_length(
std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset)
{
// Try to reconstruct stream length by looking for endstream or endobj
warn(damagedPDF(*input, stream_offset, "attempting to recover stream length"));
qpdf.warn(qpdf.damagedPDF(*input, stream_offset, "attempting to recover stream length"));
PatternFinder ef(*this, &QPDF::findEndstream);
PatternFinder ef(qpdf, &QPDF::findEndstream);
size_t length = 0;
if (m->file->findFirst("end", stream_offset, 0, ef)) {
length = toS(m->file->tell() - stream_offset);
// Reread endstream but, if it was endobj, don't skip that.
QPDFTokenizer::Token t = readToken(*m->file);
QPDFTokenizer::Token t = qpdf.readToken(*m->file);
if (t.getValue() == "endobj") {
m->file->seek(m->file->getLastOffset(), SEEK_SET);
}
@ -1361,10 +1364,10 @@ QPDF::recoverStreamLength(
}
if (length == 0) {
warn(damagedPDF(
qpdf.warn(qpdf.damagedPDF(
*input, stream_offset, "unable to recover stream data; treating stream as empty"));
} else {
warn(damagedPDF(
qpdf.warn(qpdf.damagedPDF(
*input, stream_offset, "recovered stream length: " + std::to_string(length)));
}
@ -1373,7 +1376,7 @@ QPDF::recoverStreamLength(
}
QPDFObjectHandle
QPDF::readObjectAtOffset(
QPDF::Objects::read(
bool try_recovery,
qpdf_offset_t offset,
std::string const& description,
@ -1392,7 +1395,7 @@ QPDF::readObjectAtOffset(
check_og = false;
try_recovery = false;
}
setLastObjectDescription(description, exp_og);
qpdf.setLastObjectDescription(description, exp_og);
if (!m->attempt_recovery) {
try_recovery = false;
@ -1404,49 +1407,49 @@ QPDF::readObjectAtOffset(
// these.
if (offset == 0) {
QTC::TC("qpdf", "QPDF bogus 0 offset", 0);
warn(damagedPDF(0, "object has offset 0"));
qpdf.warn(qpdf.damagedPDF(0, "object has offset 0"));
return QPDFObjectHandle::newNull();
}
m->file->seek(offset, SEEK_SET);
try {
QPDFTokenizer::Token tobjid = readToken(*m->file);
QPDFTokenizer::Token tobjid = qpdf.readToken(*m->file);
bool objidok = tobjid.isInteger();
QTC::TC("qpdf", "QPDF check objid", objidok ? 1 : 0);
if (!objidok) {
QTC::TC("qpdf", "QPDF expected n n obj");
throw damagedPDF(offset, "expected n n obj");
throw qpdf.damagedPDF(offset, "expected n n obj");
}
QPDFTokenizer::Token tgen = readToken(*m->file);
QPDFTokenizer::Token tgen = qpdf.readToken(*m->file);
bool genok = tgen.isInteger();
QTC::TC("qpdf", "QPDF check generation", genok ? 1 : 0);
if (!genok) {
throw damagedPDF(offset, "expected n n obj");
throw qpdf.damagedPDF(offset, "expected n n obj");
}
QPDFTokenizer::Token tobj = readToken(*m->file);
QPDFTokenizer::Token tobj = qpdf.readToken(*m->file);
bool objok = tobj.isWord("obj");
QTC::TC("qpdf", "QPDF check obj", objok ? 1 : 0);
if (!objok) {
throw damagedPDF(offset, "expected n n obj");
throw qpdf.damagedPDF(offset, "expected n n obj");
}
int objid = QUtil::string_to_int(tobjid.getValue().c_str());
int generation = QUtil::string_to_int(tgen.getValue().c_str());
og = QPDFObjGen(objid, generation);
if (objid == 0) {
QTC::TC("qpdf", "QPDF object id 0");
throw damagedPDF(offset, "object with ID 0");
throw qpdf.damagedPDF(offset, "object with ID 0");
}
if (check_og && (exp_og != og)) {
QTC::TC("qpdf", "QPDF err wrong objid/generation");
QPDFExc e = damagedPDF(offset, "expected " + exp_og.unparse(' ') + " obj");
QPDFExc e = qpdf.damagedPDF(offset, "expected " + exp_og.unparse(' ') + " obj");
if (try_recovery) {
// Will be retried below
throw e;
} else {
// We can try reading the object anyway even if the ID doesn't match.
warn(e);
qpdf.warn(e);
}
}
} catch (QPDFExc& e) {
@ -1455,11 +1458,10 @@ QPDF::readObjectAtOffset(
m->xref_table.reconstruct(e);
if (m->xref_table.type(exp_og) == 1) {
QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset");
return readObjectAtOffset(
false, m->xref_table.offset(exp_og), description, exp_og, og, false);
return read(false, m->xref_table.offset(exp_og), description, exp_og, og, false);
} else {
QTC::TC("qpdf", "QPDF object gone after xref reconstruction");
warn(damagedPDF(
qpdf.warn(qpdf.damagedPDF(
"",
0,
("object " + exp_og.unparse(' ') +
@ -1471,9 +1473,9 @@ QPDF::readObjectAtOffset(
}
}
QPDFObjectHandle oh = readObject(description, og);
QPDFObjectHandle oh = read_object(description, og);
if (isUnresolved(og)) {
if (unresolved(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 here
// through resolve).
@ -1492,7 +1494,7 @@ QPDF::readObjectAtOffset(
break;
}
} else {
throw damagedPDF(m->file->tell(), "EOF after endobj");
throw qpdf.damagedPDF(m->file->tell(), "EOF after endobj");
}
}
qpdf_offset_t end_after_space = m->file->tell();
@ -1526,7 +1528,7 @@ QPDF::readObjectAtOffset(
} else {
m->xref_table.linearization_offsets(
toS(og.getObj()), end_before_space, end_after_space);
updateCache(og, oh.getObj());
update_table(og, oh.getObj());
}
}
@ -1534,21 +1536,21 @@ QPDF::readObjectAtOffset(
}
QPDFObject*
QPDF::resolve(QPDFObjGen og)
QPDF::Objects::resolve(QPDFObjGen og)
{
if (!isUnresolved(og)) {
return m->objects.obj_cache[og].object.get();
if (!unresolved(og)) {
return obj_cache[og].object.get();
}
if (m->resolving.count(og)) {
// This can happen if an object references itself directly or indirectly in some key that
// has to be resolved during object parsing, such as stream length.
QTC::TC("qpdf", "QPDF recursion loop in resolve");
warn(damagedPDF("", "loop detected resolving object " + og.unparse(' ')));
updateCache(og, QPDF_Null::create());
return m->objects.obj_cache[og].object.get();
qpdf.warn(qpdf.damagedPDF("", "loop detected resolving object " + og.unparse(' ')));
update_table(og, QPDF_Null::create());
return obj_cache[og].object.get();
}
ResolveRecorder rr(this, og);
ResolveRecorder rr(&qpdf, og);
try {
switch (m->xref_table.type(og)) {
@ -1558,8 +1560,7 @@ QPDF::resolve(QPDFObjGen og)
{
// Object stored in cache by readObjectAtOffset
QPDFObjGen a_og;
QPDFObjectHandle oh =
readObjectAtOffset(true, m->xref_table.offset(og), "", og, a_og, false);
QPDFObjectHandle oh = read(true, m->xref_table.offset(og), "", og, a_og, false);
}
break;
@ -1568,50 +1569,50 @@ QPDF::resolve(QPDFObjGen og)
break;
default:
throw damagedPDF(
throw qpdf.damagedPDF(
"", 0, ("object " + og.unparse('/') + " has unexpected xref entry type"));
}
} catch (QPDFExc& e) {
warn(e);
qpdf.warn(e);
} catch (std::exception& e) {
warn(damagedPDF(
qpdf.warn(qpdf.damagedPDF(
"", 0, ("object " + og.unparse('/') + ": error reading object: " + e.what())));
}
if (isUnresolved(og)) {
if (unresolved(og)) {
// PDF spec says unknown objects resolve to the null object.
QTC::TC("qpdf", "QPDF resolve failure to null");
updateCache(og, QPDF_Null::create());
update_table(og, QPDF_Null::create());
}
auto result(m->objects.obj_cache[og].object);
result->setDefaultDescription(this, og);
auto result(obj_cache[og].object);
result->setDefaultDescription(&qpdf, og);
return result.get();
}
void
QPDF::resolveObjectsInStream(int obj_stream_number)
QPDF::Objects::resolveObjectsInStream(int obj_stream_number)
{
if (m->resolved_object_streams.count(obj_stream_number)) {
return;
}
m->resolved_object_streams.insert(obj_stream_number);
// Force resolution of object stream
QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0);
QPDFObjectHandle obj_stream = qpdf.getObject(obj_stream_number, 0);
if (!obj_stream.isStream()) {
throw damagedPDF(
throw qpdf.damagedPDF(
"supposed object stream " + std::to_string(obj_stream_number) + " is not a stream");
}
QPDFObjectHandle dict = obj_stream.getDict();
if (!dict.isDictionaryOfType("/ObjStm")) {
QTC::TC("qpdf", "QPDF ERR object stream with wrong type");
warn(damagedPDF(
qpdf.warn(qpdf.damagedPDF(
"supposed object stream " + std::to_string(obj_stream_number) + " has wrong type"));
}
if (!(dict.getKey("/N").isInteger() && dict.getKey("/First").isInteger())) {
throw damagedPDF(
throw qpdf.damagedPDF(
("object stream " + std::to_string(obj_stream_number) + " has incorrect keys"));
}
@ -1629,8 +1630,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
qpdf_offset_t last_offset = -1;
for (int i = 0; i < n; ++i) {
QPDFTokenizer::Token tnum = readToken(*input);
QPDFTokenizer::Token toffset = readToken(*input);
QPDFTokenizer::Token tnum = qpdf.readToken(*input);
QPDFTokenizer::Token toffset = qpdf.readToken(*input);
if (!(tnum.isInteger() && toffset.isInteger())) {
throw damagedPDF(
*input,
@ -1646,7 +1647,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
}
if (num == obj_stream_number) {
QTC::TC("qpdf", "QPDF ignore self-referential object stream");
warn(damagedPDF(
qpdf.warn(damagedPDF(
*input,
m->last_object_description,
input->getLastOffset(),
@ -1678,7 +1679,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
int offset = iter.second;
input->seek(offset, SEEK_SET);
QPDFObjectHandle oh = readObjectInStream(input, iter.first);
updateCache(og, oh.getObj());
update_table(og, oh.getObj());
} else {
QTC::TC("qpdf", "QPDF not caching overridden objstm object");
}
@ -1686,33 +1687,33 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
}
void
QPDF::updateCache(QPDFObjGen const& og, std::shared_ptr<QPDFObject> const& object)
QPDF::Objects::update_table(QPDFObjGen og, const std::shared_ptr<QPDFObject>& object)
{
object->setObjGen(this, og);
if (isCached(og)) {
auto& cache = m->objects.obj_cache[og];
object->setObjGen(&qpdf, og);
if (cached(og)) {
auto& cache = obj_cache[og];
cache.object->assign(object);
} else {
m->objects.obj_cache[og] = ObjCache(object);
obj_cache[og] = ObjCache(object);
}
}
bool
QPDF::isCached(QPDFObjGen const& og)
QPDF::Objects::cached(QPDFObjGen og)
{
return m->objects.obj_cache.count(og) != 0;
return obj_cache.count(og) != 0;
}
bool
QPDF::isUnresolved(QPDFObjGen const& og)
QPDF::Objects::unresolved(QPDFObjGen og)
{
return !isCached(og) || m->objects.obj_cache[og].object->isUnresolved();
return !cached(og) || obj_cache[og].object->isUnresolved();
}
QPDFObjGen
QPDF::nextObjGen()
QPDF::Objects::next_id()
{
int max_objid = toI(getObjectCount());
int max_objid = toI(qpdf.getObjectCount());
if (max_objid == std::numeric_limits<int>::max()) {
throw std::range_error("max object id is too high to create new objects");
}
@ -1720,41 +1721,40 @@ QPDF::nextObjGen()
}
QPDFObjectHandle
QPDF::makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj)
QPDF::Objects::make_indirect(std::shared_ptr<QPDFObject> const& obj)
{
QPDFObjGen next{nextObjGen()};
m->objects.obj_cache[next] = ObjCache(obj);
return newIndirect(next, m->objects.obj_cache[next].object);
QPDFObjGen next{next_id()};
obj_cache[next] = ObjCache(obj);
return qpdf.newIndirect(next, obj_cache[next].object);
}
std::shared_ptr<QPDFObject>
QPDF::getObjectForParser(int id, int gen, bool parse_pdf)
QPDF::Objects::get_for_parser(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->objects.obj_cache.find(og); iter != m->objects.obj_cache.end()) {
if (auto iter = obj_cache.find(og); iter != obj_cache.end()) {
return iter->second.object;
}
if (m->xref_table.type(og) || !m->xref_table.initialized()) {
return m->objects.obj_cache.insert({og, QPDF_Unresolved::create(this, og)})
.first->second.object;
return obj_cache.insert({og, QPDF_Unresolved::create(&qpdf, og)}).first->second.object;
}
if (parse_pdf) {
return QPDF_Null::create();
}
return m->objects.obj_cache.insert({og, QPDF_Null::create(this, og)}).first->second.object;
return obj_cache.insert({og, QPDF_Null::create(&qpdf, og)}).first->second.object;
}
std::shared_ptr<QPDFObject>
QPDF::getObjectForJSON(int id, int gen)
QPDF::Objects::get_for_json(int id, int gen)
{
auto og = QPDFObjGen(id, gen);
auto [it, inserted] = m->objects.obj_cache.try_emplace(og);
auto [it, inserted] = obj_cache.try_emplace(og);
auto& obj = it->second.object;
if (inserted) {
obj = (m->xref_table.initialized() && !m->xref_table.type(og))
? QPDF_Null::create(this, og)
: QPDF_Unresolved::create(this, og);
? QPDF_Null::create(&qpdf, og)
: QPDF_Unresolved::create(&qpdf, og);
}
return obj;
}
@ -1766,17 +1766,17 @@ QPDF::replaceObject(QPDFObjGen const& og, QPDFObjectHandle oh)
QTC::TC("qpdf", "QPDF replaceObject called with indirect object");
throw std::logic_error("QPDF::replaceObject called with indirect object handle");
}
updateCache(og, oh.getObj());
objects().update_table(og, oh.getObj());
}
void
QPDF::removeObject(QPDFObjGen og)
QPDF::Objects::erase(QPDFObjGen og)
{
if (auto cached = m->objects.obj_cache.find(og); cached != m->objects.obj_cache.end()) {
if (auto cached = obj_cache.find(og); cached != 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->objects.obj_cache.erase(cached);
obj_cache.erase(cached);
}
}
@ -1784,13 +1784,13 @@ void
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->objects.resolve(og1);
m->objects.resolve(og2);
m->objects.obj_cache[og1].object->swapWith(m->objects.obj_cache[og2].object);
}
size_t
QPDF::tableSize()
QPDF::Objects::table_size()
{
// If obj_cache is dense, accommodate all object in tables,else accommodate only original
// objects.
@ -1798,35 +1798,35 @@ QPDF::tableSize()
if (max_xref > 0) {
--max_xref;
}
auto max_obj = m->objects.obj_cache.size() ? m->objects.obj_cache.crbegin()->first.getObj() : 0;
auto max_obj = obj_cache.size() ? obj_cache.crbegin()->first.getObj() : 0;
auto max_id = std::numeric_limits<int>::max() - 1;
if (max_obj >= max_id || max_xref >= max_id) {
// Temporary fix. Long-term solution is
// - QPDFObjGen to enforce objgens are valid and sensible
// - xref table and obj cache to protect against insertion of impossibly large obj ids
stopOnError("Impossibly large object id encountered.");
qpdf.stopOnError("Impossibly large object id encountered.");
}
if (max_obj < 1.1 * std::max(toI(m->objects.obj_cache.size()), max_xref)) {
if (max_obj < 1.1 * std::max(toI(obj_cache.size()), max_xref)) {
return toS(++max_obj);
}
return toS(++max_xref);
}
std::vector<QPDFObjGen>
QPDF::getCompressibleObjVector()
QPDF::Objects::compressible_vector()
{
return getCompressibleObjGens<QPDFObjGen>();
return compressible<QPDFObjGen>();
}
std::vector<bool>
QPDF::getCompressibleObjSet()
QPDF::Objects::compressible_set()
{
return getCompressibleObjGens<bool>();
return compressible<bool>();
}
template <typename T>
std::vector<T>
QPDF::getCompressibleObjGens()
QPDF::Objects::compressible()
{
// Return a list of objects that are allowed to be in object streams. Walk through the objects
// by traversing the document from the root, including a traversal of the pages tree. This
@ -1838,14 +1838,14 @@ QPDF::getCompressibleObjGens()
QPDFObjectHandle encryption_dict = m->xref_table.trailer().getKey("/Encrypt");
QPDFObjGen encryption_dict_og = encryption_dict.getObjGen();
const size_t max_obj = getObjectCount();
const size_t max_obj = qpdf.getObjectCount();
std::vector<bool> visited(max_obj, false);
std::vector<QPDFObjectHandle> queue;
queue.reserve(512);
queue.push_back(m->xref_table.trailer());
std::vector<T> result;
if constexpr (std::is_same_v<T, QPDFObjGen>) {
result.reserve(m->objects.obj_cache.size());
result.reserve(obj_cache.size());
} else if constexpr (std::is_same_v<T, bool>) {
result.resize(max_obj + 1U, false);
} else {
@ -1869,9 +1869,9 @@ 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->objects.obj_cache.upper_bound(og);
if (upper != m->objects.obj_cache.end() && upper->first.getObj() == og.getObj()) {
removeObject(og);
auto upper = obj_cache.upper_bound(og);
if (upper != obj_cache.end() && upper->first.getObj() == og.getObj()) {
erase(og);
continue;
}

View File

@ -8,11 +8,54 @@
class QPDF::Objects
{
public:
Objects(QPDF& qpdf, QPDF::Members* m)
Objects(QPDF& qpdf, QPDF::Members* m) :
qpdf(qpdf),
m(m)
{
}
std::map<QPDFObjGen, ObjCache> obj_cache;
QPDFObjectHandle readObjectInStream(std::shared_ptr<InputSource>& input, int obj);
QPDFObjectHandle read(
bool attempt_recovery,
qpdf_offset_t offset,
std::string const& description,
QPDFObjGen exp_og,
QPDFObjGen& og,
bool skip_cache_if_in_xref);
QPDFObject* resolve(QPDFObjGen og);
void resolveObjectsInStream(int obj_stream_number);
void update_table(QPDFObjGen og, std::shared_ptr<QPDFObject> const& object);
QPDFObjGen next_id();
QPDFObjectHandle make_indirect(std::shared_ptr<QPDFObject> const& obj);
std::shared_ptr<QPDFObject> get_for_parser(int id, int gen, bool parse_pdf);
std::shared_ptr<QPDFObject> get_for_json(int id, int gen);
// Get a list of objects that would be permitted in an object stream.
template <typename T>
std::vector<T> compressible();
std::vector<QPDFObjGen> compressible_vector();
std::vector<bool> compressible_set();
// Used by QPDFWriter to determine the vector part of its object tables.
size_t table_size();
private:
friend class QPDF::Xref_table;
void erase(QPDFObjGen og);
bool cached(QPDFObjGen og);
bool unresolved(QPDFObjGen og);
QPDFObjectHandle read_object(std::string const& description, QPDFObjGen og);
void read_stream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
void validate_stream_line_end(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
size_t recover_stream_length(
std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset);
QPDF& qpdf;
QPDF::Members* m;
}; // Objects
#endif // QPDF_OBJECTS_HH

View File

@ -13,6 +13,7 @@ class QPDF::Xref_table
public:
Xref_table(QPDF& qpdf, QPDF::Objects& objects, InputSource* const& file) :
qpdf(qpdf),
objects(objects),
file(file)
{
tokenizer.allowEOF();
@ -340,6 +341,7 @@ class QPDF::Xref_table
}
QPDF& qpdf;
QPDF::Objects& objects;
InputSource* const& file;
QPDFTokenizer tokenizer;
@ -362,21 +364,6 @@ class QPDF::Xref_table
qpdf_offset_t first_item_offset_{0}; // actual value from file
};
// The Resolver class is restricted to QPDFObject so that only it can resolve indirect
// references.
class QPDF::Resolver
{
friend class QPDFObject;
friend class QPDF_Unresolved;
private:
static QPDFObject*
resolved(QPDF* qpdf, QPDFObjGen og)
{
return qpdf->resolve(og);
}
};
// StreamCopier class is restricted to QPDFObjectHandle so it can copy stream data.
class QPDF::StreamCopier
{
@ -408,7 +395,7 @@ class QPDF::ParseGuard
static std::shared_ptr<QPDFObject>
getObject(QPDF* qpdf, int id, int gen, bool parse_pdf)
{
return qpdf->getObjectForParser(id, gen, parse_pdf);
return qpdf->objects().get_for_parser(id, gen, parse_pdf);
}
~ParseGuard()
@ -803,11 +790,32 @@ class QPDF::Members
};
inline QPDF::Objects&
QPDF::objects()
QPDF::objects() noexcept
{
return m->objects;
}
inline QPDF::Objects const&
QPDF::objects() const noexcept
{
return m->objects;
}
// The Resolver class is restricted to QPDFObject so that only it can resolve indirect
// references.
class QPDF::Resolver
{
friend class QPDFObject;
friend class QPDF_Unresolved;
private:
static QPDFObject*
resolved(QPDF* qpdf, QPDFObjGen og)
{
return qpdf->m->objects.resolve(og);
}
};
// JobSetter class is restricted to QPDFJob.
class QPDF::JobSetter
{
@ -884,13 +892,13 @@ class QPDF::Writer
static std::vector<QPDFObjGen>
getCompressibleObjGens(QPDF& qpdf)
{
return qpdf.getCompressibleObjVector();
return qpdf.objects().compressible_vector();
}
static std::vector<bool>
getCompressibleObjSet(QPDF& qpdf)
{
return qpdf.getCompressibleObjSet();
return qpdf.objects().compressible_set();
}
static Xref_table const&
@ -902,7 +910,7 @@ class QPDF::Writer
static size_t
tableSize(QPDF& qpdf)
{
return qpdf.tableSize();
return qpdf.objects().table_size();
}
};