Push QPDF member variables into a nested class

Pushing member variables into a nested class enables addition of new
member variables without breaking binary compatibility.
This commit is contained in:
Jay Berkenbilt 2017-08-21 21:33:44 -04:00
parent 8288a4eb3a
commit a8c93bd324
6 changed files with 737 additions and 687 deletions

View File

@ -635,11 +635,11 @@ class QPDF
qpdf(qpdf),
og(og)
{
qpdf->resolving.insert(og);
qpdf->m->resolving.insert(og);
}
virtual ~ResolveRecorder()
{
this->qpdf->resolving.erase(og);
this->qpdf->m->resolving.erase(og);
}
private:
QPDF* qpdf;
@ -1113,79 +1113,95 @@ class QPDF
std::set<QPDFObjGen>& visited, bool top);
void filterCompressedObjects(std::map<int, int> const& object_stream_data);
class Members
{
friend class QPDF;
QPDFTokenizer tokenizer;
PointerHolder<InputSource> file;
std::string last_object_description;
bool encrypted;
bool encryption_initialized;
bool ignore_xref_streams;
bool suppress_warnings;
std::ostream* out_stream;
std::ostream* err_stream;
bool attempt_recovery;
int encryption_V;
int encryption_R;
bool encrypt_metadata;
std::map<std::string, encryption_method_e> crypt_filters;
encryption_method_e cf_stream;
encryption_method_e cf_string;
encryption_method_e cf_file;
std::string provided_password;
std::string user_password;
std::string encryption_key;
std::string cached_object_encryption_key;
int cached_key_objid;
int cached_key_generation;
std::string pdf_version;
std::map<QPDFObjGen, QPDFXRefEntry> xref_table;
std::set<int> deleted_objects;
std::map<QPDFObjGen, ObjCache> obj_cache;
std::set<QPDFObjGen> resolving;
QPDFObjectHandle trailer;
std::vector<QPDFObjectHandle> all_pages;
std::map<QPDFObjGen, int> pageobj_to_pages_pos;
bool pushed_inherited_attributes_to_pages;
std::vector<QPDFExc> warnings;
std::map<QPDF*, ObjCopier> object_copiers;
PointerHolder<QPDFObjectHandle::StreamDataProvider> copied_streams;
// copied_stream_data_provider is owned by copied_streams
CopiedStreamDataProvider* copied_stream_data_provider;
std::set<QPDFObjGen> attachment_streams;
bool reconstructed_xref;
public:
~Members();
// Linearization data
qpdf_offset_t first_xref_item_offset; // actual value from file
bool uncompressed_after_compressed;
private:
Members();
Members(Members const&);
// Linearization parameter dictionary and hint table data: may be
// read from file or computed prior to writing a linearized file
QPDFObjectHandle lindict;
LinParameters linp;
HPageOffset page_offset_hints;
HSharedObject shared_object_hints;
HGeneric outline_hints;
QPDFTokenizer tokenizer;
PointerHolder<InputSource> file;
std::string last_object_description;
bool encrypted;
bool encryption_initialized;
bool ignore_xref_streams;
bool suppress_warnings;
std::ostream* out_stream;
std::ostream* err_stream;
bool attempt_recovery;
int encryption_V;
int encryption_R;
bool encrypt_metadata;
std::map<std::string, encryption_method_e> crypt_filters;
encryption_method_e cf_stream;
encryption_method_e cf_string;
encryption_method_e cf_file;
std::string provided_password;
std::string user_password;
std::string encryption_key;
std::string cached_object_encryption_key;
int cached_key_objid;
int cached_key_generation;
std::string pdf_version;
std::map<QPDFObjGen, QPDFXRefEntry> xref_table;
std::set<int> deleted_objects;
std::map<QPDFObjGen, ObjCache> obj_cache;
std::set<QPDFObjGen> resolving;
QPDFObjectHandle trailer;
std::vector<QPDFObjectHandle> all_pages;
std::map<QPDFObjGen, int> pageobj_to_pages_pos;
bool pushed_inherited_attributes_to_pages;
std::vector<QPDFExc> warnings;
std::map<QPDF*, ObjCopier> object_copiers;
PointerHolder<QPDFObjectHandle::StreamDataProvider> copied_streams;
// copied_stream_data_provider is owned by copied_streams
CopiedStreamDataProvider* copied_stream_data_provider;
std::set<QPDFObjGen> attachment_streams;
bool reconstructed_xref;
// Computed linearization data: used to populate above tables
// during writing and to compare with them during validation. c_
// means computed.
LinParameters c_linp;
CHPageOffset c_page_offset_data;
CHSharedObject c_shared_object_data;
HGeneric c_outline_data;
// Linearization data
qpdf_offset_t first_xref_item_offset; // actual value from file
bool uncompressed_after_compressed;
// Object ordering data for linearized files: initialized by
// calculateLinearizationData(). Part numbers refer to the PDF
// 1.4 specification.
std::vector<QPDFObjectHandle> part4;
std::vector<QPDFObjectHandle> part6;
std::vector<QPDFObjectHandle> part7;
std::vector<QPDFObjectHandle> part8;
std::vector<QPDFObjectHandle> part9;
// Linearization parameter dictionary and hint table data: may be
// read from file or computed prior to writing a linearized file
QPDFObjectHandle lindict;
LinParameters linp;
HPageOffset page_offset_hints;
HSharedObject shared_object_hints;
HGeneric outline_hints;
// Optimization data
std::map<ObjUser, std::set<QPDFObjGen> > obj_user_to_objects;
std::map<QPDFObjGen, std::set<ObjUser> > object_to_obj_users;
// Computed linearization data: used to populate above tables
// during writing and to compare with them during validation.
// c_ means computed.
LinParameters c_linp;
CHPageOffset c_page_offset_data;
CHSharedObject c_shared_object_data;
HGeneric c_outline_data;
// Object ordering data for linearized files: initialized by
// calculateLinearizationData(). Part numbers refer to the PDF
// 1.4 specification.
std::vector<QPDFObjectHandle> part4;
std::vector<QPDFObjectHandle> part6;
std::vector<QPDFObjectHandle> part7;
std::vector<QPDFObjectHandle> part8;
std::vector<QPDFObjectHandle> part9;
// Optimization data
std::map<ObjUser, std::set<QPDFObjGen> > obj_user_to_objects;
std::map<QPDFObjGen, std::set<ObjUser> > object_to_obj_users;
};
// Keep all member variables inside the Members object, which we
// dynamically allocate. This makes it possible to add new private
// members without breaking binary compatibility.
PointerHolder<Members> m;
};
#endif // __QPDF_HH__

