Commit d17f11e721be15e2cf3b394fd6e7539c1b49dbef

Authored by Jay Berkenbilt
1 parent 72e3a968

Make QPDF::updateObjectMaps iterative

include/qpdf/QPDF.hh
... ... @@ -1343,6 +1343,18 @@ class QPDF
1343 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 1358 class PatternFinder: public InputSource::Finder
1347 1359 {
1348 1360 public:
... ... @@ -1426,12 +1438,6 @@ class QPDF
1426 1438 ObjUser const& ou,
1427 1439 QPDFObjectHandle oh,
1428 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 1441 void filterCompressedObjects(std::map<int, int> const& object_stream_data);
1436 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 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 65 void
58 66 QPDF::optimize(
59 67 std::map<int, int> const& object_stream_data,
... ... @@ -277,78 +285,70 @@ QPDF::pushInheritedAttributesToPageInternal(
277 285  
278 286 void
279 287 QPDF::updateObjectMaps(
280   - ObjUser const& ou,
281   - QPDFObjectHandle oh,
  288 + ObjUser const& first_ou,
  289 + QPDFObjectHandle first_oh,
282 290 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
283 291 {
284 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 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 }
... ...