Commit d17f11e721be15e2cf3b394fd6e7539c1b49dbef

Authored by Jay Berkenbilt
1 parent 72e3a968

Make QPDF::updateObjectMaps iterative

include/qpdf/QPDF.hh
@@ -1343,6 +1343,18 @@ class QPDF @@ -1343,6 +1343,18 @@ class QPDF
1343 std::string key; // if ou_trailer_key or ou_root_key 1343 std::string key; // if ou_trailer_key or ou_root_key
1344 }; 1344 };
1345 1345
  1346 + struct UpdateObjectMapsFrame
  1347 + {
  1348 + UpdateObjectMapsFrame(
  1349 + ObjUser const& ou,
  1350 + QPDFObjectHandle oh,
  1351 + bool top);
  1352 +
  1353 + ObjUser const& ou;
  1354 + QPDFObjectHandle oh;
  1355 + bool top;
  1356 + };
  1357 +
1346 class PatternFinder: public InputSource::Finder 1358 class PatternFinder: public InputSource::Finder
1347 { 1359 {
1348 public: 1360 public:
@@ -1426,12 +1438,6 @@ class QPDF @@ -1426,12 +1438,6 @@ class QPDF
1426 ObjUser const& ou, 1438 ObjUser const& ou,
1427 QPDFObjectHandle oh, 1439 QPDFObjectHandle oh,
1428 std::function<int(QPDFObjectHandle&)> skip_stream_parameters); 1440 std::function<int(QPDFObjectHandle&)> skip_stream_parameters);
1429 - void updateObjectMapsInternal(  
1430 - ObjUser const& ou,  
1431 - QPDFObjectHandle oh,  
1432 - std::function<int(QPDFObjectHandle&)> skip_stream_parameters,  
1433 - QPDFObjGen::set& visited,  
1434 - bool top);  
1435 void filterCompressedObjects(std::map<int, int> const& object_stream_data); 1441 void filterCompressedObjects(std::map<int, int> const& object_stream_data);
1436 void filterCompressedObjects(QPDFWriter::ObjTable const& object_stream_data); 1442 void filterCompressedObjects(QPDFWriter::ObjTable const& object_stream_data);
1437 1443
libqpdf/QPDF_optimization.cc
@@ -54,6 +54,14 @@ QPDF::ObjUser::operator&lt;(ObjUser const&amp; rhs) const @@ -54,6 +54,14 @@ QPDF::ObjUser::operator&lt;(ObjUser const&amp; rhs) const
54 return false; 54 return false;
55 } 55 }
56 56
  57 +QPDF::UpdateObjectMapsFrame::UpdateObjectMapsFrame(
  58 + QPDF::ObjUser const& ou, QPDFObjectHandle oh, bool top) :
  59 + ou(ou),
  60 + oh(oh),
  61 + top(top)
  62 +{
  63 +}
  64 +
