diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 4dd52d2..67d6652 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -744,7 +744,6 @@ class QPDF class ObjCache; class EncryptionParameters; class ForeignStreamData; - class CopiedStreamDataProvider; class StringDecrypter; class ResolveRecorder; class JSONReactor; diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 5a7b912..2273b67 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -28,6 +28,8 @@ using namespace qpdf; using namespace std::literals; using Objects = QPDF::Doc::Objects; +using Foreign = Objects::Foreign; +using Streams = Objects::Streams; // This must be a fixed value. This API returns a const reference to it, and the C API relies on its // being static as well. @@ -122,24 +124,24 @@ QPDF::ForeignStreamData::ForeignStreamData( { } -QPDF::CopiedStreamDataProvider::CopiedStreamDataProvider(QPDF& destination_qpdf) : +Streams::Copier::Copier(QPDF& qpdf) : QPDFObjectHandle::StreamDataProvider(true), - destination_qpdf(destination_qpdf) + qpdf(qpdf) { } bool -QPDF::CopiedStreamDataProvider::provideStreamData( +Streams::Copier::provideStreamData( QPDFObjGen const& og, Pipeline* pipeline, bool suppress_warnings, bool will_retry) { - auto foreign_data = foreign_stream_data.find(og); + auto foreign_data = copied_data.find(og); bool result = false; - if (foreign_data != foreign_stream_data.end()) { - result = destination_qpdf.pipeForeignStreamData( + if (foreign_data != copied_data.end()) { + result = qpdf.pipeForeignStreamData( foreign_data->second, pipeline, suppress_warnings, will_retry); QTC::TC("qpdf", "QPDF copy foreign with data", result ? 0 : 1); } else { - auto foreign_stream = foreign_streams[og]; + auto foreign_stream = copied_streams[og]; result = foreign_stream.pipeStreamData( pipeline, nullptr, 0, qpdf_dl_none, suppress_warnings, will_retry); QTC::TC("qpdf", "QPDF copy foreign with foreign_stream", result ? 0 : 1); @@ -669,9 +671,6 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign_oh) Dictionary dict = result.getDict(); Dictionary old_dict = foreign_oh.getDict(); - if (!m->copied_stream_data_provider) { - m->copied_stream_data_provider = std::make_shared(*this); - } QPDFObjGen local_og(result.getObjGen()); // Copy information from the foreign stream so we can pipe its data later without keeping the // original QPDF object around. @@ -697,14 +696,14 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign_oh) result.replaceStreamData(stream_buffer, dict["/Filter"], dict["/DecodeParms"]); } else if (stream_provider) { // In this case, the remote stream's QPDF must stay in scope. - m->copied_stream_data_provider->registerForeignStream(local_og, foreign_oh); + m->objects.streams().copier()->register_copy(local_og, foreign_oh); result.replaceStreamData( - m->copied_stream_data_provider, dict["/Filter"], dict["/DecodeParms"]); + m->objects.streams().copier(), dict["/Filter"], dict["/DecodeParms"]); } else { auto foreign_stream_data = ForeignStreamData(foreign, foreign_oh.offset(), dict); - m->copied_stream_data_provider->registerForeignStream(local_og, foreign_stream_data); + m->objects.streams().copier()->register_copy(local_og, foreign_stream_data); result.replaceStreamData( - m->copied_stream_data_provider, dict["/Filter"], dict["/DecodeParms"]); + m->objects.streams().copier(), dict["/Filter"], dict["/DecodeParms"]); } } diff --git a/libqpdf/qpdf/QPDF_private.hh b/libqpdf/qpdf/QPDF_private.hh index 465deed..6a355c8 100644 --- a/libqpdf/qpdf/QPDF_private.hh +++ b/libqpdf/qpdf/QPDF_private.hh @@ -106,31 +106,6 @@ class QPDF::ForeignStreamData bool is_root_metadata{false}; }; -class QPDF::CopiedStreamDataProvider final: public QPDFObjectHandle::StreamDataProvider -{ - public: - CopiedStreamDataProvider(QPDF& destination_qpdf); - ~CopiedStreamDataProvider() final = default; - bool provideStreamData( - QPDFObjGen const& og, Pipeline* pipeline, bool suppress_warnings, bool will_retry) final; - void - registerForeignStream(QPDFObjGen const& local_og, QPDFObjectHandle foreign_stream) - { - foreign_streams.insert_or_assign(local_og, foreign_stream); - } - - void - registerForeignStream(QPDFObjGen local_og, ForeignStreamData foreign_stream) - { - foreign_stream_data.insert_or_assign(local_og, foreign_stream); - } - - private: - QPDF& destination_qpdf; - std::map foreign_streams; - std::map foreign_stream_data; -}; - class QPDF::StringDecrypter final: public QPDFObjectHandle::StringDecrypter { friend class QPDF; @@ -620,6 +595,57 @@ class QPDF::Doc class Streams { + // Copier manages the copying of streams into this PDF. It is used both for copying + // local and foreign streams. + class Copier final: public QPDFObjectHandle::StreamDataProvider + { + public: + Copier() = delete; + Copier(StreamDataProvider const&) = delete; + Copier(StreamDataProvider&&) = delete; + Copier& operator=(StreamDataProvider const&) = delete; + Copier& operator=(StreamDataProvider&&) = delete; + + Copier(QPDF& qpdf); + ~Copier() final = default; + + bool provideStreamData( + QPDFObjGen const& og, + Pipeline* pipeline, + bool suppress_warnings, + bool will_retry) final; + + void + register_copy(QPDFObjGen local_og, QPDFObjectHandle foreign_stream) + { + copied_streams.insert_or_assign(local_og, foreign_stream); + } + + void + register_copy(QPDFObjGen local_og, ForeignStreamData foreign_stream) + { + copied_data.insert_or_assign(local_og, foreign_stream); + } + + private: + QPDF& qpdf; + std::map copied_streams; + std::map copied_data; + }; + + public: + Streams(QPDF& qpdf) : + qpdf(qpdf) + { + } + + Streams() = delete; + Streams(Streams const&) = delete; + Streams(Streams&&) = delete; + Streams& operator=(Streams const&) = delete; + Streams& operator=(Streams&&) = delete; + ~Streams() = default; + public: static bool pipeStreamData( @@ -649,6 +675,20 @@ class QPDF::Doc { qpdf->copyStreamData(dest, src); } + + std::shared_ptr& + copier() + { + if (!copier_) { + copier_ = std::make_shared(qpdf); + } + return copier_; + } + + private: + QPDF& qpdf; + + std::shared_ptr copier_; }; public: @@ -662,7 +702,8 @@ class QPDF::Doc Objects(QPDF& qpdf, QPDF::Members* m) : qpdf(qpdf), m(m), - foreign_(qpdf) + foreign_(qpdf), + streams_(qpdf) { } @@ -672,6 +713,12 @@ class QPDF::Doc return foreign_; } + Streams& + streams() + { + return streams_; + } + void parse(char const* password); std::shared_ptr const& resolve(QPDFObjGen og); void inParse(bool); @@ -748,6 +795,7 @@ class QPDF::Doc QPDF::Members* m; Foreign foreign_; + Streams streams_; }; // class QPDF::Doc::Objects // This class is used to represent a PDF Pages tree. @@ -929,7 +977,6 @@ class QPDF::Members bool ever_pushed_inherited_attributes_to_pages{false}; bool ever_called_get_all_pages{false}; std::vector warnings; - std::shared_ptr copied_stream_data_provider; bool reconstructed_xref{false}; bool in_read_xref_stream{false}; bool fixed_dangling_refs{false};