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,6 +94,7 @@ namespace qpdf | ||
| 94 | inline QPDFObjGen id_gen() const; | 94 | inline QPDFObjGen id_gen() const; |
| 95 | inline bool indirect() const; | 95 | inline bool indirect() const; |
| 96 | inline bool null() const; | 96 | inline bool null() const; |
| 97 | + inline qpdf_offset_t offset() const; | ||
| 97 | inline QPDF* qpdf() const; | 98 | inline QPDF* qpdf() const; |
| 98 | inline qpdf_object_type_e raw_type_code() const; | 99 | inline qpdf_object_type_e raw_type_code() const; |
| 99 | inline qpdf_object_type_e resolved_type_code() const; | 100 | inline qpdf_object_type_e resolved_type_code() const; |
include/qpdf/QPDF.hh
| @@ -742,7 +742,6 @@ class QPDF | @@ -742,7 +742,6 @@ class QPDF | ||
| 742 | static std::string const qpdf_version; | 742 | static std::string const qpdf_version; |
| 743 | 743 | ||
| 744 | class ObjCache; | 744 | class ObjCache; |
| 745 | - class ObjCopier; | ||
| 746 | class EncryptionParameters; | 745 | class EncryptionParameters; |
| 747 | class ForeignStreamData; | 746 | class ForeignStreamData; |
| 748 | class CopiedStreamDataProvider; | 747 | class CopiedStreamDataProvider; |
| @@ -775,8 +774,8 @@ class QPDF | @@ -775,8 +774,8 @@ class QPDF | ||
| 775 | Pipeline* pipeline, | 774 | Pipeline* pipeline, |
| 776 | bool suppress_warnings, | 775 | bool suppress_warnings, |
| 777 | bool will_retry); | 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 | static bool pipeStreamData( | 779 | static bool pipeStreamData( |
| 781 | std::shared_ptr<QPDF::EncryptionParameters> encp, | 780 | std::shared_ptr<QPDF::EncryptionParameters> encp, |
| 782 | std::shared_ptr<InputSource> file, | 781 | std::shared_ptr<InputSource> file, |
| @@ -790,27 +789,6 @@ class QPDF | @@ -790,27 +789,6 @@ class QPDF | ||
| 790 | bool suppress_warnings, | 789 | bool suppress_warnings, |
| 791 | bool will_retry); | 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 | // methods to support encryption -- implemented in QPDF_encryption.cc | 792 | // methods to support encryption -- implemented in QPDF_encryption.cc |
| 815 | void initializeEncryption(); | 793 | void initializeEncryption(); |
| 816 | static std::string | 794 | static std::string |
| @@ -827,9 +805,6 @@ class QPDF | @@ -827,9 +805,6 @@ class QPDF | ||
| 827 | std::unique_ptr<Pipeline>& heap); | 805 | std::unique_ptr<Pipeline>& heap); |
| 828 | 806 | ||
| 829 | // Methods to support object copying | 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 | void copyStreamData(QPDFObjectHandle dest_stream, QPDFObjectHandle src_stream); | 808 | void copyStreamData(QPDFObjectHandle dest_stream, QPDFObjectHandle src_stream); |
| 834 | 809 | ||
| 835 | struct HPageOffsetEntry; | 810 | struct HPageOffsetEntry; |
libqpdf/QPDF.cc
| @@ -27,6 +27,8 @@ | @@ -27,6 +27,8 @@ | ||
| 27 | using namespace qpdf; | 27 | using namespace qpdf; |
| 28 | using namespace std::literals; | 28 | using namespace std::literals; |
| 29 | 29 | ||
| 30 | +using Objects = QPDF::Doc::Objects; | ||
| 31 | + | ||
| 30 | // This must be a fixed value. This API returns a const reference to it, and the C API relies on its | 32 | // This must be a fixed value. This API returns a const reference to it, and the C API relies on its |
| 31 | // being static as well. | 33 | // being static as well. |
| 32 | std::string const QPDF::qpdf_version(QPDF_VERSION); | 34 | std::string const QPDF::qpdf_version(QPDF_VERSION); |
| @@ -109,20 +111,14 @@ namespace | @@ -109,20 +111,14 @@ namespace | ||
| 109 | } // namespace | 111 | } // namespace |
| 110 | 112 | ||
| 111 | QPDF::ForeignStreamData::ForeignStreamData( | 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 | offset(offset), | 118 | offset(offset), |
| 123 | - length(length), | 119 | + length(foreign.getLength()), |
| 124 | local_dict(local_dict), | 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,11 +132,11 @@ bool | ||
| 136 | QPDF::CopiedStreamDataProvider::provideStreamData( | 132 | QPDF::CopiedStreamDataProvider::provideStreamData( |
| 137 | QPDFObjGen const& og, Pipeline* pipeline, bool suppress_warnings, bool will_retry) | 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 | bool result = false; | 136 | bool result = false; |
| 141 | - if (foreign_data.get()) { | 137 | + if (foreign_data != foreign_stream_data.end()) { |
| 142 | result = destination_qpdf.pipeForeignStreamData( | 138 | result = destination_qpdf.pipeForeignStreamData( |
| 143 | - foreign_data, pipeline, suppress_warnings, will_retry); | 139 | + foreign_data->second, pipeline, suppress_warnings, will_retry); |
| 144 | QTC::TC("qpdf", "QPDF copy foreign with data", result ? 0 : 1); | 140 | QTC::TC("qpdf", "QPDF copy foreign with data", result ? 0 : 1); |
| 145 | } else { | 141 | } else { |
| 146 | auto foreign_stream = foreign_streams[og]; | 142 | auto foreign_stream = foreign_streams[og]; |
| @@ -151,20 +147,6 @@ QPDF::CopiedStreamDataProvider::provideStreamData( | @@ -151,20 +147,6 @@ QPDF::CopiedStreamDataProvider::provideStreamData( | ||
| 151 | return result; | 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 | QPDF::StringDecrypter::StringDecrypter(QPDF* qpdf, QPDFObjGen og) : | 150 | QPDF::StringDecrypter::StringDecrypter(QPDF* qpdf, QPDFObjGen og) : |
| 169 | qpdf(qpdf), | 151 | qpdf(qpdf), |
| 170 | og(og) | 152 | og(og) |
| @@ -184,8 +166,8 @@ QPDF::Members::Members(QPDF& qpdf) : | @@ -184,8 +166,8 @@ QPDF::Members::Members(QPDF& qpdf) : | ||
| 184 | objects(doc.objects()), | 166 | objects(doc.objects()), |
| 185 | pages(doc.pages()), | 167 | pages(doc.pages()), |
| 186 | log(QPDFLogger::defaultLogger()), | 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,6 +473,25 @@ QPDF::getObjectByID(int objid, int generation) | ||
| 491 | QPDFObjectHandle | 473 | QPDFObjectHandle |
| 492 | QPDF::copyForeignObject(QPDFObjectHandle foreign) | 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 | // Here's an explanation of what's going on here. | 495 | // Here's an explanation of what's going on here. |
| 495 | // | 496 | // |
| 496 | // A QPDFObjectHandle that is an indirect object has an owning QPDF. The object ID and | 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,7 +500,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) | ||
| 499 | // references to the corresponding object in the local file. | 500 | // references to the corresponding object in the local file. |
| 500 | // | 501 | // |
| 501 | // To do this, we maintain mappings from foreign object IDs to local object IDs for each foreign | 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 | // mapping from the foreign ObjGen to the local QPDFObjectHandle. | 504 | // mapping from the foreign ObjGen to the local QPDFObjectHandle. |
| 504 | // | 505 | // |
| 505 | // To copy, we do a deep traversal of the foreign object with loop detection to discover all | 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,218 +526,185 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) | ||
| 525 | 526 | ||
| 526 | // Note that we explicitly allow use of copyForeignObject on page objects. It is a documented | 527 | // Note that we explicitly allow use of copyForeignObject on page objects. It is a documented |
| 527 | // use case to copy pages this way if the intention is to not update the pages tree. | 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 | // Make sure we have an object in this file for every referenced object in the old file. | 533 | // Make sure we have an object in this file for every referenced object in the old file. |
| 545 | // obj_copier.object_map maps foreign QPDFObjGen to local objects. For everything new that we | 534 | // obj_copier.object_map maps foreign QPDFObjGen to local objects. For everything new that we |
| 546 | // have to copy, the local object will be a reservation, unless it is a stream, in which case | 535 | // have to copy, the local object will be a reservation, unless it is a stream, in which case |
| 547 | // the local object will already be a stream. | 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 | // Copy any new objects and replace the reservations. | 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 | auto og = foreign.getObjGen(); | 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 | "unexpected reference to /Pages object while copying foreign object; replacing with " | 555 | "unexpected reference to /Pages object while copying foreign object; replacing with " |
| 570 | "null")); | 556 | "null")); |
| 571 | return QPDFObjectHandle::newNull(); | 557 | return QPDFObjectHandle::newNull(); |
| 572 | } | 558 | } |
| 573 | - return obj_copier.object_map[foreign.getObjGen()]; | 559 | + return object_map[foreign]; |
| 574 | } | 560 | } |
| 575 | 561 | ||
| 576 | void | 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 | if (foreign.isPagesObject()) { | 569 | if (foreign.isPagesObject()) { |
| 585 | return; | 570 | return; |
| 586 | } | 571 | } |
| 587 | 572 | ||
| 588 | - if (foreign.isIndirect()) { | 573 | + if (foreign.indirect()) { |
| 589 | QPDFObjGen foreign_og(foreign.getObjGen()); | 574 | QPDFObjGen foreign_og(foreign.getObjGen()); |
| 590 | - if (!obj_copier.visiting.add(foreign_og)) { | 575 | + if (!visiting.add(foreign_og)) { |
| 591 | return; | 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 | return; | 581 | return; |
| 597 | } | 582 | } |
| 598 | } else { | 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 | if (!top && foreign.isPageObject()) { | 585 | if (!top && foreign.isPageObject()) { |
| 602 | - obj_copier.visiting.erase(foreign_og); | 586 | + visiting.erase(foreign_og); |
| 603 | return; | 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 | if (foreign_tc == ::ot_array) { | 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 | } else if (foreign_tc == ::ot_dictionary) { | 597 | } else if (foreign_tc == ::ot_dictionary) { |
| 614 | - for (auto const& item: foreign.as_dictionary()) { | 598 | + for (auto const& item: Dictionary(foreign)) { |
| 615 | if (!item.second.null()) { | 599 | if (!item.second.null()) { |
| 616 | - reserveObjects(item.second, obj_copier, false); | 600 | + reserve_objects(item.second); |
| 617 | } | 601 | } |
| 618 | } | 602 | } |
| 619 | } else if (foreign_tc == ::ot_stream) { | 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 | QPDFObjectHandle | 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 | // This case would occur if this is a reference to a Pages object that we didn't | 618 | // This case would occur if this is a reference to a Pages object that we didn't |
| 636 | // traverse into. | 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 | if (!value.null()) { | 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 | if (!value.null()) { | 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 | return result; | 661 | return result; |
| 678 | } | 662 | } |
| 679 | 663 | ||
| 680 | void | 664 | void |
| 681 | -QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign) | 665 | +QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign_oh) |
| 682 | { | 666 | { |
| 683 | // This method was originally written for copying foreign streams, but it is used by | 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 | QPDFObjGen local_og(result.getObjGen()); | 675 | QPDFObjGen local_og(result.getObjGen()); |
| 694 | // Copy information from the foreign stream so we can pipe its data later without keeping the | 676 | // Copy information from the foreign stream so we can pipe its data later without keeping the |
| 695 | // original QPDF object around. | 677 | // original QPDF object around. |
| 696 | 678 | ||
| 697 | QPDF& foreign_stream_qpdf = | 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 | throw std::logic_error("unable to retrieve underlying stream object from foreign stream"); | 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 | // Pull the stream data into a buffer before attempting the copy operation. Do it on the | 688 | // Pull the stream data into a buffer before attempting the copy operation. Do it on the |
| 707 | // source stream so that if the source stream is copied multiple times, we don't have to | 689 | // source stream so that if the source stream is copied multiple times, we don't have to |
| 708 | // keep duplicating the memory. | 690 | // keep duplicating the memory. |
| 709 | - QTC::TC("qpdf", "QPDF immediate copy stream data"); | ||
| 710 | foreign.replaceStreamData( | 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 | // In this case, the remote stream's QPDF must stay in scope. | 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 | result.replaceStreamData( | 701 | result.replaceStreamData( |
| 727 | - m->copied_streams, dict.getKey("/Filter"), dict.getKey("/DecodeParms")); | 702 | + m->copied_stream_data_provider, dict["/Filter"], dict["/DecodeParms"]); |
| 728 | } else { | 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 | m->copied_stream_data_provider->registerForeignStream(local_og, foreign_stream_data); | 705 | m->copied_stream_data_provider->registerForeignStream(local_og, foreign_stream_data); |
| 738 | result.replaceStreamData( | 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,11 +788,11 @@ QPDF::getRoot() | ||
| 820 | std::map<QPDFObjGen, QPDFXRefEntry> | 788 | std::map<QPDFObjGen, QPDFXRefEntry> |
| 821 | QPDF::getXRefTable() | 789 | QPDF::getXRefTable() |
| 822 | { | 790 | { |
| 823 | - return getXRefTableInternal(); | 791 | + return m->objects.getXRefTableInternal(); |
| 824 | } | 792 | } |
| 825 | 793 | ||
| 826 | std::map<QPDFObjGen, QPDFXRefEntry> const& | 794 | std::map<QPDFObjGen, QPDFXRefEntry> const& |
| 827 | -QPDF::getXRefTableInternal() | 795 | +Objects::getXRefTableInternal() |
| 828 | { | 796 | { |
| 829 | if (!m->parsed) { | 797 | if (!m->parsed) { |
| 830 | throw std::logic_error("QPDF::getXRefTable called before parsing."); | 798 | throw std::logic_error("QPDF::getXRefTable called before parsing."); |
| @@ -927,23 +895,20 @@ QPDF::pipeStreamData( | @@ -927,23 +895,20 @@ QPDF::pipeStreamData( | ||
| 927 | 895 | ||
| 928 | bool | 896 | bool |
| 929 | QPDF::pipeForeignStreamData( | 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 | QTC::TC("qpdf", "QPDF pipe foreign encrypted stream"); | 901 | QTC::TC("qpdf", "QPDF pipe foreign encrypted stream"); |
| 937 | } | 902 | } |
| 938 | return pipeStreamData( | 903 | return pipeStreamData( |
| 939 | - foreign->encp, | ||
| 940 | - foreign->file, | 904 | + foreign.encp, |
| 905 | + foreign.file, | ||
| 941 | *this, | 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 | pipeline, | 912 | pipeline, |
| 948 | suppress_warnings, | 913 | suppress_warnings, |
| 949 | will_retry); | 914 | will_retry); |
libqpdf/QPDFObjectHandle.cc
| @@ -1690,7 +1690,7 @@ QPDFObjectHandle::parse( | @@ -1690,7 +1690,7 @@ QPDFObjectHandle::parse( | ||
| 1690 | qpdf_offset_t | 1690 | qpdf_offset_t |
| 1691 | QPDFObjectHandle::getParsedOffset() const | 1691 | QPDFObjectHandle::getParsedOffset() const |
| 1692 | { | 1692 | { |
| 1693 | - return obj ? obj->getParsedOffset() : -1; | 1693 | + return offset(); |
| 1694 | } | 1694 | } |
| 1695 | 1695 | ||
| 1696 | QPDFObjectHandle | 1696 | QPDFObjectHandle |
| @@ -1935,24 +1935,6 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams) | @@ -1935,24 +1935,6 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams) | ||
| 1935 | visited.erase(cur_og); | 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 | void | 1938 | void |
| 1957 | QPDFObjectHandle::makeDirect(bool allow_streams) | 1939 | QPDFObjectHandle::makeDirect(bool allow_streams) |
| 1958 | { | 1940 | { |
libqpdf/QPDFWriter.cc
| @@ -320,7 +320,7 @@ class QPDF::Doc::Writer | @@ -320,7 +320,7 @@ class QPDF::Doc::Writer | ||
| 320 | std::map<QPDFObjGen, QPDFXRefEntry> const& | 320 | std::map<QPDFObjGen, QPDFXRefEntry> const& |
| 321 | getXRefTable() | 321 | getXRefTable() |
| 322 | { | 322 | { |
| 323 | - return pdf.getXRefTableInternal(); | 323 | + return objects.getXRefTableInternal(); |
| 324 | } | 324 | } |
| 325 | 325 | ||
| 326 | size_t | 326 | size_t |
libqpdf/QPDF_Stream.cc
| @@ -4,7 +4,6 @@ | @@ -4,7 +4,6 @@ | ||
| 4 | #include <qpdf/JSON_writer.hh> | 4 | #include <qpdf/JSON_writer.hh> |
| 5 | #include <qpdf/Pipeline.hh> | 5 | #include <qpdf/Pipeline.hh> |
| 6 | #include <qpdf/Pipeline_private.hh> | 6 | #include <qpdf/Pipeline_private.hh> |
| 7 | -#include <qpdf/Pl_Base64.hh> | ||
| 8 | #include <qpdf/Pl_Buffer.hh> | 7 | #include <qpdf/Pl_Buffer.hh> |
| 9 | #include <qpdf/Pl_Count.hh> | 8 | #include <qpdf/Pl_Count.hh> |
| 10 | #include <qpdf/Pl_Discard.hh> | 9 | #include <qpdf/Pl_Discard.hh> |
| @@ -45,6 +44,12 @@ class QPDF::Doc::Streams | @@ -45,6 +44,12 @@ class QPDF::Doc::Streams | ||
| 45 | return qpdf->pipeStreamData( | 44 | return qpdf->pipeStreamData( |
| 46 | og, offset, length, dict, is_root_metadata, pipeline, suppress_warnings, will_retry); | 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 | namespace | 55 | namespace |
| @@ -207,6 +212,15 @@ Stream::Stream( | @@ -207,6 +212,15 @@ Stream::Stream( | ||
| 207 | setDictDescription(); | 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 | void | 224 | void |
| 211 | Stream::registerStreamFilter( | 225 | Stream::registerStreamFilter( |
| 212 | std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory) | 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,12 +364,11 @@ Stream::getStreamData(qpdf_stream_decode_level_e decode_level) | ||
| 350 | if (!filtered) { | 364 | if (!filtered) { |
| 351 | throw QPDFExc( | 365 | throw QPDFExc( |
| 352 | qpdf_e_unsupported, | 366 | qpdf_e_unsupported, |
| 353 | - obj->getQPDF()->getFilename(), | 367 | + qpdf()->getFilename(), |
| 354 | "", | 368 | "", |
| 355 | - obj->getParsedOffset(), | 369 | + offset(), |
| 356 | "getStreamData called on unfilterable stream"); | 370 | "getStreamData called on unfilterable stream"); |
| 357 | } | 371 | } |
| 358 | - QTC::TC("qpdf", "QPDF_Stream getStreamData"); | ||
| 359 | return result; | 372 | return result; |
| 360 | } | 373 | } |
| 361 | 374 | ||
| @@ -367,23 +380,21 @@ Stream::getRawStreamData() | @@ -367,23 +380,21 @@ Stream::getRawStreamData() | ||
| 367 | if (!pipeStreamData(&buf, nullptr, 0, qpdf_dl_none, false, false)) { | 380 | if (!pipeStreamData(&buf, nullptr, 0, qpdf_dl_none, false, false)) { |
| 368 | throw QPDFExc( | 381 | throw QPDFExc( |
| 369 | qpdf_e_unsupported, | 382 | qpdf_e_unsupported, |
| 370 | - obj->getQPDF()->getFilename(), | 383 | + qpdf()->getFilename(), |
| 371 | "", | 384 | "", |
| 372 | - obj->getParsedOffset(), | 385 | + offset(), |
| 373 | "error getting raw stream data"); | 386 | "error getting raw stream data"); |
| 374 | } | 387 | } |
| 375 | - QTC::TC("qpdf", "QPDF_Stream getRawStreamData"); | ||
| 376 | return result; | 388 | return result; |
| 377 | } | 389 | } |
| 378 | 390 | ||
| 379 | bool | 391 | bool |
| 380 | Stream::isRootMetadata() const | 392 | Stream::isRootMetadata() const |
| 381 | { | 393 | { |
| 382 | - if (!getDict().isDictionaryOfType("/Metadata", "/XML")) { | 394 | + if (!stream()->stream_dict.isDictionaryOfType("/Metadata", "/XML")) { |
| 383 | return false; | 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 | bool | 400 | bool |
| @@ -579,15 +590,13 @@ Stream::pipeStreamData( | @@ -579,15 +590,13 @@ Stream::pipeStreamData( | ||
| 579 | s->stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(actual_length)); | 590 | s->stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(actual_length)); |
| 580 | } | 591 | } |
| 581 | } else { | 592 | } else { |
| 582 | - if (obj->getParsedOffset() == 0) { | ||
| 583 | - QTC::TC("qpdf", "QPDF_Stream pipe no stream data"); | 593 | + if (offset() == 0) { |
| 584 | throw std::logic_error("pipeStreamData called for stream with no data"); | 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 | if (!QPDF::Doc::Streams::pipeStreamData( | 596 | if (!QPDF::Doc::Streams::pipeStreamData( |
| 588 | - obj->getQPDF(), | ||
| 589 | - obj->getObjGen(), | ||
| 590 | - obj->getParsedOffset(), | 597 | + qpdf(), |
| 598 | + id_gen(), | ||
| 599 | + offset(), | ||
| 591 | s->length, | 600 | s->length, |
| 592 | s->stream_dict, | 601 | s->stream_dict, |
| 593 | isRootMetadata(), | 602 | isRootMetadata(), |
| @@ -619,6 +628,16 @@ Stream::pipeStreamData( | @@ -619,6 +628,16 @@ Stream::pipeStreamData( | ||
| 619 | 628 | ||
| 620 | void | 629 | void |
| 621 | Stream::replaceStreamData( | 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 | std::shared_ptr<Buffer> data, | 641 | std::shared_ptr<Buffer> data, |
| 623 | QPDFObjectHandle const& filter, | 642 | QPDFObjectHandle const& filter, |
| 624 | QPDFObjectHandle const& decode_parms) | 643 | QPDFObjectHandle const& decode_parms) |
| @@ -626,7 +645,7 @@ Stream::replaceStreamData( | @@ -626,7 +645,7 @@ Stream::replaceStreamData( | ||
| 626 | auto s = stream(); | 645 | auto s = stream(); |
| 627 | s->stream_data = data; | 646 | s->stream_data = data; |
| 628 | s->stream_provider = nullptr; | 647 | s->stream_provider = nullptr; |
| 629 | - replaceFilterData(filter, decode_parms, data->getSize()); | 648 | + replaceFilterData(filter, decode_parms, data->size()); |
| 630 | } | 649 | } |
| 631 | 650 | ||
| 632 | void | 651 | void |
| @@ -664,7 +683,7 @@ Stream::replaceFilterData( | @@ -664,7 +683,7 @@ Stream::replaceFilterData( | ||
| 664 | void | 683 | void |
| 665 | Stream::warn(std::string const& message) | 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 | QPDFObjectHandle | 689 | QPDFObjectHandle |
| @@ -772,12 +791,8 @@ void | @@ -772,12 +791,8 @@ void | ||
| 772 | QPDFObjectHandle::replaceStreamData( | 791 | QPDFObjectHandle::replaceStreamData( |
| 773 | std::string const& data, QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms) | 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 | void | 798 | void |
| @@ -856,3 +871,9 @@ QPDFObjectHandle::getStreamJSON( | @@ -856,3 +871,9 @@ QPDFObjectHandle::getStreamJSON( | ||
| 856 | { | 871 | { |
| 857 | return as_stream(error).getStreamJSON(json_version, json_data, decode_level, p, data_filename); | 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,8 +424,7 @@ QPDF::JSONReactor::replaceObject(QPDFObjectHandle&& replacement, JSON const& val | ||
| 424 | auto og = tos.object.getObjGen(); | 424 | auto og = tos.object.getObjGen(); |
| 425 | if (replacement.isIndirect() && !(replacement.isStream() && replacement.getObjGen() == og)) { | 425 | if (replacement.isIndirect() && !(replacement.isStream() && replacement.getObjGen() == og)) { |
| 426 | error( | 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 | return; | 428 | return; |
| 430 | } | 429 | } |
| 431 | pdf.replaceObject(og, replacement); | 430 | pdf.replaceObject(og, replacement); |
| @@ -885,7 +884,7 @@ QPDF::writeJSON( | @@ -885,7 +884,7 @@ QPDF::writeJSON( | ||
| 885 | } else { | 884 | } else { |
| 886 | jw << "\n },\n \"" << key; | 885 | jw << "\n },\n \"" << key; |
| 887 | } | 886 | } |
| 888 | - if (auto stream = obj.as_stream()) { | 887 | + if (Stream stream = obj) { |
| 889 | jw << "\": {\n \"stream\": "; | 888 | jw << "\": {\n \"stream\": "; |
| 890 | if (json_stream_data == qpdf_sj_file) { | 889 | if (json_stream_data == qpdf_sj_file) { |
| 891 | writeJSONStreamFile( | 890 | writeJSONStreamFile( |
libqpdf/QPDF_objects.cc
| @@ -1634,7 +1634,7 @@ Objects::resolveObjectsInStream(int obj_stream_number) | @@ -1634,7 +1634,7 @@ Objects::resolveObjectsInStream(int obj_stream_number) | ||
| 1634 | } | 1634 | } |
| 1635 | m->resolved_object_streams.insert(obj_stream_number); | 1635 | m->resolved_object_streams.insert(obj_stream_number); |
| 1636 | // Force resolution of object stream | 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 | if (!obj_stream) { | 1638 | if (!obj_stream) { |
| 1639 | throw qpdf.damagedPDF( | 1639 | throw qpdf.damagedPDF( |
| 1640 | "object " + std::to_string(obj_stream_number) + " 0", | 1640 | "object " + std::to_string(obj_stream_number) + " 0", |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| @@ -445,6 +445,23 @@ namespace qpdf | @@ -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 | Stream( | 465 | Stream( |
| 449 | QPDF& qpdf, | 466 | QPDF& qpdf, |
| 450 | QPDFObjGen og, | 467 | QPDFObjGen og, |
| @@ -452,10 +469,12 @@ namespace qpdf | @@ -452,10 +469,12 @@ namespace qpdf | ||
| 452 | qpdf_offset_t offset, | 469 | qpdf_offset_t offset, |
| 453 | size_t length); | 470 | size_t length); |
| 454 | 471 | ||
| 455 | - QPDFObjectHandle | 472 | + Stream copy() const; |
| 473 | + | ||
| 474 | + Dictionary | ||
| 456 | getDict() const | 475 | getDict() const |
| 457 | { | 476 | { |
| 458 | - return stream()->stream_dict; | 477 | + return {stream()->stream_dict}; |
| 459 | } | 478 | } |
| 460 | bool | 479 | bool |
| 461 | isDataModified() const | 480 | isDataModified() const |
| @@ -501,6 +520,10 @@ namespace qpdf | @@ -501,6 +520,10 @@ namespace qpdf | ||
| 501 | std::string getStreamData(qpdf_stream_decode_level_e level); | 520 | std::string getStreamData(qpdf_stream_decode_level_e level); |
| 502 | std::string getRawStreamData(); | 521 | std::string getRawStreamData(); |
| 503 | void replaceStreamData( | 522 | void replaceStreamData( |
| 523 | + std::string&& data, | ||
| 524 | + QPDFObjectHandle const& filter, | ||
| 525 | + QPDFObjectHandle const& decode_parms); | ||
| 526 | + void replaceStreamData( | ||
| 504 | std::shared_ptr<Buffer> data, | 527 | std::shared_ptr<Buffer> data, |
| 505 | QPDFObjectHandle const& filter, | 528 | QPDFObjectHandle const& filter, |
| 506 | QPDFObjectHandle const& decode_parms); | 529 | QPDFObjectHandle const& decode_parms); |
| @@ -657,6 +680,12 @@ namespace qpdf | @@ -657,6 +680,12 @@ namespace qpdf | ||
| 657 | return !obj || type_code() == ::ot_null; | 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 | inline QPDF* | 689 | inline QPDF* |
| 661 | BaseHandle::qpdf() const | 690 | BaseHandle::qpdf() const |
| 662 | { | 691 | { |
libqpdf/qpdf/QPDFObject_private.hh
| @@ -438,11 +438,6 @@ class QPDFObject | @@ -438,11 +438,6 @@ class QPDFObject | ||
| 438 | parsed_offset = offset; | 438 | parsed_offset = offset; |
| 439 | } | 439 | } |
| 440 | } | 440 | } |
| 441 | - qpdf_offset_t | ||
| 442 | - getParsedOffset() | ||
| 443 | - { | ||
| 444 | - return parsed_offset; | ||
| 445 | - } | ||
| 446 | QPDF* | 441 | QPDF* |
| 447 | getQPDF() | 442 | getQPDF() |
| 448 | { | 443 | { |
libqpdf/qpdf/QPDF_private.hh
| @@ -13,10 +13,14 @@ | @@ -13,10 +13,14 @@ | ||
| 13 | 13 | ||
| 14 | using namespace qpdf; | 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 | class BitStream; | 25 | class BitStream; |
| 22 | class BitWriter; | 26 | class BitWriter; |
| @@ -40,14 +44,6 @@ class QPDF::ObjCache | @@ -40,14 +44,6 @@ class QPDF::ObjCache | ||
| 40 | qpdf_offset_t end_after_space{0}; | 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 | class QPDF::EncryptionParameters | 47 | class QPDF::EncryptionParameters |
| 52 | { | 48 | { |
| 53 | friend class QPDF; | 49 | friend class QPDF; |
| @@ -98,14 +94,7 @@ class QPDF::ForeignStreamData | @@ -98,14 +94,7 @@ class QPDF::ForeignStreamData | ||
| 98 | friend class QPDF; | 94 | friend class QPDF; |
| 99 | 95 | ||
| 100 | public: | 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 | private: | 99 | private: |
| 111 | std::shared_ptr<EncryptionParameters> encp; | 100 | std::shared_ptr<EncryptionParameters> encp; |
| @@ -117,20 +106,29 @@ class QPDF::ForeignStreamData | @@ -117,20 +106,29 @@ class QPDF::ForeignStreamData | ||
| 117 | bool is_root_metadata{false}; | 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 | public: | 111 | public: |
| 123 | CopiedStreamDataProvider(QPDF& destination_qpdf); | 112 | CopiedStreamDataProvider(QPDF& destination_qpdf); |
| 124 | - ~CopiedStreamDataProvider() override = default; | 113 | + ~CopiedStreamDataProvider() final = default; |
| 125 | bool provideStreamData( | 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 | private: | 128 | private: |
| 131 | QPDF& destination_qpdf; | 129 | QPDF& destination_qpdf; |
| 132 | std::map<QPDFObjGen, QPDFObjectHandle> foreign_streams; | 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 | class QPDF::StringDecrypter final: public QPDFObjectHandle::StringDecrypter | 134 | class QPDF::StringDecrypter final: public QPDFObjectHandle::StringDecrypter |
| @@ -572,6 +570,57 @@ class QPDF::Doc | @@ -572,6 +570,57 @@ class QPDF::Doc | ||
| 572 | class Objects | 570 | class Objects |
| 573 | { | 571 | { |
| 574 | public: | 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 | Objects() = delete; | 624 | Objects() = delete; |
| 576 | Objects(Objects const&) = delete; | 625 | Objects(Objects const&) = delete; |
| 577 | Objects(Objects&&) = delete; | 626 | Objects(Objects&&) = delete; |
| @@ -581,10 +630,17 @@ class QPDF::Doc | @@ -581,10 +630,17 @@ class QPDF::Doc | ||
| 581 | 630 | ||
| 582 | Objects(QPDF& qpdf, QPDF::Members* m) : | 631 | Objects(QPDF& qpdf, QPDF::Members* m) : |
| 583 | qpdf(qpdf), | 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 | void parse(char const* password); | 644 | void parse(char const* password); |
| 589 | std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og); | 645 | std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og); |
| 590 | void inParse(bool); | 646 | void inParse(bool); |
| @@ -607,6 +663,7 @@ class QPDF::Doc | @@ -607,6 +663,7 @@ class QPDF::Doc | ||
| 607 | 663 | ||
| 608 | // For QPDFWriter: | 664 | // For QPDFWriter: |
| 609 | 665 | ||
| 666 | + std::map<QPDFObjGen, QPDFXRefEntry> const& getXRefTableInternal(); | ||
| 610 | // Get a list of objects that would be permitted in an object stream. | 667 | // Get a list of objects that would be permitted in an object stream. |
| 611 | template <typename T> | 668 | template <typename T> |
| 612 | std::vector<T> getCompressibleObjGens(); | 669 | std::vector<T> getCompressibleObjGens(); |
| @@ -656,9 +713,10 @@ class QPDF::Doc | @@ -656,9 +713,10 @@ class QPDF::Doc | ||
| 656 | bool isUnresolved(QPDFObjGen og); | 713 | bool isUnresolved(QPDFObjGen og); |
| 657 | void setLastObjectDescription(std::string const& description, QPDFObjGen og); | 714 | void setLastObjectDescription(std::string const& description, QPDFObjGen og); |
| 658 | 715 | ||
| 659 | - private: | ||
| 660 | QPDF& qpdf; | 716 | QPDF& qpdf; |
| 661 | QPDF::Members* m; | 717 | QPDF::Members* m; |
| 718 | + | ||
| 719 | + Foreign foreign_; | ||
| 662 | }; // class QPDF::Doc::Objects | 720 | }; // class QPDF::Doc::Objects |
| 663 | 721 | ||
| 664 | // This class is used to represent a PDF Pages tree. | 722 | // This class is used to represent a PDF Pages tree. |
| @@ -699,19 +757,6 @@ class QPDF::Doc | @@ -699,19 +757,6 @@ class QPDF::Doc | ||
| 699 | QPDF::Members* m; | 757 | QPDF::Members* m; |
| 700 | }; // class QPDF::Doc::Pages | 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 | Doc() = delete; | 760 | Doc() = delete; |
| 716 | Doc(Doc const&) = delete; | 761 | Doc(Doc const&) = delete; |
| 717 | Doc(Doc&&) = delete; | 762 | Doc(Doc&&) = delete; |
| @@ -853,10 +898,7 @@ class QPDF::Members | @@ -853,10 +898,7 @@ class QPDF::Members | ||
| 853 | bool ever_pushed_inherited_attributes_to_pages{false}; | 898 | bool ever_pushed_inherited_attributes_to_pages{false}; |
| 854 | bool ever_called_get_all_pages{false}; | 899 | bool ever_called_get_all_pages{false}; |
| 855 | std::vector<QPDFExc> warnings; | 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 | bool reconstructed_xref{false}; | 902 | bool reconstructed_xref{false}; |
| 861 | bool in_read_xref_stream{false}; | 903 | bool in_read_xref_stream{false}; |
| 862 | bool fixed_dangling_refs{false}; | 904 | bool fixed_dangling_refs{false}; |
qpdf/qpdf.testcov
| @@ -98,16 +98,12 @@ qpdf-c called qpdf_set_r4_encryption_parameters_insecure 0 | @@ -98,16 +98,12 @@ qpdf-c called qpdf_set_r4_encryption_parameters_insecure 0 | ||
| 98 | qpdf-c called qpdf_set_static_aes_IV 0 | 98 | qpdf-c called qpdf_set_static_aes_IV 0 |
| 99 | qpdf-c called qpdf_has_error 0 | 99 | qpdf-c called qpdf_has_error 0 |
| 100 | qpdf-c called qpdf_get_qpdf_version 0 | 100 | qpdf-c called qpdf_get_qpdf_version 0 |
| 101 | -QPDF_Stream pipe original stream data 0 | ||
| 102 | QPDF_Stream pipe replaced stream data 0 | 101 | QPDF_Stream pipe replaced stream data 0 |
| 103 | QPDF_Stream provider length mismatch 0 | 102 | QPDF_Stream provider length mismatch 0 |
| 104 | QPDFObjectHandle newStream 0 | 103 | QPDFObjectHandle newStream 0 |
| 105 | QPDFObjectHandle newStream with data 0 | 104 | QPDFObjectHandle newStream with data 0 |
| 106 | -QPDF_Stream pipe no stream data 0 | ||
| 107 | QPDFObjectHandle prepend page contents 0 | 105 | QPDFObjectHandle prepend page contents 0 |
| 108 | QPDFObjectHandle append page contents 0 | 106 | QPDFObjectHandle append page contents 0 |
| 109 | -QPDF_Stream getRawStreamData 0 | ||
| 110 | -QPDF_Stream getStreamData 0 | ||
| 111 | qpdf-c called qpdf_read_memory 0 | 107 | qpdf-c called qpdf_read_memory 0 |
| 112 | QPDF stream with CRNL 0 | 108 | QPDF stream with CRNL 0 |
| 113 | QPDFWriter copy encrypt metadata 1 | 109 | QPDFWriter copy encrypt metadata 1 |
| @@ -128,13 +124,6 @@ QPDFObjectHandle newStream with string 0 | @@ -128,13 +124,6 @@ QPDFObjectHandle newStream with string 0 | ||
| 128 | QPDF_Stream provider length not provided 0 | 124 | QPDF_Stream provider length not provided 0 |
| 129 | QPDF_Stream unknown stream length 0 | 125 | QPDF_Stream unknown stream length 0 |
| 130 | QPDF replaceReserved 0 | 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 | QPDFWriter copy use_aes 1 | 127 | QPDFWriter copy use_aes 1 |
| 139 | QPDFParser indirect without context 0 | 128 | QPDFParser indirect without context 0 |
| 140 | QPDFObjectHandle trailing data in parse 0 | 129 | QPDFObjectHandle trailing data in parse 0 |
| @@ -256,9 +245,6 @@ QPDFJob image optimize no pipeline 0 | @@ -256,9 +245,6 @@ QPDFJob image optimize no pipeline 0 | ||
| 256 | QPDFJob image optimize no shrink 0 | 245 | QPDFJob image optimize no shrink 0 |
| 257 | QPDFJob image optimize too small 0 | 246 | QPDFJob image optimize too small 0 |
| 258 | QPDF pipe foreign encrypted stream 0 | 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 | QPDFJob copy same page more than once 1 | 248 | QPDFJob copy same page more than once 1 |
| 263 | QPDFPageObjectHelper bad token finding names 0 | 249 | QPDFPageObjectHelper bad token finding names 0 |
| 264 | QPDFJob password mode bytes 0 | 250 | QPDFJob password mode bytes 0 |