Commit b20ff9680529692d5058b0f1fcd87c2b984b4195

Authored by m-holger
1 parent 91b8b0d8

Refactor `ObjCopier` and `copyForeignObject`: enhance encapsulation, streamline …

…`Copier` usage, and simplify object copying logic.
libqpdf/QPDF.cc
... ... @@ -471,6 +471,12 @@ QPDF::getObjectByID(int objid, int generation)
471 471 return getObject(QPDFObjGen(objid, generation));
472 472 }
473 473  
  474 +QPDFObjectHandle
  475 +QPDF::copyForeignObject(QPDFObjectHandle foreign)
  476 +{
  477 + return m->obj_copier.copied(foreign);
  478 +}
  479 +
474 480 QPDF::ObjCopier::Copier&
475 481 QPDF::ObjCopier::copier(QPDFObjectHandle const& foreign)
476 482 {
... ... @@ -485,7 +491,7 @@ QPDF::ObjCopier::copier(QPDFObjectHandle const& foreign)
485 491 }
486 492  
487 493 QPDFObjectHandle
488   -QPDF::copyForeignObject(QPDFObjectHandle foreign)
  494 +QPDF::ObjCopier::Copier::copied(QPDFObjectHandle const& foreign)
489 495 {
490 496 // Here's an explanation of what's going on here.
491 497 //
... ... @@ -522,42 +528,36 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
522 528 // Note that we explicitly allow use of copyForeignObject on page objects. It is a documented
523 529 // use case to copy pages this way if the intention is to not update the pages tree.
524 530  
525   - auto& obj_copier = m->obj_copier.copier(foreign);
526   - if (!obj_copier.visiting.empty()) {
527   - throw std::logic_error(
528   - "obj_copier.visiting is not empty at the beginning of copyForeignObject");
529   - }
  531 + util::assertion(
  532 + visiting.empty(), "obj_copier.visiting is not empty at the beginning of copyForeignObject");
530 533  
531 534 // Make sure we have an object in this file for every referenced object in the old file.
532 535 // obj_copier.object_map maps foreign QPDFObjGen to local objects. For everything new that we
533 536 // have to copy, the local object will be a reservation, unless it is a stream, in which case
534 537 // the local object will already be a stream.
535   - obj_copier.reserve_objects(foreign);
  538 + reserve_objects(foreign, true);
536 539  
537   - if (!obj_copier.visiting.empty()) {
538   - throw std::logic_error("obj_copier.visiting is not empty after reserving objects");
539   - }
  540 + util::assertion(visiting.empty(), "obj_copier.visiting is not empty after reserving objects");
540 541  
541 542 // Copy any new objects and replace the reservations.
542   - for (auto& to_copy: obj_copier.to_copy) {
543   - auto copy = obj_copier.replace_indirect_object(to_copy);
544   - if (!to_copy.isStream()) {
545   - QPDFObjGen og(to_copy.getObjGen());
546   - replaceReserved(obj_copier.object_map[og], copy);
  543 + for (auto& oh: to_copy) {
  544 + auto copy = replace_indirect_object(oh, true);
  545 + if (!oh.isStream()) {
  546 + qpdf.replaceReserved(object_map[oh], copy);
547 547 }
548 548 }
549   - obj_copier.to_copy.clear();
  549 + to_copy.clear();
550 550  
551 551 auto og = foreign.getObjGen();
552   - if (!obj_copier.object_map.contains(og)) {
553   - warn(damagedPDF(
  552 + if (!object_map.contains(og)) {
  553 + qpdf.warn(qpdf.damagedPDF(
554 554 foreign.qpdf()->getFilename() + " object " + og.unparse(' '),
555 555 foreign.offset(),
556 556 "unexpected reference to /Pages object while copying foreign object; replacing with "
557 557 "null"));
558 558 return QPDFObjectHandle::newNull();
559 559 }
560   - return obj_copier.object_map[foreign.getObjGen()];
  560 + return object_map[foreign];
561 561 }
562 562  
563 563 void
... ... @@ -592,17 +592,17 @@ QPDF::ObjCopier::Copier::reserve_objects(QPDFObjectHandle const& foreign, bool t
592 592 }
593 593  
594 594 if (foreign_tc == ::ot_array) {
595   - for (auto const& item: foreign.as_array()) {
596   - reserve_objects(item, false);
  595 + for (auto const& item: Array(foreign)) {
  596 + reserve_objects(item);
597 597 }
598 598 } else if (foreign_tc == ::ot_dictionary) {
599 599 for (auto const& item: Dictionary(foreign)) {
600 600 if (!item.second.null()) {
601   - reserve_objects(item.second, false);
  601 + reserve_objects(item.second);
602 602 }
603 603 }
604 604 } else if (foreign_tc == ::ot_stream) {
605   - reserve_objects(foreign.getDict(), false);
  605 + reserve_objects(foreign.getDict());
606 606 }
607 607  
608 608 visiting.erase(foreign);
... ... @@ -611,7 +611,7 @@ QPDF::ObjCopier::Copier::reserve_objects(QPDFObjectHandle const& foreign, bool t
611 611 QPDFObjectHandle
612 612 QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const& foreign, bool top)
613 613 {
614   - auto foreign_tc = foreign.getTypeCode();
  614 + auto foreign_tc = foreign.type_code();
615 615  
616 616 if (!top && foreign.indirect()) {
617 617 auto mapping = object_map.find(foreign.id_gen());
... ... @@ -628,7 +628,7 @@ QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const& foreign
628 628 std::vector<QPDFObjectHandle> result;
629 629 result.reserve(array.size());
630 630 for (auto const& item: array) {
631   - result.emplace_back(replace_indirect_object(item, false));
  631 + result.emplace_back(replace_indirect_object(item));
632 632 }
633 633 return Array(std::move(result));
634 634 }
... ... @@ -637,7 +637,7 @@ QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const&amp; foreign
637 637 auto result = Dictionary::empty();
638 638 for (auto const& [key, value]: Dictionary(foreign)) {
639 639 if (!value.null()) {
640   - result.replaceKey(key, replace_indirect_object(value, false));
  640 + result.replaceKey(key, replace_indirect_object(value));
641 641 }
642 642 }
643 643 return result;
... ... @@ -649,7 +649,7 @@ QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const&amp; foreign
649 649 auto dict = result.getDict();
650 650 for (auto const& [key, value]: stream.getDict()) {
651 651 if (!value.null()) {
652   - dict.replaceKey(key, replace_indirect_object(value, false));
  652 + dict.replaceKey(key, replace_indirect_object(value));
653 653 }
654 654 }
655 655 qpdf.copyStreamData(result, foreign);
... ...
libqpdf/qpdf/QPDF_private.hh
... ... @@ -46,7 +46,6 @@ class QPDF::ObjCache
46 46  
47 47 class QPDF::ObjCopier
48 48 {
49   - public:
50 49 class Copier
51 50 {
52 51 public:
... ... @@ -55,9 +54,11 @@ class QPDF::ObjCopier
55 54 {
56 55 }
57 56  
58   - QPDFObjectHandle replace_indirect_object(QPDFObjectHandle const& foreign, bool top = true);
  57 + QPDFObjectHandle copied(QPDFObjectHandle const& foreign);
59 58  
60   - void reserve_objects(QPDFObjectHandle const& foreign, bool top = true);
  59 + private:
  60 + QPDFObjectHandle replace_indirect_object(QPDFObjectHandle const& foreign, bool top = false);
  61 + void reserve_objects(QPDFObjectHandle const& foreign, bool top = false);
61 62  
62 63 QPDF& qpdf;
63 64 std::map<QPDFObjGen, QPDFObjectHandle> object_map;
... ... @@ -65,11 +66,27 @@ class QPDF::ObjCopier
65 66 QPDFObjGen::set visiting;
66 67 };
67 68  
  69 + public:
68 70 ObjCopier(QPDF& qpdf) :
69 71 qpdf(qpdf)
70 72 {
71 73 }
72 74  
  75 + ObjCopier() = delete;
  76 + ObjCopier(ObjCopier const&) = delete;
  77 + ObjCopier(ObjCopier&&) = delete;
  78 + ObjCopier& operator=(ObjCopier const&) = delete;
  79 + ObjCopier& operator=(ObjCopier&&) = delete;
  80 + ~ObjCopier() = default;
  81 +
  82 + // Return a local handle to the foreign object. Copy the foreign object if necessary.
  83 + QPDFObjectHandle
  84 + copied(QPDFObjectHandle const& foreign)
  85 + {
  86 + return copier(foreign).copied(foreign);
  87 + }
  88 +
  89 + private:
73 90 Copier& copier(QPDFObjectHandle const& foreign);
74 91  
75 92 QPDF& qpdf;
... ...