Commit b20ff9680529692d5058b0f1fcd87c2b984b4195
1 parent
91b8b0d8
Refactor `ObjCopier` and `copyForeignObject`: enhance encapsulation, streamline …
…`Copier` usage, and simplify object copying logic.
Showing
2 changed files
with
47 additions
and
30 deletions
libqpdf/QPDF.cc
| @@ -471,6 +471,12 @@ QPDF::getObjectByID(int objid, int generation) | @@ -471,6 +471,12 @@ QPDF::getObjectByID(int objid, int generation) | ||
| 471 | return getObject(QPDFObjGen(objid, generation)); | 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 | QPDF::ObjCopier::Copier& | 480 | QPDF::ObjCopier::Copier& |
| 475 | QPDF::ObjCopier::copier(QPDFObjectHandle const& foreign) | 481 | QPDF::ObjCopier::copier(QPDFObjectHandle const& foreign) |
| 476 | { | 482 | { |
| @@ -485,7 +491,7 @@ QPDF::ObjCopier::copier(QPDFObjectHandle const& foreign) | @@ -485,7 +491,7 @@ QPDF::ObjCopier::copier(QPDFObjectHandle const& foreign) | ||
| 485 | } | 491 | } |
| 486 | 492 | ||
| 487 | QPDFObjectHandle | 493 | QPDFObjectHandle |
| 488 | -QPDF::copyForeignObject(QPDFObjectHandle foreign) | 494 | +QPDF::ObjCopier::Copier::copied(QPDFObjectHandle const& foreign) |
| 489 | { | 495 | { |
| 490 | // Here's an explanation of what's going on here. | 496 | // Here's an explanation of what's going on here. |
| 491 | // | 497 | // |
| @@ -522,42 +528,36 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) | @@ -522,42 +528,36 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) | ||
| 522 | // Note that we explicitly allow use of copyForeignObject on page objects. It is a documented | 528 | // Note that we explicitly allow use of copyForeignObject on page objects. It is a documented |
| 523 | // use case to copy pages this way if the intention is to not update the pages tree. | 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 | // Make sure we have an object in this file for every referenced object in the old file. | 534 | // Make sure we have an object in this file for every referenced object in the old file. |
| 532 | // obj_copier.object_map maps foreign QPDFObjGen to local objects. For everything new that we | 535 | // obj_copier.object_map maps foreign QPDFObjGen to local objects. For everything new that we |
| 533 | // have to copy, the local object will be a reservation, unless it is a stream, in which case | 536 | // have to copy, the local object will be a reservation, unless it is a stream, in which case |
| 534 | // the local object will already be a stream. | 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 | // Copy any new objects and replace the reservations. | 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 | auto og = foreign.getObjGen(); | 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 | foreign.qpdf()->getFilename() + " object " + og.unparse(' '), | 554 | foreign.qpdf()->getFilename() + " object " + og.unparse(' '), |
| 555 | foreign.offset(), | 555 | foreign.offset(), |
| 556 | "unexpected reference to /Pages object while copying foreign object; replacing with " | 556 | "unexpected reference to /Pages object while copying foreign object; replacing with " |
| 557 | "null")); | 557 | "null")); |
| 558 | return QPDFObjectHandle::newNull(); | 558 | return QPDFObjectHandle::newNull(); |
| 559 | } | 559 | } |
| 560 | - return obj_copier.object_map[foreign.getObjGen()]; | 560 | + return object_map[foreign]; |
| 561 | } | 561 | } |
| 562 | 562 | ||
| 563 | void | 563 | void |
| @@ -592,17 +592,17 @@ QPDF::ObjCopier::Copier::reserve_objects(QPDFObjectHandle const& foreign, bool t | @@ -592,17 +592,17 @@ QPDF::ObjCopier::Copier::reserve_objects(QPDFObjectHandle const& foreign, bool t | ||
| 592 | } | 592 | } |
| 593 | 593 | ||
| 594 | if (foreign_tc == ::ot_array) { | 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 | } else if (foreign_tc == ::ot_dictionary) { | 598 | } else if (foreign_tc == ::ot_dictionary) { |
| 599 | for (auto const& item: Dictionary(foreign)) { | 599 | for (auto const& item: Dictionary(foreign)) { |
| 600 | if (!item.second.null()) { | 600 | if (!item.second.null()) { |
| 601 | - reserve_objects(item.second, false); | 601 | + reserve_objects(item.second); |
| 602 | } | 602 | } |
| 603 | } | 603 | } |
| 604 | } else if (foreign_tc == ::ot_stream) { | 604 | } else if (foreign_tc == ::ot_stream) { |
| 605 | - reserve_objects(foreign.getDict(), false); | 605 | + reserve_objects(foreign.getDict()); |
| 606 | } | 606 | } |
| 607 | 607 | ||
| 608 | visiting.erase(foreign); | 608 | visiting.erase(foreign); |
| @@ -611,7 +611,7 @@ QPDF::ObjCopier::Copier::reserve_objects(QPDFObjectHandle const& foreign, bool t | @@ -611,7 +611,7 @@ QPDF::ObjCopier::Copier::reserve_objects(QPDFObjectHandle const& foreign, bool t | ||
| 611 | QPDFObjectHandle | 611 | QPDFObjectHandle |
| 612 | QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const& foreign, bool top) | 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 | if (!top && foreign.indirect()) { | 616 | if (!top && foreign.indirect()) { |
| 617 | auto mapping = object_map.find(foreign.id_gen()); | 617 | auto mapping = object_map.find(foreign.id_gen()); |
| @@ -628,7 +628,7 @@ QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const& foreign | @@ -628,7 +628,7 @@ QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const& foreign | ||
| 628 | std::vector<QPDFObjectHandle> result; | 628 | std::vector<QPDFObjectHandle> result; |
| 629 | result.reserve(array.size()); | 629 | result.reserve(array.size()); |
| 630 | for (auto const& item: array) { | 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 | return Array(std::move(result)); | 633 | return Array(std::move(result)); |
| 634 | } | 634 | } |
| @@ -637,7 +637,7 @@ QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const& foreign | @@ -637,7 +637,7 @@ QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const& foreign | ||
| 637 | auto result = Dictionary::empty(); | 637 | auto result = Dictionary::empty(); |
| 638 | for (auto const& [key, value]: Dictionary(foreign)) { | 638 | for (auto const& [key, value]: Dictionary(foreign)) { |
| 639 | if (!value.null()) { | 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 | return result; | 643 | return result; |
| @@ -649,7 +649,7 @@ QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const& foreign | @@ -649,7 +649,7 @@ QPDF::ObjCopier::Copier::replace_indirect_object(QPDFObjectHandle const& foreign | ||
| 649 | auto dict = result.getDict(); | 649 | auto dict = result.getDict(); |
| 650 | for (auto const& [key, value]: stream.getDict()) { | 650 | for (auto const& [key, value]: stream.getDict()) { |
| 651 | if (!value.null()) { | 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 | qpdf.copyStreamData(result, foreign); | 655 | qpdf.copyStreamData(result, foreign); |
libqpdf/qpdf/QPDF_private.hh
| @@ -46,7 +46,6 @@ class QPDF::ObjCache | @@ -46,7 +46,6 @@ class QPDF::ObjCache | ||
| 46 | 46 | ||
| 47 | class QPDF::ObjCopier | 47 | class QPDF::ObjCopier |
| 48 | { | 48 | { |
| 49 | - public: | ||
| 50 | class Copier | 49 | class Copier |
| 51 | { | 50 | { |
| 52 | public: | 51 | public: |
| @@ -55,9 +54,11 @@ class QPDF::ObjCopier | @@ -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 | QPDF& qpdf; | 63 | QPDF& qpdf; |
| 63 | std::map<QPDFObjGen, QPDFObjectHandle> object_map; | 64 | std::map<QPDFObjGen, QPDFObjectHandle> object_map; |
| @@ -65,11 +66,27 @@ class QPDF::ObjCopier | @@ -65,11 +66,27 @@ class QPDF::ObjCopier | ||
| 65 | QPDFObjGen::set visiting; | 66 | QPDFObjGen::set visiting; |
| 66 | }; | 67 | }; |
| 67 | 68 | ||
| 69 | + public: | ||
| 68 | ObjCopier(QPDF& qpdf) : | 70 | ObjCopier(QPDF& qpdf) : |
| 69 | qpdf(qpdf) | 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 | Copier& copier(QPDFObjectHandle const& foreign); | 90 | Copier& copier(QPDFObjectHandle const& foreign); |
| 74 | 91 | ||
| 75 | QPDF& qpdf; | 92 | QPDF& qpdf; |