Commit 2dc837f40f781c271ae2f0461778678a10c11e86
Committed by
GitHub
Merge pull request #1554 from m-holger/foreign_stream
Refactor foreign object copying
Showing
12 changed files
with
306 additions
and
311 deletions
include/qpdf/ObjectHandle.hh
| ... | ... | @@ -94,6 +94,7 @@ namespace qpdf |
| 94 | 94 | inline QPDFObjGen id_gen() const; |
| 95 | 95 | inline bool indirect() const; |
| 96 | 96 | inline bool null() const; |
| 97 | + inline qpdf_offset_t offset() const; | |
| 97 | 98 | inline QPDF* qpdf() const; |
| 98 | 99 | inline qpdf_object_type_e raw_type_code() const; |
| 99 | 100 | inline qpdf_object_type_e resolved_type_code() const; | ... | ... |
include/qpdf/QPDF.hh
| ... | ... | @@ -742,7 +742,6 @@ class QPDF |
| 742 | 742 | static std::string const qpdf_version; |
| 743 | 743 | |
| 744 | 744 | class ObjCache; |
| 745 | - class ObjCopier; | |
| 746 | 745 | class EncryptionParameters; |
| 747 | 746 | class ForeignStreamData; |
| 748 | 747 | class CopiedStreamDataProvider; |
| ... | ... | @@ -775,8 +774,8 @@ class QPDF |
| 775 | 774 | Pipeline* pipeline, |
| 776 | 775 | bool suppress_warnings, |
| 777 | 776 | bool will_retry); |
| 778 | - bool pipeForeignStreamData( | |
| 779 | - std::shared_ptr<ForeignStreamData>, Pipeline*, bool suppress_warnings, bool will_retry); | |
| 777 | + bool | |
| 778 | + pipeForeignStreamData(ForeignStreamData&, Pipeline*, bool suppress_warnings, bool will_retry); | |
| 780 | 779 | static bool pipeStreamData( |
| 781 | 780 | std::shared_ptr<QPDF::EncryptionParameters> encp, |
| 782 | 781 | std::shared_ptr<InputSource> file, |
| ... | ... | @@ -790,27 +789,6 @@ class QPDF |
| 790 | 789 | bool suppress_warnings, |
| 791 | 790 | bool will_retry); |
| 792 | 791 | |
| 793 | - // For QPDFWriter: | |
| 794 | - | |
| 795 | - std::map<QPDFObjGen, QPDFXRefEntry> const& getXRefTableInternal(); | |
| 796 | - // Get a list of objects that would be permitted in an object stream. | |
| 797 | - template <typename T> | |
| 798 | - std::vector<T> getCompressibleObjGens(); | |
| 799 | - std::vector<QPDFObjGen> getCompressibleObjVector(); | |
| 800 | - std::vector<bool> getCompressibleObjSet(); | |
| 801 | - | |
| 802 | - // methods to support page handling | |
| 803 | - | |
| 804 | - void getAllPagesInternal( | |
| 805 | - QPDFObjectHandle cur_pages, | |
| 806 | - QPDFObjGen::set& visited, | |
| 807 | - QPDFObjGen::set& seen, | |
| 808 | - bool media_box, | |
| 809 | - bool resources); | |
| 810 | - void insertPage(QPDFObjectHandle newpage, int pos); | |
| 811 | - void flattenPagesTree(); | |
| 812 | - void insertPageobjToPage(QPDFObjectHandle const& obj, int pos, bool check_duplicate); | |
| 813 | - | |
| 814 | 792 | // methods to support encryption -- implemented in QPDF_encryption.cc |
| 815 | 793 | void initializeEncryption(); |
| 816 | 794 | static std::string |
| ... | ... | @@ -827,9 +805,6 @@ class QPDF |
| 827 | 805 | std::unique_ptr<Pipeline>& heap); |
| 828 | 806 | |
| 829 | 807 | // Methods to support object copying |
| 830 | - void reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top); | |
| 831 | - QPDFObjectHandle | |
| 832 | - replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top); | |
| 833 | 808 | void copyStreamData(QPDFObjectHandle dest_stream, QPDFObjectHandle src_stream); |
| 834 | 809 | |
| 835 | 810 | struct HPageOffsetEntry; | ... | ... |
libqpdf/QPDF.cc
| ... | ... | @@ -27,6 +27,8 @@ |
| 27 | 27 | using namespace qpdf; |
| 28 | 28 | using namespace std::literals; |
| 29 | 29 | |
| 30 | +using Objects = QPDF::Doc::Objects; | |
| 31 | + | |
| 30 | 32 | // This must be a fixed value. This API returns a const reference to it, and the C API relies on its |
| 31 | 33 | // being static as well. |
| 32 | 34 | std::string const QPDF::qpdf_version(QPDF_VERSION); |
| ... | ... | @@ -109,20 +111,14 @@ namespace |
| 109 | 111 | } // namespace |
| 110 | 112 | |
| 111 | 113 | QPDF::ForeignStreamData::ForeignStreamData( |
| 112 | - std::shared_ptr<EncryptionParameters> encp, | |
| 113 | - std::shared_ptr<InputSource> file, | |
| 114 | - QPDFObjGen foreign_og, | |
| 115 | - qpdf_offset_t offset, | |
| 116 | - size_t length, | |
| 117 | - QPDFObjectHandle local_dict, | |
| 118 | - bool is_root_metadata) : | |
| 119 | - encp(encp), | |
| 120 | - file(file), | |
| 121 | - foreign_og(foreign_og), | |
| 114 | + Stream& foreign, qpdf_offset_t offset, QPDFObjectHandle local_dict) : | |
| 115 | + encp(foreign.qpdf()->m->encp), | |
| 116 | + file(foreign.qpdf()->m->file), | |
| 117 | + foreign_og(foreign.id_gen()), | |
| 122 | 118 | offset(offset), |
| 123 | - length(length), | |
| 119 | + length(foreign.getLength()), | |
| 124 | 120 | local_dict(local_dict), |
| 125 | - is_root_metadata(is_root_metadata) | |
| 121 | + is_root_metadata(foreign.isRootMetadata()) | |
| 126 | 122 | { |
| 127 | 123 | } |
| 128 | 124 | |
| ... | ... | @@ -136,11 +132,11 @@ bool |
| 136 | 132 | QPDF::CopiedStreamDataProvider::provideStreamData( |
| 137 | 133 | QPDFObjGen const& og, Pipeline* pipeline, bool suppress_warnings, bool will_retry) |
| 138 | 134 | { |
| 139 | - std::shared_ptr<ForeignStreamData> foreign_data = foreign_stream_data[og]; | |
| 135 | + auto foreign_data = foreign_stream_data.find(og); | |
| 140 | 136 | bool result = false; |
| 141 | - if (foreign_data.get()) { | |
| 137 | + if (foreign_data != foreign_stream_data.end()) { | |
| 142 | 138 | result = destination_qpdf.pipeForeignStreamData( |
| 143 | - foreign_data, pipeline, suppress_warnings, will_retry); | |
| 139 | + foreign_data->second, pipeline, suppress_warnings, will_retry); | |
| 144 | 140 | QTC::TC("qpdf", "QPDF copy foreign with data", result ? 0 : 1); |
| 145 | 141 | } else { |
| 146 | 142 | auto foreign_stream = foreign_streams[og]; |
| ... | ... | @@ -151,20 +147,6 @@ QPDF::CopiedStreamDataProvider::provideStreamData( |
| 151 | 147 | return result; |
| 152 | 148 | } |
| 153 | 149 | |
| 154 | -void | |
| 155 | -QPDF::CopiedStreamDataProvider::registerForeignStream( | |
| 156 | - QPDFObjGen const& local_og, QPDFObjectHandle foreign_stream) | |
| 157 | -{ | |
| 158 | - this->foreign_streams[local_og] = foreign_stream; | |
| 159 | -} | |
| 160 | - | |
| 161 | -void | |
| 162 | -QPDF::CopiedStreamDataProvider::registerForeignStream( | |
| 163 | - QPDFObjGen const& local_og, std::shared_ptr<ForeignStreamData> foreign_stream) | |
| 164 | -{ | |
| 165 | - this->foreign_stream_data[local_og] = foreign_stream; | |
| 166 | -} | |
| 167 | - | |
| 168 | 150 | QPDF::StringDecrypter::StringDecrypter(QPDF* qpdf, QPDFObjGen og) : |
| 169 | 151 | qpdf(qpdf), |
| 170 | 152 | og(og) |
| ... | ... | @@ -184,8 +166,8 @@ QPDF::Members::Members(QPDF& qpdf) : |
| 184 | 166 | objects(doc.objects()), |
| 185 | 167 | pages(doc.pages()), |
| 186 | 168 | log(QPDFLogger::defaultLogger()), |
| 187 | - file(new InvalidInputSource()), | |
| 188 | - encp(new EncryptionParameters) | |
| 169 | + file(std::make_shared<InvalidInputSource>()), | |
| 170 | + encp(std::make_shared<EncryptionParameters>()) | |
| 189 | 171 | { |
| 190 | 172 | } |
| 191 | 173 | |
| ... | ... | @@ -491,6 +473,25 @@ QPDF::getObjectByID(int objid, int generation) |
| 491 | 473 | QPDFObjectHandle |
| 492 | 474 | QPDF::copyForeignObject(QPDFObjectHandle foreign) |
| 493 | 475 | { |
| 476 | + return m->objects.foreign().copied(foreign); | |
| 477 | +} | |
| 478 | + | |
| 479 | +Objects ::Foreign::Copier& | |
| 480 | +Objects::Foreign::copier(QPDFObjectHandle const& foreign) | |
| 481 | +{ | |
| 482 | + if (!foreign.isIndirect()) { | |
| 483 | + throw std::logic_error("QPDF::copyForeign called with direct object handle"); | |
| 484 | + } | |
| 485 | + QPDF& other = *foreign.qpdf(); | |
| 486 | + if (&other == &qpdf) { | |
| 487 | + throw std::logic_error("QPDF::copyForeign called with object from this QPDF"); | |
| 488 | + } | |
| 489 | + return copiers.insert({other.getUniqueId(), {qpdf}}).first->second; | |
| 490 | +} | |
| 491 | + | |
| 492 | +QPDFObjectHandle | |
| 493 | +Objects::Foreign::Copier::copied(QPDFObjectHandle const& foreign) | |
| 494 | +{ | |
| 494 | 495 | // Here's an explanation of what's going on here. |
| 495 | 496 | // |
| 496 | 497 | // A QPDFObjectHandle that is an indirect object has an owning QPDF. The object ID and |
| ... | ... | @@ -499,7 +500,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) |
| 499 | 500 | // references to the corresponding object in the local file. |
| 500 | 501 | // |
| 501 | 502 | // To do this, we maintain mappings from foreign object IDs to local object IDs for each foreign |
| 502 | - // QPDF that we are copying from. The mapping is stored in an ObjCopier, which contains a | |
| 503 | + // QPDF that we are copying from. The mapping is stored in an Foreign::Copier, which contains a | |
| 503 | 504 | // mapping from the foreign ObjGen to the local QPDFObjectHandle. |
| 504 | 505 | // |
| 505 | 506 | // To copy, we do a deep traversal of the foreign object with loop detection to discover all |
| ... | ... | @@ -525,218 +526,185 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) |
| 525 | 526 | |
| 526 | 527 | // Note that we explicitly allow use of copyForeignObject on page objects. It is a documented |
| 527 | 528 | // use case to copy pages this way if the intention is to not update the pages tree. |
| 528 | - if (!foreign.isIndirect()) { | |
| 529 | - QTC::TC("qpdf", "QPDF copyForeign direct"); | |
| 530 | - throw std::logic_error("QPDF::copyForeign called with direct object handle"); | |
| 531 | - } | |
| 532 | - QPDF& other = foreign.getQPDF(); | |
| 533 | - if (&other == this) { | |
| 534 | - QTC::TC("qpdf", "QPDF copyForeign not foreign"); | |
| 535 | - throw std::logic_error("QPDF::copyForeign called with object from this QPDF"); | |
| 536 | - } | |
| 537 | 529 | |
| 538 | - ObjCopier& obj_copier = m->object_copiers[other.m->unique_id]; | |
| 539 | - if (!obj_copier.visiting.empty()) { | |
| 540 | - throw std::logic_error( | |
| 541 | - "obj_copier.visiting is not empty at the beginning of copyForeignObject"); | |
| 542 | - } | |
| 530 | + util::assertion( | |
| 531 | + visiting.empty(), "obj_copier.visiting is not empty at the beginning of copyForeignObject"); | |
| 543 | 532 | |
| 544 | 533 | // Make sure we have an object in this file for every referenced object in the old file. |
| 545 | 534 | // obj_copier.object_map maps foreign QPDFObjGen to local objects. For everything new that we |
| 546 | 535 | // have to copy, the local object will be a reservation, unless it is a stream, in which case |
| 547 | 536 | // the local object will already be a stream. |
| 548 | - reserveObjects(foreign, obj_copier, true); | |
| 537 | + reserve_objects(foreign, true); | |
| 549 | 538 | |
| 550 | - if (!obj_copier.visiting.empty()) { | |
| 551 | - throw std::logic_error("obj_copier.visiting is not empty after reserving objects"); | |
| 552 | - } | |
| 539 | + util::assertion(visiting.empty(), "obj_copier.visiting is not empty after reserving objects"); | |
| 553 | 540 | |
| 554 | 541 | // Copy any new objects and replace the reservations. |
| 555 | - for (auto& to_copy: obj_copier.to_copy) { | |
| 556 | - QPDFObjectHandle copy = replaceForeignIndirectObjects(to_copy, obj_copier, true); | |
| 557 | - if (!to_copy.isStream()) { | |
| 558 | - QPDFObjGen og(to_copy.getObjGen()); | |
| 559 | - replaceReserved(obj_copier.object_map[og], copy); | |
| 542 | + for (auto& oh: to_copy) { | |
| 543 | + auto copy = replace_indirect_object(oh, true); | |
| 544 | + if (!oh.isStream()) { | |
| 545 | + qpdf.replaceReserved(object_map[oh], copy); | |
| 560 | 546 | } |
| 561 | 547 | } |
| 562 | - obj_copier.to_copy.clear(); | |
| 548 | + to_copy.clear(); | |
| 563 | 549 | |
| 564 | 550 | auto og = foreign.getObjGen(); |
| 565 | - if (!obj_copier.object_map.contains(og)) { | |
| 566 | - warn(damagedPDF( | |
| 567 | - other.getFilename() + " object " + og.unparse(' '), | |
| 568 | - foreign.getParsedOffset(), | |
| 551 | + if (!object_map.contains(og)) { | |
| 552 | + qpdf.warn(qpdf.damagedPDF( | |
| 553 | + foreign.qpdf()->getFilename() + " object " + og.unparse(' '), | |
| 554 | + foreign.offset(), | |
| 569 | 555 | "unexpected reference to /Pages object while copying foreign object; replacing with " |
| 570 | 556 | "null")); |
| 571 | 557 | return QPDFObjectHandle::newNull(); |
| 572 | 558 | } |
| 573 | - return obj_copier.object_map[foreign.getObjGen()]; | |
| 559 | + return object_map[foreign]; | |
| 574 | 560 | } |
| 575 | 561 | |
| 576 | 562 | void |
| 577 | -QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top) | |
| 563 | +Objects::Foreign::Copier::reserve_objects(QPDFObjectHandle const& foreign, bool top) | |
| 578 | 564 | { |
| 579 | - auto foreign_tc = foreign.getTypeCode(); | |
| 580 | - if (foreign_tc == ::ot_reserved) { | |
| 581 | - throw std::logic_error("QPDF: attempting to copy a foreign reserved object"); | |
| 582 | - } | |
| 565 | + auto foreign_tc = foreign.type_code(); | |
| 566 | + util::assertion( | |
| 567 | + foreign_tc != ::ot_reserved, "QPDF: attempting to copy a foreign reserved object"); | |
| 583 | 568 | |
| 584 | 569 | if (foreign.isPagesObject()) { |
| 585 | 570 | return; |
| 586 | 571 | } |
| 587 | 572 | |
| 588 | - if (foreign.isIndirect()) { | |
| 573 | + if (foreign.indirect()) { | |
| 589 | 574 | QPDFObjGen foreign_og(foreign.getObjGen()); |
| 590 | - if (!obj_copier.visiting.add(foreign_og)) { | |
| 575 | + if (!visiting.add(foreign_og)) { | |
| 591 | 576 | return; |
| 592 | 577 | } |
| 593 | - if (obj_copier.object_map.contains(foreign_og)) { | |
| 594 | - if (!(top && foreign.isPageObject() && obj_copier.object_map[foreign_og].null())) { | |
| 595 | - obj_copier.visiting.erase(foreign); | |
| 578 | + if (object_map.contains(foreign_og)) { | |
| 579 | + if (!(top && foreign.isPageObject() && object_map[foreign_og].null())) { | |
| 580 | + visiting.erase(foreign); | |
| 596 | 581 | return; |
| 597 | 582 | } |
| 598 | 583 | } else { |
| 599 | - obj_copier.object_map[foreign_og] = | |
| 600 | - foreign.isStream() ? newStream() : newIndirectNull(); | |
| 584 | + object_map[foreign_og] = foreign.isStream() ? qpdf.newStream() : qpdf.newIndirectNull(); | |
| 601 | 585 | if (!top && foreign.isPageObject()) { |
| 602 | - obj_copier.visiting.erase(foreign_og); | |
| 586 | + visiting.erase(foreign_og); | |
| 603 | 587 | return; |
| 604 | 588 | } |
| 605 | 589 | } |
| 606 | - obj_copier.to_copy.emplace_back(foreign); | |
| 590 | + to_copy.emplace_back(foreign); | |
| 607 | 591 | } |
| 608 | 592 | |
| 609 | 593 | if (foreign_tc == ::ot_array) { |
| 610 | - for (auto const& item: foreign.as_array()) { | |
| 611 | - reserveObjects(item, obj_copier, false); | |
| 594 | + for (auto const& item: Array(foreign)) { | |
| 595 | + reserve_objects(item); | |
| 612 | 596 | } |
| 613 | 597 | } else if (foreign_tc == ::ot_dictionary) { |
| 614 | - for (auto const& item: foreign.as_dictionary()) { | |
| 598 | + for (auto const& item: Dictionary(foreign)) { | |
| 615 | 599 | if (!item.second.null()) { |
| 616 | - reserveObjects(item.second, obj_copier, false); | |
| 600 | + reserve_objects(item.second); | |
| 617 | 601 | } |
| 618 | 602 | } |
| 619 | 603 | } else if (foreign_tc == ::ot_stream) { |
| 620 | - reserveObjects(foreign.getDict(), obj_copier, false); | |
| 604 | + reserve_objects(foreign.getDict()); | |
| 621 | 605 | } |
| 622 | 606 | |
| 623 | - obj_copier.visiting.erase(foreign); | |
| 607 | + visiting.erase(foreign); | |
| 624 | 608 | } |
| 625 | 609 | |
| 626 | 610 | QPDFObjectHandle |
| 627 | -QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top) | |
| 628 | -{ | |
| 629 | - auto foreign_tc = foreign.getTypeCode(); | |
| 630 | - QPDFObjectHandle result; | |
| 631 | - if ((!top) && foreign.isIndirect()) { | |
| 632 | - QTC::TC("qpdf", "QPDF replace indirect"); | |
| 633 | - auto mapping = obj_copier.object_map.find(foreign.getObjGen()); | |
| 634 | - if (mapping == obj_copier.object_map.end()) { | |
| 611 | +Objects::Foreign::Copier::replace_indirect_object(QPDFObjectHandle const& foreign, bool top) | |
| 612 | +{ | |
| 613 | + auto foreign_tc = foreign.type_code(); | |
| 614 | + | |
| 615 | + if (!top && foreign.indirect()) { | |
| 616 | + auto mapping = object_map.find(foreign.id_gen()); | |
| 617 | + if (mapping == object_map.end()) { | |
| 635 | 618 | // This case would occur if this is a reference to a Pages object that we didn't |
| 636 | 619 | // traverse into. |
| 637 | - QTC::TC("qpdf", "QPDF replace foreign indirect with null"); | |
| 638 | - result = QPDFObjectHandle::newNull(); | |
| 639 | - } else { | |
| 640 | - result = mapping->second; | |
| 620 | + return QPDFObjectHandle::newNull(); | |
| 641 | 621 | } |
| 642 | - } else if (foreign_tc == ::ot_array) { | |
| 643 | - QTC::TC("qpdf", "QPDF replace array"); | |
| 644 | - result = QPDFObjectHandle::newArray(); | |
| 645 | - for (auto const& item: foreign.as_array()) { | |
| 646 | - result.appendItem(replaceForeignIndirectObjects(item, obj_copier, false)); | |
| 622 | + return mapping->second; | |
| 623 | + } | |
| 624 | + | |
| 625 | + if (foreign_tc == ::ot_array) { | |
| 626 | + Array array = foreign; | |
| 627 | + std::vector<QPDFObjectHandle> result; | |
| 628 | + result.reserve(array.size()); | |
| 629 | + for (auto const& item: array) { | |
| 630 | + result.emplace_back(replace_indirect_object(item)); | |
| 647 | 631 | } |
| 648 | - } else if (foreign_tc == ::ot_dictionary) { | |
| 649 | - QTC::TC("qpdf", "QPDF replace dictionary"); | |
| 650 | - result = QPDFObjectHandle::newDictionary(); | |
| 651 | - for (auto const& [key, value]: foreign.as_dictionary()) { | |
| 632 | + return Array(std::move(result)); | |
| 633 | + } | |
| 634 | + | |
| 635 | + if (foreign_tc == ::ot_dictionary) { | |
| 636 | + auto result = Dictionary::empty(); | |
| 637 | + for (auto const& [key, value]: Dictionary(foreign)) { | |
| 652 | 638 | if (!value.null()) { |
| 653 | - result.replaceKey(key, replaceForeignIndirectObjects(value, obj_copier, false)); | |
| 639 | + result.replaceKey(key, replace_indirect_object(value)); | |
| 654 | 640 | } |
| 655 | 641 | } |
| 656 | - } else if (foreign_tc == ::ot_stream) { | |
| 657 | - QTC::TC("qpdf", "QPDF replace stream"); | |
| 658 | - result = obj_copier.object_map[foreign.getObjGen()]; | |
| 659 | - QPDFObjectHandle dict = result.getDict(); | |
| 660 | - QPDFObjectHandle old_dict = foreign.getDict(); | |
| 661 | - for (auto const& [key, value]: old_dict.as_dictionary()) { | |
| 642 | + return result; | |
| 643 | + } | |
| 644 | + | |
| 645 | + if (foreign_tc == ::ot_stream) { | |
| 646 | + Stream stream = foreign; | |
| 647 | + Stream result = object_map[foreign]; | |
| 648 | + auto dict = result.getDict(); | |
| 649 | + for (auto const& [key, value]: stream.getDict()) { | |
| 662 | 650 | if (!value.null()) { |
| 663 | - dict.replaceKey(key, replaceForeignIndirectObjects(value, obj_copier, false)); | |
| 651 | + dict.replaceKey(key, replace_indirect_object(value)); | |
| 664 | 652 | } |
| 665 | 653 | } |
| 666 | - copyStreamData(result, foreign); | |
| 667 | - } else { | |
| 668 | - foreign.assertScalar(); | |
| 669 | - result = foreign; | |
| 670 | - result.makeDirect(); | |
| 671 | - } | |
| 672 | - | |
| 673 | - if (top && (!result.isStream()) && result.isIndirect()) { | |
| 674 | - throw std::logic_error("replacement for foreign object is indirect"); | |
| 654 | + qpdf.copyStreamData(result, foreign); | |
| 655 | + return result; | |
| 675 | 656 | } |
| 676 | 657 | |
| 658 | + foreign.assertScalar(); | |
| 659 | + auto result = foreign; | |
| 660 | + result.makeDirect(); | |
| 677 | 661 | return result; |
| 678 | 662 | } |
| 679 | 663 | |
| 680 | 664 | void |
| 681 | -QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign) | |
| 665 | +QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign_oh) | |
| 682 | 666 | { |
| 683 | 667 | // This method was originally written for copying foreign streams, but it is used by |
| 684 | - // QPDFObjectHandle to copy streams from the same QPDF object as well. | |
| 685 | - | |
| 686 | - QPDFObjectHandle dict = result.getDict(); | |
| 687 | - QPDFObjectHandle old_dict = foreign.getDict(); | |
| 688 | - if (m->copied_stream_data_provider == nullptr) { | |
| 689 | - m->copied_stream_data_provider = new CopiedStreamDataProvider(*this); | |
| 690 | - m->copied_streams = | |
| 691 | - std::shared_ptr<QPDFObjectHandle::StreamDataProvider>(m->copied_stream_data_provider); | |
| 668 | + // Stream::copy to copy streams from the same QPDF object as well. | |
| 669 | + | |
| 670 | + Dictionary dict = result.getDict(); | |
| 671 | + Dictionary old_dict = foreign_oh.getDict(); | |
| 672 | + if (!m->copied_stream_data_provider) { | |
| 673 | + m->copied_stream_data_provider = std::make_shared<CopiedStreamDataProvider>(*this); | |
| 692 | 674 | } |
| 693 | 675 | QPDFObjGen local_og(result.getObjGen()); |
| 694 | 676 | // Copy information from the foreign stream so we can pipe its data later without keeping the |
| 695 | 677 | // original QPDF object around. |
| 696 | 678 | |
| 697 | 679 | QPDF& foreign_stream_qpdf = |
| 698 | - foreign.getQPDF("unable to retrieve owning qpdf from foreign stream"); | |
| 680 | + foreign_oh.getQPDF("unable to retrieve owning qpdf from foreign stream"); | |
| 699 | 681 | |
| 700 | - auto stream = foreign.as_stream(); | |
| 701 | - if (!stream) { | |
| 682 | + Stream foreign = foreign_oh; | |
| 683 | + if (!foreign) { | |
| 702 | 684 | throw std::logic_error("unable to retrieve underlying stream object from foreign stream"); |
| 703 | 685 | } |
| 704 | - std::shared_ptr<Buffer> stream_buffer = stream.getStreamDataBuffer(); | |
| 705 | - if ((foreign_stream_qpdf.m->immediate_copy_from) && (stream_buffer == nullptr)) { | |
| 686 | + std::shared_ptr<Buffer> stream_buffer = foreign.getStreamDataBuffer(); | |
| 687 | + if (foreign_stream_qpdf.m->immediate_copy_from && !stream_buffer) { | |
| 706 | 688 | // Pull the stream data into a buffer before attempting the copy operation. Do it on the |
| 707 | 689 | // source stream so that if the source stream is copied multiple times, we don't have to |
| 708 | 690 | // keep duplicating the memory. |
| 709 | - QTC::TC("qpdf", "QPDF immediate copy stream data"); | |
| 710 | 691 | foreign.replaceStreamData( |
| 711 | - foreign.getRawStreamData(), | |
| 712 | - old_dict.getKey("/Filter"), | |
| 713 | - old_dict.getKey("/DecodeParms")); | |
| 714 | - stream_buffer = stream.getStreamDataBuffer(); | |
| 692 | + foreign.getRawStreamData(), old_dict["/Filter"], old_dict["/DecodeParms"]); | |
| 693 | + stream_buffer = foreign.getStreamDataBuffer(); | |
| 715 | 694 | } |
| 716 | - std::shared_ptr<QPDFObjectHandle::StreamDataProvider> stream_provider = | |
| 717 | - stream.getStreamDataProvider(); | |
| 718 | - if (stream_buffer.get()) { | |
| 719 | - QTC::TC("qpdf", "QPDF copy foreign stream with buffer"); | |
| 720 | - result.replaceStreamData( | |
| 721 | - stream_buffer, dict.getKey("/Filter"), dict.getKey("/DecodeParms")); | |
| 722 | - } else if (stream_provider.get()) { | |
| 695 | + auto stream_provider = foreign.getStreamDataProvider(); | |
| 696 | + if (stream_buffer) { | |
| 697 | + result.replaceStreamData(stream_buffer, dict["/Filter"], dict["/DecodeParms"]); | |
| 698 | + } else if (stream_provider) { | |
| 723 | 699 | // In this case, the remote stream's QPDF must stay in scope. |
| 724 | - QTC::TC("qpdf", "QPDF copy foreign stream with provider"); | |
| 725 | - m->copied_stream_data_provider->registerForeignStream(local_og, foreign); | |
| 700 | + m->copied_stream_data_provider->registerForeignStream(local_og, foreign_oh); | |
| 726 | 701 | result.replaceStreamData( |
| 727 | - m->copied_streams, dict.getKey("/Filter"), dict.getKey("/DecodeParms")); | |
| 702 | + m->copied_stream_data_provider, dict["/Filter"], dict["/DecodeParms"]); | |
| 728 | 703 | } else { |
| 729 | - auto foreign_stream_data = std::make_shared<ForeignStreamData>( | |
| 730 | - foreign_stream_qpdf.m->encp, | |
| 731 | - foreign_stream_qpdf.m->file, | |
| 732 | - foreign, | |
| 733 | - foreign.getParsedOffset(), | |
| 734 | - stream.getLength(), | |
| 735 | - dict, | |
| 736 | - stream.isRootMetadata()); | |
| 704 | + auto foreign_stream_data = ForeignStreamData(foreign, foreign_oh.offset(), dict); | |
| 737 | 705 | m->copied_stream_data_provider->registerForeignStream(local_og, foreign_stream_data); |
| 738 | 706 | result.replaceStreamData( |
| 739 | - m->copied_streams, dict.getKey("/Filter"), dict.getKey("/DecodeParms")); | |
| 707 | + m->copied_stream_data_provider, dict["/Filter"], dict["/DecodeParms"]); | |
| 740 | 708 | } |
| 741 | 709 | } |
| 742 | 710 | |
| ... | ... | @@ -820,11 +788,11 @@ QPDF::getRoot() |
| 820 | 788 | std::map<QPDFObjGen, QPDFXRefEntry> |
| 821 | 789 | QPDF::getXRefTable() |
| 822 | 790 | { |
| 823 | - return getXRefTableInternal(); | |
| 791 | + return m->objects.getXRefTableInternal(); | |
| 824 | 792 | } |
| 825 | 793 | |
| 826 | 794 | std::map<QPDFObjGen, QPDFXRefEntry> const& |
| 827 | -QPDF::getXRefTableInternal() | |
| 795 | +Objects::getXRefTableInternal() | |
| 828 | 796 | { |
| 829 | 797 | if (!m->parsed) { |
| 830 | 798 | throw std::logic_error("QPDF::getXRefTable called before parsing."); |
| ... | ... | @@ -927,23 +895,20 @@ QPDF::pipeStreamData( |
| 927 | 895 | |
| 928 | 896 | bool |
| 929 | 897 | QPDF::pipeForeignStreamData( |
| 930 | - std::shared_ptr<ForeignStreamData> foreign, | |
| 931 | - Pipeline* pipeline, | |
| 932 | - bool suppress_warnings, | |
| 933 | - bool will_retry) | |
| 898 | + ForeignStreamData& foreign, Pipeline* pipeline, bool suppress_warnings, bool will_retry) | |
| 934 | 899 | { |
| 935 | - if (foreign->encp->encrypted) { | |
| 900 | + if (foreign.encp->encrypted) { | |
| 936 | 901 | QTC::TC("qpdf", "QPDF pipe foreign encrypted stream"); |
| 937 | 902 | } |
| 938 | 903 | return pipeStreamData( |
| 939 | - foreign->encp, | |
| 940 | - foreign->file, | |
| 904 | + foreign.encp, | |
| 905 | + foreign.file, | |
| 941 | 906 | *this, |
| 942 | - foreign->foreign_og, | |
| 943 | - foreign->offset, | |
| 944 | - foreign->length, | |
| 945 | - foreign->local_dict, | |
| 946 | - foreign->is_root_metadata, | |
| 907 | + foreign.foreign_og, | |
| 908 | + foreign.offset, | |
| 909 | + foreign.length, | |
| 910 | + foreign.local_dict, | |
| 911 | + foreign.is_root_metadata, | |
| 947 | 912 | pipeline, |
| 948 | 913 | suppress_warnings, |
| 949 | 914 | will_retry); | ... | ... |
libqpdf/QPDFObjectHandle.cc
| ... | ... | @@ -1690,7 +1690,7 @@ QPDFObjectHandle::parse( |
| 1690 | 1690 | qpdf_offset_t |
| 1691 | 1691 | QPDFObjectHandle::getParsedOffset() const |
| 1692 | 1692 | { |
| 1693 | - return obj ? obj->getParsedOffset() : -1; | |
| 1693 | + return offset(); | |
| 1694 | 1694 | } |
| 1695 | 1695 | |
| 1696 | 1696 | QPDFObjectHandle |
| ... | ... | @@ -1935,24 +1935,6 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams) |
| 1935 | 1935 | visited.erase(cur_og); |
| 1936 | 1936 | } |
| 1937 | 1937 | |
| 1938 | -QPDFObjectHandle | |
| 1939 | -QPDFObjectHandle::copyStream() | |
| 1940 | -{ | |
| 1941 | - assertStream(); | |
| 1942 | - QPDFObjectHandle result = newStream(getOwningQPDF()); | |
| 1943 | - QPDFObjectHandle dict = result.getDict(); | |
| 1944 | - QPDFObjectHandle old_dict = getDict(); | |
| 1945 | - for (auto& iter: QPDFDictItems(old_dict)) { | |
| 1946 | - if (iter.second.isIndirect()) { | |
| 1947 | - dict.replaceKey(iter.first, iter.second); | |
| 1948 | - } else { | |
| 1949 | - dict.replaceKey(iter.first, iter.second.shallowCopy()); | |
| 1950 | - } | |
| 1951 | - } | |
| 1952 | - QPDF::Doc::StreamCopier::copyStreamData(getOwningQPDF(), result, *this); | |
| 1953 | - return result; | |
| 1954 | -} | |
| 1955 | - | |
| 1956 | 1938 | void |
| 1957 | 1939 | QPDFObjectHandle::makeDirect(bool allow_streams) |
| 1958 | 1940 | { | ... | ... |
libqpdf/QPDFWriter.cc
libqpdf/QPDF_Stream.cc
| ... | ... | @@ -4,7 +4,6 @@ |
| 4 | 4 | #include <qpdf/JSON_writer.hh> |
| 5 | 5 | #include <qpdf/Pipeline.hh> |
| 6 | 6 | #include <qpdf/Pipeline_private.hh> |
| 7 | -#include <qpdf/Pl_Base64.hh> | |
| 8 | 7 | #include <qpdf/Pl_Buffer.hh> |
| 9 | 8 | #include <qpdf/Pl_Count.hh> |
| 10 | 9 | #include <qpdf/Pl_Discard.hh> |
| ... | ... | @@ -45,6 +44,12 @@ class QPDF::Doc::Streams |
| 45 | 44 | return qpdf->pipeStreamData( |
| 46 | 45 | og, offset, length, dict, is_root_metadata, pipeline, suppress_warnings, will_retry); |
| 47 | 46 | } |
| 47 | + | |
| 48 | + static void | |
| 49 | + copyStreamData(QPDF* qpdf, QPDFObjectHandle const& dest, QPDFObjectHandle const& src) | |
| 50 | + { | |
| 51 | + qpdf->copyStreamData(dest, src); | |
| 52 | + } | |
| 48 | 53 | }; |
| 49 | 54 | |
| 50 | 55 | namespace |
| ... | ... | @@ -207,6 +212,15 @@ Stream::Stream( |
| 207 | 212 | setDictDescription(); |
| 208 | 213 | } |
| 209 | 214 | |
| 215 | +Stream | |
| 216 | +Stream::copy() const | |
| 217 | +{ | |
| 218 | + Stream result = qpdf()->newStream(); | |
| 219 | + result.stream()->stream_dict = getDict().copy(); | |
| 220 | + QPDF::Doc::Streams::copyStreamData(qpdf(), result, *this); | |
| 221 | + return result; | |
| 222 | +} | |
| 223 | + | |
| 210 | 224 | void |
| 211 | 225 | Stream::registerStreamFilter( |
| 212 | 226 | std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory) |
| ... | ... | @@ -350,12 +364,11 @@ Stream::getStreamData(qpdf_stream_decode_level_e decode_level) |
| 350 | 364 | if (!filtered) { |
| 351 | 365 | throw QPDFExc( |
| 352 | 366 | qpdf_e_unsupported, |
| 353 | - obj->getQPDF()->getFilename(), | |
| 367 | + qpdf()->getFilename(), | |
| 354 | 368 | "", |
| 355 | - obj->getParsedOffset(), | |
| 369 | + offset(), | |
| 356 | 370 | "getStreamData called on unfilterable stream"); |
| 357 | 371 | } |
| 358 | - QTC::TC("qpdf", "QPDF_Stream getStreamData"); | |
| 359 | 372 | return result; |
| 360 | 373 | } |
| 361 | 374 | |
| ... | ... | @@ -367,23 +380,21 @@ Stream::getRawStreamData() |
| 367 | 380 | if (!pipeStreamData(&buf, nullptr, 0, qpdf_dl_none, false, false)) { |
| 368 | 381 | throw QPDFExc( |
| 369 | 382 | qpdf_e_unsupported, |
| 370 | - obj->getQPDF()->getFilename(), | |
| 383 | + qpdf()->getFilename(), | |
| 371 | 384 | "", |
| 372 | - obj->getParsedOffset(), | |
| 385 | + offset(), | |
| 373 | 386 | "error getting raw stream data"); |
| 374 | 387 | } |
| 375 | - QTC::TC("qpdf", "QPDF_Stream getRawStreamData"); | |
| 376 | 388 | return result; |
| 377 | 389 | } |
| 378 | 390 | |
| 379 | 391 | bool |
| 380 | 392 | Stream::isRootMetadata() const |
| 381 | 393 | { |
| 382 | - if (!getDict().isDictionaryOfType("/Metadata", "/XML")) { | |
| 394 | + if (!stream()->stream_dict.isDictionaryOfType("/Metadata", "/XML")) { | |
| 383 | 395 | return false; |
| 384 | 396 | } |
| 385 | - auto root_metadata = qpdf()->getRoot().getKey("/Metadata"); | |
| 386 | - return root_metadata.isSameObjectAs(obj); | |
| 397 | + return qpdf()->getRoot()["/Metadata"].isSameObjectAs(obj); | |
| 387 | 398 | } |
| 388 | 399 | |
| 389 | 400 | bool |
| ... | ... | @@ -579,15 +590,13 @@ Stream::pipeStreamData( |
| 579 | 590 | s->stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(actual_length)); |
| 580 | 591 | } |
| 581 | 592 | } else { |
| 582 | - if (obj->getParsedOffset() == 0) { | |
| 583 | - QTC::TC("qpdf", "QPDF_Stream pipe no stream data"); | |
| 593 | + if (offset() == 0) { | |
| 584 | 594 | throw std::logic_error("pipeStreamData called for stream with no data"); |
| 585 | 595 | } |
| 586 | - QTC::TC("qpdf", "QPDF_Stream pipe original stream data"); | |
| 587 | 596 | if (!QPDF::Doc::Streams::pipeStreamData( |
| 588 | - obj->getQPDF(), | |
| 589 | - obj->getObjGen(), | |
| 590 | - obj->getParsedOffset(), | |
| 597 | + qpdf(), | |
| 598 | + id_gen(), | |
| 599 | + offset(), | |
| 591 | 600 | s->length, |
| 592 | 601 | s->stream_dict, |
| 593 | 602 | isRootMetadata(), |
| ... | ... | @@ -619,6 +628,16 @@ Stream::pipeStreamData( |
| 619 | 628 | |
| 620 | 629 | void |
| 621 | 630 | Stream::replaceStreamData( |
| 631 | + std::string&& data, QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms) | |
| 632 | +{ | |
| 633 | + auto s = stream(); | |
| 634 | + s->stream_data = std::make_shared<Buffer>(std::move(data)); | |
| 635 | + s->stream_provider = nullptr; | |
| 636 | + replaceFilterData(filter, decode_parms, s->stream_data->getSize()); | |
| 637 | +} | |
| 638 | + | |
| 639 | +void | |
| 640 | +Stream::replaceStreamData( | |
| 622 | 641 | std::shared_ptr<Buffer> data, |
| 623 | 642 | QPDFObjectHandle const& filter, |
| 624 | 643 | QPDFObjectHandle const& decode_parms) |
| ... | ... | @@ -626,7 +645,7 @@ Stream::replaceStreamData( |
| 626 | 645 | auto s = stream(); |
| 627 | 646 | s->stream_data = data; |
| 628 | 647 | s->stream_provider = nullptr; |
| 629 | - replaceFilterData(filter, decode_parms, data->getSize()); | |
| 648 | + replaceFilterData(filter, decode_parms, data->size()); | |
| 630 | 649 | } |
| 631 | 650 | |
| 632 | 651 | void |
| ... | ... | @@ -664,7 +683,7 @@ Stream::replaceFilterData( |
| 664 | 683 | void |
| 665 | 684 | Stream::warn(std::string const& message) |
| 666 | 685 | { |
| 667 | - obj->getQPDF()->warn(qpdf_e_damaged_pdf, "", obj->getParsedOffset(), message); | |
| 686 | + qpdf()->warn(qpdf_e_damaged_pdf, "", offset(), message); | |
| 668 | 687 | } |
| 669 | 688 | |
| 670 | 689 | QPDFObjectHandle |
| ... | ... | @@ -772,12 +791,8 @@ void |
| 772 | 791 | QPDFObjectHandle::replaceStreamData( |
| 773 | 792 | std::string const& data, QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms) |
| 774 | 793 | { |
| 775 | - auto b = std::make_shared<Buffer>(data.length()); | |
| 776 | - unsigned char* bp = b->getBuffer(); | |
| 777 | - if (bp) { | |
| 778 | - memcpy(bp, data.c_str(), data.length()); | |
| 779 | - } | |
| 780 | - as_stream(error).replaceStreamData(b, filter, decode_parms); | |
| 794 | + std::string s(data); | |
| 795 | + as_stream(error).replaceStreamData(std::move(s), filter, decode_parms); | |
| 781 | 796 | } |
| 782 | 797 | |
| 783 | 798 | void |
| ... | ... | @@ -856,3 +871,9 @@ QPDFObjectHandle::getStreamJSON( |
| 856 | 871 | { |
| 857 | 872 | return as_stream(error).getStreamJSON(json_version, json_data, decode_level, p, data_filename); |
| 858 | 873 | } |
| 874 | + | |
| 875 | +QPDFObjectHandle | |
| 876 | +QPDFObjectHandle::copyStream() | |
| 877 | +{ | |
| 878 | + return as_stream(error).copy(); | |
| 879 | +} | ... | ... |
libqpdf/QPDF_json.cc
| ... | ... | @@ -424,8 +424,7 @@ QPDF::JSONReactor::replaceObject(QPDFObjectHandle&& replacement, JSON const& val |
| 424 | 424 | auto og = tos.object.getObjGen(); |
| 425 | 425 | if (replacement.isIndirect() && !(replacement.isStream() && replacement.getObjGen() == og)) { |
| 426 | 426 | error( |
| 427 | - replacement.getParsedOffset(), | |
| 428 | - "the value of an object may not be an indirect object reference"); | |
| 427 | + replacement.offset(), "the value of an object may not be an indirect object reference"); | |
| 429 | 428 | return; |
| 430 | 429 | } |
| 431 | 430 | pdf.replaceObject(og, replacement); |
| ... | ... | @@ -885,7 +884,7 @@ QPDF::writeJSON( |
| 885 | 884 | } else { |
| 886 | 885 | jw << "\n },\n \"" << key; |
| 887 | 886 | } |
| 888 | - if (auto stream = obj.as_stream()) { | |
| 887 | + if (Stream stream = obj) { | |
| 889 | 888 | jw << "\": {\n \"stream\": "; |
| 890 | 889 | if (json_stream_data == qpdf_sj_file) { |
| 891 | 890 | writeJSONStreamFile( | ... | ... |
libqpdf/QPDF_objects.cc
| ... | ... | @@ -1634,7 +1634,7 @@ Objects::resolveObjectsInStream(int obj_stream_number) |
| 1634 | 1634 | } |
| 1635 | 1635 | m->resolved_object_streams.insert(obj_stream_number); |
| 1636 | 1636 | // Force resolution of object stream |
| 1637 | - auto obj_stream = qpdf.getObject(obj_stream_number, 0).as_stream(); | |
| 1637 | + Stream obj_stream = qpdf.getObject(obj_stream_number, 0); | |
| 1638 | 1638 | if (!obj_stream) { |
| 1639 | 1639 | throw qpdf.damagedPDF( |
| 1640 | 1640 | "object " + std::to_string(obj_stream_number) + " 0", | ... | ... |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| ... | ... | @@ -445,6 +445,23 @@ namespace qpdf |
| 445 | 445 | { |
| 446 | 446 | } |
| 447 | 447 | |
| 448 | + Stream() = default; | |
| 449 | + Stream(Stream const&) = default; | |
| 450 | + Stream(Stream&&) = default; | |
| 451 | + Stream& operator=(Stream const&) = default; | |
| 452 | + Stream& operator=(Stream&&) = default; | |
| 453 | + ~Stream() = default; | |
| 454 | + | |
| 455 | + Stream(QPDFObjectHandle const& oh) : | |
| 456 | + BaseHandle(oh.type_code() == ::ot_stream ? oh : QPDFObjectHandle()) | |
| 457 | + { | |
| 458 | + } | |
| 459 | + | |
| 460 | + Stream(QPDFObjectHandle&& oh) : | |
| 461 | + BaseHandle(oh.type_code() == ::ot_stream ? std::move(oh) : QPDFObjectHandle()) | |
| 462 | + { | |
| 463 | + } | |
| 464 | + | |
| 448 | 465 | Stream( |
| 449 | 466 | QPDF& qpdf, |
| 450 | 467 | QPDFObjGen og, |
| ... | ... | @@ -452,10 +469,12 @@ namespace qpdf |
| 452 | 469 | qpdf_offset_t offset, |
| 453 | 470 | size_t length); |
| 454 | 471 | |
| 455 | - QPDFObjectHandle | |
| 472 | + Stream copy() const; | |
| 473 | + | |
| 474 | + Dictionary | |
| 456 | 475 | getDict() const |
| 457 | 476 | { |
| 458 | - return stream()->stream_dict; | |
| 477 | + return {stream()->stream_dict}; | |
| 459 | 478 | } |
| 460 | 479 | bool |
| 461 | 480 | isDataModified() const |
| ... | ... | @@ -501,6 +520,10 @@ namespace qpdf |
| 501 | 520 | std::string getStreamData(qpdf_stream_decode_level_e level); |
| 502 | 521 | std::string getRawStreamData(); |
| 503 | 522 | void replaceStreamData( |
| 523 | + std::string&& data, | |
| 524 | + QPDFObjectHandle const& filter, | |
| 525 | + QPDFObjectHandle const& decode_parms); | |
| 526 | + void replaceStreamData( | |
| 504 | 527 | std::shared_ptr<Buffer> data, |
| 505 | 528 | QPDFObjectHandle const& filter, |
| 506 | 529 | QPDFObjectHandle const& decode_parms); |
| ... | ... | @@ -657,6 +680,12 @@ namespace qpdf |
| 657 | 680 | return !obj || type_code() == ::ot_null; |
| 658 | 681 | } |
| 659 | 682 | |
| 683 | + inline qpdf_offset_t | |
| 684 | + BaseHandle::offset() const | |
| 685 | + { | |
| 686 | + return obj ? obj->parsed_offset : -1; | |
| 687 | + } | |
| 688 | + | |
| 660 | 689 | inline QPDF* |
| 661 | 690 | BaseHandle::qpdf() const |
| 662 | 691 | { | ... | ... |
libqpdf/qpdf/QPDFObject_private.hh
libqpdf/qpdf/QPDF_private.hh
| ... | ... | @@ -13,10 +13,14 @@ |
| 13 | 13 | |
| 14 | 14 | using namespace qpdf; |
| 15 | 15 | |
| 16 | -namespace qpdf::is | |
| 16 | +namespace qpdf | |
| 17 | 17 | { |
| 18 | - class OffsetBuffer; | |
| 19 | -} // namespace qpdf::is | |
| 18 | + class Stream; | |
| 19 | + namespace is | |
| 20 | + { | |
| 21 | + class OffsetBuffer; | |
| 22 | + } // namespace is | |
| 23 | +} // namespace qpdf | |
| 20 | 24 | |
| 21 | 25 | class BitStream; |
| 22 | 26 | class BitWriter; |
| ... | ... | @@ -40,14 +44,6 @@ class QPDF::ObjCache |
| 40 | 44 | qpdf_offset_t end_after_space{0}; |
| 41 | 45 | }; |
| 42 | 46 | |
| 43 | -class QPDF::ObjCopier | |
| 44 | -{ | |
| 45 | - public: | |
| 46 | - std::map<QPDFObjGen, QPDFObjectHandle> object_map; | |
| 47 | - std::vector<QPDFObjectHandle> to_copy; | |
| 48 | - QPDFObjGen::set visiting; | |
| 49 | -}; | |
| 50 | - | |
| 51 | 47 | class QPDF::EncryptionParameters |
| 52 | 48 | { |
| 53 | 49 | friend class QPDF; |
| ... | ... | @@ -98,14 +94,7 @@ class QPDF::ForeignStreamData |
| 98 | 94 | friend class QPDF; |
| 99 | 95 | |
| 100 | 96 | public: |
| 101 | - ForeignStreamData( | |
| 102 | - std::shared_ptr<EncryptionParameters> encp, | |
| 103 | - std::shared_ptr<InputSource> file, | |
| 104 | - QPDFObjGen foreign_og, | |
| 105 | - qpdf_offset_t offset, | |
| 106 | - size_t length, | |
| 107 | - QPDFObjectHandle local_dict, | |
| 108 | - bool is_root_metadata); | |
| 97 | + ForeignStreamData(Stream& foreign, qpdf_offset_t offset, QPDFObjectHandle local_dict); | |
| 109 | 98 | |
| 110 | 99 | private: |
| 111 | 100 | std::shared_ptr<EncryptionParameters> encp; |
| ... | ... | @@ -117,20 +106,29 @@ class QPDF::ForeignStreamData |
| 117 | 106 | bool is_root_metadata{false}; |
| 118 | 107 | }; |
| 119 | 108 | |
| 120 | -class QPDF::CopiedStreamDataProvider: public QPDFObjectHandle::StreamDataProvider | |
| 109 | +class QPDF::CopiedStreamDataProvider final: public QPDFObjectHandle::StreamDataProvider | |
| 121 | 110 | { |
| 122 | 111 | public: |
| 123 | 112 | CopiedStreamDataProvider(QPDF& destination_qpdf); |
| 124 | - ~CopiedStreamDataProvider() override = default; | |
| 113 | + ~CopiedStreamDataProvider() final = default; | |
| 125 | 114 | bool provideStreamData( |
| 126 | - QPDFObjGen const& og, Pipeline* pipeline, bool suppress_warnings, bool will_retry) override; | |
| 127 | - void registerForeignStream(QPDFObjGen const& local_og, QPDFObjectHandle foreign_stream); | |
| 128 | - void registerForeignStream(QPDFObjGen const& local_og, std::shared_ptr<ForeignStreamData>); | |
| 115 | + QPDFObjGen const& og, Pipeline* pipeline, bool suppress_warnings, bool will_retry) final; | |
| 116 | + void | |
| 117 | + registerForeignStream(QPDFObjGen const& local_og, QPDFObjectHandle foreign_stream) | |
| 118 | + { | |
| 119 | + foreign_streams.insert_or_assign(local_og, foreign_stream); | |
| 120 | + } | |
| 121 | + | |
| 122 | + void | |
| 123 | + registerForeignStream(QPDFObjGen local_og, ForeignStreamData foreign_stream) | |
| 124 | + { | |
| 125 | + foreign_stream_data.insert_or_assign(local_og, foreign_stream); | |
| 126 | + } | |
| 129 | 127 | |
| 130 | 128 | private: |
| 131 | 129 | QPDF& destination_qpdf; |
| 132 | 130 | std::map<QPDFObjGen, QPDFObjectHandle> foreign_streams; |
| 133 | - std::map<QPDFObjGen, std::shared_ptr<ForeignStreamData>> foreign_stream_data; | |
| 131 | + std::map<QPDFObjGen, ForeignStreamData> foreign_stream_data; | |
| 134 | 132 | }; |
| 135 | 133 | |
| 136 | 134 | class QPDF::StringDecrypter final: public QPDFObjectHandle::StringDecrypter |
| ... | ... | @@ -572,6 +570,57 @@ class QPDF::Doc |
| 572 | 570 | class Objects |
| 573 | 571 | { |
| 574 | 572 | public: |
| 573 | + class Foreign | |
| 574 | + { | |
| 575 | + class Copier | |
| 576 | + { | |
| 577 | + public: | |
| 578 | + Copier(QPDF& qpdf) : | |
| 579 | + qpdf(qpdf) | |
| 580 | + { | |
| 581 | + } | |
| 582 | + | |
| 583 | + QPDFObjectHandle copied(QPDFObjectHandle const& foreign); | |
| 584 | + | |
| 585 | + private: | |
| 586 | + QPDFObjectHandle | |
| 587 | + replace_indirect_object(QPDFObjectHandle const& foreign, bool top = false); | |
| 588 | + void reserve_objects(QPDFObjectHandle const& foreign, bool top = false); | |
| 589 | + | |
| 590 | + QPDF& qpdf; | |
| 591 | + std::map<QPDFObjGen, QPDFObjectHandle> object_map; | |
| 592 | + std::vector<QPDFObjectHandle> to_copy; | |
| 593 | + QPDFObjGen::set visiting; | |
| 594 | + }; | |
| 595 | + | |
| 596 | + public: | |
| 597 | + Foreign(QPDF& qpdf) : | |
| 598 | + qpdf(qpdf) | |
| 599 | + { | |
| 600 | + } | |
| 601 | + | |
| 602 | + Foreign() = delete; | |
| 603 | + Foreign(Foreign const&) = delete; | |
| 604 | + Foreign(Foreign&&) = delete; | |
| 605 | + Foreign& operator=(Foreign const&) = delete; | |
| 606 | + Foreign& operator=(Foreign&&) = delete; | |
| 607 | + ~Foreign() = default; | |
| 608 | + | |
| 609 | + // Return a local handle to the foreign object. Copy the foreign object if necessary. | |
| 610 | + QPDFObjectHandle | |
| 611 | + copied(QPDFObjectHandle const& foreign) | |
| 612 | + { | |
| 613 | + return copier(foreign).copied(foreign); | |
| 614 | + } | |
| 615 | + | |
| 616 | + private: | |
| 617 | + Copier& copier(QPDFObjectHandle const& foreign); | |
| 618 | + | |
| 619 | + QPDF& qpdf; | |
| 620 | + std::map<unsigned long long, Copier> copiers; | |
| 621 | + }; // class QPDF::Doc::Objects::Foreign | |
| 622 | + | |
| 623 | + public: | |
| 575 | 624 | Objects() = delete; |
| 576 | 625 | Objects(Objects const&) = delete; |
| 577 | 626 | Objects(Objects&&) = delete; |
| ... | ... | @@ -581,10 +630,17 @@ class QPDF::Doc |
| 581 | 630 | |
| 582 | 631 | Objects(QPDF& qpdf, QPDF::Members* m) : |
| 583 | 632 | qpdf(qpdf), |
| 584 | - m(m) | |
| 633 | + m(m), | |
| 634 | + foreign_(qpdf) | |
| 585 | 635 | { |
| 586 | 636 | } |
| 587 | 637 | |
| 638 | + Foreign& | |
| 639 | + foreign() | |
| 640 | + { | |
| 641 | + return foreign_; | |
| 642 | + } | |
| 643 | + | |
| 588 | 644 | void parse(char const* password); |
| 589 | 645 | std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og); |
| 590 | 646 | void inParse(bool); |
| ... | ... | @@ -607,6 +663,7 @@ class QPDF::Doc |
| 607 | 663 | |
| 608 | 664 | // For QPDFWriter: |
| 609 | 665 | |
| 666 | + std::map<QPDFObjGen, QPDFXRefEntry> const& getXRefTableInternal(); | |
| 610 | 667 | // Get a list of objects that would be permitted in an object stream. |
| 611 | 668 | template <typename T> |
| 612 | 669 | std::vector<T> getCompressibleObjGens(); |
| ... | ... | @@ -656,9 +713,10 @@ class QPDF::Doc |
| 656 | 713 | bool isUnresolved(QPDFObjGen og); |
| 657 | 714 | void setLastObjectDescription(std::string const& description, QPDFObjGen og); |
| 658 | 715 | |
| 659 | - private: | |
| 660 | 716 | QPDF& qpdf; |
| 661 | 717 | QPDF::Members* m; |
| 718 | + | |
| 719 | + Foreign foreign_; | |
| 662 | 720 | }; // class QPDF::Doc::Objects |
| 663 | 721 | |
| 664 | 722 | // This class is used to represent a PDF Pages tree. |
| ... | ... | @@ -699,19 +757,6 @@ class QPDF::Doc |
| 699 | 757 | QPDF::Members* m; |
| 700 | 758 | }; // class QPDF::Doc::Pages |
| 701 | 759 | |
| 702 | - // StreamCopier class is restricted to QPDFObjectHandle so it can copy stream data. | |
| 703 | - class StreamCopier | |
| 704 | - { | |
| 705 | - friend class QPDFObjectHandle; | |
| 706 | - | |
| 707 | - private: | |
| 708 | - static void | |
| 709 | - copyStreamData(QPDF* qpdf, QPDFObjectHandle const& dest, QPDFObjectHandle const& src) | |
| 710 | - { | |
| 711 | - qpdf->copyStreamData(dest, src); | |
| 712 | - } | |
| 713 | - }; | |
| 714 | - | |
| 715 | 760 | Doc() = delete; |
| 716 | 761 | Doc(Doc const&) = delete; |
| 717 | 762 | Doc(Doc&&) = delete; |
| ... | ... | @@ -853,10 +898,7 @@ class QPDF::Members |
| 853 | 898 | bool ever_pushed_inherited_attributes_to_pages{false}; |
| 854 | 899 | bool ever_called_get_all_pages{false}; |
| 855 | 900 | std::vector<QPDFExc> warnings; |
| 856 | - std::map<unsigned long long, ObjCopier> object_copiers; | |
| 857 | - std::shared_ptr<QPDFObjectHandle::StreamDataProvider> copied_streams; | |
| 858 | - // copied_stream_data_provider is owned by copied_streams | |
| 859 | - CopiedStreamDataProvider* copied_stream_data_provider{nullptr}; | |
| 901 | + std::shared_ptr<CopiedStreamDataProvider> copied_stream_data_provider; | |
| 860 | 902 | bool reconstructed_xref{false}; |
| 861 | 903 | bool in_read_xref_stream{false}; |
| 862 | 904 | bool fixed_dangling_refs{false}; | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -98,16 +98,12 @@ qpdf-c called qpdf_set_r4_encryption_parameters_insecure 0 |
| 98 | 98 | qpdf-c called qpdf_set_static_aes_IV 0 |
| 99 | 99 | qpdf-c called qpdf_has_error 0 |
| 100 | 100 | qpdf-c called qpdf_get_qpdf_version 0 |
| 101 | -QPDF_Stream pipe original stream data 0 | |
| 102 | 101 | QPDF_Stream pipe replaced stream data 0 |
| 103 | 102 | QPDF_Stream provider length mismatch 0 |
| 104 | 103 | QPDFObjectHandle newStream 0 |
| 105 | 104 | QPDFObjectHandle newStream with data 0 |
| 106 | -QPDF_Stream pipe no stream data 0 | |
| 107 | 105 | QPDFObjectHandle prepend page contents 0 |
| 108 | 106 | QPDFObjectHandle append page contents 0 |
| 109 | -QPDF_Stream getRawStreamData 0 | |
| 110 | -QPDF_Stream getStreamData 0 | |
| 111 | 107 | qpdf-c called qpdf_read_memory 0 |
| 112 | 108 | QPDF stream with CRNL 0 |
| 113 | 109 | QPDFWriter copy encrypt metadata 1 |
| ... | ... | @@ -128,13 +124,6 @@ QPDFObjectHandle newStream with string 0 |
| 128 | 124 | QPDF_Stream provider length not provided 0 |
| 129 | 125 | QPDF_Stream unknown stream length 0 |
| 130 | 126 | QPDF replaceReserved 0 |
| 131 | -QPDF copyForeign direct 0 | |
| 132 | -QPDF copyForeign not foreign 0 | |
| 133 | -QPDF replace indirect 0 | |
| 134 | -QPDF replace array 0 | |
| 135 | -QPDF replace dictionary 0 | |
| 136 | -QPDF replace stream 0 | |
| 137 | -QPDF replace foreign indirect with null 0 | |
| 138 | 127 | QPDFWriter copy use_aes 1 |
| 139 | 128 | QPDFParser indirect without context 0 |
| 140 | 129 | QPDFObjectHandle trailing data in parse 0 |
| ... | ... | @@ -256,9 +245,6 @@ QPDFJob image optimize no pipeline 0 |
| 256 | 245 | QPDFJob image optimize no shrink 0 |
| 257 | 246 | QPDFJob image optimize too small 0 |
| 258 | 247 | QPDF pipe foreign encrypted stream 0 |
| 259 | -QPDF copy foreign stream with provider 0 | |
| 260 | -QPDF copy foreign stream with buffer 0 | |
| 261 | -QPDF immediate copy stream data 0 | |
| 262 | 248 | QPDFJob copy same page more than once 1 |
| 263 | 249 | QPDFPageObjectHelper bad token finding names 0 |
| 264 | 250 | QPDFJob password mode bytes 0 | ... | ... |