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 | 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& target, QPDFObjectHandle const& 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& target, QPDFObjectHandle const& 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& target, QPDFObjectHandle const& 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& target, QPDFObjectHandle const& 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& target, QPDFObjectHandle const& 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 | ... | ... |