File diff suppressed because it is too large Load Diff

View File

@ -760,9 +760,9 @@ QPDF::interpretCF(QPDFObjectHandle cf)
if (cf.isName())
{
std::string filter = cf.getName();
if (this->crypt_filters.count(filter) != 0)
if (this->m->crypt_filters.count(filter) != 0)
{
return this->crypt_filters[filter];
return this->m->crypt_filters[filter];
}
else if (filter == "/Identity")
{
@ -783,29 +783,29 @@ QPDF::interpretCF(QPDFObjectHandle cf)
void
QPDF::initializeEncryption()
{
if (this->encryption_initialized)
if (this->m->encryption_initialized)
{
return;
}
this->encryption_initialized = true;
this->m->encryption_initialized = true;
// After we initialize encryption parameters, we must used stored
// key information and never look at /Encrypt again. Otherwise,
// things could go wrong if someone mutates the encryption
// dictionary.
if (! this->trailer.hasKey("/Encrypt"))
if (! this->m->trailer.hasKey("/Encrypt"))
{
return;
}
// Go ahead and set this->encryption here. That way, isEncrypted
// Go ahead and set this->m->encrypted here. That way, isEncrypted
// will return true even if there were errors reading the
// encryption dictionary.
this->encrypted = true;
this->m->encrypted = true;
std::string id1;
QPDFObjectHandle id_obj = this->trailer.getKey("/ID");
QPDFObjectHandle id_obj = this->m->trailer.getKey("/ID");
if ((id_obj.isArray() &&
(id_obj.getArrayNItems() == 2) &&
id_obj.getArrayItem(0).isString()))
@ -817,31 +817,31 @@ QPDF::initializeEncryption()
// Treating a missing ID as the empty string enables qpdf to
// decrypt some invalid encrypted files with no /ID that
// poppler can read but Adobe Reader can't.
warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
"trailer", this->file->getLastOffset(),
warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"trailer", this->m->file->getLastOffset(),
"invalid /ID in trailer dictionary"));
}
QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt");
QPDFObjectHandle encryption_dict = this->m->trailer.getKey("/Encrypt");
if (! encryption_dict.isDictionary())
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
this->last_object_description,
this->file->getLastOffset(),
throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->m->last_object_description,
this->m->file->getLastOffset(),
"/Encrypt in trailer dictionary is not a dictionary");
}
if (! (encryption_dict.getKey("/Filter").isName() &&
(encryption_dict.getKey("/Filter").getName() == "/Standard")))
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary", this->m->file->getLastOffset(),
"unsupported encryption filter");
}
if (! encryption_dict.getKey("/SubFilter").isNull())
{
warn(QPDFExc(qpdf_e_unsupported, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
warn(QPDFExc(qpdf_e_unsupported, this->m->file->getName(),
"encryption dictionary", this->m->file->getLastOffset(),
"file uses encryption SubFilters,"
" which qpdf does not support"));
}
@ -852,8 +852,8 @@ QPDF::initializeEncryption()
encryption_dict.getKey("/U").isString() &&
encryption_dict.getKey("/P").isInteger()))
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary", this->m->file->getLastOffset(),
"some encryption dictionary parameters are missing "
"or the wrong type");
}
@ -869,15 +869,15 @@ QPDF::initializeEncryption()
if (! (((R >= 2) && (R <= 6)) &&
((V == 1) || (V == 2) || (V == 4) || (V == 5))))
{
throw QPDFExc(qpdf_e_unsupported, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
throw QPDFExc(qpdf_e_unsupported, this->m->file->getName(),
"encryption dictionary", this->m->file->getLastOffset(),
"Unsupported /R or /V in encryption dictionary; R = " +
QUtil::int_to_string(R) + " (max 6), V = " +
QUtil::int_to_string(V) + " (max 5)");
}
this->encryption_V = V;
this->encryption_R = R;
this->m->encryption_V = V;
this->m->encryption_R = R;
// OE, UE, and Perms are only present if V >= 5.
std::string OE;
@ -890,8 +890,9 @@ QPDF::initializeEncryption()
pad_short_parameter(U, key_bytes);
if (! ((O.length() == key_bytes) && (U.length() == key_bytes)))
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary",
this->m->file->getLastOffset(),
"incorrect length for /O and/or /U in "
"encryption dictionary");
}
@ -902,8 +903,9 @@ QPDF::initializeEncryption()
encryption_dict.getKey("/UE").isString() &&
encryption_dict.getKey("/Perms").isString()))
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary",
this->m->file->getLastOffset(),
"some V=5 encryption dictionary parameters are "
"missing or the wrong type");
}
@ -922,8 +924,9 @@ QPDF::initializeEncryption()
(UE.length() < OUE_key_bytes_V5) ||
(Perms.length() < Perms_key_bytes_V5))
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary",
this->m->file->getLastOffset(),
"incorrect length for some of"
" /O, /U, /OE, /UE, or /Perms in"
" encryption dictionary");
@ -936,16 +939,17 @@ QPDF::initializeEncryption()
Length = encryption_dict.getKey("/Length").getIntValue();
if ((Length % 8) || (Length < 40) || (Length > 256))
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary",
this->m->file->getLastOffset(),
"invalid /Length value in encryption dictionary");
}
}
this->encrypt_metadata = true;
this->m->encrypt_metadata = true;
if ((V >= 4) && (encryption_dict.getKey("/EncryptMetadata").isBool()))
{
this->encrypt_metadata =
this->m->encrypt_metadata =
encryption_dict.getKey("/EncryptMetadata").getBoolValue();
}
@ -986,40 +990,40 @@ QPDF::initializeEncryption()
method = e_unknown;
}
}
this->crypt_filters[filter] = method;
this->m->crypt_filters[filter] = method;
}
}
QPDFObjectHandle StmF = encryption_dict.getKey("/StmF");
QPDFObjectHandle StrF = encryption_dict.getKey("/StrF");
QPDFObjectHandle EFF = encryption_dict.getKey("/EFF");
this->cf_stream = interpretCF(StmF);
this->cf_string = interpretCF(StrF);
this->m->cf_stream = interpretCF(StmF);
this->m->cf_string = interpretCF(StrF);
if (EFF.isName())
{
this->cf_file = interpretCF(EFF);
this->m->cf_file = interpretCF(EFF);
}
else
{
this->cf_file = this->cf_stream;
this->m->cf_file = this->m->cf_stream;
}
}
EncryptionData data(V, R, Length / 8, P, O, U, OE, UE, Perms,
id1, this->encrypt_metadata);
id1, this->m->encrypt_metadata);
if (check_owner_password(
this->user_password, this->provided_password, data))
this->m->user_password, this->m->provided_password, data))
{
// password supplied was owner password; user_password has
// been initialized for V < 5
}
else if (check_user_password(this->provided_password, data))
else if (check_user_password(this->m->provided_password, data))
{
this->user_password = this->provided_password;
this->m->user_password = this->m->provided_password;
}
else
{
throw QPDFExc(qpdf_e_password, this->file->getName(),
throw QPDFExc(qpdf_e_password, this->m->file->getName(),
"", 0, "invalid password");
}
@ -1028,8 +1032,8 @@ QPDF::initializeEncryption()
// For V < 5, the user password is encrypted with the owner
// password, and the user password is always used for
// computing the encryption key.
this->encryption_key = compute_encryption_key(
this->user_password, data);
this->m->encryption_key = compute_encryption_key(
this->m->user_password, data);
}
else
{
@ -1037,12 +1041,13 @@ QPDF::initializeEncryption()
// compute the encryption key, and neither password can be
// used to recover the other.
bool perms_valid;
this->encryption_key = recover_encryption_key_with_password(
this->provided_password, data, perms_valid);
this->m->encryption_key = recover_encryption_key_with_password(
this->m->provided_password, data, perms_valid);
if (! perms_valid)
{
warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
"encryption dictionary", this->file->getLastOffset(),
warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary",
this->m->file->getLastOffset(),
"/Perms field in encryption dictionary"
" doesn't match expected value"));
}
@ -1052,23 +1057,24 @@ QPDF::initializeEncryption()
std::string
QPDF::getKeyForObject(int objid, int generation, bool use_aes)
{
if (! this->encrypted)
if (! this->m->encrypted)
{
throw std::logic_error(
"request for encryption key in non-encrypted PDF");
}
if (! ((objid == this->cached_key_objid) &&
(generation == this->cached_key_generation)))
if (! ((objid == this->m->cached_key_objid) &&
(generation == this->m->cached_key_generation)))
{
this->cached_object_encryption_key =
compute_data_key(this->encryption_key, objid, generation,
use_aes, this->encryption_V, this->encryption_R);
this->cached_key_objid = objid;
this->cached_key_generation = generation;
this->m->cached_object_encryption_key =
compute_data_key(this->m->encryption_key, objid, generation,
use_aes, this->m->encryption_V,
this->m->encryption_R);
this->m->cached_key_objid = objid;
this->m->cached_key_generation = generation;
}
return this->cached_object_encryption_key;
return this->m->cached_object_encryption_key;
}
void
@ -1079,9 +1085,9 @@ QPDF::decryptString(std::string& str, int objid, int generation)
return;
}
bool use_aes = false;
if (this->encryption_V >= 4)
if (this->m->encryption_V >= 4)
{
switch (this->cf_string)
switch (this->m->cf_string)
{
case e_none:
return;
@ -1098,15 +1104,15 @@ QPDF::decryptString(std::string& str, int objid, int generation)
break;
default:
warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
this->last_object_description,
this->file->getLastOffset(),
warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->m->last_object_description,
this->m->file->getLastOffset(),
"unknown encryption filter for strings"
" (check /StrF in /Encrypt dictionary);"
" strings may be decrypted improperly"));
// To avoid repeated warnings, reset cf_string. Assume
// we'd want to use AES if V == 4.
this->cf_string = e_aes;
this->m->cf_string = e_aes;
break;
}
}
@ -1145,9 +1151,9 @@ QPDF::decryptString(std::string& str, int objid, int generation)
}
catch (std::runtime_error& e)
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
this->last_object_description,
this->file->getLastOffset(),
throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->m->last_object_description,
this->m->file->getLastOffset(),
"error decrypting string for object " +
QUtil::int_to_string(objid) + " " +
QUtil::int_to_string(generation) + ": " + e.what());
@ -1170,7 +1176,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
return;
}
bool use_aes = false;
if (this->encryption_V >= 4)
if (this->m->encryption_V >= 4)
{
encryption_method_e method = e_unknown;
std::string method_source = "/StmF from /Encrypt dictionary";
@ -1221,21 +1227,21 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
if (method == e_unknown)
{
if ((! this->encrypt_metadata) && (type == "/Metadata"))
if ((! this->m->encrypt_metadata) && (type == "/Metadata"))
{
QTC::TC("qpdf", "QPDF_encryption cleartext metadata");
method = e_none;
}
else
{
if (this->attachment_streams.count(
if (this->m->attachment_streams.count(
QPDFObjGen(objid, generation)) > 0)
{
method = this->cf_file;
method = this->m->cf_file;
}
else
{
method = this->cf_stream;
method = this->m->cf_stream;
}
}
}
@ -1259,15 +1265,15 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
default:
// filter local to this stream.
warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
this->last_object_description,
this->file->getLastOffset(),
warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->m->last_object_description,
this->m->file->getLastOffset(),
"unknown encryption filter for streams"
" (check " + method_source + ");"
" streams may be decrypted improperly"));
// To avoid repeated warnings, reset cf_stream. Assume
// we'd want to use AES if V == 4.
this->cf_stream = e_aes;
this->m->cf_stream = e_aes;
break;
}
}
@ -1331,13 +1337,13 @@ QPDF::compute_encryption_parameters_V5(
std::string const&
QPDF::getPaddedUserPassword() const
{
return this->user_password;
return this->m->user_password;
}
std::string
QPDF::getTrimmedUserPassword() const
{
std::string result = this->user_password;
std::string result = this->m->user_password;
trim_user_password(result);
return result;
}
@ -1345,13 +1351,13 @@ QPDF::getTrimmedUserPassword() const
std::string
QPDF::getEncryptionKey() const
{
return this->encryption_key;
return this->m->encryption_key;
}
bool
QPDF::isEncrypted() const
{
return this->encrypted;
return this->m->encrypted;
}
bool
@ -1368,7 +1374,7 @@ QPDF::isEncrypted(int& R, int& P, int& V,
encryption_method_e& string_method,
encryption_method_e& file_method)
{
if (this->encrypted)
if (this->m->encrypted)
{
QPDFObjectHandle trailer = getTrailer();
QPDFObjectHandle encrypt = trailer.getKey("/Encrypt");
@ -1378,9 +1384,9 @@ QPDF::isEncrypted(int& R, int& P, int& V,
P = Pkey.getIntValue();
R = Rkey.getIntValue();
V = Vkey.getIntValue();
stream_method = this->cf_stream;
string_method = this->cf_stream;
file_method = this->cf_file;
stream_method = this->m->cf_stream;
string_method = this->m->cf_stream;
file_method = this->m->cf_file;
return true;
}
else

File diff suppressed because it is too large Load Diff

View File

@ -62,7 +62,7 @@ void
QPDF::optimize(std::map<int, int> const& object_stream_data,
bool allow_changes)
{
if (! this->obj_user_to_objects.empty())
if (! this->m->obj_user_to_objects.empty())
{
// already optimized
return;
@ -83,19 +83,19 @@ QPDF::optimize(std::map<int, int> const& object_stream_data,
}
// Traverse pages tree pushing all inherited resources down to the
// page level. This also initializes this->all_pages.
// page level. This also initializes this->m->all_pages.
pushInheritedAttributesToPage(allow_changes, false);
// Traverse pages
int n = this->all_pages.size();
int n = this->m->all_pages.size();
for (int pageno = 0; pageno < n; ++pageno)
{
updateObjectMaps(ObjUser(ObjUser::ou_page, pageno),
this->all_pages.at(pageno));
this->m->all_pages.at(pageno));
}
// Traverse document-level items
std::set<std::string> keys = this->trailer.getKeys();
std::set<std::string> keys = this->m->trailer.getKeys();
for (std::set<std::string>::iterator iter = keys.begin();
iter != keys.end(); ++iter)
{
@ -107,7 +107,7 @@ QPDF::optimize(std::map<int, int> const& object_stream_data,
else
{
updateObjectMaps(ObjUser(ObjUser::ou_trailer_key, key),
this->trailer.getKey(key));
this->m->trailer.getKey(key));
}
}
@ -129,8 +129,8 @@ QPDF::optimize(std::map<int, int> const& object_stream_data,
ObjUser root_ou = ObjUser(ObjUser::ou_root);
QPDFObjGen root_og = QPDFObjGen(root.getObjGen());
obj_user_to_objects[root_ou].insert(root_og);
object_to_obj_users[root_og].insert(root_ou);
this->m->obj_user_to_objects[root_ou].insert(root_og);
this->m->object_to_obj_users[root_og].insert(root_ou);
filterCompressedObjects(object_stream_data);
}
@ -151,7 +151,7 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys)
// The record of whether we've done this is cleared by
// updateAllPagesCache(). If we're warning for skipped keys,
// re-traverse unconditionally.
if (this->pushed_inherited_attributes_to_pages && (! warn_skipped_keys))
if (this->m->pushed_inherited_attributes_to_pages && (! warn_skipped_keys))
{
return;
}
@ -159,12 +159,12 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys)
// key_ancestors is a mapping of page attribute keys to a stack of
// Pages nodes that contain values for them.
std::map<std::string, std::vector<QPDFObjectHandle> > key_ancestors;
this->all_pages.clear();
this->m->all_pages.clear();
pushInheritedAttributesToPageInternal(
this->trailer.getKey("/Root").getKey("/Pages"),
key_ancestors, this->all_pages, allow_changes, warn_skipped_keys);
this->m->trailer.getKey("/Root").getKey("/Pages"),
key_ancestors, this->m->all_pages, allow_changes, warn_skipped_keys);
assert(key_ancestors.empty());
this->pushed_inherited_attributes_to_pages = true;
this->m->pushed_inherited_attributes_to_pages = true;
}
void
@ -192,8 +192,8 @@ QPDF::pushInheritedAttributesToPageInternal2(
if (visited.count(this_og) > 0)
{
throw QPDFExc(
qpdf_e_pages, this->file->getName(),
this->last_object_description, 0,
qpdf_e_pages, this->m->file->getName(),
this->m->last_object_description, 0,
"Loop detected in /Pages structure (inherited attributes)");
}
visited.insert(this_og);
@ -219,9 +219,9 @@ QPDF::pushInheritedAttributesToPageInternal2(
{
if (! allow_changes)
{
throw QPDFExc(qpdf_e_internal, this->file->getName(),
this->last_object_description,
this->file->getLastOffset(),
throw QPDFExc(qpdf_e_internal, this->m->file->getName(),
this->m->last_object_description,
this->m->file->getLastOffset(),
"optimize detected an "
"inheritable attribute when called "
"in no-change mode");
@ -269,8 +269,8 @@ QPDF::pushInheritedAttributesToPageInternal2(
setLastObjectDescription("Pages object",
cur_pages.getObjectID(),
cur_pages.getGeneration());
warn(QPDFExc(qpdf_e_pages, this->file->getName(),
this->last_object_description, 0,
warn(QPDFExc(qpdf_e_pages, this->m->file->getName(),
this->m->last_object_description, 0,
"Unknown key " + key + " in /Pages object"
" is being discarded as a result of"
" flattening the /Pages tree"));
@ -337,9 +337,9 @@ QPDF::pushInheritedAttributesToPageInternal2(
}
else
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
this->last_object_description,
this->file->getLastOffset(),
throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->m->last_object_description,
this->m->file->getLastOffset(),
"invalid Type " + type + " in page tree");
}
visited.erase(this_og);
@ -382,8 +382,8 @@ QPDF::updateObjectMapsInternal(ObjUser const& ou, QPDFObjectHandle oh,
QTC::TC("qpdf", "QPDF opt loop detected");
return;
}
this->obj_user_to_objects[ou].insert(og);
this->object_to_obj_users[og].insert(ou);
this->m->obj_user_to_objects[ou].insert(og);
this->m->object_to_obj_users[og].insert(ou);
visited.insert(og);
}
@ -445,8 +445,8 @@ QPDF::filterCompressedObjects(std::map<int, int> const& object_stream_data)
std::map<QPDFObjGen, std::set<ObjUser> > t_object_to_obj_users;
for (std::map<ObjUser, std::set<QPDFObjGen> >::iterator i1 =
this->obj_user_to_objects.begin();
i1 != this->obj_user_to_objects.end(); ++i1)
this->m->obj_user_to_objects.begin();
i1 != this->m->obj_user_to_objects.end(); ++i1)
{
ObjUser const& ou = (*i1).first;
std::set<QPDFObjGen> const& objects = (*i1).second;
@ -468,8 +468,8 @@ QPDF::filterCompressedObjects(std::map<int, int> const& object_stream_data)
}
for (std::map<QPDFObjGen, std::set<ObjUser> >::iterator i1 =
this->object_to_obj_users.begin();
i1 != this->object_to_obj_users.end(); ++i1)
this->m->object_to_obj_users.begin();
i1 != this->m->object_to_obj_users.end(); ++i1)
{
QPDFObjGen const& og = (*i1).first;
std::set<ObjUser> const& objusers = (*i1).second;
@ -490,6 +490,6 @@ QPDF::filterCompressedObjects(std::map<int, int> const& object_stream_data)
}
}
this->obj_user_to_objects = t_obj_user_to_objects;
this->object_to_obj_users = t_object_to_obj_users;
this->m->obj_user_to_objects = t_obj_user_to_objects;
this->m->object_to_obj_users = t_object_to_obj_users;
}

View File

@ -44,12 +44,12 @@ std::vector<QPDFObjectHandle> const&
QPDF::getAllPages()
{
// Note that pushInheritedAttributesToPage may also be used to
// initialize this->all_pages.
if (this->all_pages.empty())
// initialize this->m->all_pages.
if (this->m->all_pages.empty())
{
getAllPagesInternal(getRoot().getKey("/Pages"), this->all_pages);
getAllPagesInternal(getRoot().getKey("/Pages"), this->m->all_pages);
}
return this->all_pages;
return this->m->all_pages;
}
void
@ -69,8 +69,8 @@ QPDF::getAllPagesInternal2(QPDFObjectHandle cur_pages,
if (visited.count(this_og) > 0)
{
throw QPDFExc(
qpdf_e_pages, this->file->getName(),
this->last_object_description, 0,
qpdf_e_pages, this->m->file->getName(),
this->m->last_object_description, 0,
"Loop detected in /Pages structure (getAllPages)");
}
visited.insert(this_og);
@ -103,9 +103,9 @@ QPDF::getAllPagesInternal2(QPDFObjectHandle cur_pages,
}
else
{
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
this->last_object_description,
this->file->getLastOffset(),
throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->m->last_object_description,
this->m->file->getLastOffset(),
"invalid Type " + type + " in page tree");
}
visited.erase(this_og);
@ -119,9 +119,9 @@ QPDF::updateAllPagesCache()
// it that they got from calls to getAllPages(). We can defer
// recalculation of pageobj_to_pages_pos until needed.
QTC::TC("qpdf", "QPDF updateAllPagesCache");
this->all_pages.clear();
this->pageobj_to_pages_pos.clear();
this->pushed_inherited_attributes_to_pages = false;
this->m->all_pages.clear();
this->m->pageobj_to_pages_pos.clear();
this->m->pushed_inherited_attributes_to_pages = false;
getAllPages();
}
@ -131,26 +131,26 @@ QPDF::flattenPagesTree()
// If not already done, flatten the /Pages structure and
// initialize pageobj_to_pages_pos.
if (! this->pageobj_to_pages_pos.empty())
if (! this->m->pageobj_to_pages_pos.empty())
{
return;
}
// Push inherited objects down to the /Page level. As a side
// effect this->all_pages will also be generated.
// effect this->m->all_pages will also be generated.
pushInheritedAttributesToPage(true, true);
QPDFObjectHandle pages = getRoot().getKey("/Pages");
int const len = this->all_pages.size();
int const len = this->m->all_pages.size();
for (int pos = 0; pos < len; ++pos)
{
// populate pageobj_to_pages_pos and fix parent pointer
insertPageobjToPage(this->all_pages.at(pos), pos, true);
this->all_pages.at(pos).replaceKey("/Parent", pages);
insertPageobjToPage(this->m->all_pages.at(pos), pos, true);
this->m->all_pages.at(pos).replaceKey("/Parent", pages);
}
pages.replaceKey("/Kids", QPDFObjectHandle::newArray(this->all_pages));
pages.replaceKey("/Kids", QPDFObjectHandle::newArray(this->m->all_pages));
// /Count has not changed
if (pages.getKey("/Count").getIntValue() != len)
{
@ -165,21 +165,22 @@ QPDF::insertPageobjToPage(QPDFObjectHandle const& obj, int pos,
QPDFObjGen og(obj.getObjGen());
if (check_duplicate)
{
if (! this->pageobj_to_pages_pos.insert(std::make_pair(og, pos)).second)
if (! this->m->pageobj_to_pages_pos.insert(
std::make_pair(og, pos)).second)
{
QTC::TC("qpdf", "QPDF duplicate page reference");
setLastObjectDescription("page " + QUtil::int_to_string(pos) +
" (numbered from zero)",
og.getObj(), og.getGen());
throw QPDFExc(qpdf_e_pages, this->file->getName(),
this->last_object_description, 0,
throw QPDFExc(qpdf_e_pages, this->m->file->getName(),
this->m->last_object_description, 0,
"duplicate page reference found;"
" this would cause loss of data");
}
}
else
{
this->pageobj_to_pages_pos[og] = pos;
this->m->pageobj_to_pages_pos[og] = pos;
}
}
@ -195,13 +196,13 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos)
if (! newpage.isIndirect())
{
QTC::TC("qpdf", "QPDF insert non-indirect page");
newpage = this->makeIndirectObject(newpage);
newpage = makeIndirectObject(newpage);
}
else if (newpage.getOwningQPDF() != this)
{
QTC::TC("qpdf", "QPDF insert foreign page");
newpage.getOwningQPDF()->pushInheritedAttributesToPage();
newpage = this->copyForeignObject(newpage, true);
newpage = copyForeignObject(newpage, true);
}
else
{
@ -210,26 +211,26 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos)
QTC::TC("qpdf", "QPDF insert page",
(pos == 0) ? 0 : // insert at beginning
(pos == static_cast<int>(this->all_pages.size())) ? 1 : // at end
(pos == static_cast<int>(this->m->all_pages.size())) ? 1 : // at end
2); // insert in middle
QPDFObjectHandle pages = getRoot().getKey("/Pages");
QPDFObjectHandle kids = pages.getKey("/Kids");
assert ((pos >= 0) &&
(static_cast<size_t>(pos) <= this->all_pages.size()));
(static_cast<size_t>(pos) <= this->m->all_pages.size()));
newpage.replaceKey("/Parent", pages);
kids.insertItem(pos, newpage);
int npages = kids.getArrayNItems();
pages.replaceKey("/Count", QPDFObjectHandle::newInteger(npages));
this->all_pages.insert(this->all_pages.begin() + pos, newpage);
assert(this->all_pages.size() == static_cast<size_t>(npages));
this->m->all_pages.insert(this->m->all_pages.begin() + pos, newpage);
assert(this->m->all_pages.size() == static_cast<size_t>(npages));
for (int i = pos + 1; i < npages; ++i)
{
insertPageobjToPage(this->all_pages.at(i), i, false);
insertPageobjToPage(this->m->all_pages.at(i), i, false);
}
insertPageobjToPage(newpage, pos, true);
assert(this->pageobj_to_pages_pos.size() == static_cast<size_t>(npages));
assert(this->m->pageobj_to_pages_pos.size() == static_cast<size_t>(npages));
}
void
@ -238,7 +239,8 @@ QPDF::removePage(QPDFObjectHandle page)
int pos = findPage(page); // also ensures flat /Pages
QTC::TC("qpdf", "QPDF remove page",
(pos == 0) ? 0 : // remove at beginning
(pos == static_cast<int>(this->all_pages.size() - 1)) ? 1 : // end
(pos == static_cast<int>(
this->m->all_pages.size() - 1)) ? 1 : // end
2); // remove in middle
QPDFObjectHandle pages = getRoot().getKey("/Pages");
@ -247,13 +249,13 @@ QPDF::removePage(QPDFObjectHandle page)
kids.eraseItem(pos);
int npages = kids.getArrayNItems();
pages.replaceKey("/Count", QPDFObjectHandle::newInteger(npages));
this->all_pages.erase(this->all_pages.begin() + pos);
assert(this->all_pages.size() == static_cast<size_t>(npages));
this->pageobj_to_pages_pos.erase(page.getObjGen());
assert(this->pageobj_to_pages_pos.size() == static_cast<size_t>(npages));
this->m->all_pages.erase(this->m->all_pages.begin() + pos);
assert(this->m->all_pages.size() == static_cast<size_t>(npages));
this->m->pageobj_to_pages_pos.erase(page.getObjGen());
assert(this->m->pageobj_to_pages_pos.size() == static_cast<size_t>(npages));
for (int i = pos; i < npages; ++i)
{
insertPageobjToPage(this->all_pages.at(i), i, false);
insertPageobjToPage(this->m->all_pages.at(i), i, false);
}
}
@ -295,12 +297,12 @@ QPDF::findPage(QPDFObjGen const& og)
{
flattenPagesTree();
std::map<QPDFObjGen, int>::iterator it =
this->pageobj_to_pages_pos.find(og);
if (it == this->pageobj_to_pages_pos.end())
this->m->pageobj_to_pages_pos.find(og);
if (it == this->m->pageobj_to_pages_pos.end())
{
setLastObjectDescription("page object", og.getObj(), og.getGen());
throw QPDFExc(qpdf_e_pages, this->file->getName(),
this->last_object_description, 0,
throw QPDFExc(qpdf_e_pages, this->m->file->getName(),
this->m->last_object_description, 0,
"page object not referenced in /Pages tree");
}
return (*it).second;