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,8 +166,9 @@ QPDF::Members::Members(QPDF& qpdf) :
166 objects(doc.objects()), 166 objects(doc.objects()),
167 pages(doc.pages()), 167 pages(doc.pages()),
168 log(QPDFLogger::defaultLogger()), 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,6 +471,19 @@ QPDF::getObjectByID(int objid, int generation)
470 return getObject(QPDFObjGen(objid, generation)); 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 QPDFObjectHandle 487 QPDFObjectHandle
474 QPDF::copyForeignObject(QPDFObjectHandle foreign) 488 QPDF::copyForeignObject(QPDFObjectHandle foreign)
475 { 489 {
@@ -507,17 +521,8 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) @@ -507,17 +521,8 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
507 521
508 // Note that we explicitly allow use of copyForeignObject on page objects. It is a documented 522 // Note that we explicitly allow use of copyForeignObject on page objects. It is a documented
509 // use case to copy pages this way if the intention is to not update the pages tree. 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 if (!obj_copier.visiting.empty()) { 526 if (!obj_copier.visiting.empty()) {
522 throw std::logic_error( 527 throw std::logic_error(
523 "obj_copier.visiting is not empty at the beginning of copyForeignObject"); 528 "obj_copier.visiting is not empty at the beginning of copyForeignObject");
@@ -527,7 +532,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) @@ -527,7 +532,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
527 // obj_copier.object_map maps foreign QPDFObjGen to local objects. For everything new that we 532 // obj_copier.object_map maps foreign QPDFObjGen to local objects. For everything new that we
528 // have to copy, the local object will be a reservation, unless it is a stream, in which case 533 // have to copy, the local object will be a reservation, unless it is a stream, in which case
529 // the local object will already be a stream. 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 if (!obj_copier.visiting.empty()) { 537 if (!obj_copier.visiting.empty()) {
533 throw std::logic_error("obj_copier.visiting is not empty after reserving objects"); 538 throw std::logic_error("obj_copier.visiting is not empty after reserving objects");
@@ -535,7 +540,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) @@ -535,7 +540,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
535 540
536 // Copy any new objects and replace the reservations. 541 // Copy any new objects and replace the reservations.
537 for (auto& to_copy: obj_copier.to_copy) { 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 if (!to_copy.isStream()) { 544 if (!to_copy.isStream()) {
540 QPDFObjGen og(to_copy.getObjGen()); 545 QPDFObjGen og(to_copy.getObjGen());
541 replaceReserved(obj_copier.object_map[og], copy); 546 replaceReserved(obj_copier.object_map[og], copy);
@@ -546,7 +551,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) @@ -546,7 +551,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
546 auto og = foreign.getObjGen(); 551 auto og = foreign.getObjGen();
547 if (!obj_copier.object_map.contains(og)) { 552 if (!obj_copier.object_map.contains(og)) {
548 warn(damagedPDF( 553 warn(damagedPDF(
549 - other.getFilename() + " object " + og.unparse(' '), 554 + foreign.qpdf()->getFilename() + " object " + og.unparse(' '),
550 foreign.offset(), 555 foreign.offset(),
551 "unexpected reference to /Pages object while copying foreign object; replacing with " 556 "unexpected reference to /Pages object while copying foreign object; replacing with "
552 "null")); 557 "null"));
@@ -556,7 +561,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) @@ -556,7 +561,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
556 } 561 }
557 562
558 void 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 auto foreign_tc = foreign.type_code(); 566 auto foreign_tc = foreign.type_code();
562 util::assertion( 567 util::assertion(
@@ -577,8 +582,7 @@ QPDF::ObjCopier::reserve_objects(QPDF&amp; target, QPDFObjectHandle const&amp; foreign, @@ -577,8 +582,7 @@ QPDF::ObjCopier::reserve_objects(QPDF&amp; target, QPDFObjectHandle const&amp; foreign,
577 return; 582 return;
578 } 583 }
579 } else { 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 if (!top && foreign.isPageObject()) { 586 if (!top && foreign.isPageObject()) {
583 visiting.erase(foreign_og); 587 visiting.erase(foreign_og);
584 return; 588 return;
@@ -589,23 +593,23 @@ QPDF::ObjCopier::reserve_objects(QPDF&amp; target, QPDFObjectHandle const&amp; foreign, @@ -589,23 +593,23 @@ QPDF::ObjCopier::reserve_objects(QPDF&amp; target, QPDFObjectHandle const&amp; foreign,
589 593
590 if (foreign_tc == ::ot_array) { 594 if (foreign_tc == ::ot_array) {
591 for (auto const& item: foreign.as_array()) { 595 for (auto const& item: foreign.as_array()) {
592 - reserve_objects(target, item, false); 596 + reserve_objects(item, false);
593 } 597 }
594 } else if (foreign_tc == ::ot_dictionary) { 598 } else if (foreign_tc == ::ot_dictionary) {
595 for (auto const& item: Dictionary(foreign)) { 599 for (auto const& item: Dictionary(foreign)) {
596 if (!item.second.null()) { 600 if (!item.second.null()) {
597 - reserve_objects(target, item.second, false); 601 + reserve_objects(item.second, false);
598 } 602 }
599 } 603 }
600 } else if (foreign_tc == ::ot_stream) { 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 visiting.erase(foreign); 608 visiting.erase(foreign);
605 } 609 }
606 610
607 QPDFObjectHandle 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 auto foreign_tc = foreign.getTypeCode(); 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,7 +628,7 @@ QPDF::ObjCopier::replace_indirect_object(QPDF&amp; target, QPDFObjectHandle const&amp; f
624 std::vector<QPDFObjectHandle> result; 628 std::vector<QPDFObjectHandle> result;
625 result.reserve(array.size()); 629 result.reserve(array.size());
626 for (auto const& item: array) { 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 return Array(std::move(result)); 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,7 +637,7 @@ QPDF::ObjCopier::replace_indirect_object(QPDF&amp; target, QPDFObjectHandle const&amp; f
633 auto result = Dictionary::empty(); 637 auto result = Dictionary::empty();
634 for (auto const& [key, value]: Dictionary(foreign)) { 638 for (auto const& [key, value]: Dictionary(foreign)) {
635 if (!value.null()) { 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 return result; 643 return result;
@@ -645,10 +649,10 @@ QPDF::ObjCopier::replace_indirect_object(QPDF&amp; target, QPDFObjectHandle const&amp; f @@ -645,10 +649,10 @@ QPDF::ObjCopier::replace_indirect_object(QPDF&amp; target, QPDFObjectHandle const&amp; f
645 auto dict = result.getDict(); 649 auto dict = result.getDict();
646 for (auto const& [key, value]: stream.getDict()) { 650 for (auto const& [key, value]: stream.getDict()) {
647 if (!value.null()) { 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 return result; 656 return result;
653 } 657 }
654 658
libqpdf/qpdf/QPDF_private.hh
@@ -47,14 +47,33 @@ class QPDF::ObjCache @@ -47,14 +47,33 @@ class QPDF::ObjCache
47 class QPDF::ObjCopier 47 class QPDF::ObjCopier
48 { 48 {
49 public: 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 class QPDF::EncryptionParameters 79 class QPDF::EncryptionParameters
@@ -852,7 +871,7 @@ class QPDF::Members @@ -852,7 +871,7 @@ class QPDF::Members
852 bool ever_pushed_inherited_attributes_to_pages{false}; 871 bool ever_pushed_inherited_attributes_to_pages{false};
853 bool ever_called_get_all_pages{false}; 872 bool ever_called_get_all_pages{false};
854 std::vector<QPDFExc> warnings; 873 std::vector<QPDFExc> warnings;
855 - std::map<unsigned long long, ObjCopier> object_copiers; 874 + QPDF::ObjCopier obj_copier;
856 std::shared_ptr<CopiedStreamDataProvider> copied_stream_data_provider; 875 std::shared_ptr<CopiedStreamDataProvider> copied_stream_data_provider;
857 bool reconstructed_xref{false}; 876 bool reconstructed_xref{false};
858 bool in_read_xref_stream{false}; 877 bool in_read_xref_stream{false};
qpdf/qpdf.testcov
@@ -124,8 +124,6 @@ QPDFObjectHandle newStream with string 0 @@ -124,8 +124,6 @@ QPDFObjectHandle newStream with string 0
124 QPDF_Stream provider length not provided 0 124 QPDF_Stream provider length not provided 0
125 QPDF_Stream unknown stream length 0 125 QPDF_Stream unknown stream length 0
126 QPDF replaceReserved 0 126 QPDF replaceReserved 0
127 -QPDF copyForeign direct 0  
128 -QPDF copyForeign not foreign 0  
129 QPDFWriter copy use_aes 1 127 QPDFWriter copy use_aes 1
130 QPDFParser indirect without context 0 128 QPDFParser indirect without context 0
131 QPDFObjectHandle trailing data in parse 0 129 QPDFObjectHandle trailing data in parse 0