2
1
mirror of https://github.com/qpdf/qpdf.git synced 2025-02-02 11:58:25 +00:00

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), qpdf(qpdf),
og(og) og(og)
{ {
qpdf->resolving.insert(og); qpdf->m->resolving.insert(og);
} }
virtual ~ResolveRecorder() virtual ~ResolveRecorder()
{ {
this->qpdf->resolving.erase(og); this->qpdf->m->resolving.erase(og);
} }
private: private:
QPDF* qpdf; QPDF* qpdf;
@ -1113,79 +1113,95 @@ class QPDF
std::set<QPDFObjGen>& visited, bool top); std::set<QPDFObjGen>& visited, bool top);
void filterCompressedObjects(std::map<int, int> const& object_stream_data); void filterCompressedObjects(std::map<int, int> const& object_stream_data);
class Members
{
friend class QPDF;
QPDFTokenizer tokenizer; public:
PointerHolder<InputSource> file; ~Members();
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;
// Linearization data private:
qpdf_offset_t first_xref_item_offset; // actual value from file Members();
bool uncompressed_after_compressed; Members(Members const&);
// Linearization parameter dictionary and hint table data: may be QPDFTokenizer tokenizer;
// read from file or computed prior to writing a linearized file PointerHolder<InputSource> file;
QPDFObjectHandle lindict; std::string last_object_description;
LinParameters linp; bool encrypted;
HPageOffset page_offset_hints; bool encryption_initialized;
HSharedObject shared_object_hints; bool ignore_xref_streams;
HGeneric outline_hints; 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 // Linearization data
// during writing and to compare with them during validation. c_ qpdf_offset_t first_xref_item_offset; // actual value from file
// means computed. bool uncompressed_after_compressed;
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 // Linearization parameter dictionary and hint table data: may be
// calculateLinearizationData(). Part numbers refer to the PDF // read from file or computed prior to writing a linearized file
// 1.4 specification. QPDFObjectHandle lindict;
std::vector<QPDFObjectHandle> part4; LinParameters linp;
std::vector<QPDFObjectHandle> part6; HPageOffset page_offset_hints;
std::vector<QPDFObjectHandle> part7; HSharedObject shared_object_hints;
std::vector<QPDFObjectHandle> part8; HGeneric outline_hints;
std::vector<QPDFObjectHandle> part9;
// Optimization data // Computed linearization data: used to populate above tables
std::map<ObjUser, std::set<QPDFObjGen> > obj_user_to_objects; // during writing and to compare with them during validation.
std::map<QPDFObjGen, std::set<ObjUser> > object_to_obj_users; // 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__ #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()) if (cf.isName())
{ {
std::string filter = cf.getName(); 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") else if (filter == "/Identity")
{ {
@ -783,29 +783,29 @@ QPDF::interpretCF(QPDFObjectHandle cf)
void void
QPDF::initializeEncryption() QPDF::initializeEncryption()
{ {
if (this->encryption_initialized) if (this->m->encryption_initialized)
{ {
return; return;
} }
this->encryption_initialized = true; this->m->encryption_initialized = true;
// After we initialize encryption parameters, we must used stored // After we initialize encryption parameters, we must used stored
// key information and never look at /Encrypt again. Otherwise, // key information and never look at /Encrypt again. Otherwise,
// things could go wrong if someone mutates the encryption // things could go wrong if someone mutates the encryption
// dictionary. // dictionary.
if (! this->trailer.hasKey("/Encrypt")) if (! this->m->trailer.hasKey("/Encrypt"))
{ {
return; 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 // will return true even if there were errors reading the
// encryption dictionary. // encryption dictionary.
this->encrypted = true; this->m->encrypted = true;
std::string id1; std::string id1;
QPDFObjectHandle id_obj = this->trailer.getKey("/ID"); QPDFObjectHandle id_obj = this->m->trailer.getKey("/ID");
if ((id_obj.isArray() && if ((id_obj.isArray() &&
(id_obj.getArrayNItems() == 2) && (id_obj.getArrayNItems() == 2) &&
id_obj.getArrayItem(0).isString())) id_obj.getArrayItem(0).isString()))
@ -817,31 +817,31 @@ QPDF::initializeEncryption()
// Treating a missing ID as the empty string enables qpdf to // Treating a missing ID as the empty string enables qpdf to
// decrypt some invalid encrypted files with no /ID that // decrypt some invalid encrypted files with no /ID that
// poppler can read but Adobe Reader can't. // poppler can read but Adobe Reader can't.
warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"trailer", this->file->getLastOffset(), "trailer", this->m->file->getLastOffset(),
"invalid /ID in trailer dictionary")); "invalid /ID in trailer dictionary"));
} }
QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt"); QPDFObjectHandle encryption_dict = this->m->trailer.getKey("/Encrypt");
if (! encryption_dict.isDictionary()) if (! encryption_dict.isDictionary())
{ {
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->last_object_description, this->m->last_object_description,
this->file->getLastOffset(), this->m->file->getLastOffset(),
"/Encrypt in trailer dictionary is not a dictionary"); "/Encrypt in trailer dictionary is not a dictionary");
} }
if (! (encryption_dict.getKey("/Filter").isName() && if (! (encryption_dict.getKey("/Filter").isName() &&
(encryption_dict.getKey("/Filter").getName() == "/Standard"))) (encryption_dict.getKey("/Filter").getName() == "/Standard")))
{ {
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary", this->file->getLastOffset(), "encryption dictionary", this->m->file->getLastOffset(),
"unsupported encryption filter"); "unsupported encryption filter");
} }
if (! encryption_dict.getKey("/SubFilter").isNull()) if (! encryption_dict.getKey("/SubFilter").isNull())
{ {
warn(QPDFExc(qpdf_e_unsupported, this->file->getName(), warn(QPDFExc(qpdf_e_unsupported, this->m->file->getName(),
"encryption dictionary", this->file->getLastOffset(), "encryption dictionary", this->m->file->getLastOffset(),
"file uses encryption SubFilters," "file uses encryption SubFilters,"
" which qpdf does not support")); " which qpdf does not support"));
} }
@ -852,8 +852,8 @@ QPDF::initializeEncryption()
encryption_dict.getKey("/U").isString() && encryption_dict.getKey("/U").isString() &&
encryption_dict.getKey("/P").isInteger())) encryption_dict.getKey("/P").isInteger()))
{ {
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary", this->file->getLastOffset(), "encryption dictionary", this->m->file->getLastOffset(),
"some encryption dictionary parameters are missing " "some encryption dictionary parameters are missing "
"or the wrong type"); "or the wrong type");
} }
@ -869,15 +869,15 @@ QPDF::initializeEncryption()
if (! (((R >= 2) && (R <= 6)) && if (! (((R >= 2) && (R <= 6)) &&
((V == 1) || (V == 2) || (V == 4) || (V == 5)))) ((V == 1) || (V == 2) || (V == 4) || (V == 5))))
{ {
throw QPDFExc(qpdf_e_unsupported, this->file->getName(), throw QPDFExc(qpdf_e_unsupported, this->m->file->getName(),
"encryption dictionary", this->file->getLastOffset(), "encryption dictionary", this->m->file->getLastOffset(),
"Unsupported /R or /V in encryption dictionary; R = " + "Unsupported /R or /V in encryption dictionary; R = " +
QUtil::int_to_string(R) + " (max 6), V = " + QUtil::int_to_string(R) + " (max 6), V = " +
QUtil::int_to_string(V) + " (max 5)"); QUtil::int_to_string(V) + " (max 5)");
} }
this->encryption_V = V; this->m->encryption_V = V;
this->encryption_R = R; this->m->encryption_R = R;
// OE, UE, and Perms are only present if V >= 5. // OE, UE, and Perms are only present if V >= 5.
std::string OE; std::string OE;
@ -890,8 +890,9 @@ QPDF::initializeEncryption()
pad_short_parameter(U, key_bytes); pad_short_parameter(U, key_bytes);
if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) if (! ((O.length() == key_bytes) && (U.length() == key_bytes)))
{ {
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary", this->file->getLastOffset(), "encryption dictionary",
this->m->file->getLastOffset(),
"incorrect length for /O and/or /U in " "incorrect length for /O and/or /U in "
"encryption dictionary"); "encryption dictionary");
} }
@ -902,8 +903,9 @@ QPDF::initializeEncryption()
encryption_dict.getKey("/UE").isString() && encryption_dict.getKey("/UE").isString() &&
encryption_dict.getKey("/Perms").isString())) encryption_dict.getKey("/Perms").isString()))
{ {
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary", this->file->getLastOffset(), "encryption dictionary",
this->m->file->getLastOffset(),
"some V=5 encryption dictionary parameters are " "some V=5 encryption dictionary parameters are "
"missing or the wrong type"); "missing or the wrong type");
} }
@ -922,8 +924,9 @@ QPDF::initializeEncryption()
(UE.length() < OUE_key_bytes_V5) || (UE.length() < OUE_key_bytes_V5) ||
(Perms.length() < Perms_key_bytes_V5)) (Perms.length() < Perms_key_bytes_V5))
{ {
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary", this->file->getLastOffset(), "encryption dictionary",
this->m->file->getLastOffset(),
"incorrect length for some of" "incorrect length for some of"
" /O, /U, /OE, /UE, or /Perms in" " /O, /U, /OE, /UE, or /Perms in"
" encryption dictionary"); " encryption dictionary");
@ -936,16 +939,17 @@ QPDF::initializeEncryption()
Length = encryption_dict.getKey("/Length").getIntValue(); Length = encryption_dict.getKey("/Length").getIntValue();
if ((Length % 8) || (Length < 40) || (Length > 256)) if ((Length % 8) || (Length < 40) || (Length > 256))
{ {
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary", this->file->getLastOffset(), "encryption dictionary",
this->m->file->getLastOffset(),
"invalid /Length value in encryption dictionary"); "invalid /Length value in encryption dictionary");
} }
} }
this->encrypt_metadata = true; this->m->encrypt_metadata = true;
if ((V >= 4) && (encryption_dict.getKey("/EncryptMetadata").isBool())) if ((V >= 4) && (encryption_dict.getKey("/EncryptMetadata").isBool()))
{ {
this->encrypt_metadata = this->m->encrypt_metadata =
encryption_dict.getKey("/EncryptMetadata").getBoolValue(); encryption_dict.getKey("/EncryptMetadata").getBoolValue();
} }
@ -986,40 +990,40 @@ QPDF::initializeEncryption()
method = e_unknown; method = e_unknown;
} }
} }
this->crypt_filters[filter] = method; this->m->crypt_filters[filter] = method;
} }
} }
QPDFObjectHandle StmF = encryption_dict.getKey("/StmF"); QPDFObjectHandle StmF = encryption_dict.getKey("/StmF");
QPDFObjectHandle StrF = encryption_dict.getKey("/StrF"); QPDFObjectHandle StrF = encryption_dict.getKey("/StrF");
QPDFObjectHandle EFF = encryption_dict.getKey("/EFF"); QPDFObjectHandle EFF = encryption_dict.getKey("/EFF");
this->cf_stream = interpretCF(StmF); this->m->cf_stream = interpretCF(StmF);
this->cf_string = interpretCF(StrF); this->m->cf_string = interpretCF(StrF);
if (EFF.isName()) if (EFF.isName())
{ {
this->cf_file = interpretCF(EFF); this->m->cf_file = interpretCF(EFF);
} }
else 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, 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( 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 // password supplied was owner password; user_password has
// been initialized for V < 5 // 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 else
{ {
throw QPDFExc(qpdf_e_password, this->file->getName(), throw QPDFExc(qpdf_e_password, this->m->file->getName(),
"", 0, "invalid password"); "", 0, "invalid password");
} }
@ -1028,8 +1032,8 @@ QPDF::initializeEncryption()
// For V < 5, the user password is encrypted with the owner // For V < 5, the user password is encrypted with the owner
// password, and the user password is always used for // password, and the user password is always used for
// computing the encryption key. // computing the encryption key.
this->encryption_key = compute_encryption_key( this->m->encryption_key = compute_encryption_key(
this->user_password, data); this->m->user_password, data);
} }
else else
{ {
@ -1037,12 +1041,13 @@ QPDF::initializeEncryption()
// compute the encryption key, and neither password can be // compute the encryption key, and neither password can be
// used to recover the other. // used to recover the other.
bool perms_valid; bool perms_valid;
this->encryption_key = recover_encryption_key_with_password( this->m->encryption_key = recover_encryption_key_with_password(
this->provided_password, data, perms_valid); this->m->provided_password, data, perms_valid);
if (! perms_valid) if (! perms_valid)
{ {
warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
"encryption dictionary", this->file->getLastOffset(), "encryption dictionary",
this->m->file->getLastOffset(),
"/Perms field in encryption dictionary" "/Perms field in encryption dictionary"
" doesn't match expected value")); " doesn't match expected value"));
} }
@ -1052,23 +1057,24 @@ QPDF::initializeEncryption()
std::string std::string
QPDF::getKeyForObject(int objid, int generation, bool use_aes) QPDF::getKeyForObject(int objid, int generation, bool use_aes)
{ {
if (! this->encrypted) if (! this->m->encrypted)
{ {
throw std::logic_error( throw std::logic_error(
"request for encryption key in non-encrypted PDF"); "request for encryption key in non-encrypted PDF");
} }
if (! ((objid == this->cached_key_objid) && if (! ((objid == this->m->cached_key_objid) &&
(generation == this->cached_key_generation))) (generation == this->m->cached_key_generation)))
{ {
this->cached_object_encryption_key = this->m->cached_object_encryption_key =
compute_data_key(this->encryption_key, objid, generation, compute_data_key(this->m->encryption_key, objid, generation,
use_aes, this->encryption_V, this->encryption_R); use_aes, this->m->encryption_V,
this->cached_key_objid = objid; this->m->encryption_R);
this->cached_key_generation = generation; 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 void
@ -1079,9 +1085,9 @@ QPDF::decryptString(std::string& str, int objid, int generation)
return; return;
} }
bool use_aes = false; 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: case e_none:
return; return;
@ -1098,15 +1104,15 @@ QPDF::decryptString(std::string& str, int objid, int generation)
break; break;
default: default:
warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->last_object_description, this->m->last_object_description,
this->file->getLastOffset(), this->m->file->getLastOffset(),
"unknown encryption filter for strings" "unknown encryption filter for strings"
" (check /StrF in /Encrypt dictionary);" " (check /StrF in /Encrypt dictionary);"
" strings may be decrypted improperly")); " strings may be decrypted improperly"));
// To avoid repeated warnings, reset cf_string. Assume // To avoid repeated warnings, reset cf_string. Assume
// we'd want to use AES if V == 4. // we'd want to use AES if V == 4.
this->cf_string = e_aes; this->m->cf_string = e_aes;
break; break;
} }
} }
@ -1145,9 +1151,9 @@ QPDF::decryptString(std::string& str, int objid, int generation)
} }
catch (std::runtime_error& e) catch (std::runtime_error& e)
{ {
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->last_object_description, this->m->last_object_description,
this->file->getLastOffset(), this->m->file->getLastOffset(),
"error decrypting string for object " + "error decrypting string for object " +
QUtil::int_to_string(objid) + " " + QUtil::int_to_string(objid) + " " +
QUtil::int_to_string(generation) + ": " + e.what()); QUtil::int_to_string(generation) + ": " + e.what());
@ -1170,7 +1176,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
return; return;
} }
bool use_aes = false; bool use_aes = false;
if (this->encryption_V >= 4) if (this->m->encryption_V >= 4)
{ {
encryption_method_e method = e_unknown; encryption_method_e method = e_unknown;
std::string method_source = "/StmF from /Encrypt dictionary"; 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 (method == e_unknown)
{ {
if ((! this->encrypt_metadata) && (type == "/Metadata")) if ((! this->m->encrypt_metadata) && (type == "/Metadata"))
{ {
QTC::TC("qpdf", "QPDF_encryption cleartext metadata"); QTC::TC("qpdf", "QPDF_encryption cleartext metadata");
method = e_none; method = e_none;
} }
else else
{ {
if (this->attachment_streams.count( if (this->m->attachment_streams.count(
QPDFObjGen(objid, generation)) > 0) QPDFObjGen(objid, generation)) > 0)
{ {
method = this->cf_file; method = this->m->cf_file;
} }
else else
{ {
method = this->cf_stream; method = this->m->cf_stream;
} }
} }
} }
@ -1259,15 +1265,15 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
default: default:
// filter local to this stream. // filter local to this stream.
warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->last_object_description, this->m->last_object_description,
this->file->getLastOffset(), this->m->file->getLastOffset(),
"unknown encryption filter for streams" "unknown encryption filter for streams"
" (check " + method_source + ");" " (check " + method_source + ");"
" streams may be decrypted improperly")); " streams may be decrypted improperly"));
// To avoid repeated warnings, reset cf_stream. Assume // To avoid repeated warnings, reset cf_stream. Assume
// we'd want to use AES if V == 4. // we'd want to use AES if V == 4.
this->cf_stream = e_aes; this->m->cf_stream = e_aes;
break; break;
} }
} }
@ -1331,13 +1337,13 @@ QPDF::compute_encryption_parameters_V5(
std::string const& std::string const&
QPDF::getPaddedUserPassword() const QPDF::getPaddedUserPassword() const
{ {
return this->user_password; return this->m->user_password;
} }
std::string std::string
QPDF::getTrimmedUserPassword() const QPDF::getTrimmedUserPassword() const
{ {
std::string result = this->user_password; std::string result = this->m->user_password;
trim_user_password(result); trim_user_password(result);
return result; return result;
} }
@ -1345,13 +1351,13 @@ QPDF::getTrimmedUserPassword() const
std::string std::string
QPDF::getEncryptionKey() const QPDF::getEncryptionKey() const
{ {
return this->encryption_key; return this->m->encryption_key;
} }
bool bool
QPDF::isEncrypted() const QPDF::isEncrypted() const
{ {
return this->encrypted; return this->m->encrypted;
} }
bool bool
@ -1368,7 +1374,7 @@ QPDF::isEncrypted(int& R, int& P, int& V,
encryption_method_e& string_method, encryption_method_e& string_method,
encryption_method_e& file_method) encryption_method_e& file_method)
{ {
if (this->encrypted) if (this->m->encrypted)
{ {
QPDFObjectHandle trailer = getTrailer(); QPDFObjectHandle trailer = getTrailer();
QPDFObjectHandle encrypt = trailer.getKey("/Encrypt"); QPDFObjectHandle encrypt = trailer.getKey("/Encrypt");
@ -1378,9 +1384,9 @@ QPDF::isEncrypted(int& R, int& P, int& V,
P = Pkey.getIntValue(); P = Pkey.getIntValue();
R = Rkey.getIntValue(); R = Rkey.getIntValue();
V = Vkey.getIntValue(); V = Vkey.getIntValue();
stream_method = this->cf_stream; stream_method = this->m->cf_stream;
string_method = this->cf_stream; string_method = this->m->cf_stream;
file_method = this->cf_file; file_method = this->m->cf_file;
return true; return true;
} }
else 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, QPDF::optimize(std::map<int, int> const& object_stream_data,
bool allow_changes) bool allow_changes)
{ {
if (! this->obj_user_to_objects.empty()) if (! this->m->obj_user_to_objects.empty())
{ {
// already optimized // already optimized
return; 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 // 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); pushInheritedAttributesToPage(allow_changes, false);
// Traverse pages // Traverse pages
int n = this->all_pages.size(); int n = this->m->all_pages.size();
for (int pageno = 0; pageno < n; ++pageno) for (int pageno = 0; pageno < n; ++pageno)
{ {
updateObjectMaps(ObjUser(ObjUser::ou_page, pageno), updateObjectMaps(ObjUser(ObjUser::ou_page, pageno),
this->all_pages.at(pageno)); this->m->all_pages.at(pageno));
} }
// Traverse document-level items // 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(); for (std::set<std::string>::iterator iter = keys.begin();
iter != keys.end(); ++iter) iter != keys.end(); ++iter)
{ {
@ -107,7 +107,7 @@ QPDF::optimize(std::map<int, int> const& object_stream_data,
else else
{ {
updateObjectMaps(ObjUser(ObjUser::ou_trailer_key, key), 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); ObjUser root_ou = ObjUser(ObjUser::ou_root);
QPDFObjGen root_og = QPDFObjGen(root.getObjGen()); QPDFObjGen root_og = QPDFObjGen(root.getObjGen());
obj_user_to_objects[root_ou].insert(root_og); this->m->obj_user_to_objects[root_ou].insert(root_og);
object_to_obj_users[root_og].insert(root_ou); this->m->object_to_obj_users[root_og].insert(root_ou);
filterCompressedObjects(object_stream_data); 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 // The record of whether we've done this is cleared by
// updateAllPagesCache(). If we're warning for skipped keys, // updateAllPagesCache(). If we're warning for skipped keys,
// re-traverse unconditionally. // 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; 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 // key_ancestors is a mapping of page attribute keys to a stack of
// Pages nodes that contain values for them. // Pages nodes that contain values for them.
std::map<std::string, std::vector<QPDFObjectHandle> > key_ancestors; std::map<std::string, std::vector<QPDFObjectHandle> > key_ancestors;
this->all_pages.clear(); this->m->all_pages.clear();
pushInheritedAttributesToPageInternal( pushInheritedAttributesToPageInternal(
this->trailer.getKey("/Root").getKey("/Pages"), this->m->trailer.getKey("/Root").getKey("/Pages"),
key_ancestors, this->all_pages, allow_changes, warn_skipped_keys); key_ancestors, this->m->all_pages, allow_changes, warn_skipped_keys);
assert(key_ancestors.empty()); assert(key_ancestors.empty());
this->pushed_inherited_attributes_to_pages = true; this->m->pushed_inherited_attributes_to_pages = true;
} }
void void
@ -192,8 +192,8 @@ QPDF::pushInheritedAttributesToPageInternal2(
if (visited.count(this_og) > 0) if (visited.count(this_og) > 0)
{ {
throw QPDFExc( throw QPDFExc(
qpdf_e_pages, this->file->getName(), qpdf_e_pages, this->m->file->getName(),
this->last_object_description, 0, this->m->last_object_description, 0,
"Loop detected in /Pages structure (inherited attributes)"); "Loop detected in /Pages structure (inherited attributes)");
} }
visited.insert(this_og); visited.insert(this_og);
@ -219,9 +219,9 @@ QPDF::pushInheritedAttributesToPageInternal2(
{ {
if (! allow_changes) if (! allow_changes)
{ {
throw QPDFExc(qpdf_e_internal, this->file->getName(), throw QPDFExc(qpdf_e_internal, this->m->file->getName(),
this->last_object_description, this->m->last_object_description,
this->file->getLastOffset(), this->m->file->getLastOffset(),
"optimize detected an " "optimize detected an "
"inheritable attribute when called " "inheritable attribute when called "
"in no-change mode"); "in no-change mode");
@ -269,8 +269,8 @@ QPDF::pushInheritedAttributesToPageInternal2(
setLastObjectDescription("Pages object", setLastObjectDescription("Pages object",
cur_pages.getObjectID(), cur_pages.getObjectID(),
cur_pages.getGeneration()); cur_pages.getGeneration());
warn(QPDFExc(qpdf_e_pages, this->file->getName(), warn(QPDFExc(qpdf_e_pages, this->m->file->getName(),
this->last_object_description, 0, this->m->last_object_description, 0,
"Unknown key " + key + " in /Pages object" "Unknown key " + key + " in /Pages object"
" is being discarded as a result of" " is being discarded as a result of"
" flattening the /Pages tree")); " flattening the /Pages tree"));
@ -337,9 +337,9 @@ QPDF::pushInheritedAttributesToPageInternal2(
} }
else else
{ {
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
this->last_object_description, this->m->last_object_description,
this->file->getLastOffset(), this->m->file->getLastOffset(),
"invalid Type " + type + " in page tree"); "invalid Type " + type + " in page tree");
} }
visited.erase(this_og); visited.erase(this_og);
@ -382,8 +382,8 @@ QPDF::updateObjectMapsInternal(ObjUser const& ou, QPDFObjectHandle oh,
QTC::TC("qpdf", "QPDF opt loop detected"); QTC::TC("qpdf", "QPDF opt loop detected");
return; return;
} }
this->obj_user_to_objects[ou].insert(og); this->m->obj_user_to_objects[ou].insert(og);
this->object_to_obj_users[og].insert(ou); this->m->object_to_obj_users[og].insert(ou);
visited.insert(og); 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; std::map<QPDFObjGen, std::set<ObjUser> > t_object_to_obj_users;
for (std::map<ObjUser, std::set<QPDFObjGen> >::iterator i1 = for (std::map<ObjUser, std::set<QPDFObjGen> >::iterator i1 =
this->obj_user_to_objects.begin(); this->m->obj_user_to_objects.begin();
i1 != this->obj_user_to_objects.end(); ++i1) i1 != this->m->obj_user_to_objects.end(); ++i1)
{ {
ObjUser const& ou = (*i1).first; ObjUser const& ou = (*i1).first;
std::set<QPDFObjGen> const& objects = (*i1).second; 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 = for (std::map<QPDFObjGen, std::set<ObjUser> >::iterator i1 =
this->object_to_obj_users.begin(); this->m->object_to_obj_users.begin();
i1 != this->object_to_obj_users.end(); ++i1) i1 != this->m->object_to_obj_users.end(); ++i1)
{ {
QPDFObjGen const& og = (*i1).first; QPDFObjGen const& og = (*i1).first;
std::set<ObjUser> const& objusers = (*i1).second; 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->m->obj_user_to_objects = t_obj_user_to_objects;
this->object_to_obj_users = t_object_to_obj_users; this->m->object_to_obj_users = t_object_to_obj_users;
} }

View File

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