diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 7feb602d..83e3592f 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -152,6 +152,9 @@ class QPDFObjectHandle // Remove key, doing nothing if key does not exist QPDF_DLL void removeKey(std::string const& key); + // If the object is null, remove the key. Otherwise, replace it. + QPDF_DLL + void replaceOrRemoveKey(std::string const& key, QPDFObjectHandle); // Methods for stream objects QPDF_DLL @@ -183,6 +186,64 @@ class QPDFObjectHandle bool pipeStreamData(Pipeline*, bool filter, bool normalize, bool compress); + // Replace this stream's stream data with the given data buffer, + // and replace the /Filter and /DecodeParms keys in the stream + // dictionary with the given values. (If either value is empty, + // the corresponding key is removed.) The stream's /Length key is + // replaced with the length of the data buffer. The stream is + // interpreted as if the data read from the file, after any + // decryption filters have been applied, is as presented. + QPDF_DLL + void replaceStreamData(PointerHolder data, + QPDFObjectHandle filter, + QPDFObjectHandle decode_parms); + class StreamDataHandler + { + public: + QPDF_DLL + virtual ~StreamDataHandler() + { + } + + // See replaceStreamData(StreamDataHandler) below for a + // description of how to override this function. + virtual void + replaceStreamData(Buffer const& in_data, + std::string const& in_filter, + std::string const& in_decode_parms, + bool filtered, + Buffer& out_data, + std::string& out_filter, + std::string& out_decode_parms, + bool& persist) = 0; + }; + // Provide a hook for doing dynamic replacement of the stream's + // data. When the stream's data is accessed either with + // pipeStreamData or with getStreamData, if the stream doesn't + // already have replacement data, an attempt is first made to + // filter the stream's original data. If the attempt is + // successful, the stream's filtered original data is passed to + // the handler as in_data, and filtered is true. If the original + // data cannot be processed, then in_data is the original raw data + // (after any decryption filters have been applied) and filtered + // is false. If the original input data has no filters applied, + // the filtered is true. This way, if filtered is true, the + // caller knows that in_data contains the fully filtered data. + // The handler then provides replacement data, /Filter, and + // /DecodeParms (handled is in the simpler form of + // replaceStreamData above). If the persist argument is set to + // true, then the replacement data is stored in the stream object + // where it will be used on subsequent attempts to retrieve the + // data (rather than calling the handler). If persist is set to + // false, then the data will be used that one time and not saved. + // In that case, the handler will be invoked again if the stream + // data is accessed another time. Writing a handler that sets + // persist to true essentially allows delaying the computation of + // the stream data, while setting it to false reduces the amount + // of memory that is used. + QPDF_DLL + void replaceStreamData(PointerHolder dh); + // return 0 for direct objects QPDF_DLL int getObjectID() const; diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 677c2347..2fff0e31 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -319,6 +319,15 @@ QPDFObjectHandle::removeKey(std::string const& key) return dynamic_cast(obj.getPointer())->removeKey(key); } +void +QPDFObjectHandle::replaceOrRemoveKey(std::string const& key, + QPDFObjectHandle value) +{ + assertType("Dictionary", isDictionary()); + return dynamic_cast( + obj.getPointer())->replaceOrRemoveKey(key, value); +} + // Stream accessors QPDFObjectHandle QPDFObjectHandle::getDict() @@ -343,6 +352,23 @@ QPDFObjectHandle::pipeStreamData(Pipeline* p, bool filter, p, filter, normalize, compress); } +void +QPDFObjectHandle::replaceStreamData(PointerHolder data, + QPDFObjectHandle filter, + QPDFObjectHandle decode_parms) +{ + assertType("Stream", isStream()); + dynamic_cast(obj.getPointer())->replaceStreamData( + data, filter, decode_parms); +} + +void +QPDFObjectHandle::replaceStreamData(PointerHolder dh) +{ + assertType("Stream", isStream()); + dynamic_cast(obj.getPointer())->replaceStreamData(dh); +} + int QPDFObjectHandle::getObjectID() const { diff --git a/libqpdf/QPDF_Dictionary.cc b/libqpdf/QPDF_Dictionary.cc index ccaab4a8..838a37e6 100644 --- a/libqpdf/QPDF_Dictionary.cc +++ b/libqpdf/QPDF_Dictionary.cc @@ -92,3 +92,17 @@ QPDF_Dictionary::removeKey(std::string const& key) // no-op if key does not exist this->items.erase(key); } + +void +QPDF_Dictionary::replaceOrRemoveKey(std::string const& key, + QPDFObjectHandle value) +{ + if (value.isNull()) + { + removeKey(key); + } + else + { + replaceKey(key, value); + } +} diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index d0dd2e5f..500dfb10 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -319,9 +319,30 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool filter, } } + // XXX handle stream_data and stream_data_handler QPDF::Pipe::pipeStreamData(this->qpdf, this->objid, this->generation, this->offset, this->length, this->stream_dict, pipeline); return filter; } + +void +QPDF_Stream::replaceStreamData(PointerHolder data, + QPDFObjectHandle filter, + QPDFObjectHandle decode_parms) +{ + this->stream_data = data; + this->stream_dict.replaceOrRemoveKey("/Filter", filter); + this->stream_dict.replaceOrRemoveKey("/DecodeParms", decode_parms); + this->stream_dict.replaceKey("/Length", + QPDFObjectHandle::newInteger( + data.getPointer()->getSize())); +} + +void +QPDF_Stream::replaceStreamData( + PointerHolder dh) +{ + this->stream_data_handler = dh; +} diff --git a/libqpdf/qpdf/QPDF_Dictionary.hh b/libqpdf/qpdf/QPDF_Dictionary.hh index e75de01b..e84b549e 100644 --- a/libqpdf/qpdf/QPDF_Dictionary.hh +++ b/libqpdf/qpdf/QPDF_Dictionary.hh @@ -26,6 +26,8 @@ class QPDF_Dictionary: public QPDFObject void replaceKey(std::string const& key, QPDFObjectHandle const&); // Remove key, doing nothing if key does not exist void removeKey(std::string const& key); + // If object is null, replace key; otherwise, remove key + void replaceOrRemoveKey(std::string const& key, QPDFObjectHandle); protected: virtual void releaseResolved(); diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh index c969255d..cc7cbf0a 100644 --- a/libqpdf/qpdf/QPDF_Stream.hh +++ b/libqpdf/qpdf/QPDF_Stream.hh @@ -18,24 +18,28 @@ class QPDF_Stream: public QPDFObject virtual std::string unparse(); QPDFObjectHandle getDict() const; - // See comments in QPDFObjectHandle.hh + // See comments in QPDFObjectHandle.hh for these methods. bool pipeStreamData(Pipeline*, bool filter, bool normalize, bool compress); - - // See comments in QPDFObjectHandle.hh PointerHolder getStreamData(); + void replaceStreamData(PointerHolder data, + QPDFObjectHandle filter, + QPDFObjectHandle decode_parms); + void replaceStreamData( + PointerHolder dh); private: bool filterable(std::vector& filters, int& predictor, int& columns, bool& early_code_change); - QPDF* qpdf; int objid; int generation; QPDFObjectHandle stream_dict; off_t offset; int length; + PointerHolder stream_data_handler; + PointerHolder stream_data; }; #endif // __QPDF_STREAM_HH__