aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2012-12-27 17:39:01 +0100
committerJay Berkenbilt <ejb@ql.org>2012-12-31 11:36:48 +0100
commit04c203ae060458ae39253263c7dd1c603b931bf0 (patch)
tree2ef17e10a0e62146ae9a8b57d1c6e86fe452bf5d /libqpdf
parentb4b8b28ed237f127c6b8c31fbb244efacc70e0a8 (diff)
downloadqpdf-04c203ae060458ae39253263c7dd1c603b931bf0.tar.zst
Eliminate flattenScalarReferences
Diffstat (limited to 'libqpdf')
-rw-r--r--libqpdf/QPDF.cc39
-rw-r--r--libqpdf/QPDFWriter.cc133
-rw-r--r--libqpdf/QPDF_optimization.cc101
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");
}
}