Commit 91b8b0d80761877bff2a23fc40acd3153ff69bb4

Authored by m-holger
1 parent 242ddf15

Refactor `ObjCopier` and `copyForeignObject`: introduce `Copier` class, streamli…

…ne object copying logic, and simplify method structure.
libqpdf/QPDF.cc
... ... @@ -166,8 +166,9 @@ QPDF::Members::Members(QPDF& qpdf) :
166 166 objects(doc.objects()),
167 167 pages(doc.pages()),
168 168 log(QPDFLogger::defaultLogger()),
169   - file(new InvalidInputSource()),
170   - encp(new EncryptionParameters)
  169 + file(std::make_shared<InvalidInputSource>()),
  170 + encp(std::make_shared<EncryptionParameters>()),
  171 + obj_copier(qpdf)
171 172 {
172 173 }
173 174  
... ... @@ -470,6 +471,19 @@ QPDF::getObjectByID(int objid, int generation)
470 471 return getObject(QPDFObjGen(objid, generation));
471 472 }
472 473  
  474 +QPDF::ObjCopier::Copier&
  475 +QPDF::ObjCopier::copier(QPDFObjectHandle const& foreign)
  476 +{
  477 + if (!foreign.isIndirect()) {
  478 + throw std::logic_error("QPDF::copyForeign called with direct object handle");
  479 + }
  480 + QPDF& other = *foreign.qpdf();
  481 + if (&other == &qpdf) {
  482 + throw std::logic_error("QPDF::copyForeign called with object from this QPDF");
  483 + }
  484 + return copiers.insert({other.getUniqueId(), {qpdf}}).first->second;
  485 +}
  486 +
