From fddbcab0e7cc5978802251696055efa64667f637 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sun, 6 Jan 2019 21:18:36 -0500 Subject: Mostly don't require original QPDF for copyForeignObject (fixes #219) The original QPDF is only required now when the source QPDFObjectHandle is a stream that gets its stream data from a QPDFObjectHandle::StreamDataProvider. --- libqpdf/QPDF.cc | 135 +++++++++++++++++++++++++++++++++++++++++--- libqpdf/QPDF_Stream.cc | 24 ++++++++ libqpdf/qpdf/QPDF_Stream.hh | 6 ++ 3 files changed, 156 insertions(+), 9 deletions(-) (limited to 'libqpdf') diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index f5267dea..c5172fd7 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -18,6 +18,7 @@ #include #include #include +#include std::string QPDF::qpdf_version = "8.2.1"; @@ -39,13 +40,50 @@ static char const* EMPTY_PDF = "110\n" "%%EOF\n"; +QPDF::ForeignStreamData::ForeignStreamData( + PointerHolder encp, + PointerHolder file, + int foreign_objid, + int foreign_generation, + qpdf_offset_t offset, + size_t length, + bool is_attachment_stream, + QPDFObjectHandle local_dict) + : + encp(encp), + file(file), + foreign_objid(foreign_objid), + foreign_generation(foreign_generation), + offset(offset), + length(length), + is_attachment_stream(is_attachment_stream), + local_dict(local_dict) +{ +} + +QPDF::CopiedStreamDataProvider::CopiedStreamDataProvider( + QPDF& destination_qpdf) : + destination_qpdf(destination_qpdf) +{ +} + void QPDF::CopiedStreamDataProvider::provideStreamData( int objid, int generation, Pipeline* pipeline) { - QPDFObjectHandle foreign_stream = - this->foreign_streams[QPDFObjGen(objid, generation)]; - foreign_stream.pipeStreamData(pipeline, 0, qpdf_dl_none); + PointerHolder foreign_data = + this->foreign_stream_data[QPDFObjGen(objid, generation)]; + if (foreign_data.getPointer()) + { + destination_qpdf.pipeForeignStreamData( + foreign_data, pipeline, 0, qpdf_dl_none); + } + else + { + QPDFObjectHandle foreign_stream = + this->foreign_streams[QPDFObjGen(objid, generation)]; + foreign_stream.pipeStreamData(pipeline, 0, qpdf_dl_none); + } } void @@ -55,6 +93,14 @@ QPDF::CopiedStreamDataProvider::registerForeignStream( this->foreign_streams[local_og] = foreign_stream; } +void +QPDF::CopiedStreamDataProvider::registerForeignStream( + QPDFObjGen const& local_og, + PointerHolder foreign_stream) +{ + this->foreign_stream_data[local_og] = foreign_stream; +} + QPDF::StringDecrypter::StringDecrypter(QPDF* qpdf, int objid, int gen) : qpdf(qpdf), objid(objid), @@ -2307,15 +2353,67 @@ QPDF::replaceForeignIndirectObjects( if (this->m->copied_stream_data_provider == 0) { this->m->copied_stream_data_provider = - new CopiedStreamDataProvider(); + new CopiedStreamDataProvider(*this); this->m->copied_streams = this->m->copied_stream_data_provider; } QPDFObjGen local_og(result.getObjGen()); - this->m->copied_stream_data_provider->registerForeignStream( - local_og, foreign); - result.replaceStreamData(this->m->copied_streams, - dict.getKey("/Filter"), - dict.getKey("/DecodeParms")); + // Copy information from the foreign stream so we can pipe its + // data later without keeping the original QPDF object around. + QPDF* foreign_stream_qpdf = foreign.getOwningQPDF(); + if (! foreign_stream_qpdf) + { + throw std::logic_error("unable to retrieve owning qpdf" + " from foreign stream"); + } + QPDF_Stream* stream = + dynamic_cast( + QPDFObjectHandle::ObjAccessor::getObject( + foreign).getPointer()); + if (! stream) + { + throw std::logic_error("unable to retrieve underlying" + " stream object from foreign stream"); + } + PointerHolder stream_buffer = + stream->getStreamDataBuffer(); + PointerHolder stream_provider = + stream->getStreamDataProvider(); + if (stream_buffer.getPointer()) + { +// QTC::TC("qpdf", "QPDF copy foreign stream with buffer"); + result.replaceStreamData(stream_buffer, + dict.getKey("/Filter"), + dict.getKey("/DecodeParms")); + } + else if (stream_provider.getPointer()) + { + // In this case, the remote stream's QPDF must stay in scope. +// QTC::TC("qpdf", "QPDF copy foreign stream with provider"); + this->m->copied_stream_data_provider->registerForeignStream( + local_og, foreign); + result.replaceStreamData(this->m->copied_streams, + dict.getKey("/Filter"), + dict.getKey("/DecodeParms")); + } + else + { + PointerHolder foreign_stream_data = + new ForeignStreamData( + foreign_stream_qpdf->m->encp, + foreign_stream_qpdf->m->file, + foreign.getObjectID(), + foreign.getGeneration(), + stream->getOffset(), + stream->getLength(), + (foreign_stream_qpdf->m->attachment_streams.count( + foreign.getObjGen()) > 0), + dict); + this->m->copied_stream_data_provider->registerForeignStream( + local_og, foreign_stream_data); + result.replaceStreamData(this->m->copied_streams, + dict.getKey("/Filter"), + dict.getKey("/DecodeParms")); + } } else { @@ -2613,6 +2711,25 @@ QPDF::pipeStreamData(int objid, int generation, pipeline, suppress_warnings, will_retry); } +bool +QPDF::pipeForeignStreamData( + PointerHolder foreign, + Pipeline* pipeline, + unsigned long encode_flags, + qpdf_stream_decode_level_e decode_level) +{ + if (foreign->encp->encrypted) + { + QTC::TC("qpdf", "QPDF pipe foreign encrypted stream"); + } + return pipeStreamData( + foreign->encp, foreign->file, *this, + foreign->foreign_objid, foreign->foreign_generation, + foreign->offset, foreign->length, + foreign->local_dict, foreign->is_attachment_stream, + pipeline, false, false); +} + void QPDF::findAttachmentStreams() { diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 0a5f53b4..3733940d 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -133,6 +133,30 @@ QPDF_Stream::isDataModified() const return (! this->token_filters.empty()); } +qpdf_offset_t +QPDF_Stream::getOffset() const +{ + return this->offset; +} + +size_t +QPDF_Stream::getLength() const +{ + return this->length; +} + +PointerHolder +QPDF_Stream::getStreamDataBuffer() const +{ + return this->stream_data; +} + +PointerHolder +QPDF_Stream::getStreamDataProvider() const +{ + return this->stream_provider; +} + PointerHolder QPDF_Stream::getStreamData(qpdf_stream_decode_level_e decode_level) { diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh index b38bed08..647b600d 100644 --- a/libqpdf/qpdf/QPDF_Stream.hh +++ b/libqpdf/qpdf/QPDF_Stream.hh @@ -24,6 +24,12 @@ class QPDF_Stream: public QPDFObject QPDFObjectHandle getDict() const; bool isDataModified() const; + // Methods to help QPDF copy foreign streams + qpdf_offset_t getOffset() const; + size_t getLength() const; + PointerHolder getStreamDataBuffer() const; + PointerHolder getStreamDataProvider() const; + // See comments in QPDFObjectHandle.hh for these methods. bool pipeStreamData(Pipeline*, unsigned long encode_flags, -- cgit v1.2.3-54-g00ecf