Commit b6f5330d9b6ca2564c9860b6626fb28d14af315c

Authored by m-holger
1 parent b20e89b8

Use QPDFObjectHandle::as_dictionary instead of getKeys in library

libqpdf/QPDF.cc
... ... @@ -2352,8 +2352,10 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top)
2352 2352 }
2353 2353 } else if (foreign_tc == ::ot_dictionary) {
2354 2354 QTC::TC("qpdf", "QPDF reserve dictionary");
2355   - for (auto const& key: foreign.getKeys()) {
2356   - reserveObjects(foreign.getKey(key), obj_copier, false);
  2355 + for (auto const& item: foreign.as_dictionary()) {
  2356 + if (!item.second.null()) {
  2357 + reserveObjects(item.second, obj_copier, false);
  2358 + }
2357 2359 }
2358 2360 } else if (foreign_tc == ::ot_stream) {
2359 2361 QTC::TC("qpdf", "QPDF reserve stream");
... ... @@ -2391,21 +2393,20 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier& obj_cop
2391 2393 } else if (foreign_tc == ::ot_dictionary) {
2392 2394 QTC::TC("qpdf", "QPDF replace dictionary");
2393 2395 result = QPDFObjectHandle::newDictionary();
2394   - std::set<std::string> keys = foreign.getKeys();
2395   - for (auto const& iter: keys) {
2396   - result.replaceKey(
2397   - iter, replaceForeignIndirectObjects(foreign.getKey(iter), obj_copier, false));
  2396 + for (auto const& [key, value]: foreign.as_dictionary()) {
  2397 + if (!value.null()) {
  2398 + result.replaceKey(key, replaceForeignIndirectObjects(value, obj_copier, false));
  2399 + }
2398 2400 }
2399 2401 } else if (foreign_tc == ::ot_stream) {
2400 2402 QTC::TC("qpdf", "QPDF replace stream");
2401 2403 result = obj_copier.object_map[foreign.getObjGen()];
2402   - result.assertStream();
2403 2404 QPDFObjectHandle dict = result.getDict();
2404 2405 QPDFObjectHandle old_dict = foreign.getDict();
2405   - std::set<std::string> keys = old_dict.getKeys();
2406   - for (auto const& iter: keys) {
2407   - dict.replaceKey(
2408   - iter, replaceForeignIndirectObjects(old_dict.getKey(iter), obj_copier, false));
  2406 + for (auto const& [key, value]: old_dict.as_dictionary()) {
  2407 + if (!value.null()) {
  2408 + dict.replaceKey(key, replaceForeignIndirectObjects(value, obj_copier, false));
  2409 + }
2409 2410 }
2410 2411 copyStreamData(result, foreign);
2411 2412 } else {
... ... @@ -2689,24 +2690,29 @@ QPDF::getCompressibleObjGens()
2689 2690 }
2690 2691 }
2691 2692 if (obj.isStream()) {
2692   - QPDFObjectHandle dict = obj.getDict();
2693   - std::set<std::string> keys = dict.getKeys();
2694   - for (auto iter = keys.rbegin(); iter != keys.rend(); ++iter) {
2695   - std::string const& key = *iter;
2696   - QPDFObjectHandle value = dict.getKey(key);
2697   - if (key == "/Length") {
2698   - // omit stream lengths
2699   - if (value.isIndirect()) {
2700   - QTC::TC("qpdf", "QPDF exclude indirect length");
  2693 + auto dict = obj.getDict().as_dictionary();
  2694 + auto end = dict.crend();
  2695 + for (auto iter = dict.crbegin(); iter != end; ++iter) {
  2696 + std::string const& key = iter->first;
  2697 + QPDFObjectHandle const& value = iter->second;
  2698 + if (!value.null()) {
  2699 + if (key == "/Length") {
  2700 + // omit stream lengths
  2701 + if (value.isIndirect()) {
  2702 + QTC::TC("qpdf", "QPDF exclude indirect length");
  2703 + }
  2704 + } else {
  2705 + queue.emplace_back(value);
2701 2706 }
2702   - } else {
2703   - queue.push_back(value);
2704 2707 }
2705 2708 }
2706 2709 } else if (obj.isDictionary()) {
2707   - std::set<std::string> keys = obj.getKeys();
2708   - for (auto iter = keys.rbegin(); iter != keys.rend(); ++iter) {
2709   - queue.push_back(obj.getKey(*iter));
  2710 + auto dict = obj.as_dictionary();
  2711 + auto end = dict.crend();
  2712 + for (auto iter = dict.crbegin(); iter != end; ++iter) {
  2713 + if (!iter->second.null()) {
  2714 + queue.emplace_back(iter->second);
  2715 + }
2710 2716 }
2711 2717 } else if (obj.isArray()) {
2712 2718 int n = obj.getArrayNItems();
... ...
libqpdf/QPDFJob.cc
... ... @@ -19,6 +19,7 @@
19 19 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
20 20 #include <qpdf/QPDFExc.hh>
21 21 #include <qpdf/QPDFLogger.hh>
  22 +#include <qpdf/QPDFObjectHandle_private.hh>
22 23 #include <qpdf/QPDFOutlineDocumentHelper.hh>
23 24 #include <qpdf/QPDFPageDocumentHelper.hh>
24 25 #include <qpdf/QPDFPageLabelDocumentHelper.hh>
... ... @@ -2347,12 +2348,10 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2347 2348 return true;
2348 2349 }
2349 2350 }
2350   - if (xobject.isDictionary()) {
2351   - for (auto const& k: xobject.getKeys()) {
2352   - QPDFObjectHandle xobj = xobject.getKey(k);
2353   - if (xobj.isFormXObject()) {
2354   - queue.push_back(xobj);
2355   - }
  2351 +
  2352 + for (auto const& xobj: xobject.as_dictionary()) {
  2353 + if (xobj.second.isFormXObject()) {
  2354 + queue.emplace_back(xobj.second);
2356 2355 }
2357 2356 }
2358 2357 }
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -1257,14 +1257,10 @@ QPDFObjectHandle::getResourceNames() const
1257 1257 {
1258 1258 // Return second-level dictionary keys
1259 1259 std::set<std::string> result;
1260   - if (!isDictionary()) {
1261   - return result;
1262   - }
1263   - for (auto const& key: getKeys()) {
1264   - QPDFObjectHandle val = getKey(key);
1265   - if (val.isDictionary()) {
1266   - for (auto const& val_key: val.getKeys()) {
1267   - result.insert(val_key);
  1260 + for (auto const& item: as_dictionary(strict)) {
  1261 + for (auto const& [key2, val2]: item.second.as_dictionary(strict)) {
  1262 + if (!val2.null()) {
  1263 + result.insert(key2);
1268 1264 }
1269 1265 }
1270 1266 }
... ... @@ -1956,10 +1952,11 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams)
1956 1952 this->obj = QPDFObject::create<QPDF_Array>(items);
1957 1953 } else if (isDictionary()) {
1958 1954 std::map<std::string, QPDFObjectHandle> items;
1959   - auto dict = as_dictionary(strict);
1960   - for (auto const& key: getKeys()) {
1961   - items[key] = dict.getKey(key);
1962   - items[key].makeDirect(visited, stop_at_streams);
  1955 + for (auto const& [key, value]: as_dictionary(strict)) {
  1956 + if (!value.null()) {
  1957 + items.insert({key, value});
  1958 + items[key].makeDirect(visited, stop_at_streams);
  1959 + }
1963 1960 }
1964 1961 this->obj = QPDFObject::create<QPDF_Dictionary>(items);
1965 1962 } else if (isStream()) {
... ...
libqpdf/QPDFPageObjectHelper.cc
... ... @@ -7,6 +7,7 @@
7 7 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
8 8 #include <qpdf/QPDFExc.hh>
9 9 #include <qpdf/QPDFMatrix.hh>
  10 +#include <qpdf/QPDFObjectHandle_private.hh>
10 11 #include <qpdf/QTC.hh>
11 12 #include <qpdf/QUtil.hh>
12 13 #include <qpdf/ResourceFinder.hh>
... ... @@ -72,9 +73,12 @@ InlineImageTracker::convertIIDict(QPDFObjectHandle odict)
72 73 QPDFObjectHandle dict = QPDFObjectHandle::newDictionary();
73 74 dict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject"));
74 75 dict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Image"));
75   - std::set<std::string> keys = odict.getKeys();
76   - for (auto key: keys) {
77   - QPDFObjectHandle value = odict.getKey(key);
  76 + for (auto const& [k, v]: odict.as_dictionary()) {
  77 + if (v.null()) {
  78 + continue;
  79 + }
  80 + auto key = k;
  81 + auto value = v;
78 82 if (key == "/BPC") {
79 83 key = "/BitsPerComponent";
80 84 } else if (key == "/CS") {
... ... @@ -327,20 +331,21 @@ QPDFPageObjectHelper::forEachXObject(
327 331 recursive ? (oh().isFormXObject() ? 0 : 1) : (oh().isFormXObject() ? 2 : 3));
328 332 QPDFObjGen::set seen;
329 333 std::list<QPDFPageObjectHelper> queue;
330   - queue.push_back(*this);
  334 + queue.emplace_back(*this);
331 335 while (!queue.empty()) {
332 336 auto& ph = queue.front();
333 337 if (seen.add(ph)) {
334 338 auto xobj_dict = ph.getAttribute("/Resources", false).getKeyIfDict("/XObject");
335   - if (xobj_dict.isDictionary()) {
336   - for (auto const& key: xobj_dict.getKeys()) {
337   - QPDFObjectHandle obj = xobj_dict.getKey(key);
338   - if ((!selector) || selector(obj)) {
339   - action(obj, xobj_dict, key);
340   - }
341   - if (recursive && obj.isFormXObject()) {
342   - queue.emplace_back(obj);
343   - }
  339 + for (auto const& [key, value]: xobj_dict.as_dictionary()) {
  340 + if (value.null()) {
  341 + continue;
  342 + }
  343 + auto obj = value;
  344 + if ((!selector) || selector(obj)) {
  345 + action(obj, xobj_dict, key);
  346 + }
  347 + if (recursive && obj.isFormXObject()) {
  348 + queue.emplace_back(obj);
344 349 }
345 350 }
346 351 }
... ...
libqpdf/QPDFWriter.cc
... ... @@ -1173,7 +1173,10 @@ QPDFWriter::writeTrailer(
1173 1173 writeString(" /Size ");
1174 1174 writeString(std::to_string(size));
1175 1175 } else {
1176   - for (auto const& key: trailer.getKeys()) {
  1176 + for (auto const& [key, value]: trailer.as_dictionary()) {
  1177 + if (value.null()) {
  1178 + continue;
  1179 + }
1177 1180 writeStringQDF(" ");
1178 1181 writeStringNoQDF(" ");
1179 1182 writeString(Name::normalize(key));
... ... @@ -1187,7 +1190,7 @@ QPDFWriter::writeTrailer(
1187 1190 writePad(QIntC::to_size(pos - m->pipeline->getCount() + 21));
1188 1191 }
1189 1192 } else {
1190   - unparseChild(trailer.getKey(key), 1, 0);
  1193 + unparseChild(value, 1, 0);
1191 1194 }
1192 1195 writeStringQDF("\n");
1193 1196 }
... ... @@ -2937,8 +2940,10 @@ QPDFWriter::enqueueObjectsStandard()
2937 2940  
2938 2941 // Next place any other objects referenced from the trailer dictionary into the queue, handling
2939 2942 // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op.
2940   - for (auto const& key: trailer.getKeys()) {
2941   - enqueueObject(trailer.getKey(key));
  2943 + for (auto& item: trailer.as_dictionary()) {
  2944 + if (!item.second.null()) {
  2945 + enqueueObject(item.second);
  2946 + }
2942 2947 }
2943 2948 }
2944 2949  
... ... @@ -2960,9 +2965,11 @@ QPDFWriter::enqueueObjectsPCLm()
2960 2965  
2961 2966 // enqueue all the strips for each page
2962 2967 QPDFObjectHandle strips = page.getKey("/Resources").getKey("/XObject");
2963   - for (auto const& image: strips.getKeys()) {
2964   - enqueueObject(strips.getKey(image));
2965   - enqueueObject(QPDFObjectHandle::newStream(&m->pdf, image_transform_content));
  2968 + for (auto& image: strips.as_dictionary()) {
  2969 + if (!image.second.null()) {
  2970 + enqueueObject(image.second);
  2971 + enqueueObject(QPDFObjectHandle::newStream(&m->pdf, image_transform_content));
  2972 + }
2966 2973 }
2967 2974 }
2968 2975  
... ...
libqpdf/QPDF_optimization.cc
... ... @@ -114,24 +114,25 @@ QPDF::optimize_internal(
114 114 }
115 115  
116 116 // Traverse document-level items
117   - for (auto const& key: m->trailer.getKeys()) {
  117 + for (auto const& [key, value]: m->trailer.as_dictionary()) {
118 118 if (key == "/Root") {
119 119 // handled separately
120 120 } else {
121   - updateObjectMaps(
122   - ObjUser(ObjUser::ou_trailer_key, key),
123   - m->trailer.getKey(key),
124   - skip_stream_parameters);
  121 + if (!value.null()) {
  122 + updateObjectMaps(
  123 + ObjUser(ObjUser::ou_trailer_key, key), value, skip_stream_parameters);
  124 + }
125 125 }
126 126 }
127 127  
128   - for (auto const& key: root.getKeys()) {
  128 + for (auto const& [key, value]: root.as_dictionary()) {
129 129 // Technically, /I keys from /Thread dictionaries are supposed to be handled separately, but
130 130 // we are going to disregard that specification for now. There is loads of evidence that
131 131 // pdlin and Acrobat both disregard things like this from time to time, so this is almost
132 132 // certain not to cause any problems.
133   - updateObjectMaps(
134   - ObjUser(ObjUser::ou_root_key, key), root.getKey(key), skip_stream_parameters);
  133 + if (!value.null()) {
  134 + updateObjectMaps(ObjUser(ObjUser::ou_root_key, key), value, skip_stream_parameters);
  135 + }
135 136 }
136 137  
137 138 ObjUser root_ou = ObjUser(ObjUser::ou_root);
... ... @@ -333,7 +334,11 @@ QPDF::updateObjectMaps(
333 334 }
334 335 }
335 336  
336   - for (auto const& key: dict.getKeys()) {
  337 + for (auto& [key, value]: dict.as_dictionary()) {
  338 + if (value.null()) {
  339 + continue;
  340 + }
  341 +
337 342 if (is_page_node && (key == "/Thumb")) {
338 343 // Traverse page thumbnail dictionaries as a special case. There can only ever
339 344 // be one /Thumb key on a page, and we see at most one page node per call.
... ... @@ -346,7 +351,7 @@ QPDF::updateObjectMaps(
346 351 ((ssp >= 2) && ((key == "/Filter") || (key == "/DecodeParms")))) {
347 352 // Don't traverse into stream parameters that we are not going to write.
348 353 } else {
349   - pending.emplace_back(cur.ou, dict.getKey(key), false);
  354 + pending.emplace_back(cur.ou, value, false);
350 355 }
351 356 }
352 357 }
... ...