Commit 91b8b0d80761877bff2a23fc40acd3153ff69bb4
1 parent
242ddf15
Refactor `ObjCopier` and `copyForeignObject`: introduce `Copier` class, streamli…
…ne object copying logic, and simplify method structure.
Showing
3 changed files
with
56 additions
and
35 deletions
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& target, QPDFObjectHandle const& foreign, | @@ -577,8 +582,7 @@ QPDF::ObjCopier::reserve_objects(QPDF& target, QPDFObjectHandle const& 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& target, QPDFObjectHandle const& foreign, | @@ -589,23 +593,23 @@ QPDF::ObjCopier::reserve_objects(QPDF& target, QPDFObjectHandle const& 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& target, QPDFObjectHandle const& f | @@ -624,7 +628,7 @@ QPDF::ObjCopier::replace_indirect_object(QPDF& target, QPDFObjectHandle const& 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& target, QPDFObjectHandle const& f | @@ -633,7 +637,7 @@ QPDF::ObjCopier::replace_indirect_object(QPDF& target, QPDFObjectHandle const& 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& target, QPDFObjectHandle const& f | @@ -645,10 +649,10 @@ QPDF::ObjCopier::replace_indirect_object(QPDF& target, QPDFObjectHandle const& 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 |