473 487 QPDFObjectHandle
474 488 QPDF::copyForeignObject(QPDFObjectHandle foreign)
475 489 {
... ... @@ -507,17 +521,8 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
507 521  
508 522 // Note that we explicitly allow use of copyForeignObject on page objects. It is a documented
509 523 // use case to copy pages this way if the intention is to not update the pages tree.
510   - if (!foreign.isIndirect()) {
511   - QTC::TC("qpdf", "QPDF copyForeign direct");
512   - throw std::logic_error("QPDF::copyForeign called with direct object handle");
513   - }
514   - QPDF& other = foreign.getQPDF();
515   - if (&other == this) {
516   - QTC::TC("qpdf", "QPDF copyForeign not foreign");
517   - throw std::logic_error("QPDF::copyForeign called with object from this QPDF");
518   - }
519 524  
520   - ObjCopier& obj_copier = m->object_copiers[other.m->unique_id];
  525 + auto& obj_copier = m->obj_copier.copier(foreign);
521 526 if (!obj_copier.visiting.empty()) {
522 527 throw std::logic_error(
523 528 "obj_copier.visiting is not empty at the beginning of copyForeignObject");
... ... @@ -527,7 +532,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
527 532 // obj_copier.object_map maps foreign QPDFObjGen to local objects. For everything new that we
528 533 // have to copy, the local object will be a reservation, unless it is a stream, in which case
529 534 // the local object will already be a stream.
530   - obj_copier.reserve_objects(*this, foreign);
  535 + obj_copier.reserve_objects(foreign);
531 536  
532 537 if (!obj_copier.visiting.empty()) {
533 538 throw std::logic_error("obj_copier.visiting is not empty after reserving objects");
... ... @@ -535,7 +540,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
535 540  
536 541 // Copy any new objects and replace the reservations.
537 542 for (auto& to_copy: obj_copier.to_copy) {
538   - auto copy = obj_copier.replace_indirect_object(*this, to_copy);
  543 + auto copy = obj_copier.replace_indirect_object(to_copy);
539 544 if (!to_copy.isStream()) {
540 545 QPDFObjGen og(to_copy.getObjGen());
541 546 replaceReserved(obj_copier.object_map[og], copy);
... ... @@ -546,7 +551,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
546 551 auto og = foreign.getObjGen();
547 552 if (!obj_copier.object_map.contains(og)) {
548 553 warn(damagedPDF(
549   - other.getFilename() + " object " + og.unparse(' '),
  554 + foreign.qpdf()->getFilename() + " object " + og.unparse(' '),
550 555 foreign.offset(),
551 556 "unexpected reference to /Pages object while copying foreign object; replacing with "
552 557 "null"));
... ... @@ -556,7 +561,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
556 561 }
557 562  
558 563 void
559   -QPDF::ObjCopier::reserve_objects(QPDF& target, QPDFObjectHandle const& foreign, bool top)
  564 +QPDF::ObjCopier::Copier::reserve_objects(QPDFObjectHandle const& foreign, bool top)
560 565 {
561 566 auto foreign_tc = foreign.type_code();
562 567 util::assertion(
... ... @@ -577,8 +582,7 @@ QPDF::ObjCopier::reserve_objects(QPDF&amp; target, QPDFObjectHandle const&amp; foreign,
577 582 return;
578 583 }
579 584 } else {
580   - object_map[foreign_og] =
581   - foreign.isStream() ? target.newStream() : target.newIndirectNull();
  585 + object_map[foreign_og] = foreign.isStream() ? qpdf.newStream() : qpdf.newIndirectNull();
582 586 if (!top && foreign.isPageObject()) {
583 587 visiting.erase(foreign_og);
584 588 return;
... ... @@ -589,23 +593,23 @@ QPDF::ObjCopier::reserve_objects(QPDF&amp; target, QPDFObjectHandle const&amp; foreign,
589 593  
590 594 if (foreign_tc == ::ot_array) {
591 595 for (auto const& item: foreign.as_array()) {
592   - reserve_objects(target, item, false);
  596 + reserve_objects(item, false);
593 597 }
594 598 } else if (foreign_tc == ::ot_dictionary) {
595 599 for (auto const& item: Dictionary(foreign)) {
596 600 if (!item.second.null()) {
597   - reserve_objects(target, item.second, false);
  601 + reserve_objects(item.second, false);
598 602 }
599 603 }
600 604 } else if (foreign_tc == ::ot_stream) {
601   - reserve_objects(target, foreign.getDict(), false);
  605 + reserve_objects(foreign.getDict(), false);
602 606 }
603 607  
604 608 visiting.erase(foreign);
605 609 }
606 610  
607 611 QPDFObjectHandle
608   -QPDF::ObjCopier::replace_indirect_object(QPDF& target, QPDFObjectHandle const& foreign, bool top)
  612 +QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const& foreign, bool top)
609 613 {
610 614 auto foreign_tc = foreign.getTypeCode();
611 615  
... ... @@ -624,7 +628,7 @@ QPDF::ObjCopier::replace_indirect_object(QPDF&amp; target, QPDFObjectHandle const&amp; f
624 628 std::vector<QPDFObjectHandle> result;
625 629 result.reserve(array.size());
626 630 for (auto const& item: array) {
627   - result.emplace_back(replace_indirect_object(target, item, false));
  631 + result.emplace_back(replace_indirect_object(item, false));
628 632 }
629 633 return Array(std::move(result));
630 634 }
... ... @@ -633,7 +637,7 @@ QPDF::ObjCopier::replace_indirect_object(QPDF&amp; target, QPDFObjectHandle const&amp; f
633 637 auto result = Dictionary::empty();
634 638 for (auto const& [key, value]: Dictionary(foreign)) {
635 639 if (!value.null()) {
636   - result.replaceKey(key, replace_indirect_object(target, value, false));
  640 + result.replaceKey(key, replace_indirect_object(value, false));
637 641 }
638 642 }
639 643 return result;
... ... @@ -645,10 +649,10 @@ QPDF::ObjCopier::replace_indirect_object(QPDF&amp; target, QPDFObjectHandle const&amp; f
645 649 auto dict = result.getDict();
646 650 for (auto const& [key, value]: stream.getDict()) {
647 651 if (!value.null()) {
648   - dict.replaceKey(key, replace_indirect_object(target, value, false));
  652 + dict.replaceKey(key, replace_indirect_object(value, false));
649 653 }
650 654 }
651   - target.copyStreamData(result, foreign);
  655 + qpdf.copyStreamData(result, foreign);
652 656 return result;
653 657 }
654 658  
... ...
libqpdf/qpdf/QPDF_private.hh
... ... @@ -47,14 +47,33 @@ class QPDF::ObjCache
47 47 class QPDF::ObjCopier
48 48 {
49 49 public:
50   - QPDFObjectHandle
51   - replace_indirect_object(QPDF& target, QPDFObjectHandle const& oh, bool top = true);
  50 + class Copier
  51 + {
  52 + public:
  53 + Copier(QPDF& qpdf) :
  54 + qpdf(qpdf)
  55 + {
  56 + }
52 57  
53   - void reserve_objects(QPDF& target, QPDFObjectHandle const& oh, bool top = true);
  58 + QPDFObjectHandle replace_indirect_object(QPDFObjectHandle const& foreign, bool top = true);
54 59  
55   - std::map<QPDFObjGen, QPDFObjectHandle> object_map;
56   - std::vector<QPDFObjectHandle> to_copy;
57   - QPDFObjGen::set visiting;
  60 + void reserve_objects(QPDFObjectHandle const& foreign, bool top = true);
  61 +
  62 + QPDF& qpdf;
  63 + std::map<QPDFObjGen, QPDFObjectHandle> object_map;
  64 + std::vector<QPDFObjectHandle> to_copy;
  65 + QPDFObjGen::set visiting;
  66 + };
  67 +
  68 + ObjCopier(QPDF& qpdf) :
  69 + qpdf(qpdf)
  70 + {
  71 + }
  72 +
  73 + Copier& copier(QPDFObjectHandle const& foreign);
  74 +
  75 + QPDF& qpdf;
  76 + std::map<unsigned long long, Copier> copiers;
58 77 };
59 78  
60 79 class QPDF::EncryptionParameters
... ... @@ -852,7 +871,7 @@ class QPDF::Members
852 871 bool ever_pushed_inherited_attributes_to_pages{false};
853 872 bool ever_called_get_all_pages{false};
854 873 std::vector<QPDFExc> warnings;
855   - std::map<unsigned long long, ObjCopier> object_copiers;
  874 + QPDF::ObjCopier obj_copier;
856 875 std::shared_ptr<CopiedStreamDataProvider> copied_stream_data_provider;
857 876 bool reconstructed_xref{false};
858 877 bool in_read_xref_stream{false};
... ...
qpdf/qpdf.testcov
... ... @@ -124,8 +124,6 @@ QPDFObjectHandle newStream with string 0
124 124 QPDF_Stream provider length not provided 0
125 125 QPDF_Stream unknown stream length 0
126 126 QPDF replaceReserved 0
127   -QPDF copyForeign direct 0
128   -QPDF copyForeign not foreign 0
129 127 QPDFWriter copy use_aes 1
130 128 QPDFParser indirect without context 0
131 129 QPDFObjectHandle trailing data in parse 0
... ...