From b6f5330d9b6ca2564c9860b6626fb28d14af315c Mon Sep 17 00:00:00 2001 From: m-holger Date: Thu, 20 Feb 2025 18:09:38 +0000 Subject: [PATCH] Use QPDFObjectHandle::as_dictionary instead of getKeys in library --- libqpdf/QPDF.cc | 56 +++++++++++++++++++++++++++++++------------------------- libqpdf/QPDFJob.cc | 11 +++++------ libqpdf/QPDFObjectHandle.cc | 21 +++++++++------------ libqpdf/QPDFPageObjectHelper.cc | 31 ++++++++++++++++++------------- libqpdf/QPDFWriter.cc | 21 ++++++++++++++------- libqpdf/QPDF_optimization.cc | 25 +++++++++++++++---------- 6 files changed, 92 insertions(+), 73 deletions(-) diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index e89c48b..1a73085 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -2352,8 +2352,10 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top) } } else if (foreign_tc == ::ot_dictionary) { QTC::TC("qpdf", "QPDF reserve dictionary"); - for (auto const& key: foreign.getKeys()) { - reserveObjects(foreign.getKey(key), obj_copier, false); + for (auto const& item: foreign.as_dictionary()) { + if (!item.second.null()) { + reserveObjects(item.second, obj_copier, false); + } } } else if (foreign_tc == ::ot_stream) { QTC::TC("qpdf", "QPDF reserve stream"); @@ -2391,21 +2393,20 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier& obj_cop } else if (foreign_tc == ::ot_dictionary) { QTC::TC("qpdf", "QPDF replace dictionary"); result = QPDFObjectHandle::newDictionary(); - std::set keys = foreign.getKeys(); - for (auto const& iter: keys) { - result.replaceKey( - iter, replaceForeignIndirectObjects(foreign.getKey(iter), obj_copier, false)); + for (auto const& [key, value]: foreign.as_dictionary()) { + if (!value.null()) { + result.replaceKey(key, replaceForeignIndirectObjects(value, obj_copier, false)); + } } } else if (foreign_tc == ::ot_stream) { QTC::TC("qpdf", "QPDF replace stream"); result = obj_copier.object_map[foreign.getObjGen()]; - result.assertStream(); QPDFObjectHandle dict = result.getDict(); QPDFObjectHandle old_dict = foreign.getDict(); - std::set keys = old_dict.getKeys(); - for (auto const& iter: keys) { - dict.replaceKey( - iter, replaceForeignIndirectObjects(old_dict.getKey(iter), obj_copier, false)); + for (auto const& [key, value]: old_dict.as_dictionary()) { + if (!value.null()) { + dict.replaceKey(key, replaceForeignIndirectObjects(value, obj_copier, false)); + } } copyStreamData(result, foreign); } else { @@ -2689,24 +2690,29 @@ QPDF::getCompressibleObjGens() } } if (obj.isStream()) { - QPDFObjectHandle dict = obj.getDict(); - std::set keys = dict.getKeys(); - for (auto iter = keys.rbegin(); iter != keys.rend(); ++iter) { - std::string const& key = *iter; - QPDFObjectHandle value = dict.getKey(key); - if (key == "/Length") { - // omit stream lengths - if (value.isIndirect()) { - QTC::TC("qpdf", "QPDF exclude indirect length"); + auto dict = obj.getDict().as_dictionary(); + auto end = dict.crend(); + for (auto iter = dict.crbegin(); iter != end; ++iter) { + std::string const& key = iter->first; + QPDFObjectHandle const& value = iter->second; + if (!value.null()) { + if (key == "/Length") { + // omit stream lengths + if (value.isIndirect()) { + QTC::TC("qpdf", "QPDF exclude indirect length"); + } + } else { + queue.emplace_back(value); } - } else { - queue.push_back(value); } } } else if (obj.isDictionary()) { - std::set keys = obj.getKeys(); - for (auto iter = keys.rbegin(); iter != keys.rend(); ++iter) { - queue.push_back(obj.getKey(*iter)); + auto dict = obj.as_dictionary(); + auto end = dict.crend(); + for (auto iter = dict.crbegin(); iter != end; ++iter) { + if (!iter->second.null()) { + queue.emplace_back(iter->second); + } } } else if (obj.isArray()) { int n = obj.getArrayNItems(); diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index 8ea06dc..7553554 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -2347,12 +2348,10 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF& pdf) return true; } } - if (xobject.isDictionary()) { - for (auto const& k: xobject.getKeys()) { - QPDFObjectHandle xobj = xobject.getKey(k); - if (xobj.isFormXObject()) { - queue.push_back(xobj); - } + + for (auto const& xobj: xobject.as_dictionary()) { + if (xobj.second.isFormXObject()) { + queue.emplace_back(xobj.second); } } } diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 304c0bb..8ea2bae 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -1257,14 +1257,10 @@ QPDFObjectHandle::getResourceNames() const { // Return second-level dictionary keys std::set result; - if (!isDictionary()) { - return result; - } - for (auto const& key: getKeys()) { - QPDFObjectHandle val = getKey(key); - if (val.isDictionary()) { - for (auto const& val_key: val.getKeys()) { - result.insert(val_key); + for (auto const& item: as_dictionary(strict)) { + for (auto const& [key2, val2]: item.second.as_dictionary(strict)) { + if (!val2.null()) { + result.insert(key2); } } } @@ -1956,10 +1952,11 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams) this->obj = QPDFObject::create(items); } else if (isDictionary()) { std::map items; - auto dict = as_dictionary(strict); - for (auto const& key: getKeys()) { - items[key] = dict.getKey(key); - items[key].makeDirect(visited, stop_at_streams); + for (auto const& [key, value]: as_dictionary(strict)) { + if (!value.null()) { + items.insert({key, value}); + items[key].makeDirect(visited, stop_at_streams); + } } this->obj = QPDFObject::create(items); } else if (isStream()) { diff --git a/libqpdf/QPDFPageObjectHelper.cc b/libqpdf/QPDFPageObjectHelper.cc index 858b63a..1e22778 100644 --- a/libqpdf/QPDFPageObjectHelper.cc +++ b/libqpdf/QPDFPageObjectHelper.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -72,9 +73,12 @@ InlineImageTracker::convertIIDict(QPDFObjectHandle odict) QPDFObjectHandle dict = QPDFObjectHandle::newDictionary(); dict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject")); dict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Image")); - std::set keys = odict.getKeys(); - for (auto key: keys) { - QPDFObjectHandle value = odict.getKey(key); + for (auto const& [k, v]: odict.as_dictionary()) { + if (v.null()) { + continue; + } + auto key = k; + auto value = v; if (key == "/BPC") { key = "/BitsPerComponent"; } else if (key == "/CS") { @@ -327,20 +331,21 @@ QPDFPageObjectHelper::forEachXObject( recursive ? (oh().isFormXObject() ? 0 : 1) : (oh().isFormXObject() ? 2 : 3)); QPDFObjGen::set seen; std::list queue; - queue.push_back(*this); + queue.emplace_back(*this); while (!queue.empty()) { auto& ph = queue.front(); if (seen.add(ph)) { auto xobj_dict = ph.getAttribute("/Resources", false).getKeyIfDict("/XObject"); - if (xobj_dict.isDictionary()) { - for (auto const& key: xobj_dict.getKeys()) { - QPDFObjectHandle obj = xobj_dict.getKey(key); - if ((!selector) || selector(obj)) { - action(obj, xobj_dict, key); - } - if (recursive && obj.isFormXObject()) { - queue.emplace_back(obj); - } + for (auto const& [key, value]: xobj_dict.as_dictionary()) { + if (value.null()) { + continue; + } + auto obj = value; + if ((!selector) || selector(obj)) { + action(obj, xobj_dict, key); + } + if (recursive && obj.isFormXObject()) { + queue.emplace_back(obj); } } } diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 00b03b0..401300f 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -1173,7 +1173,10 @@ QPDFWriter::writeTrailer( writeString(" /Size "); writeString(std::to_string(size)); } else { - for (auto const& key: trailer.getKeys()) { + for (auto const& [key, value]: trailer.as_dictionary()) { + if (value.null()) { + continue; + } writeStringQDF(" "); writeStringNoQDF(" "); writeString(Name::normalize(key)); @@ -1187,7 +1190,7 @@ QPDFWriter::writeTrailer( writePad(QIntC::to_size(pos - m->pipeline->getCount() + 21)); } } else { - unparseChild(trailer.getKey(key), 1, 0); + unparseChild(value, 1, 0); } writeStringQDF("\n"); } @@ -2937,8 +2940,10 @@ QPDFWriter::enqueueObjectsStandard() // Next place any other objects referenced from the trailer dictionary into the queue, handling // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op. - for (auto const& key: trailer.getKeys()) { - enqueueObject(trailer.getKey(key)); + for (auto& item: trailer.as_dictionary()) { + if (!item.second.null()) { + enqueueObject(item.second); + } } } @@ -2960,9 +2965,11 @@ QPDFWriter::enqueueObjectsPCLm() // enqueue all the strips for each page QPDFObjectHandle strips = page.getKey("/Resources").getKey("/XObject"); - for (auto const& image: strips.getKeys()) { - enqueueObject(strips.getKey(image)); - enqueueObject(QPDFObjectHandle::newStream(&m->pdf, image_transform_content)); + for (auto& image: strips.as_dictionary()) { + if (!image.second.null()) { + enqueueObject(image.second); + enqueueObject(QPDFObjectHandle::newStream(&m->pdf, image_transform_content)); + } } } diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index 1fd66b6..0c757f0 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -114,24 +114,25 @@ QPDF::optimize_internal( } // Traverse document-level items - for (auto const& key: m->trailer.getKeys()) { + for (auto const& [key, value]: m->trailer.as_dictionary()) { if (key == "/Root") { // handled separately } else { - updateObjectMaps( - ObjUser(ObjUser::ou_trailer_key, key), - m->trailer.getKey(key), - skip_stream_parameters); + if (!value.null()) { + updateObjectMaps( + ObjUser(ObjUser::ou_trailer_key, key), value, skip_stream_parameters); + } } } - for (auto const& key: root.getKeys()) { + for (auto const& [key, value]: root.as_dictionary()) { // Technically, /I keys from /Thread dictionaries are supposed to be handled separately, but // we are going to disregard that specification for now. There is loads of evidence that // pdlin and Acrobat both disregard things like this from time to time, so this is almost // certain not to cause any problems. - updateObjectMaps( - ObjUser(ObjUser::ou_root_key, key), root.getKey(key), skip_stream_parameters); + if (!value.null()) { + updateObjectMaps(ObjUser(ObjUser::ou_root_key, key), value, skip_stream_parameters); + } } ObjUser root_ou = ObjUser(ObjUser::ou_root); @@ -333,7 +334,11 @@ QPDF::updateObjectMaps( } } - for (auto const& key: dict.getKeys()) { + for (auto& [key, value]: dict.as_dictionary()) { + if (value.null()) { + continue; + } + if (is_page_node && (key == "/Thumb")) { // Traverse page thumbnail dictionaries as a special case. There can only ever // be one /Thumb key on a page, and we see at most one page node per call. @@ -346,7 +351,7 @@ QPDF::updateObjectMaps( ((ssp >= 2) && ((key == "/Filter") || (key == "/DecodeParms")))) { // Don't traverse into stream parameters that we are not going to write. } else { - pending.emplace_back(cur.ou, dict.getKey(key), false); + pending.emplace_back(cur.ou, value, false); } } } -- libgit2 0.21.4