Commit a85635b839755765713eb27d767f42b357056b2e

Authored by Jay Berkenbilt
Committed by GitHub
2 parents fd17c8e3 50bc82b4

Merge pull request #929 from m-holger/ogguard

Add new convenience class QPDFObjGen::Guard
include/qpdf/QPDF.hh
@@ -1017,7 +1017,7 @@ class QPDF @@ -1017,7 +1017,7 @@ class QPDF
1017 public: 1017 public:
1018 std::map<QPDFObjGen, QPDFObjectHandle> object_map; 1018 std::map<QPDFObjGen, QPDFObjectHandle> object_map;
1019 std::vector<QPDFObjectHandle> to_copy; 1019 std::vector<QPDFObjectHandle> to_copy;
1020 - std::set<QPDFObjGen> visiting; 1020 + QPDFObjGen::set visiting;
1021 }; 1021 };
1022 1022
1023 class EncryptionParameters 1023 class EncryptionParameters
@@ -1252,8 +1252,8 @@ class QPDF @@ -1252,8 +1252,8 @@ class QPDF
1252 1252
1253 void getAllPagesInternal( 1253 void getAllPagesInternal(
1254 QPDFObjectHandle cur_pages, 1254 QPDFObjectHandle cur_pages,
1255 - std::set<QPDFObjGen>& visited,  
1256 - std::set<QPDFObjGen>& seen); 1255 + QPDFObjGen::set& visited,
  1256 + QPDFObjGen::set& seen);