57 void 65 void
58 QPDF::optimize( 66 QPDF::optimize(
59 std::map<int, int> const& object_stream_data, 67 std::map<int, int> const& object_stream_data,
@@ -277,78 +285,70 @@ QPDF::pushInheritedAttributesToPageInternal( @@ -277,78 +285,70 @@ QPDF::pushInheritedAttributesToPageInternal(
277 285
278 void 286 void
279 QPDF::updateObjectMaps( 287 QPDF::updateObjectMaps(
280 - ObjUser const& ou,  
281 - QPDFObjectHandle oh, 288 + ObjUser const& first_ou,
  289 + QPDFObjectHandle first_oh,
282 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 290 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
283 { 291 {
284 QPDFObjGen::set visited; 292 QPDFObjGen::set visited;
285 - updateObjectMapsInternal(ou, oh, skip_stream_parameters, visited, true);  
286 -}  
287 -  
288 -void  
289 -QPDF::updateObjectMapsInternal(  
290 - ObjUser const& ou,  
291 - QPDFObjectHandle oh,  
292 - std::function<int(QPDFObjectHandle&)> skip_stream_parameters,  
293 - QPDFObjGen::set& visited,  
294 - bool top)  
295 -{ 293 + std::vector<UpdateObjectMapsFrame> pending;
  294 + pending.emplace_back(first_ou, first_oh, true);
296 // Traverse the object tree from this point taking care to avoid crossing page boundaries. 295 // Traverse the object tree from this point taking care to avoid crossing page boundaries.
  296 + std::unique_ptr<ObjUser> thumb_ou;
  297 + while (!pending.empty()) {
  298 + auto cur = pending.back();
  299 + pending.pop_back();
297 300
298 - bool is_page_node = false; 301 + bool is_page_node = false;
299 302
300 - if (oh.isDictionaryOfType("/Page")) {  
301 - is_page_node = true;  
302 - if (!top) {  
303 - return; 303 + if (cur.oh.isDictionaryOfType("/Page")) {
  304 + is_page_node = true;
  305 + if (!cur.top) {
  306 + continue;
  307 + }
304 } 308 }
305 - }  
306 309
307 - if (oh.isIndirect()) {  
308 - QPDFObjGen og(oh.getObjGen());  
309 - if (!visited.add(og)) {  
310 - QTC::TC("qpdf", "QPDF opt loop detected");  
311 - return; 310 + if (cur.oh.isIndirect()) {
  311 + QPDFObjGen og(cur.oh.getObjGen());
  312 + if (!visited.add(og)) {
  313 + QTC::TC("qpdf", "QPDF opt loop detected");
  314 + continue;
  315 + }
  316 + m->obj_user_to_objects[cur.ou].insert(og);
  317 + m->object_to_obj_users[og].insert(cur.ou);
312 } 318 }
313 - m->obj_user_to_objects[ou].insert(og);  
314 - m->object_to_obj_users[og].insert(ou);  
315 - }  
316 319
317 - if (oh.isArray()) {  
318 - int n = oh.getArrayNItems();  
319 - for (int i = 0; i < n; ++i) {  
320 - updateObjectMapsInternal(  
321 - ou, oh.getArrayItem(i), skip_stream_parameters, visited, false);  
322 - }  
323 - } else if (oh.isDictionary() || oh.isStream()) {  
324 - QPDFObjectHandle dict = oh;  
325 - bool is_stream = oh.isStream();  
326 - int ssp = 0;  
327 - if (is_stream) {  
328 - dict = oh.getDict();  
329 - if (skip_stream_parameters) {  
330 - ssp = skip_stream_parameters(oh); 320 + if (cur.oh.isArray()) {
  321 + int n = cur.oh.getArrayNItems();
  322 + for (int i = 0; i < n; ++i) {
  323 + pending.emplace_back(cur.ou, cur.oh.getArrayItem(i), false);
  324 + }
  325 + } else if (cur.oh.isDictionary() || cur.oh.isStream()) {
  326 + QPDFObjectHandle dict = cur.oh;
  327 + bool is_stream = cur.oh.isStream();
  328 + int ssp = 0;
  329 + if (is_stream) {
  330 + dict = cur.oh.getDict();
  331 + if (skip_stream_parameters) {
  332 + ssp = skip_stream_parameters(cur.oh);
  333 + }
331 } 334 }
332 - }  
333 335
334 - for (auto const& key: dict.getKeys()) {  
335 - if (is_page_node && (key == "/Thumb")) {  
336 - // Traverse page thumbnail dictionaries as a special case.  
337 - updateObjectMapsInternal(  
338 - ObjUser(ObjUser::ou_thumb, ou.pageno),  
339 - dict.getKey(key),  
340 - skip_stream_parameters,  
341 - visited,  
342 - false);  
343 - } else if (is_page_node && (key == "/Parent")) {  
344 - // Don't traverse back up the page tree  
345 - } else if (  
346 - ((ssp >= 1) && (key == "/Length")) ||  
347 - ((ssp >= 2) && ((key == "/Filter") || (key == "/DecodeParms")))) {  
348 - // Don't traverse into stream parameters that we are not going to write.  
349 - } else {  
350 - updateObjectMapsInternal(  
351 - ou, dict.getKey(key), skip_stream_parameters, visited, false); 336 + for (auto const& key: dict.getKeys()) {
  337 + if (is_page_node && (key == "/Thumb")) {
  338 + // 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.
  340 + thumb_ou = std::make_unique<ObjUser>(ObjUser::ou_thumb, cur.ou.pageno);
  341 + pending.emplace_back(*thumb_ou, dict.getKey(key), false);
  342 + } else if (is_page_node && (key == "/Parent")) {
  343 + // Don't traverse back up the page tree
  344 + } else if (
  345 + ((ssp >= 1) && (key == "/Length")) ||
  346 + ((ssp >= 2) && ((key == "/Filter") || (key == "/DecodeParms")))) {
  347 + // Don't traverse into stream parameters that we are not going to write.
  348 + } else {
  349 + pending.emplace_back(
  350 + cur.ou, dict.getKey(key), false);
  351 + }
352 } 352 }
353 } 353 }
354 } 354 }