diff options
Diffstat (limited to 'libqpdf')
-rw-r--r-- | libqpdf/QPDF.cc | 39 | ||||
-rw-r--r-- | libqpdf/QPDFWriter.cc | 133 | ||||
-rw-r--r-- | libqpdf/QPDF_optimization.cc | 101 |
3 files changed, 113 insertions, 160 deletions
diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 777fb837..fac79796 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1860,28 +1860,6 @@ QPDF::swapObjects(int objid1, int generation1, int objid2, int generation2) this->obj_cache[og2] = t; } -void -QPDF::trimTrailerForWrite() -{ - // Note that removing the encryption dictionary does not interfere - // with reading encrypted files. QPDF loads all the information - // it needs from the encryption dictionary at the beginning and - // never looks at it again. - this->trailer.removeKey("/ID"); - this->trailer.removeKey("/Encrypt"); - this->trailer.removeKey("/Prev"); - - // Remove all trailer keys that potentially come from a - // cross-reference stream - this->trailer.removeKey("/Index"); - this->trailer.removeKey("/W"); - this->trailer.removeKey("/Length"); - this->trailer.removeKey("/Filter"); - this->trailer.removeKey("/DecodeParms"); - this->trailer.removeKey("/Type"); - this->trailer.removeKey("/XRefStm"); -} - std::string QPDF::getFilename() const { @@ -2067,20 +2045,3 @@ QPDF::pipeStreamData(int objid, int generation, } pipeline->finish(); } - -void -QPDF::decodeStreams() -{ - for (std::map<ObjGen, QPDFXRefEntry>::iterator iter = - this->xref_table.begin(); - iter != this->xref_table.end(); ++iter) - { - ObjGen const& og = (*iter).first; - QPDFObjectHandle obj = getObjectByID(og.obj, og.gen); - if (obj.isStream()) - { - Pl_Discard pl; - obj.pipeStreamData(&pl, true, false, false); - } - } -} diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index c1e4e1dd..05b5a9a9 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -834,16 +834,6 @@ QPDFWriter::enqueueObject(QPDFObjectHandle object) { // This is a place-holder object for an object stream } - else if (object.isScalar()) - { - // flattenScalarReferences is supposed to have removed all - // indirect scalars. - throw std::logic_error( - "INTERNAL ERROR: QPDFWriter::enqueueObject: indirect scalar: " + - std::string(this->filename) + " " + - QUtil::int_to_string(object.getObjectID()) + " " + - QUtil::int_to_string(object.getGeneration())); - } int objid = object.getObjectID(); if (obj_renumber.count(objid) == 0) @@ -916,15 +906,6 @@ QPDFWriter::unparseChild(QPDFObjectHandle child, int level, int flags) } if (child.isIndirect()) { - if (child.isScalar()) - { - // flattenScalarReferences is supposed to have removed all - // indirect scalars. - throw std::logic_error( - "INTERNAL ERROR: QPDFWriter::unparseChild: indirect scalar: " + - QUtil::int_to_string(child.getObjectID()) + " " + - QUtil::int_to_string(child.getGeneration())); - } int old_id = child.getObjectID(); int new_id = obj_renumber[old_id]; writeString(QUtil::int_to_string(new_id)); @@ -1648,6 +1629,117 @@ QPDFWriter::generateObjectStreams() } void +QPDFWriter::prepareFileForWrite() +{ + // Remove keys from the trailer that necessarily have to be + // replaced when writing the file. + + QPDFObjectHandle trailer = pdf.getTrailer(); + + // Note that removing the encryption dictionary does not interfere + // with reading encrypted files. QPDF loads all the information + // it needs from the encryption dictionary at the beginning and + // never looks at it again. + trailer.removeKey("/ID"); + trailer.removeKey("/Encrypt"); + trailer.removeKey("/Prev"); + + // Remove all trailer keys that potentially come from a + // cross-reference stream + trailer.removeKey("/Index"); + trailer.removeKey("/W"); + trailer.removeKey("/Length"); + trailer.removeKey("/Filter"); + trailer.removeKey("/DecodeParms"); + trailer.removeKey("/Type"); + trailer.removeKey("/XRefStm"); + + // Do a traversal of the entire PDF file structure replacing all + // indirect objects that QPDFWriter wants to be direct. This + // includes stream lengths, stream filtering parameters, and + // document extension level information. Also replace all + // indirect null references with direct nulls. This way, the only + // indirect nulls queued for output will be object stream place + // holders. + + std::list<QPDFObjectHandle> queue; + queue.push_back(pdf.getTrailer()); + std::set<int> visited; + + while (! queue.empty()) + { + QPDFObjectHandle node = queue.front(); + queue.pop_front(); + if (node.isIndirect()) + { + if (visited.count(node.getObjectID()) > 0) + { + continue; + } + visited.insert(node.getObjectID()); + } + + if (node.isArray()) + { + int nitems = node.getArrayNItems(); + for (int i = 0; i < nitems; ++i) + { + QPDFObjectHandle oh = node.getArrayItem(i); + if (oh.isIndirect() && oh.isNull()) + { + QTC::TC("qpdf", "QPDFWriter flatten array null"); + oh.makeDirect(); + node.setArrayItem(i, oh); + } + else if (! oh.isScalar()) + { + queue.push_back(oh); + } + } + } + else if (node.isDictionary() || node.isStream()) + { + bool is_stream = false; + QPDFObjectHandle dict = node; + if (node.isStream()) + { + is_stream = true; + dict = node.getDict(); + } + + std::set<std::string> keys = dict.getKeys(); + for (std::set<std::string>::iterator iter = keys.begin(); + iter != keys.end(); ++iter) + { + std::string const& key = *iter; + QPDFObjectHandle oh = dict.getKey(key); + bool add_to_queue = true; + if (oh.isIndirect()) + { + if (is_stream) + { + if ((key == "/Length") || + (key == "/Filter") || + (key == "/DecodeParms")) + { + QTC::TC("qpdf", "QPDF make stream key direct"); + add_to_queue = false; + oh.makeDirect(); + dict.replaceKey(key, oh); + } + } + } + + if (add_to_queue) + { + queue.push_back(oh); + } + } + } + } +} + +void QPDFWriter::write() { // Do preliminary setup @@ -1785,8 +1877,7 @@ QPDFWriter::write() generateID(); - pdf.trimTrailerForWrite(); - pdf.flattenScalarReferences(); + prepareFileForWrite(); if (this->linearized) { diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index 67f147f3..f832a883 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -59,103 +59,6 @@ QPDF::ObjUser::operator<(ObjUser const& rhs) const } void -QPDF::flattenScalarReferences() -{ - // Do a traversal of the entire PDF file structure replacing all - // indirect objects that are not arrays, streams, or dictionaries - // with direct objects. - - std::list<QPDFObjectHandle> queue; - queue.push_back(this->trailer); - std::set<ObjGen> visited; - - // Add every object in the xref table to the queue. This ensures - // that we flatten scalar references in unreferenced objects. - // This becomes important if we are preserving object streams in a - // file that has unreferenced objects in its object streams. (See - // QPDF bug 2974522 at SourceForge.) - for (std::map<ObjGen, QPDFXRefEntry>::iterator iter = - this->xref_table.begin(); - iter != this->xref_table.end(); ++iter) - { - ObjGen const& og = (*iter).first; - queue.push_back(getObjectByID(og.obj, og.gen)); - } - - while (! queue.empty()) - { - QPDFObjectHandle node = queue.front(); - queue.pop_front(); - if (node.isIndirect()) - { - ObjGen og(node.getObjectID(), node.getGeneration()); - if (visited.count(og) > 0) - { - continue; - } - visited.insert(og); - } - - if (node.isArray()) - { - int nitems = node.getArrayNItems(); - for (int i = 0; i < nitems; ++i) - { - QPDFObjectHandle oh = node.getArrayItem(i); - if (oh.isScalar()) - { - if (oh.isIndirect()) - { - QTC::TC("qpdf", "QPDF opt flatten array scalar"); - oh.makeDirect(); - node.setArrayItem(i, oh); - } - } - else - { - queue.push_back(oh); - } - } - } - else if (node.isDictionary() || node.isStream()) - { - QPDFObjectHandle dict = node; - if (node.isStream()) - { - dict = node.getDict(); - } - std::set<std::string> keys = dict.getKeys(); - for (std::set<std::string>::iterator iter = keys.begin(); - iter != keys.end(); ++iter) - { - std::string const& key = *iter; - QPDFObjectHandle oh = dict.getKey(key); - if (oh.isNull()) - { - // QPDF_Dictionary.getKeys() never returns null - // keys. - throw std::logic_error( - "INTERNAL ERROR: dictionary with null key found"); - } - else if (oh.isScalar()) - { - if (oh.isIndirect()) - { - QTC::TC("qpdf", "QPDF opt flatten dict scalar"); - oh.makeDirect(); - dict.replaceKey(key, oh); - } - } - else - { - queue.push_back(oh); - } - } - } - } -} - -void QPDF::optimize(std::map<int, int> const& object_stream_data, bool allow_changes) { @@ -304,9 +207,7 @@ QPDF::pushInheritedAttributesToPageInternal( } else { - // Don't defeat flattenScalarReferences which - // would have already been called by this - // time. + // It's okay to copy scalars. QTC::TC("qpdf", "QPDF opt inherited scalar"); } } |