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,8 +2352,10 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top)
2352 } 2352 }
2353 } else if (foreign_tc == ::ot_dictionary) { 2353 } else if (foreign_tc == ::ot_dictionary) {
2354 QTC::TC("qpdf", "QPDF reserve dictionary"); 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 } else if (foreign_tc == ::ot_stream) { 2360 } else if (foreign_tc == ::ot_stream) {
2359 QTC::TC("qpdf", "QPDF reserve stream"); 2361 QTC::TC("qpdf", "QPDF reserve stream");
@@ -2391,21 +2393,20 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier& obj_cop @@ -2391,21 +2393,20 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier& obj_cop
2391 } else if (foreign_tc == ::ot_dictionary) { 2393 } else if (foreign_tc == ::ot_dictionary) {
2392 QTC::TC("qpdf", "QPDF replace dictionary"); 2394 QTC::TC("qpdf", "QPDF replace dictionary");
2393 result = QPDFObjectHandle::newDictionary(); 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 } else if (foreign_tc == ::ot_stream) { 2401 } else if (foreign_tc == ::ot_stream) {
2400 QTC::TC("qpdf", "QPDF replace stream"); 2402 QTC::TC("qpdf", "QPDF replace stream");
2401 result = obj_copier.object_map[foreign.getObjGen()]; 2403 result = obj_copier.object_map[foreign.getObjGen()];
2402 - result.assertStream();  
2403 QPDFObjectHandle dict = result.getDict(); 2404 QPDFObjectHandle dict = result.getDict();
2404 QPDFObjectHandle old_dict = foreign.getDict(); 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 copyStreamData(result, foreign); 2411 copyStreamData(result, foreign);
2411 } else { 2412 } else {
@@ -2689,24 +2690,29 @@ QPDF::getCompressibleObjGens() @@ -2689,24 +2690,29 @@ QPDF::getCompressibleObjGens()
2689 } 2690 }
2690 } 2691 }
2691 if (obj.isStream()) { 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 } else if (obj.isDictionary()) { 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 } else if (obj.isArray()) { 2717 } else if (obj.isArray()) {
2712 int n = obj.getArrayNItems(); 2718 int n = obj.getArrayNItems();
libqpdf/QPDFJob.cc
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> 19 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
20 #include <qpdf/QPDFExc.hh> 20 #include <qpdf/QPDFExc.hh>
21 #include <qpdf/QPDFLogger.hh> 21 #include <qpdf/QPDFLogger.hh>
  22 +#include <qpdf/QPDFObjectHandle_private.hh>
22 #include <qpdf/QPDFOutlineDocumentHelper.hh> 23 #include <qpdf/QPDFOutlineDocumentHelper.hh>
23 #include <qpdf/QPDFPageDocumentHelper.hh> 24 #include <qpdf/QPDFPageDocumentHelper.hh>
24 #include <qpdf/QPDFPageLabelDocumentHelper.hh> 25 #include <qpdf/QPDFPageLabelDocumentHelper.hh>
@@ -2347,12 +2348,10 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf) @@ -2347,12 +2348,10 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2347 return true; 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,14 +1257,10 @@ QPDFObjectHandle::getResourceNames() const
1257 { 1257 {
1258 // Return second-level dictionary keys 1258 // Return second-level dictionary keys
1259 std::set<std::string> result; 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,10 +1952,11 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams)
1956 this->obj = QPDFObject::create<QPDF_Array>(items); 1952 this->obj = QPDFObject::create<QPDF_Array>(items);
1957 } else if (isDictionary()) { 1953 } else if (isDictionary()) {
1958 std::map<std::string, QPDFObjectHandle> items; 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 this->obj = QPDFObject::create<QPDF_Dictionary>(items); 1961 this->obj = QPDFObject::create<QPDF_Dictionary>(items);
1965 } else if (isStream()) { 1962 } else if (isStream()) {
libqpdf/QPDFPageObjectHelper.cc
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 #include <qpdf/QPDFAcroFormDocumentHelper.hh> 7 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
8 #include <qpdf/QPDFExc.hh> 8 #include <qpdf/QPDFExc.hh>
9 #include <qpdf/QPDFMatrix.hh> 9 #include <qpdf/QPDFMatrix.hh>
  10 +#include <qpdf/QPDFObjectHandle_private.hh>
10 #include <qpdf/QTC.hh> 11 #include <qpdf/QTC.hh>
11 #include <qpdf/QUtil.hh> 12 #include <qpdf/QUtil.hh>
12 #include <qpdf/ResourceFinder.hh> 13 #include <qpdf/ResourceFinder.hh>
@@ -72,9 +73,12 @@ InlineImageTracker::convertIIDict(QPDFObjectHandle odict) @@ -72,9 +73,12 @@ InlineImageTracker::convertIIDict(QPDFObjectHandle odict)
72 QPDFObjectHandle dict = QPDFObjectHandle::newDictionary(); 73 QPDFObjectHandle dict = QPDFObjectHandle::newDictionary();
73 dict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject")); 74 dict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject"));
74 dict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Image")); 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 if (key == "/BPC") { 82 if (key == "/BPC") {
79 key = "/BitsPerComponent"; 83 key = "/BitsPerComponent";
80 } else if (key == "/CS") { 84 } else if (key == "/CS") {
@@ -327,20 +331,21 @@ QPDFPageObjectHelper::forEachXObject( @@ -327,20 +331,21 @@ QPDFPageObjectHelper::forEachXObject(
327 recursive ? (oh().isFormXObject() ? 0 : 1) : (oh().isFormXObject() ? 2 : 3)); 331 recursive ? (oh().isFormXObject() ? 0 : 1) : (oh().isFormXObject() ? 2 : 3));
328 QPDFObjGen::set seen; 332 QPDFObjGen::set seen;
329 std::list<QPDFPageObjectHelper> queue; 333 std::list<QPDFPageObjectHelper> queue;
330 - queue.push_back(*this); 334 + queue.emplace_back(*this);
331 while (!queue.empty()) { 335 while (!queue.empty()) {
332 auto& ph = queue.front(); 336 auto& ph = queue.front();
333 if (seen.add(ph)) { 337 if (seen.add(ph)) {
334 auto xobj_dict = ph.getAttribute("/Resources", false).getKeyIfDict("/XObject"); 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,7 +1173,10 @@ QPDFWriter::writeTrailer(
1173 writeString(" /Size "); 1173 writeString(" /Size ");
1174 writeString(std::to_string(size)); 1174 writeString(std::to_string(size));
1175 } else { 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 writeStringQDF(" "); 1180 writeStringQDF(" ");
1178 writeStringNoQDF(" "); 1181 writeStringNoQDF(" ");
1179 writeString(Name::normalize(key)); 1182 writeString(Name::normalize(key));
@@ -1187,7 +1190,7 @@ QPDFWriter::writeTrailer( @@ -1187,7 +1190,7 @@ QPDFWriter::writeTrailer(
1187 writePad(QIntC::to_size(pos - m->pipeline->getCount() + 21)); 1190 writePad(QIntC::to_size(pos - m->pipeline->getCount() + 21));
1188 } 1191 }
1189 } else { 1192 } else {
1190 - unparseChild(trailer.getKey(key), 1, 0); 1193 + unparseChild(value, 1, 0);
1191 } 1194 }
1192 writeStringQDF("\n"); 1195 writeStringQDF("\n");
1193 } 1196 }
@@ -2937,8 +2940,10 @@ QPDFWriter::enqueueObjectsStandard() @@ -2937,8 +2940,10 @@ QPDFWriter::enqueueObjectsStandard()
2937 2940
2938 // Next place any other objects referenced from the trailer dictionary into the queue, handling 2941 // Next place any other objects referenced from the trailer dictionary into the queue, handling
2939 // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op. 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,9 +2965,11 @@ QPDFWriter::enqueueObjectsPCLm()
2960 2965
2961 // enqueue all the strips for each page 2966 // enqueue all the strips for each page
2962 QPDFObjectHandle strips = page.getKey("/Resources").getKey("/XObject"); 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,24 +114,25 @@ QPDF::optimize_internal(
114 } 114 }
115 115
116 // Traverse document-level items 116 // Traverse document-level items
117 - for (auto const& key: m->trailer.getKeys()) { 117 + for (auto const& [key, value]: m->trailer.as_dictionary()) {
118 if (key == "/Root") { 118 if (key == "/Root") {
119 // handled separately 119 // handled separately
120 } else { 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 // Technically, /I keys from /Thread dictionaries are supposed to be handled separately, but 129 // Technically, /I keys from /Thread dictionaries are supposed to be handled separately, but
130 // we are going to disregard that specification for now. There is loads of evidence that 130 // we are going to disregard that specification for now. There is loads of evidence that
131 // pdlin and Acrobat both disregard things like this from time to time, so this is almost 131 // pdlin and Acrobat both disregard things like this from time to time, so this is almost
132 // certain not to cause any problems. 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 ObjUser root_ou = ObjUser(ObjUser::ou_root); 138 ObjUser root_ou = ObjUser(ObjUser::ou_root);
@@ -333,7 +334,11 @@ QPDF::updateObjectMaps( @@ -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 if (is_page_node && (key == "/Thumb")) { 342 if (is_page_node && (key == "/Thumb")) {
338 // Traverse page thumbnail dictionaries as a special case. There can only ever 343 // Traverse page thumbnail dictionaries as a special case. There can only ever
339 // be one /Thumb key on a page, and we see at most one page node per call. 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,7 +351,7 @@ QPDF::updateObjectMaps(
346 ((ssp >= 2) && ((key == "/Filter") || (key == "/DecodeParms")))) { 351 ((ssp >= 2) && ((key == "/Filter") || (key == "/DecodeParms")))) {
347 // Don't traverse into stream parameters that we are not going to write. 352 // Don't traverse into stream parameters that we are not going to write.
348 } else { 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 }