1257 void insertPage(QPDFObjectHandle newpage, int pos); 1257 void insertPage(QPDFObjectHandle newpage, int pos);
1258 void flattenPagesTree(); 1258 void flattenPagesTree();
1259 void insertPageobjToPage( 1259 void insertPageobjToPage(
@@ -1645,7 +1645,7 @@ class QPDF @@ -1645,7 +1645,7 @@ class QPDF
1645 ObjUser const& ou, 1645 ObjUser const& ou,
1646 QPDFObjectHandle oh, 1646 QPDFObjectHandle oh,
1647 std::function<int(QPDFObjectHandle&)> skip_stream_parameters, 1647 std::function<int(QPDFObjectHandle&)> skip_stream_parameters,
1648 - std::set<QPDFObjGen>& visited, 1648 + QPDFObjGen::set& visited,
1649 bool top); 1649 bool top);
1650 void filterCompressedObjects(std::map<int, int> const& object_stream_data); 1650 void filterCompressedObjects(std::map<int, int> const& object_stream_data);
1651 1651
include/qpdf/QPDFAcroFormDocumentHelper.hh
@@ -254,7 +254,7 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper @@ -254,7 +254,7 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
254 QPDFObjectHandle field, 254 QPDFObjectHandle field,
255 QPDFObjectHandle parent, 255 QPDFObjectHandle parent,
256 int depth, 256 int depth,
257 - std::set<QPDFObjGen>& visited); 257 + QPDFObjGen::set& visited);
258 QPDFObjectHandle getOrCreateAcroForm(); 258 QPDFObjectHandle getOrCreateAcroForm();
259 void adjustInheritedFields( 259 void adjustInheritedFields(
260 QPDFObjectHandle obj, 260 QPDFObjectHandle obj,
include/qpdf/QPDFJob.hh
@@ -571,7 +571,7 @@ class QPDFJob @@ -571,7 +571,7 @@ class QPDFJob
571 571
572 // JSON 572 // JSON
573 void doJSON(QPDF& pdf, Pipeline*); 573 void doJSON(QPDF& pdf, Pipeline*);
574 - std::set<QPDFObjGen> getWantedJSONObjects(); 574 + QPDFObjGen::set getWantedJSONObjects();
575 void doJSONObject( 575 void doJSONObject(
576 Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle&); 576 Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle&);
577 void doJSONObjects(Pipeline* p, bool& first, QPDF& pdf); 577 void doJSONObjects(Pipeline* p, bool& first, QPDF& pdf);
include/qpdf/QPDFObjGen.hh
@@ -24,6 +24,10 @@ @@ -24,6 +24,10 @@
24 24
25 #include <qpdf/DLL.h> 25 #include <qpdf/DLL.h>
26 #include <iostream> 26 #include <iostream>
  27 +#include <set>
  28 +
  29 +class QPDFObjectHandle;
  30 +class QPDFObjectHelper;
27 31
28 // This class represents an object ID and generation pair. It is 32 // This class represents an object ID and generation pair. It is
29 // suitable to use as a key in a map or set. 33 // suitable to use as a key in a map or set.
@@ -31,6 +35,7 @@ @@ -31,6 +35,7 @@
31 class QPDFObjGen 35 class QPDFObjGen
32 { 36 {
33 public: 37 public:
  38 + // ABI: change to default.
34 QPDF_DLL 39 QPDF_DLL
35 QPDFObjGen() : 40 QPDFObjGen() :
36 obj(0), 41 obj(0),
@@ -84,12 +89,72 @@ class QPDFObjGen @@ -84,12 +89,72 @@ class QPDFObjGen
84 QPDF_DLL 89 QPDF_DLL
85 friend std::ostream& operator<<(std::ostream& os, const QPDFObjGen& og); 90 friend std::ostream& operator<<(std::ostream& os, const QPDFObjGen& og);
86 91
  92 + // Convenience class for loop detection when processing objects.
  93 + //
  94 + // The class adds 'add' methods to a std::set<QPDFObjGen> which allows
  95 + // to test whether an QPDFObjGen is present in the set and to insert it in
  96 + // a single operation. The 'add' method is overloaded to take a QPDFObjGen,
  97 + // QPDFObjectHandle or an QPDFObjectHelper as parameter.
  98 + //
  99 + // The erase method is modified to ignore requests to erase
  100 + // QPDFObjGen(0, 0).
  101 + //
  102 + // Usage example:
  103 + //
  104 + // void process_object(QPDFObjectHandle oh, QPDFObjGen::Tracker& seen)
  105 + // {
  106 + // if (seen.add(oh)) {
  107 + // // handle first encounter of oh
  108 + // } else {
  109 + // // handle loop / subsequent encounter of oh
  110 + // }
  111 + // }
  112 + class QPDF_DLL_CLASS set: public std::set<QPDFObjGen>
  113 + {
  114 + public:
  115 + // Add 'og' to the set. Return false if 'og' is already present in
  116 + // the set. Attempts to insert QPDFObjGen(0, 0) are ignored.
  117 + QPDF_DLL
  118 + bool
  119 + add(QPDFObjGen og)
  120 + {
  121 + if (og.isIndirect()) {
  122 + if (count(og) > 0) {
  123 + return false;
  124 + }
  125 + emplace(og);
  126 + }
  127 + return true;
  128 + }
  129 +
  130 + QPDF_DLL
  131 + bool add(QPDFObjectHandle const& oh);
  132 +
  133 + QPDF_DLL
  134 + bool add(QPDFObjectHelper const& oh);
  135 +
  136 + QPDF_DLL
  137 + void
  138 + erase(QPDFObjGen og)
  139 + {
  140 + if (og.isIndirect()) {
  141 + std::set<QPDFObjGen>::erase(og);
  142 + }
  143 + }
  144 +
  145 + QPDF_DLL
  146 + void erase(QPDFObjectHandle const& oh);
  147 +
  148 + QPDF_DLL
  149 + void erase(QPDFObjectHelper const& oh);
  150 + };
  151 +
87 private: 152 private:
88 // This class does not use the Members pattern to avoid a memory 153 // This class does not use the Members pattern to avoid a memory
89 // allocation for every one of these. A lot of these get created 154 // allocation for every one of these. A lot of these get created
90 // and destroyed. 155 // and destroyed.
91 - int obj;  
92 - int gen; 156 + int obj{0};
  157 + int gen{0};
93 }; 158 };
94 159
95 #endif // QPDFOBJGEN_HH 160 #endif // QPDFOBJGEN_HH
include/qpdf/QPDFObjectHandle.hh
@@ -1611,7 +1611,7 @@ class QPDFObjectHandle @@ -1611,7 +1611,7 @@ class QPDFObjectHandle
1611 void objectWarning(std::string const& warning); 1611 void objectWarning(std::string const& warning);
1612 void assertType(char const* type_name, bool istype); 1612 void assertType(char const* type_name, bool istype);
1613 inline bool dereference(); 1613 inline bool dereference();
1614 - void makeDirect(std::set<QPDFObjGen>& visited, bool stop_at_streams); 1614 + void makeDirect(QPDFObjGen::set& visited, bool stop_at_streams);
1615 void disconnect(); 1615 void disconnect();
1616 void setParsedOffset(qpdf_offset_t offset); 1616 void setParsedOffset(qpdf_offset_t offset);
1617 void parseContentStream_internal( 1617 void parseContentStream_internal(
include/qpdf/QPDFOutlineDocumentHelper.hh
@@ -22,13 +22,13 @@ @@ -22,13 +22,13 @@
22 #ifndef QPDFOUTLINEDOCUMENTHELPER_HH 22 #ifndef QPDFOUTLINEDOCUMENTHELPER_HH
23 #define QPDFOUTLINEDOCUMENTHELPER_HH 23 #define QPDFOUTLINEDOCUMENTHELPER_HH
24 24
  25 +#include <qpdf/QPDF.hh>
25 #include <qpdf/QPDFDocumentHelper.hh> 26 #include <qpdf/QPDFDocumentHelper.hh>
26 #include <qpdf/QPDFNameTreeObjectHelper.hh> 27 #include <qpdf/QPDFNameTreeObjectHelper.hh>
  28 +#include <qpdf/QPDFObjGen.hh>
27 #include <qpdf/QPDFOutlineObjectHelper.hh> 29 #include <qpdf/QPDFOutlineObjectHelper.hh>
28 30
29 -#include <qpdf/QPDF.hh>  
30 #include <map> 31 #include <map>
31 -#include <set>  
32 #include <vector> 32 #include <vector>
33 33
34 #include <qpdf/DLL.h> 34 #include <qpdf/DLL.h>
@@ -69,16 +69,16 @@ class QPDFOutlineDocumentHelper: public QPDFDocumentHelper @@ -69,16 +69,16 @@ class QPDFOutlineDocumentHelper: public QPDFDocumentHelper
69 { 69 {
70 friend class QPDFOutlineObjectHelper; 70 friend class QPDFOutlineObjectHelper;
71 71
  72 + // ABI: remove QPDF_DLL and pass og by value.
72 QPDF_DLL 73 QPDF_DLL
73 static bool 74 static bool
74 checkSeen(QPDFOutlineDocumentHelper& dh, QPDFObjGen const& og) 75 checkSeen(QPDFOutlineDocumentHelper& dh, QPDFObjGen const& og)
75 { 76 {
76 - return dh.checkSeen(og); 77 + return !dh.m->seen.add(og);
77 } 78 }
78 }; 79 };
79 80
80 private: 81 private:
81 - bool checkSeen(QPDFObjGen const& og);  
82 void initializeByPage(); 82 void initializeByPage();
83 83
84 class Members 84 class Members
@@ -94,7 +94,7 @@ class QPDFOutlineDocumentHelper: public QPDFDocumentHelper @@ -94,7 +94,7 @@ class QPDFOutlineDocumentHelper: public QPDFDocumentHelper
94 Members(Members const&) = delete; 94 Members(Members const&) = delete;
95 95
96 std::vector<QPDFOutlineObjectHelper> outlines; 96 std::vector<QPDFOutlineObjectHelper> outlines;
97 - std::set<QPDFObjGen> seen; 97 + QPDFObjGen::set seen;
98 QPDFObjectHandle dest_dict; 98 QPDFObjectHandle dest_dict;
99 std::shared_ptr<QPDFNameTreeObjectHelper> names_dest; 99 std::shared_ptr<QPDFNameTreeObjectHelper> names_dest;
100 std::map<QPDFObjGen, std::vector<QPDFOutlineObjectHelper>> by_page; 100 std::map<QPDFObjGen, std::vector<QPDFOutlineObjectHelper>> by_page;
libqpdf/NNTree.cc
@@ -638,26 +638,21 @@ NNTreeIterator::deepen(QPDFObjectHandle node, bool first, bool allow_empty) @@ -638,26 +638,21 @@ NNTreeIterator::deepen(QPDFObjectHandle node, bool first, bool allow_empty)
638 auto opath = this->path; 638 auto opath = this->path;
639 bool failed = false; 639 bool failed = false;
640 640
641 - std::set<QPDFObjGen> seen; 641 + QPDFObjGen::set seen;
642 for (auto i: this->path) { 642 for (auto i: this->path) {
643 - if (i.node.isIndirect()) {  
644 - seen.insert(i.node.getObjGen());  
645 - } 643 + seen.add(i.node);
646 } 644 }
647 while (!failed) { 645 while (!failed) {
648 - if (node.isIndirect()) {  
649 - auto og = node.getObjGen();  
650 - if (seen.count(og)) {  
651 - QTC::TC("qpdf", "NNTree deepen: loop");  
652 - warn(  
653 - impl.qpdf,  
654 - node,  
655 - "loop detected while traversing name/number tree");  
656 - failed = true;  
657 - break;  
658 - }  
659 - seen.insert(og); 646 + if (!seen.add(node)) {
  647 + QTC::TC("qpdf", "NNTree deepen: loop");
  648 + warn(
  649 + impl.qpdf,
  650 + node,
  651 + "loop detected while traversing name/number tree");
  652 + failed = true;
  653 + break;
660 } 654 }
  655 +
661 if (!node.isDictionary()) { 656 if (!node.isDictionary()) {
662 QTC::TC("qpdf", "NNTree node is not a dictionary"); 657 QTC::TC("qpdf", "NNTree node is not a dictionary");
663 warn( 658 warn(
@@ -928,17 +923,15 @@ NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found) @@ -928,17 +923,15 @@ NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found)
928 } 923 }
929 } 924 }
930 925
931 - std::set<QPDFObjGen> seen; 926 + QPDFObjGen::set seen;
932 auto node = this->oh; 927 auto node = this->oh;
933 iterator result(*this); 928 iterator result(*this);
934 929
935 while (true) { 930 while (true) {
936 - auto og = node.getObjGen();  
937 - if (seen.count(og)) { 931 + if (!seen.add(node)) {
938 QTC::TC("qpdf", "NNTree loop in find"); 932 QTC::TC("qpdf", "NNTree loop in find");
939 error(qpdf, node, "loop detected in find"); 933 error(qpdf, node, "loop detected in find");
940 } 934 }
941 - seen.insert(og);  
942 935
943 auto kids = node.getKey("/Kids"); 936 auto kids = node.getKey("/Kids");
944 int nkids = kids.isArray() ? kids.getArrayNItems() : 0; 937 int nkids = kids.isArray() ? kids.getArrayNItems() : 0;
libqpdf/QPDF.cc
@@ -2176,7 +2176,8 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) @@ -2176,7 +2176,8 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
2176 void 2176 void
2177 QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top) 2177 QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top)
2178 { 2178 {
2179 - if (foreign.isReserved()) { 2179 + auto foreign_tc = foreign.getTypeCode();
  2180 + if (foreign_tc == ::ot_reserved) {
2180 throw std::logic_error( 2181 throw std::logic_error(
2181 "QPDF: attempting to copy a foreign reserved object"); 2182 "QPDF: attempting to copy a foreign reserved object");
2182 } 2183 }
@@ -2193,70 +2194,62 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier&amp; obj_copier, bool top) @@ -2193,70 +2194,62 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier&amp; obj_copier, bool top)
2193 2194
2194 if (foreign.isIndirect()) { 2195 if (foreign.isIndirect()) {
2195 QPDFObjGen foreign_og(foreign.getObjGen()); 2196 QPDFObjGen foreign_og(foreign.getObjGen());
2196 - if (obj_copier.visiting.find(foreign_og) != obj_copier.visiting.end()) {  
2197 - QTC::TC("qpdf", "QPDF loop reserving objects"); 2197 + if (obj_copier.object_map.count(foreign_og) > 0) {
  2198 + QTC::TC("qpdf", "QPDF already reserved object");
  2199 + if (obj_copier.visiting.count(foreign_og)) {
  2200 + QTC::TC("qpdf", "QPDF loop reserving objects");
  2201 + }
2198 return; 2202 return;
2199 } 2203 }
2200 - if (obj_copier.object_map.find(foreign_og) !=  
2201 - obj_copier.object_map.end()) {  
2202 - QTC::TC("qpdf", "QPDF already reserved object"); 2204 + if (!obj_copier.visiting.add(foreign_og)) {
2203 return; 2205 return;
2204 } 2206 }
2205 QTC::TC("qpdf", "QPDF copy indirect"); 2207 QTC::TC("qpdf", "QPDF copy indirect");
2206 - obj_copier.visiting.insert(foreign_og);  
2207 - auto mapping = obj_copier.object_map.find(foreign_og);  
2208 - if (mapping == obj_copier.object_map.end()) { 2208 + if (obj_copier.object_map.count(foreign_og) == 0) {
2209 obj_copier.to_copy.push_back(foreign); 2209 obj_copier.to_copy.push_back(foreign);
2210 - QPDFObjectHandle reservation;  
2211 - if (foreign.isStream()) {  
2212 - reservation = newStream();  
2213 - } else {  
2214 - reservation = QPDFObjectHandle::newReserved(this);  
2215 - }  
2216 - obj_copier.object_map[foreign_og] = reservation; 2210 + obj_copier.object_map[foreign_og] = foreign.isStream()
  2211 + ? newStream()
  2212 + : QPDFObjectHandle::newReserved(this);
2217 } 2213 }
2218 } 2214 }
2219 2215
2220 - if (foreign.isArray()) { 2216 + if (foreign_tc == ::ot_array) {
2221 QTC::TC("qpdf", "QPDF reserve array"); 2217 QTC::TC("qpdf", "QPDF reserve array");
2222 int n = foreign.getArrayNItems(); 2218 int n = foreign.getArrayNItems();
2223 for (int i = 0; i < n; ++i) { 2219 for (int i = 0; i < n; ++i) {
2224 reserveObjects(foreign.getArrayItem(i), obj_copier, false); 2220 reserveObjects(foreign.getArrayItem(i), obj_copier, false);
2225 } 2221 }
2226 - } else if (foreign.isDictionary()) { 2222 + } else if (foreign_tc == ::ot_dictionary) {
2227 QTC::TC("qpdf", "QPDF reserve dictionary"); 2223 QTC::TC("qpdf", "QPDF reserve dictionary");
2228 for (auto const& key: foreign.getKeys()) { 2224 for (auto const& key: foreign.getKeys()) {
2229 reserveObjects(foreign.getKey(key), obj_copier, false); 2225 reserveObjects(foreign.getKey(key), obj_copier, false);
2230 } 2226 }
2231 - } else if (foreign.isStream()) { 2227 + } else if (foreign_tc == ::ot_stream) {
2232 QTC::TC("qpdf", "QPDF reserve stream"); 2228 QTC::TC("qpdf", "QPDF reserve stream");
2233 reserveObjects(foreign.getDict(), obj_copier, false); 2229 reserveObjects(foreign.getDict(), obj_copier, false);
2234 } 2230 }
2235 2231
2236 - if (foreign.isIndirect()) {  
2237 - QPDFObjGen foreign_og(foreign.getObjGen());  
2238 - obj_copier.visiting.erase(foreign_og);  
2239 - } 2232 + obj_copier.visiting.erase(foreign);
2240 } 2233 }
2241 2234
2242 QPDFObjectHandle 2235 QPDFObjectHandle
2243 QPDF::replaceForeignIndirectObjects( 2236 QPDF::replaceForeignIndirectObjects(
2244 QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top) 2237 QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top)
2245 { 2238 {
  2239 + auto foreign_tc = foreign.getTypeCode();
2246 QPDFObjectHandle result; 2240 QPDFObjectHandle result;
2247 if ((!top) && foreign.isIndirect()) { 2241 if ((!top) && foreign.isIndirect()) {
2248 QTC::TC("qpdf", "QPDF replace indirect"); 2242 QTC::TC("qpdf", "QPDF replace indirect");
2249 - QPDFObjGen foreign_og(foreign.getObjGen());  
2250 - auto mapping = obj_copier.object_map.find(foreign_og); 2243 + auto mapping = obj_copier.object_map.find(foreign.getObjGen());
2251 if (mapping == obj_copier.object_map.end()) { 2244 if (mapping == obj_copier.object_map.end()) {
2252 // This case would occur if this is a reference to a Page 2245 // This case would occur if this is a reference to a Page
2253 // or Pages object that we didn't traverse into. 2246 // or Pages object that we didn't traverse into.
2254 QTC::TC("qpdf", "QPDF replace foreign indirect with null"); 2247 QTC::TC("qpdf", "QPDF replace foreign indirect with null");
2255 result = QPDFObjectHandle::newNull(); 2248 result = QPDFObjectHandle::newNull();
2256 } else { 2249 } else {
2257 - result = obj_copier.object_map[foreign_og]; 2250 + result = mapping->second;
2258 } 2251 }
2259 - } else if (foreign.isArray()) { 2252 + } else if (foreign_tc == ::ot_array) {
2260 QTC::TC("qpdf", "QPDF replace array"); 2253 QTC::TC("qpdf", "QPDF replace array");
2261 result = QPDFObjectHandle::newArray(); 2254 result = QPDFObjectHandle::newArray();
2262 int n = foreign.getArrayNItems(); 2255 int n = foreign.getArrayNItems();
@@ -2266,7 +2259,7 @@ QPDF::replaceForeignIndirectObjects( @@ -2266,7 +2259,7 @@ QPDF::replaceForeignIndirectObjects(
2266 replaceForeignIndirectObjects( 2259 replaceForeignIndirectObjects(
2267 foreign.getArrayItem(i), obj_copier, false)); 2260 foreign.getArrayItem(i), obj_copier, false));
2268 } 2261 }
2269 - } else if (foreign.isDictionary()) { 2262 + } else if (foreign_tc == ::ot_dictionary) {
2270 QTC::TC("qpdf", "QPDF replace dictionary"); 2263 QTC::TC("qpdf", "QPDF replace dictionary");
2271 result = QPDFObjectHandle::newDictionary(); 2264 result = QPDFObjectHandle::newDictionary();
2272 std::set<std::string> keys = foreign.getKeys(); 2265 std::set<std::string> keys = foreign.getKeys();
@@ -2276,10 +2269,9 @@ QPDF::replaceForeignIndirectObjects( @@ -2276,10 +2269,9 @@ QPDF::replaceForeignIndirectObjects(
2276 replaceForeignIndirectObjects( 2269 replaceForeignIndirectObjects(
2277 foreign.getKey(iter), obj_copier, false)); 2270 foreign.getKey(iter), obj_copier, false));
2278 } 2271 }
2279 - } else if (foreign.isStream()) { 2272 + } else if (foreign_tc == ::ot_stream) {
2280 QTC::TC("qpdf", "QPDF replace stream"); 2273 QTC::TC("qpdf", "QPDF replace stream");
2281 - QPDFObjGen foreign_og(foreign.getObjGen());  
2282 - result = obj_copier.object_map[foreign_og]; 2274 + result = obj_copier.object_map[foreign.getObjGen()];
2283 result.assertStream(); 2275 result.assertStream();
2284 QPDFObjectHandle dict = result.getDict(); 2276 QPDFObjectHandle dict = result.getDict();
2285 QPDFObjectHandle old_dict = foreign.getDict(); 2277 QPDFObjectHandle old_dict = foreign.getDict();
@@ -2511,7 +2503,7 @@ QPDF::getCompressibleObjGens() @@ -2511,7 +2503,7 @@ QPDF::getCompressibleObjGens()
2511 QPDFObjectHandle encryption_dict = this->m->trailer.getKey("/Encrypt"); 2503 QPDFObjectHandle encryption_dict = this->m->trailer.getKey("/Encrypt");
2512 QPDFObjGen encryption_dict_og = encryption_dict.getObjGen(); 2504 QPDFObjGen encryption_dict_og = encryption_dict.getObjGen();
2513 2505
2514 - std::set<QPDFObjGen> visited; 2506 + QPDFObjGen::set visited;
2515 std::list<QPDFObjectHandle> queue; 2507 std::list<QPDFObjectHandle> queue;
2516 queue.push_front(this->m->trailer); 2508 queue.push_front(this->m->trailer);
2517 std::vector<QPDFObjGen> result; 2509 std::vector<QPDFObjGen> result;
@@ -2520,7 +2512,7 @@ QPDF::getCompressibleObjGens() @@ -2520,7 +2512,7 @@ QPDF::getCompressibleObjGens()
2520 queue.pop_front(); 2512 queue.pop_front();
2521 if (obj.isIndirect()) { 2513 if (obj.isIndirect()) {
2522 QPDFObjGen og = obj.getObjGen(); 2514 QPDFObjGen og = obj.getObjGen();
2523 - if (visited.count(og)) { 2515 + if (!visited.add(og)) {
2524 QTC::TC("qpdf", "QPDF loop detected traversing objects"); 2516 QTC::TC("qpdf", "QPDF loop detected traversing objects");
2525 continue; 2517 continue;
2526 } 2518 }
@@ -2532,7 +2524,6 @@ QPDF::getCompressibleObjGens() @@ -2532,7 +2524,6 @@ QPDF::getCompressibleObjGens()
2532 obj.hasKey("/Contents")))) { 2524 obj.hasKey("/Contents")))) {
2533 result.push_back(og); 2525 result.push_back(og);
2534 } 2526 }
2535 - visited.insert(og);  
2536 } 2527 }
2537 if (obj.isStream()) { 2528 if (obj.isStream()) {
2538 QPDFObjectHandle dict = obj.getDict(); 2529 QPDFObjectHandle dict = obj.getDict();
libqpdf/QPDFAcroFormDocumentHelper.cc
@@ -57,7 +57,7 @@ QPDFAcroFormDocumentHelper::addFormField(QPDFFormFieldObjectHelper ff) @@ -57,7 +57,7 @@ QPDFAcroFormDocumentHelper::addFormField(QPDFFormFieldObjectHelper ff)
57 "/Fields", QPDFObjectHandle::newArray()); 57 "/Fields", QPDFObjectHandle::newArray());
58 } 58 }
59 fields.appendItem(ff.getObjectHandle()); 59 fields.appendItem(ff.getObjectHandle());
60 - std::set<QPDFObjGen> visited; 60 + QPDFObjGen::set visited;
61 traverseField( 61 traverseField(
62 ff.getObjectHandle(), QPDFObjectHandle::newNull(), 0, visited); 62 ff.getObjectHandle(), QPDFObjectHandle::newNull(), 0, visited);
63 } 63 }
@@ -68,53 +68,48 @@ QPDFAcroFormDocumentHelper::addAndRenameFormFields( @@ -68,53 +68,48 @@ QPDFAcroFormDocumentHelper::addAndRenameFormFields(
68 { 68 {
69 analyze(); 69 analyze();
70 std::map<std::string, std::string> renames; 70 std::map<std::string, std::string> renames;
71 - std::list<QPDFObjectHandle> queue;  
72 - queue.insert(queue.begin(), fields.begin(), fields.end());  
73 - std::set<QPDFObjGen> seen;  
74 - while (!queue.empty()) {  
75 - QPDFObjectHandle obj = queue.front();  
76 - queue.pop_front();  
77 - auto og = obj.getObjGen();  
78 - if (seen.count(og)) {  
79 - // loop  
80 - continue;  
81 - }  
82 - seen.insert(og);  
83 - auto kids = obj.getKey("/Kids");  
84 - if (kids.isArray()) {  
85 - for (auto kid: kids.aitems()) {  
86 - queue.push_back(kid); 71 + QPDFObjGen::set seen;
  72 + for (std::list<QPDFObjectHandle> queue{fields.begin(), fields.end()};
  73 + !queue.empty();
  74 + queue.pop_front()) {
  75 + auto& obj = queue.front();
  76 + if (seen.add(obj)) {
  77 + auto kids = obj.getKey("/Kids");
  78 + if (kids.isArray()) {
  79 + for (auto kid: kids.aitems()) {
  80 + queue.push_back(kid);
  81 + }
87 } 82 }
88 - }  
89 83
90 - if (obj.hasKey("/T")) {  
91 - // Find something we can append to the partial name that  
92 - // makes the fully qualified name unique. When we find  
93 - // something, reuse the same suffix for all fields in this  
94 - // group with the same name. We can only change the name  
95 - // of fields that have /T, and this field's /T is always  
96 - // at the end of the fully qualified name, appending to /T  
97 - // has the effect of appending the same thing to the fully  
98 - // qualified name.  
99 - std::string old_name =  
100 - QPDFFormFieldObjectHelper(obj).getFullyQualifiedName();  
101 - if (renames.count(old_name) == 0) {  
102 - std::string new_name = old_name;  
103 - int suffix = 0;  
104 - std::string append;  
105 - while (!getFieldsWithQualifiedName(new_name).empty()) {  
106 - ++suffix;  
107 - append = "+" + std::to_string(suffix);  
108 - new_name = old_name + append; 84 + if (obj.hasKey("/T")) {
  85 + // Find something we can append to the partial name that
  86 + // makes the fully qualified name unique. When we find
  87 + // something, reuse the same suffix for all fields in this
  88 + // group with the same name. We can only change the name
  89 + // of fields that have /T, and this field's /T is always
  90 + // at the end of the fully qualified name, appending to /T
  91 + // has the effect of appending the same thing to the fully
  92 + // qualified name.
  93 + std::string old_name =
  94 + QPDFFormFieldObjectHelper(obj).getFullyQualifiedName();
  95 + if (renames.count(old_name) == 0) {
  96 + std::string new_name = old_name;
  97 + int suffix = 0;
  98 + std::string append;
  99 + while (!getFieldsWithQualifiedName(new_name).empty()) {
  100 + ++suffix;
  101 + append = "+" + std::to_string(suffix);
  102 + new_name = old_name + append;
  103 + }
  104 + renames[old_name] = append;
  105 + }
  106 + std::string append = renames[old_name];
  107 + if (!append.empty()) {
  108 + obj.replaceKey(
  109 + "/T",
  110 + QPDFObjectHandle::newUnicodeString(
  111 + obj.getKey("/T").getUTF8Value() + append));
109 } 112 }
110 - renames[old_name] = append;  
111 - }  
112 - std::string append = renames[old_name];  
113 - if (!append.empty()) {  
114 - obj.replaceKey(  
115 - "/T",  
116 - QPDFObjectHandle::newUnicodeString(  
117 - obj.getKey("/T").getUTF8Value() + append));  
118 } 113 }
119 } 114 }
120 } 115 }
@@ -172,7 +167,7 @@ QPDFAcroFormDocumentHelper::setFormFieldName( @@ -172,7 +167,7 @@ QPDFAcroFormDocumentHelper::setFormFieldName(
172 QPDFFormFieldObjectHelper ff, std::string const& name) 167 QPDFFormFieldObjectHelper ff, std::string const& name)
173 { 168 {
174 ff.setFieldAttribute("/T", name); 169 ff.setFieldAttribute("/T", name);
175 - std::set<QPDFObjGen> visited; 170 + QPDFObjGen::set visited;
176 auto ff_oh = ff.getObjectHandle(); 171 auto ff_oh = ff.getObjectHandle();
177 traverseField(ff_oh, ff_oh.getKey("/Parent"), 0, visited); 172 traverseField(ff_oh, ff_oh.getKey("/Parent"), 0, visited);
178 } 173 }
@@ -193,12 +188,11 @@ QPDFAcroFormDocumentHelper::getFieldsWithQualifiedName(std::string const&amp; name) @@ -193,12 +188,11 @@ QPDFAcroFormDocumentHelper::getFieldsWithQualifiedName(std::string const&amp; name)
193 { 188 {
194 analyze(); 189 analyze();
195 // Keep from creating an empty entry 190 // Keep from creating an empty entry
196 - std::set<QPDFObjGen> result;  
197 auto iter = this->m->name_to_fields.find(name); 191 auto iter = this->m->name_to_fields.find(name);
198 if (iter != this->m->name_to_fields.end()) { 192 if (iter != this->m->name_to_fields.end()) {
199 - result = iter->second; 193 + return iter->second;
200 } 194 }
201 - return result; 195 + return {};
202 } 196 }
203 197
204 std::vector<QPDFAnnotationObjectHelper> 198 std::vector<QPDFAnnotationObjectHelper>
@@ -223,18 +217,12 @@ std::vector&lt;QPDFFormFieldObjectHelper&gt; @@ -223,18 +217,12 @@ std::vector&lt;QPDFFormFieldObjectHelper&gt;
223 QPDFAcroFormDocumentHelper::getFormFieldsForPage(QPDFPageObjectHelper ph) 217 QPDFAcroFormDocumentHelper::getFormFieldsForPage(QPDFPageObjectHelper ph)
224 { 218 {
225 analyze(); 219 analyze();
226 - std::set<QPDFObjGen> added; 220 + QPDFObjGen::set todo;
227 std::vector<QPDFFormFieldObjectHelper> result; 221 std::vector<QPDFFormFieldObjectHelper> result;
228 - auto widget_annotations = getWidgetAnnotationsForPage(ph);  
229 - for (auto annot: widget_annotations) {  
230 - auto field = getFieldForAnnotation(annot);  
231 - field = field.getTopLevelField();  
232 - auto og = field.getObjectHandle().getObjGen();  
233 - if (!added.count(og)) {  
234 - added.insert(og);  
235 - if (field.getObjectHandle().isDictionary()) {  
236 - result.push_back(field);  
237 - } 222 + for (auto& annot: getWidgetAnnotationsForPage(ph)) {
  223 + auto field = getFieldForAnnotation(annot).getTopLevelField();
  224 + if (todo.add(field) && field.getObjectHandle().isDictionary()) {
  225 + result.push_back(field);
238 } 226 }
239 } 227 }
240 return result; 228 return result;
@@ -278,7 +266,7 @@ QPDFAcroFormDocumentHelper::analyze() @@ -278,7 +266,7 @@ QPDFAcroFormDocumentHelper::analyze()
278 // Traverse /AcroForm to find annotations and map them 266 // Traverse /AcroForm to find annotations and map them
279 // bidirectionally to fields. 267 // bidirectionally to fields.
280 268
281 - std::set<QPDFObjGen> visited; 269 + QPDFObjGen::set visited;
282 int nfields = fields.getArrayNItems(); 270 int nfields = fields.getArrayNItems();
283 QPDFObjectHandle null(QPDFObjectHandle::newNull()); 271 QPDFObjectHandle null(QPDFObjectHandle::newNull());
284 for (int i = 0; i < nfields; ++i) { 272 for (int i = 0; i < nfields; ++i) {
@@ -324,7 +312,7 @@ QPDFAcroFormDocumentHelper::traverseField( @@ -324,7 +312,7 @@ QPDFAcroFormDocumentHelper::traverseField(
324 QPDFObjectHandle field, 312 QPDFObjectHandle field,
325 QPDFObjectHandle parent, 313 QPDFObjectHandle parent,
326 int depth, 314 int depth,
327 - std::set<QPDFObjGen>& visited) 315 + QPDFObjGen::set& visited)
328 { 316 {
329 if (depth > 100) { 317 if (depth > 100) {
330 // Arbitrarily cut off recursion at a fixed depth to avoid 318 // Arbitrarily cut off recursion at a fixed depth to avoid
@@ -346,12 +334,11 @@ QPDFAcroFormDocumentHelper::traverseField( @@ -346,12 +334,11 @@ QPDFAcroFormDocumentHelper::traverseField(
346 return; 334 return;
347 } 335 }
348 QPDFObjGen og(field.getObjGen()); 336 QPDFObjGen og(field.getObjGen());
349 - if (visited.count(og) != 0) { 337 + if (!visited.add(og)) {
350 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper loop"); 338 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper loop");
351 field.warnIfPossible("loop detected while traversing /AcroForm"); 339 field.warnIfPossible("loop detected while traversing /AcroForm");
352 return; 340 return;
353 } 341 }
354 - visited.insert(og);  
355 342
356 // A dictionary encountered while traversing the /AcroForm field 343 // A dictionary encountered while traversing the /AcroForm field
357 // may be a form field, an annotation, or the merger of the two. A 344 // may be a form field, an annotation, or the merger of the two. A
@@ -888,7 +875,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -888,7 +875,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
888 875
889 // Now do the actual copies. 876 // Now do the actual copies.
890 877
891 - std::set<QPDFObjGen> added_new_fields; 878 + QPDFObjGen::set added_new_fields;
892 for (auto annot: old_annots.aitems()) { 879 for (auto annot: old_annots.aitems()) {
893 if (annot.isStream()) { 880 if (annot.isStream()) {
894 annot.warnIfPossible("ignoring annotation that's a stream"); 881 annot.warnIfPossible("ignoring annotation that's a stream");
@@ -970,73 +957,68 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -970,73 +957,68 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
970 // Traverse the field, copying kids, and preserving 957 // Traverse the field, copying kids, and preserving
971 // integrity. 958 // integrity.
972 std::list<QPDFObjectHandle> queue; 959 std::list<QPDFObjectHandle> queue;
  960 + QPDFObjGen::set seen;
973 if (maybe_copy_object(top_field)) { 961 if (maybe_copy_object(top_field)) {
974 queue.push_back(top_field); 962 queue.push_back(top_field);
975 } 963 }
976 - std::set<QPDFObjGen> seen;  
977 - while (!queue.empty()) {  
978 - QPDFObjectHandle obj = queue.front();  
979 - queue.pop_front();  
980 - auto orig_og = obj.getObjGen();  
981 - if (seen.count(orig_og)) {  
982 - // loop  
983 - break;  
984 - }  
985 - seen.insert(orig_og);  
986 - auto parent = obj.getKey("/Parent");  
987 - if (parent.isIndirect()) {  
988 - auto parent_og = parent.getObjGen();  
989 - if (orig_to_copy.count(parent_og)) {  
990 - obj.replaceKey("/Parent", orig_to_copy[parent_og]);  
991 - } else {  
992 - parent.warnIfPossible(  
993 - "while traversing field " +  
994 - obj.getObjGen().unparse(',') + ", found parent (" +  
995 - parent_og.unparse(',') +  
996 - ") that had not been seen, indicating likely"  
997 - " invalid field structure"); 964 + for (; !queue.empty(); queue.pop_front()) {
  965 + auto& obj = queue.front();
  966 + if (seen.add(obj)) {
  967 + auto parent = obj.getKey("/Parent");
  968 + if (parent.isIndirect()) {
  969 + auto parent_og = parent.getObjGen();
  970 + if (orig_to_copy.count(parent_og)) {
  971 + obj.replaceKey("/Parent", orig_to_copy[parent_og]);
  972 + } else {
  973 + parent.warnIfPossible(
  974 + "while traversing field " +
  975 + obj.getObjGen().unparse(',') +
  976 + ", found parent (" + parent_og.unparse(',') +
  977 + ") that had not been seen, indicating likely"
  978 + " invalid field structure");
  979 + }
998 } 980 }
999 - }  
1000 - auto kids = obj.getKey("/Kids");  
1001 - if (kids.isArray()) {  
1002 - for (int i = 0; i < kids.getArrayNItems(); ++i) {  
1003 - auto kid = kids.getArrayItem(i);  
1004 - if (maybe_copy_object(kid)) {  
1005 - kids.setArrayItem(i, kid);  
1006 - queue.push_back(kid); 981 + auto kids = obj.getKey("/Kids");
  982 + if (kids.isArray()) {
  983 + for (int i = 0; i < kids.getArrayNItems(); ++i) {
  984 + auto kid = kids.getArrayItem(i);
  985 + if (maybe_copy_object(kid)) {
  986 + kids.setArrayItem(i, kid);
  987 + queue.push_back(kid);
  988 + }
1007 } 989 }
1008 } 990 }
1009 - }  
1010 991
1011 - if (override_da || override_q) {  
1012 - adjustInheritedFields(  
1013 - obj,  
1014 - override_da,  
1015 - from_default_da,  
1016 - override_q,  
1017 - from_default_q);  
1018 - }  
1019 - if (foreign) {  
1020 - // Lazily initialize our /DR and the conflict map.  
1021 - init_dr_map();  
1022 - // The spec doesn't say anything about /DR on the  
1023 - // field, but lots of writers put one there, and  
1024 - // it is frequently the same as the document-level  
1025 - // /DR. To avoid having the field's /DR point to  
1026 - // information that we are not maintaining, just  
1027 - // reset it to that if it exists. Empirical  
1028 - // evidence suggests that many readers, including  
1029 - // Acrobat, Adobe Acrobat Reader, chrome, firefox,  
1030 - // the mac Preview application, and several of the  
1031 - // free readers on Linux all ignore /DR at the  
1032 - // field level.  
1033 - if (obj.hasKey("/DR")) {  
1034 - obj.replaceKey("/DR", dr); 992 + if (override_da || override_q) {
  993 + adjustInheritedFields(
  994 + obj,
  995 + override_da,
  996 + from_default_da,
  997 + override_q,
  998 + from_default_q);
  999 + }
  1000 + if (foreign) {
  1001 + // Lazily initialize our /DR and the conflict map.
  1002 + init_dr_map();
  1003 + // The spec doesn't say anything about /DR on the
  1004 + // field, but lots of writers put one there, and
  1005 + // it is frequently the same as the document-level
  1006 + // /DR. To avoid having the field's /DR point to
  1007 + // information that we are not maintaining, just
  1008 + // reset it to that if it exists. Empirical
  1009 + // evidence suggests that many readers, including
  1010 + // Acrobat, Adobe Acrobat Reader, chrome, firefox,
  1011 + // the mac Preview application, and several of the
  1012 + // free readers on Linux all ignore /DR at the
  1013 + // field level.
  1014 + if (obj.hasKey("/DR")) {
  1015 + obj.replaceKey("/DR", dr);
  1016 + }
  1017 + }
  1018 + if (foreign && obj.getKey("/DA").isString() &&
  1019 + (!dr_map.empty())) {
  1020 + adjustDefaultAppearances(obj, dr_map);
1035 } 1021 }
1036 - }  
1037 - if (foreign && obj.getKey("/DA").isString() &&  
1038 - (!dr_map.empty())) {  
1039 - adjustDefaultAppearances(obj, dr_map);  
1040 } 1022 }
1041 } 1023 }
1042 1024
@@ -1064,9 +1046,8 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -1064,9 +1046,8 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
1064 maybe_copy_object(annot); 1046 maybe_copy_object(annot);
1065 1047
1066 // Now we have copies, so we can safely mutate. 1048 // Now we have copies, so we can safely mutate.
1067 - if (have_field && !added_new_fields.count(top_field.getObjGen())) { 1049 + if (have_field && added_new_fields.add(top_field)) {
1068 new_fields.push_back(top_field); 1050 new_fields.push_back(top_field);
1069 - added_new_fields.insert(top_field.getObjGen());  
1070 } 1051 }
1071 new_annots.push_back(annot); 1052 new_annots.push_back(annot);
1072 1053
libqpdf/QPDFFormFieldObjectHelper.cc
@@ -36,20 +36,14 @@ QPDFFormFieldObjectHelper @@ -36,20 +36,14 @@ QPDFFormFieldObjectHelper
36 QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different) 36 QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different)
37 { 37 {
38 auto top_field = this->oh; 38 auto top_field = this->oh;
39 - std::set<QPDFObjGen> seen;  
40 - while (top_field.isDictionary() &&  
41 - (!top_field.getKey("/Parent").isNull())) { 39 + QPDFObjGen::set seen;
  40 + while (seen.add(top_field) && !top_field.getKeyIfDict("/Parent").isNull()) {
42 top_field = top_field.getKey("/Parent"); 41 top_field = top_field.getKey("/Parent");
43 if (is_different) { 42 if (is_different) {
44 *is_different = true; 43 *is_different = true;
45 } 44 }
46 - auto og = top_field.getObjGen();  
47 - if (seen.count(og)) {  
48 - break;  
49 - }  
50 - seen.insert(og);  
51 } 45 }
52 - return QPDFFormFieldObjectHelper(top_field); 46 + return {top_field};
53 } 47 }
54 48
55 QPDFObjectHandle 49 QPDFObjectHandle
@@ -76,17 +70,17 @@ QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const&amp; name) @@ -76,17 +70,17 @@ QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const&amp; name)
76 return QPDFObjectHandle::newNull(); 70 return QPDFObjectHandle::newNull();
77 } 71 }
78 QPDFObjectHandle result(node.getKey(name)); 72 QPDFObjectHandle result(node.getKey(name));
79 - std::set<QPDFObjGen> seen;  
80 - while (result.isNull() && node.hasKey("/Parent")) {  
81 - seen.insert(node.getObjGen());  
82 - node = node.getKey("/Parent");  
83 - if (seen.count(node.getObjGen())) {  
84 - break;  
85 - }  
86 - result = node.getKey(name);  
87 - if (!result.isNull()) {  
88 - QTC::TC(  
89 - "qpdf", "QPDFFormFieldObjectHelper non-trivial inheritance"); 73 + if (result.isNull()) {
  74 + QPDFObjGen::set seen;
  75 + while (seen.add(node) && node.hasKey("/Parent")) {
  76 + node = node.getKey("/Parent");
  77 + result = node.getKey(name);
  78 + if (!result.isNull()) {
  79 + QTC::TC(
  80 + "qpdf",
  81 + "QPDFFormFieldObjectHelper non-trivial inheritance");
  82 + return result;
  83 + }
90 } 84 }
91 } 85 }
92 return result; 86 return result;
@@ -127,8 +121,8 @@ QPDFFormFieldObjectHelper::getFullyQualifiedName() @@ -127,8 +121,8 @@ QPDFFormFieldObjectHelper::getFullyQualifiedName()
127 { 121 {
128 std::string result; 122 std::string result;
129 QPDFObjectHandle node = this->oh; 123 QPDFObjectHandle node = this->oh;
130 - std::set<QPDFObjGen> seen;  
131 - while ((!node.isNull()) && (seen.count(node.getObjGen()) == 0)) { 124 + QPDFObjGen::set seen;
  125 + while (!node.isNull() && seen.add(node)) {
132 if (node.getKey("/T").isString()) { 126 if (node.getKey("/T").isString()) {
133 if (!result.empty()) { 127 if (!result.empty()) {
134 QTC::TC( 128 QTC::TC(
@@ -138,7 +132,6 @@ QPDFFormFieldObjectHelper::getFullyQualifiedName() @@ -138,7 +132,6 @@ QPDFFormFieldObjectHelper::getFullyQualifiedName()
138 } 132 }
139 result = node.getKey("/T").getUTF8Value() + result; 133 result = node.getKey("/T").getUTF8Value() + result;
140 } 134 }
141 - seen.insert(node.getObjGen());  
142 node = node.getKey("/Parent"); 135 node = node.getKey("/Parent");
143 } 136 }
144 return result; 137 return result;
libqpdf/QPDFJob.cc
@@ -1001,18 +1001,16 @@ QPDFJob::parse_object_id( @@ -1001,18 +1001,16 @@ QPDFJob::parse_object_id(
1001 } 1001 }
1002 } 1002 }
1003 1003
1004 -std::set<QPDFObjGen> 1004 +QPDFObjGen::set
1005 QPDFJob::getWantedJSONObjects() 1005 QPDFJob::getWantedJSONObjects()
1006 { 1006 {
1007 - std::set<QPDFObjGen> wanted_og; 1007 + QPDFObjGen::set wanted_og;
1008 for (auto const& iter: m->json_objects) { 1008 for (auto const& iter: m->json_objects) {
1009 bool trailer; 1009 bool trailer;
1010 int obj = 0; 1010 int obj = 0;
1011 int gen = 0; 1011 int gen = 0;
1012 parse_object_id(iter, trailer, obj, gen); 1012 parse_object_id(iter, trailer, obj, gen);
1013 - if (obj) {  
1014 - wanted_og.insert(QPDFObjGen(obj, gen));  
1015 - } 1013 + wanted_og.add(QPDFObjGen(obj, gen));
1016 } 1014 }
1017 return wanted_og; 1015 return wanted_og;
1018 } 1016 }
@@ -1045,7 +1043,7 @@ QPDFJob::doJSONObjects(Pipeline* p, bool&amp; first, QPDF&amp; pdf) @@ -1045,7 +1043,7 @@ QPDFJob::doJSONObjects(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1045 bool first_object = true; 1043 bool first_object = true;
1046 JSON::writeDictionaryOpen(p, first_object, 1); 1044 JSON::writeDictionaryOpen(p, first_object, 1);
1047 bool all_objects = m->json_objects.empty(); 1045 bool all_objects = m->json_objects.empty();
1048 - std::set<QPDFObjGen> wanted_og = getWantedJSONObjects(); 1046 + auto wanted_og = getWantedJSONObjects();
1049 for (auto& obj: pdf.getAllObjects()) { 1047 for (auto& obj: pdf.getAllObjects()) {
1050 std::string key = obj.unparse(); 1048 std::string key = obj.unparse();
1051 if (this->m->json_version > 1) { 1049 if (this->m->json_version > 1) {
@@ -1065,11 +1063,8 @@ QPDFJob::doJSONObjects(Pipeline* p, bool&amp; first, QPDF&amp; pdf) @@ -1065,11 +1063,8 @@ QPDFJob::doJSONObjects(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1065 if (this->m->json_objects.count("trailer")) { 1063 if (this->m->json_objects.count("trailer")) {
1066 json_objects.insert("trailer"); 1064 json_objects.insert("trailer");
1067 } 1065 }
1068 - auto wanted = getWantedJSONObjects();  
1069 - for (auto const& og: wanted) {  
1070 - std::ostringstream s;  
1071 - s << "obj:" << og.unparse(' ') << " R";  
1072 - json_objects.insert(s.str()); 1066 + for (auto og: getWantedJSONObjects()) {
  1067 + json_objects.emplace("obj:" + og.unparse(' ') + " R");
1073 } 1068 }
1074 pdf.writeJSON( 1069 pdf.writeJSON(
1075 this->m->json_version, 1070 this->m->json_version,
@@ -1090,7 +1085,7 @@ QPDFJob::doJSONObjectinfo(Pipeline* p, bool&amp; first, QPDF&amp; pdf) @@ -1090,7 +1085,7 @@ QPDFJob::doJSONObjectinfo(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1090 bool first_object = true; 1085 bool first_object = true;
1091 JSON::writeDictionaryOpen(p, first_object, 1); 1086 JSON::writeDictionaryOpen(p, first_object, 1);
1092 bool all_objects = m->json_objects.empty(); 1087 bool all_objects = m->json_objects.empty();
1093 - std::set<QPDFObjGen> wanted_og = getWantedJSONObjects(); 1088 + auto wanted_og = getWantedJSONObjects();
1094 for (auto& obj: pdf.getAllObjects()) { 1089 for (auto& obj: pdf.getAllObjects()) {
1095 if (all_objects || wanted_og.count(obj.getObjGen())) { 1090 if (all_objects || wanted_og.count(obj.getObjGen())) {
1096 auto j_details = JSON::makeDictionary(); 1091 auto j_details = JSON::makeDictionary();
@@ -2451,8 +2446,8 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf) @@ -2451,8 +2446,8 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2451 2446
2452 // Return true as soon as we find any shared resources. 2447 // Return true as soon as we find any shared resources.
2453 2448
2454 - std::set<QPDFObjGen> resources_seen; // shared resources detection  
2455 - std::set<QPDFObjGen> nodes_seen; // loop detection 2449 + QPDFObjGen::set resources_seen; // shared resources detection
  2450 + QPDFObjGen::set nodes_seen; // loop detection
2456 2451
2457 doIfVerbose([&](Pipeline& v, std::string const& prefix) { 2452 doIfVerbose([&](Pipeline& v, std::string const& prefix) {
2458 v << prefix << ": " << pdf.getFilename() 2453 v << prefix << ": " << pdf.getFilename()
@@ -2465,10 +2460,9 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf) @@ -2465,10 +2460,9 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2465 QPDFObjectHandle node = *queue.begin(); 2460 QPDFObjectHandle node = *queue.begin();
2466 queue.pop_front(); 2461 queue.pop_front();
2467 QPDFObjGen og = node.getObjGen(); 2462 QPDFObjGen og = node.getObjGen();
2468 - if (nodes_seen.count(og)) { 2463 + if (!nodes_seen.add(og)) {
2469 continue; 2464 continue;
2470 } 2465 }
2471 - nodes_seen.insert(og);  
2472 QPDFObjectHandle dict = node.isStream() ? node.getDict() : node; 2466 QPDFObjectHandle dict = node.isStream() ? node.getDict() : node;
2473 QPDFObjectHandle kids = dict.getKey("/Kids"); 2467 QPDFObjectHandle kids = dict.getKey("/Kids");
2474 if (kids.isArray()) { 2468 if (kids.isArray()) {
@@ -2489,33 +2483,29 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf) @@ -2489,33 +2483,29 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2489 // This is a leaf node or a form XObject. 2483 // This is a leaf node or a form XObject.
2490 QPDFObjectHandle resources = dict.getKey("/Resources"); 2484 QPDFObjectHandle resources = dict.getKey("/Resources");
2491 if (resources.isIndirect()) { 2485 if (resources.isIndirect()) {
2492 - QPDFObjGen resources_og = resources.getObjGen();  
2493 - if (resources_seen.count(resources_og)) { 2486 + if (!resources_seen.add(resources)) {
2494 QTC::TC("qpdf", "QPDFJob found shared resources in leaf"); 2487 QTC::TC("qpdf", "QPDFJob found shared resources in leaf");
2495 doIfVerbose([&](Pipeline& v, std::string const& prefix) { 2488 doIfVerbose([&](Pipeline& v, std::string const& prefix) {
2496 v << " found shared resources in leaf node " 2489 v << " found shared resources in leaf node "
2497 << og.unparse(' ') << ": " 2490 << og.unparse(' ') << ": "
2498 - << resources_og.unparse(' ') << "\n"; 2491 + << resources.getObjGen().unparse(' ') << "\n";
2499 }); 2492 });
2500 return true; 2493 return true;
2501 } 2494 }
2502 - resources_seen.insert(resources_og);  
2503 } 2495 }
2504 QPDFObjectHandle xobject = 2496 QPDFObjectHandle xobject =
2505 (resources.isDictionary() ? resources.getKey("/XObject") 2497 (resources.isDictionary() ? resources.getKey("/XObject")
2506 : QPDFObjectHandle::newNull()); 2498 : QPDFObjectHandle::newNull());
2507 if (xobject.isIndirect()) { 2499 if (xobject.isIndirect()) {
2508 - QPDFObjGen xobject_og = xobject.getObjGen();  
2509 - if (resources_seen.count(xobject_og)) { 2500 + if (!resources_seen.add(xobject)) {
2510 QTC::TC("qpdf", "QPDFJob found shared xobject in leaf"); 2501 QTC::TC("qpdf", "QPDFJob found shared xobject in leaf");
2511 doIfVerbose([&](Pipeline& v, std::string const& prefix) { 2502 doIfVerbose([&](Pipeline& v, std::string const& prefix) {
2512 v << " found shared xobject in leaf node " 2503 v << " found shared xobject in leaf node "
2513 - << og.unparse(' ') << ": " << xobject_og.unparse(' ')  
2514 - << "\n"; 2504 + << og.unparse(' ') << ": "
  2505 + << xobject.getObjGen().unparse(' ') << "\n";
2515 }); 2506 });
2516 return true; 2507 return true;
2517 } 2508 }
2518 - resources_seen.insert(xobject_og);  
2519 } 2509 }
2520 if (xobject.isDictionary()) { 2510 if (xobject.isDictionary()) {
2521 for (auto const& k: xobject.getKeys()) { 2511 for (auto const& k: xobject.getKeys()) {
libqpdf/QPDFObjGen.cc
1 #include <qpdf/QPDFObjGen.hh> 1 #include <qpdf/QPDFObjGen.hh>
2 2
3 -#include <qpdf/QUtil.hh> 3 +#include <qpdf/QPDFObjectHandle.hh>
  4 +#include <qpdf/QPDFObjectHelper.hh>
  5 +#include <qpdf/QPDFObject_private.hh>
4 6
  7 +#include <stdexcept>
  8 +
  9 +// ABI: inline and pass og by value
5 std::ostream& 10 std::ostream&
6 operator<<(std::ostream& os, const QPDFObjGen& og) 11 operator<<(std::ostream& os, const QPDFObjGen& og)
7 { 12 {
@@ -9,8 +14,55 @@ operator&lt;&lt;(std::ostream&amp; os, const QPDFObjGen&amp; og) @@ -9,8 +14,55 @@ operator&lt;&lt;(std::ostream&amp; os, const QPDFObjGen&amp; og)
9 return os; 14 return os;
10 } 15 }
11 16
  17 +// ABI: inline
12 std::string 18 std::string
13 QPDFObjGen::unparse(char separator) const 19 QPDFObjGen::unparse(char separator) const
14 { 20 {
15 - return std::to_string(this->obj) + separator + std::to_string(this->gen); 21 + return std::to_string(obj) + separator + std::to_string(gen);
  22 +}
  23 +
  24 +bool
  25 +QPDFObjGen::set::add(QPDFObjectHandle const& oh)
  26 +{
  27 + if (auto* ptr = oh.getObjectPtr()) {
  28 + return add(ptr->getObjGen());
  29 + } else {
  30 + throw std::logic_error("attempt to retrieve QPDFObjGen from "
  31 + "uninitialized QPDFObjectHandle");
  32 + return false;
  33 + }
  34 +}
  35 +
  36 +bool
  37 +QPDFObjGen::set::add(QPDFObjectHelper const& helper)
  38 +{
  39 + if (auto* ptr = helper.getObjectHandle().getObjectPtr()) {
  40 + return add(ptr->getObjGen());
  41 + } else {
  42 + throw std::logic_error("attempt to retrieve QPDFObjGen from "
  43 + "uninitialized QPDFObjectHandle");
  44 + return false;
  45 + }
  46 +}
  47 +
  48 +void
  49 +QPDFObjGen::set::erase(QPDFObjectHandle const& oh)
  50 +{
  51 + if (auto* ptr = oh.getObjectPtr()) {
  52 + erase(ptr->getObjGen());
  53 + } else {
  54 + throw std::logic_error("attempt to retrieve QPDFObjGen from "
  55 + "uninitialized QPDFObjectHandle");
  56 + }
  57 +}
  58 +
  59 +void
  60 +QPDFObjGen::set::erase(QPDFObjectHelper const& helper)
  61 +{
  62 + if (auto* ptr = helper.getObjectHandle().getObjectPtr()) {
  63 + erase(ptr->getObjGen());
  64 + } else {
  65 + throw std::logic_error("attempt to retrieve QPDFObjGen from "
  66 + "uninitialized QPDFObjectHandle");
  67 + }
16 } 68 }
libqpdf/QPDFObjectHandle.cc
@@ -1588,22 +1588,12 @@ QPDFObjectHandle::rotatePage(int angle, bool relative) @@ -1588,22 +1588,12 @@ QPDFObjectHandle::rotatePage(int angle, bool relative)
1588 int new_angle = angle; 1588 int new_angle = angle;
1589 if (relative) { 1589 if (relative) {
1590 int old_angle = 0; 1590 int old_angle = 0;
1591 - bool found_rotate = false;  
1592 QPDFObjectHandle cur_obj = *this; 1591 QPDFObjectHandle cur_obj = *this;
1593 - bool searched_parent = false;  
1594 - std::set<QPDFObjGen> visited;  
1595 - while (!found_rotate) {  
1596 - if (visited.count(cur_obj.getObjGen())) {  
1597 - // Don't get stuck in an infinite loop 1592 + QPDFObjGen::set visited;
  1593 + while (visited.add(cur_obj)) {
  1594 + // Don't get stuck in an infinite loop
  1595 + if (cur_obj.getKey("/Rotate").getValueAsInt(old_angle)) {
1598 break; 1596 break;
1599 - }  
1600 - if (!visited.empty()) {  
1601 - searched_parent = true;  
1602 - }  
1603 - visited.insert(cur_obj.getObjGen());  
1604 - if (cur_obj.getKey("/Rotate").isInteger()) {  
1605 - found_rotate = true;  
1606 - old_angle = cur_obj.getKey("/Rotate").getIntValueAsInt();  
1607 } else if (cur_obj.getKey("/Parent").isDictionary()) { 1597 } else if (cur_obj.getKey("/Parent").isDictionary()) {
1608 cur_obj = cur_obj.getKey("/Parent"); 1598 cur_obj = cur_obj.getKey("/Parent");
1609 } else { 1599 } else {
@@ -1613,7 +1603,7 @@ QPDFObjectHandle::rotatePage(int angle, bool relative) @@ -1613,7 +1603,7 @@ QPDFObjectHandle::rotatePage(int angle, bool relative)
1613 QTC::TC( 1603 QTC::TC(
1614 "qpdf", 1604 "qpdf",
1615 "QPDFObjectHandle found old angle", 1605 "QPDFObjectHandle found old angle",
1616 - searched_parent ? 0 : 1); 1606 + visited.size() > 1 ? 0 : 1);
1617 if ((old_angle % 90) != 0) { 1607 if ((old_angle % 90) != 0) {
1618 old_angle = 0; 1608 old_angle = 0;
1619 } 1609 }
@@ -2181,20 +2171,15 @@ QPDFObjectHandle::unsafeShallowCopy() @@ -2181,20 +2171,15 @@ QPDFObjectHandle::unsafeShallowCopy()
2181 } 2171 }
2182 2172
2183 void 2173 void
2184 -QPDFObjectHandle::makeDirect(  
2185 - std::set<QPDFObjGen>& visited, bool stop_at_streams) 2174 +QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams)
2186 { 2175 {
2187 assertInitialized(); 2176 assertInitialized();
2188 2177
2189 auto cur_og = getObjGen(); 2178 auto cur_og = getObjGen();
2190 - if (cur_og.getObj() != 0) {  
2191 - if (visited.count(cur_og)) {  
2192 - QTC::TC("qpdf", "QPDFObjectHandle makeDirect loop");  
2193 - throw std::runtime_error(  
2194 - "loop detected while converting object from "  
2195 - "indirect to direct");  
2196 - }  
2197 - visited.insert(cur_og); 2179 + if (!visited.add(cur_og)) {
  2180 + QTC::TC("qpdf", "QPDFObjectHandle makeDirect loop");
  2181 + throw std::runtime_error("loop detected while converting object from "
  2182 + "indirect to direct");
2198 } 2183 }
2199 2184
2200 if (isBool() || isInteger() || isName() || isNull() || isReal() || 2185 if (isBool() || isInteger() || isName() || isNull() || isReal() ||
@@ -2232,9 +2217,7 @@ QPDFObjectHandle::makeDirect( @@ -2232,9 +2217,7 @@ QPDFObjectHandle::makeDirect(
2232 "unknown object type"); 2217 "unknown object type");
2233 } 2218 }
2234 2219
2235 - if (cur_og.getObj()) {  
2236 - visited.erase(cur_og);  
2237 - } 2220 + visited.erase(cur_og);
2238 } 2221 }
2239 2222
2240 QPDFObjectHandle 2223 QPDFObjectHandle
@@ -2258,7 +2241,7 @@ QPDFObjectHandle::copyStream() @@ -2258,7 +2241,7 @@ QPDFObjectHandle::copyStream()
2258 void 2241 void
2259 QPDFObjectHandle::makeDirect(bool allow_streams) 2242 QPDFObjectHandle::makeDirect(bool allow_streams)
2260 { 2243 {
2261 - std::set<QPDFObjGen> visited; 2244 + QPDFObjGen::set visited;
2262 makeDirect(visited, allow_streams); 2245 makeDirect(visited, allow_streams);
2263 } 2246 }
2264 2247
libqpdf/QPDFOutlineDocumentHelper.cc
@@ -15,13 +15,8 @@ QPDFOutlineDocumentHelper::QPDFOutlineDocumentHelper(QPDF&amp; qpdf) : @@ -15,13 +15,8 @@ QPDFOutlineDocumentHelper::QPDFOutlineDocumentHelper(QPDF&amp; qpdf) :
15 return; 15 return;
16 } 16 }
17 QPDFObjectHandle cur = outlines.getKey("/First"); 17 QPDFObjectHandle cur = outlines.getKey("/First");
18 - std::set<QPDFObjGen> seen;  
19 - while (!cur.isNull()) {  
20 - auto og = cur.getObjGen();  
21 - if (seen.count(og)) {  
22 - break;  
23 - }  
24 - seen.insert(og); 18 + QPDFObjGen::set seen;
  19 + while (!cur.isNull() && seen.add(cur)) {
25 this->m->outlines.push_back( 20 this->m->outlines.push_back(
26 QPDFOutlineObjectHelper::Accessor::create(cur, *this, 1)); 21 QPDFOutlineObjectHelper::Accessor::create(cur, *this, 1));
27 cur = cur.getKey("/Next"); 22 cur = cur.getKey("/Next");
@@ -104,13 +99,3 @@ QPDFOutlineDocumentHelper::resolveNamedDest(QPDFObjectHandle name) @@ -104,13 +99,3 @@ QPDFOutlineDocumentHelper::resolveNamedDest(QPDFObjectHandle name)
104 } 99 }
105 return result; 100 return result;
106 } 101 }
107 -  
108 -bool  
109 -QPDFOutlineDocumentHelper::checkSeen(QPDFObjGen const& og)  
110 -{  
111 - if (this->m->seen.count(og) > 0) {  
112 - return true;  
113 - }  
114 - this->m->seen.insert(og);  
115 - return false;  
116 -}  
libqpdf/QPDFPageObjectHelper.cc
@@ -246,32 +246,23 @@ QPDFPageObjectHelper::getAttribute( @@ -246,32 +246,23 @@ QPDFPageObjectHelper::getAttribute(
246 std::function<QPDFObjectHandle()> get_fallback, 246 std::function<QPDFObjectHandle()> get_fallback,
247 bool copy_if_fallback) 247 bool copy_if_fallback)
248 { 248 {
249 - QPDFObjectHandle result;  
250 - QPDFObjectHandle dict;  
251 - bool is_form_xobject = this->oh.isFormXObject(); 249 + const bool is_form_xobject = this->oh.isFormXObject();
252 bool inherited = false; 250 bool inherited = false;
253 - if (is_form_xobject) {  
254 - dict = this->oh.getDict();  
255 - result = dict.getKey(name);  
256 - } else {  
257 - dict = this->oh;  
258 - bool inheritable =  
259 - ((name == "/MediaBox") || (name == "/CropBox") ||  
260 - (name == "/Resources") || (name == "/Rotate")); 251 + auto dict = is_form_xobject ? oh.getDict() : oh;
  252 + auto result = dict.getKey(name);
261 253
  254 + if (!is_form_xobject && result.isNull() &&
  255 + (name == "/MediaBox" || name == "/CropBox" || name == "/Resources" ||
  256 + name == "/Rotate")) {
262 QPDFObjectHandle node = dict; 257 QPDFObjectHandle node = dict;
263 - result = node.getKey(name);  
264 - std::set<QPDFObjGen> seen;  
265 - while (inheritable && result.isNull() && node.hasKey("/Parent")) {  
266 - seen.insert(node.getObjGen()); 258 + QPDFObjGen::set seen{};
  259 + while (seen.add(node) && node.hasKey("/Parent")) {
267 node = node.getKey("/Parent"); 260 node = node.getKey("/Parent");
268 - if (seen.count(node.getObjGen())) {  
269 - break;  
270 - }  
271 result = node.getKey(name); 261 result = node.getKey(name);
272 if (!result.isNull()) { 262 if (!result.isNull()) {
273 QTC::TC("qpdf", "QPDFPageObjectHelper non-trivial inheritance"); 263 QTC::TC("qpdf", "QPDFPageObjectHelper non-trivial inheritance");
274 inherited = true; 264 inherited = true;
  265 + break;
275 } 266 }
276 } 267 }
277 } 268 }
@@ -361,30 +352,27 @@ QPDFPageObjectHelper::forEachXObject( @@ -361,30 +352,27 @@ QPDFPageObjectHelper::forEachXObject(
361 "QPDFPageObjectHelper::forEachXObject", 352 "QPDFPageObjectHelper::forEachXObject",
362 recursive ? (this->oh.isFormXObject() ? 0 : 1) 353 recursive ? (this->oh.isFormXObject() ? 0 : 1)
363 : (this->oh.isFormXObject() ? 2 : 3)); 354 : (this->oh.isFormXObject() ? 2 : 3));
364 - std::set<QPDFObjGen> seen; 355 + QPDFObjGen::set seen;
365 std::list<QPDFPageObjectHelper> queue; 356 std::list<QPDFPageObjectHelper> queue;
366 queue.push_back(*this); 357 queue.push_back(*this);
367 while (!queue.empty()) { 358 while (!queue.empty()) {
368 - QPDFPageObjectHelper ph = queue.front();  
369 - queue.pop_front();  
370 - QPDFObjGen og = ph.oh.getObjGen();  
371 - if (seen.count(og)) {  
372 - continue;  
373 - }  
374 - seen.insert(og);  
375 - QPDFObjectHandle resources = ph.getAttribute("/Resources", false);  
376 - if (resources.isDictionary() && resources.hasKey("/XObject")) {  
377 - QPDFObjectHandle xobj_dict = resources.getKey("/XObject");  
378 - for (auto const& key: xobj_dict.getKeys()) {  
379 - QPDFObjectHandle obj = xobj_dict.getKey(key);  
380 - if ((!selector) || selector(obj)) {  
381 - action(obj, xobj_dict, key);  
382 - }  
383 - if (recursive && obj.isFormXObject()) {  
384 - queue.push_back(QPDFPageObjectHelper(obj)); 359 + auto& ph = queue.front();
  360 + if (seen.add(ph)) {
  361 + auto xobj_dict =
  362 + ph.getAttribute("/Resources", false).getKeyIfDict("/XObject");
  363 + if (xobj_dict.isDictionary()) {
  364 + for (auto const& key: xobj_dict.getKeys()) {
  365 + QPDFObjectHandle obj = xobj_dict.getKey(key);
  366 + if ((!selector) || selector(obj)) {
  367 + action(obj, xobj_dict, key);
  368 + }
  369 + if (recursive && obj.isFormXObject()) {
  370 + queue.emplace_back(obj);
  371 + }
385 } 372 }
386 } 373 }
387 } 374 }
  375 + queue.pop_front();
388 } 376 }
389 } 377 }
390 378
libqpdf/QPDF_optimization.cc
@@ -284,7 +284,7 @@ QPDF::updateObjectMaps( @@ -284,7 +284,7 @@ QPDF::updateObjectMaps(
284 QPDFObjectHandle oh, 284 QPDFObjectHandle oh,
285 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 285 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
286 { 286 {
287 - std::set<QPDFObjGen> visited; 287 + QPDFObjGen::set visited;
288 updateObjectMapsInternal(ou, oh, skip_stream_parameters, visited, true); 288 updateObjectMapsInternal(ou, oh, skip_stream_parameters, visited, true);
289 } 289 }
290 290
@@ -293,7 +293,7 @@ QPDF::updateObjectMapsInternal( @@ -293,7 +293,7 @@ QPDF::updateObjectMapsInternal(
293 ObjUser const& ou, 293 ObjUser const& ou,
294 QPDFObjectHandle oh, 294 QPDFObjectHandle oh,
295 std::function<int(QPDFObjectHandle&)> skip_stream_parameters, 295 std::function<int(QPDFObjectHandle&)> skip_stream_parameters,
296 - std::set<QPDFObjGen>& visited, 296 + QPDFObjGen::set& visited,
297 bool top) 297 bool top)
298 { 298 {
299 // Traverse the object tree from this point taking care to avoid 299 // Traverse the object tree from this point taking care to avoid
@@ -310,13 +310,12 @@ QPDF::updateObjectMapsInternal( @@ -310,13 +310,12 @@ QPDF::updateObjectMapsInternal(
310 310
311 if (oh.isIndirect()) { 311 if (oh.isIndirect()) {
312 QPDFObjGen og(oh.getObjGen()); 312 QPDFObjGen og(oh.getObjGen());
313 - if (visited.count(og)) { 313 + if (!visited.add(og)) {
314 QTC::TC("qpdf", "QPDF opt loop detected"); 314 QTC::TC("qpdf", "QPDF opt loop detected");
315 return; 315 return;
316 } 316 }
317 this->m->obj_user_to_objects[ou].insert(og); 317 this->m->obj_user_to_objects[ou].insert(og);
318 this->m->object_to_obj_users[og].insert(ou); 318 this->m->object_to_obj_users[og].insert(ou);
319 - visited.insert(og);  
320 } 319 }
321 320
322 if (oh.isArray()) { 321 if (oh.isArray()) {
libqpdf/QPDF_pages.cc
@@ -55,13 +55,13 @@ QPDF::getAllPages() @@ -55,13 +55,13 @@ QPDF::getAllPages()
55 // initialize this->m->all_pages. 55 // initialize this->m->all_pages.
56 if (this->m->all_pages.empty()) { 56 if (this->m->all_pages.empty()) {
57 this->m->ever_called_get_all_pages = true; 57 this->m->ever_called_get_all_pages = true;
58 - std::set<QPDFObjGen> visited;  
59 - std::set<QPDFObjGen> seen; 58 + QPDFObjGen::set visited;
  59 + QPDFObjGen::set seen;
60 QPDFObjectHandle pages = getRoot().getKey("/Pages"); 60 QPDFObjectHandle pages = getRoot().getKey("/Pages");
61 bool warned = false; 61 bool warned = false;
62 bool changed_pages = false; 62 bool changed_pages = false;
63 while (pages.isDictionary() && pages.hasKey("/Parent")) { 63 while (pages.isDictionary() && pages.hasKey("/Parent")) {
64 - if (seen.count(pages.getObjGen())) { 64 + if (!seen.add(pages)) {
65 // loop -- will be detected again and reported later 65 // loop -- will be detected again and reported later
66 break; 66 break;
67 } 67 }
@@ -74,7 +74,6 @@ QPDF::getAllPages() @@ -74,7 +74,6 @@ QPDF::getAllPages()
74 " to the root of the page tree; attempting to correct"); 74 " to the root of the page tree; attempting to correct");
75 warned = true; 75 warned = true;
76 } 76 }
77 - seen.insert(pages.getObjGen());  
78 changed_pages = true; 77 changed_pages = true;
79 pages = pages.getKey("/Parent"); 78 pages = pages.getKey("/Parent");
80 } 79 }
@@ -92,12 +91,9 @@ QPDF::getAllPages() @@ -92,12 +91,9 @@ QPDF::getAllPages()
92 91
93 void 92 void
94 QPDF::getAllPagesInternal( 93 QPDF::getAllPagesInternal(
95 - QPDFObjectHandle cur_node,  
96 - std::set<QPDFObjGen>& visited,  
97 - std::set<QPDFObjGen>& seen) 94 + QPDFObjectHandle cur_node, QPDFObjGen::set& visited, QPDFObjGen::set& seen)
98 { 95 {
99 - QPDFObjGen cur_node_og = cur_node.getObjGen();  
100 - if (visited.count(cur_node_og) > 0) { 96 + if (!visited.add(cur_node)) {
101 throw QPDFExc( 97 throw QPDFExc(
102 qpdf_e_pages, 98 qpdf_e_pages,
103 this->m->file->getName(), 99 this->m->file->getName(),
@@ -105,7 +101,6 @@ QPDF::getAllPagesInternal( @@ -105,7 +101,6 @@ QPDF::getAllPagesInternal(
105 0, 101 0,
106 "Loop detected in /Pages structure (getAllPages)"); 102 "Loop detected in /Pages structure (getAllPages)");
107 } 103 }
108 - visited.insert(cur_node_og);  
109 if (!cur_node.isDictionaryOfType("/Pages")) { 104 if (!cur_node.isDictionaryOfType("/Pages")) {
110 cur_node.warnIfPossible( 105 cur_node.warnIfPossible(
111 "/Type key should be /Pages but is not; overriding"); 106 "/Type key should be /Pages but is not; overriding");
@@ -125,7 +120,7 @@ QPDF::getAllPagesInternal( @@ -125,7 +120,7 @@ QPDF::getAllPagesInternal(
125 " (from 0) is direct; converting to indirect"); 120 " (from 0) is direct; converting to indirect");
126 kid = makeIndirectObject(kid); 121 kid = makeIndirectObject(kid);
127 kids.setArrayItem(i, kid); 122 kids.setArrayItem(i, kid);
128 - } else if (seen.count(kid.getObjGen())) { 123 + } else if (!seen.add(kid)) {
129 // Make a copy of the page. This does the same as 124 // Make a copy of the page. This does the same as
130 // shallowCopyPage in QPDFPageObjectHelper. 125 // shallowCopyPage in QPDFPageObjectHelper.
131 QTC::TC("qpdf", "QPDF resolve duplicated page object"); 126 QTC::TC("qpdf", "QPDF resolve duplicated page object");
@@ -134,6 +129,7 @@ QPDF::getAllPagesInternal( @@ -134,6 +129,7 @@ QPDF::getAllPagesInternal(
134 " (from 0) appears more than once in the pages tree;" 129 " (from 0) appears more than once in the pages tree;"
135 " creating a new page object as a copy"); 130 " creating a new page object as a copy");
136 kid = makeIndirectObject(QPDFObjectHandle(kid).shallowCopy()); 131 kid = makeIndirectObject(QPDFObjectHandle(kid).shallowCopy());
  132 + seen.add(kid);
137 kids.setArrayItem(i, kid); 133 kids.setArrayItem(i, kid);
138 } 134 }
139 if (!kid.isDictionaryOfType("/Page")) { 135 if (!kid.isDictionaryOfType("/Page")) {
@@ -141,7 +137,6 @@ QPDF::getAllPagesInternal( @@ -141,7 +137,6 @@ QPDF::getAllPagesInternal(
141 "/Type key should be /Page but is not; overriding"); 137 "/Type key should be /Page but is not; overriding");
142 kid.replaceKey("/Type", "/Page"_qpdf); 138 kid.replaceKey("/Type", "/Page"_qpdf);
143 } 139 }
144 - seen.insert(kid.getObjGen());  
145 m->all_pages.push_back(kid); 140 m->all_pages.push_back(kid);
146 } 141 }
147 } 142 }