diff options
author | Jay Berkenbilt <ejb@ql.org> | 2010-08-05 21:04:22 +0200 |
---|---|---|
committer | Jay Berkenbilt <ejb@ql.org> | 2010-08-05 21:04:22 +0200 |
commit | 11df7809af7131af139be2e76f2db87128700939 (patch) | |
tree | de549eec5d64837384f60d319ce072d1455baaa8 | |
parent | 98765c3b5ceecb2c2540405eab1dd7ae1f02ec31 (diff) | |
download | qpdf-11df7809af7131af139be2e76f2db87128700939.tar.zst |
add pipeline-based stream data replacement function
git-svn-id: svn+q:///qpdf/trunk@990 71b93d88-0707-0410-a8cf-f5a4172ac649
-rw-r--r-- | TODO | 16 | ||||
-rw-r--r-- | include/qpdf/QPDFObjectHandle.hh | 38 | ||||
-rw-r--r-- | libqpdf/QPDFObjectHandle.cc | 15 | ||||
-rw-r--r-- | libqpdf/QPDF_Stream.cc | 55 | ||||
-rw-r--r-- | libqpdf/qpdf/QPDF_Stream.hh | 13 | ||||
-rw-r--r-- | qpdf/qpdf.testcov | 2 | ||||
-rw-r--r-- | qpdf/qtest/qpdf.test | 2 | ||||
-rw-r--r-- | qpdf/qtest/qpdf/test8.out | 2 | ||||
-rw-r--r-- | qpdf/test_driver.cc | 53 |
9 files changed, 167 insertions, 29 deletions
@@ -12,22 +12,6 @@ Next Stefan Heinsen <stefan.heinsen@gmx.de> in August, 2009. He seems to like to send encrypted mail. (key 01FCC336) - It appears that the only thing in the code that actually has to - change is the QPDF_Stream object. When replacing stream data, we - have to mutate the stream's dictionary to adjust /Filter, - /DecodeParms, and /Length. We should probably just provide a - method to replace the stream data, /Filter, and /DecodeParms all at - once. If new values are provided, then pipeStreamData can use the - new values, and we essentially then lose the original values. The - code for replacing stream data would be to use getStreamData to get - the old data and then to replace it all before any calls that would - cause QPDFWriter to write new stream data. Will have to go through - QPDF_Stream.cc carefully line by line to make sure everything is - adjusted properly. - - Don't forget to provide a method that provides a pipeline through - which the stream data is to be piped. - * Add helper routines for manipulating page content streams. Operations should include ability to convert page contents from a stream to an array of streams and to append or prepend to the page diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index a7b8b991..e8d24d40 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -195,8 +195,42 @@ class QPDFObjectHandle // decryption filters have been applied, is as presented. QPDF_DLL void replaceStreamData(PointerHolder<Buffer> data, - QPDFObjectHandle filter, - QPDFObjectHandle decode_parms); + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms); + + class StreamDataProvider + { + public: + QPDF_DLL + virtual ~StreamDataProvider() + { + } + // See replaceStreamData below for details on how to override + // this method. + virtual void provideStreamData(int objid, int generation, + Pipeline* pipeline) = 0; + }; + // As above, replace this stream's stream data. Instead of + // directly providing a buffer with the stream data, call the + // given provider's provideStreamData method. The method is to + // write the unencrypted, raw stream data to the provided + // pipeline. The stream's /Length key will be set to the length + // as provided. This must match the number of bytes written to + // the pipeline. The provider must write exactly the same data to + // the pipeline every time it is called. The method is invoked + // with the object ID and generation number, which are just there + // to be available to the handler in case it is useful for + // indexing purposes. This makes it easier to reuse the same + // StreamDataProvider object for multiple streams. Although it is + // more complex to use this form of replaceStreamData, it makes it + // possible to avoid allocating memory for the stream data. + // Example programs are provided that use both forms of + // replaceStreamData. + QPDF_DLL + void replaceStreamData(PointerHolder<StreamDataProvider> provider, + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms, + size_t length); // return 0 for direct objects QPDF_DLL diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 756f3f55..649ce3f0 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -354,14 +354,25 @@ QPDFObjectHandle::pipeStreamData(Pipeline* p, bool filter, void QPDFObjectHandle::replaceStreamData(PointerHolder<Buffer> data, - QPDFObjectHandle filter, - QPDFObjectHandle decode_parms) + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms) { assertType("Stream", isStream()); dynamic_cast<QPDF_Stream*>(obj.getPointer())->replaceStreamData( data, filter, decode_parms); } +void +QPDFObjectHandle::replaceStreamData(PointerHolder<StreamDataProvider> provider, + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms, + size_t length) +{ + assertType("Stream", isStream()); + dynamic_cast<QPDF_Stream*>(obj.getPointer())->replaceStreamData( + provider, filter, decode_parms, length); +} + int QPDFObjectHandle::getObjectID() const { diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 5e53b2ae..1f48b2c1 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -9,6 +9,7 @@ #include <qpdf/Pl_ASCII85Decoder.hh> #include <qpdf/Pl_ASCIIHexDecoder.hh> #include <qpdf/Pl_LZWDecoder.hh> +#include <qpdf/Pl_Count.hh> #include <qpdf/QTC.hh> #include <qpdf/QPDF.hh> @@ -326,6 +327,32 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool filter, pipeline->write(b.getBuffer(), b.getSize()); pipeline->finish(); } + else if (this->stream_provider.getPointer()) + { + QPDFObjectHandle::StreamDataProvider& p = + (*this->stream_provider.getPointer()); + Pl_Count count("stream provider count", pipeline); + p.provideStreamData(this->objid, this->generation, &count); + size_t actual_length = count.getCount(); + size_t desired_length = + this->stream_dict.getKey("/Length").getIntValue(); + if (actual_length == desired_length) + { + QTC::TC("qpdf", "QPDF_Stream pipe use stream provider"); + } + else + { + QTC::TC("qpdf", "QPDF_Stream provider length mismatch"); + throw std::logic_error( + "stream data provider for " + + QUtil::int_to_string(this->objid) + " " + + QUtil::int_to_string(this->generation) + + " provided " + + QUtil::int_to_string(actual_length) + + " bytes instead of expected " + + QUtil::int_to_string(desired_length) + " bytes"); + } + } else { QTC::TC("qpdf", "QPDF_Stream pipe original stream data"); @@ -339,13 +366,33 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool filter, void QPDF_Stream::replaceStreamData(PointerHolder<Buffer> data, - QPDFObjectHandle filter, - QPDFObjectHandle decode_parms) + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms) { this->stream_data = data; + this->stream_provider = 0; + replaceFilterData(filter, decode_parms, data.getPointer()->getSize()); +} + +void +QPDF_Stream::replaceStreamData( + PointerHolder<QPDFObjectHandle::StreamDataProvider> provider, + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms, + size_t length) +{ + this->stream_provider = provider; + this->stream_data = 0; + replaceFilterData(filter, decode_parms, length); +} + +void +QPDF_Stream::replaceFilterData(QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms, + size_t length) +{ this->stream_dict.replaceOrRemoveKey("/Filter", filter); this->stream_dict.replaceOrRemoveKey("/DecodeParms", decode_parms); this->stream_dict.replaceKey("/Length", - QPDFObjectHandle::newInteger( - data.getPointer()->getSize())); + QPDFObjectHandle::newInteger(length)); } diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh index 86a585c9..1790121e 100644 --- a/libqpdf/qpdf/QPDF_Stream.hh +++ b/libqpdf/qpdf/QPDF_Stream.hh @@ -23,10 +23,18 @@ class QPDF_Stream: public QPDFObject bool normalize, bool compress); PointerHolder<Buffer> getStreamData(); void replaceStreamData(PointerHolder<Buffer> data, - QPDFObjectHandle filter, - QPDFObjectHandle decode_parms); + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms); + void replaceStreamData( + PointerHolder<QPDFObjectHandle::StreamDataProvider> provider, + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms, + size_t length); private: + void replaceFilterData(QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms, + size_t length); bool filterable(std::vector<std::string>& filters, int& predictor, int& columns, bool& early_code_change); @@ -37,6 +45,7 @@ class QPDF_Stream: public QPDFObject off_t offset; int length; PointerHolder<Buffer> stream_data; + PointerHolder<QPDFObjectHandle::StreamDataProvider> stream_provider; }; #endif // __QPDF_STREAM_HH__ diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index c90fcba4..c880188e 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -176,3 +176,5 @@ qpdf-c called qpdf_has_error 0 qpdf-c called qpdf_get_qpdf_version 0 QPDF_Stream pipe original stream data 0 QPDF_Stream pipe replaced stream data 0 +QPDF_Stream pipe use stream provider 0 +QPDF_Stream provider length mismatch 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 69c4776e..6768ebd4 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -107,7 +107,7 @@ $td->runtest("check output", $td->runtest("replace stream data compressed", {$td->COMMAND => "test_driver 8 qstream.pdf"}, - {$td->STRING => "test 8 done\n", $td->EXIT_STATUS => 0}, + {$td->FILE => "test8.out", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); $td->runtest("check output", {$td->FILE => "a.pdf"}, diff --git a/qpdf/qtest/qpdf/test8.out b/qpdf/qtest/qpdf/test8.out new file mode 100644 index 00000000..0311b389 --- /dev/null +++ b/qpdf/qtest/qpdf/test8.out @@ -0,0 +1,2 @@ +exception: stream data provider for 7 0 provided 29 bytes instead of expected 28 bytes +test 8 done diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index db6a0619..e9739e92 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -23,6 +23,39 @@ void usage() exit(2); } +class Provider: public QPDFObjectHandle::StreamDataProvider +{ + public: + Provider(PointerHolder<Buffer> b) : + b(b), + bad_length(false) + { + } + virtual ~Provider() + { + } + virtual void provideStreamData(int objid, int generation, + Pipeline* p) + { + p->write(b.getPointer()->getBuffer(), + b.getPointer()->getSize()); + if (this->bad_length) + { + unsigned char ch = ' '; + p->write(&ch, 1); + } + p->finish(); + } + void badLength(bool v) + { + this->bad_length = v; + } + + private: + PointerHolder<Buffer> b; + bool bad_length; +}; + void runtest(int n, char const* filename) { QPDF pdf; @@ -341,9 +374,25 @@ void runtest(int n, char const* filename) p2.write((unsigned char*)"new data for stream\n", 20); // no null! p2.finish(); PointerHolder<Buffer> b = p1.getBuffer(); + // This is a bogus way to use StreamDataProvider, but it does + // adequately test its functionality. + Provider* provider = new Provider(b); + PointerHolder<QPDFObjectHandle::StreamDataProvider> p = provider; qstream.replaceStreamData( - b, QPDFObjectHandle::newName("/FlateDecode"), - QPDFObjectHandle::newNull()); + p, QPDFObjectHandle::newName("/FlateDecode"), + QPDFObjectHandle::newNull(), + b.getPointer()->getSize()); + provider->badLength(true); + try + { + qstream.getStreamData(); + std::cout << "oops -- getStreamData didn't throw" << std::endl; + } + catch (std::logic_error const& e) + { + std::cout << "exception: " << e.what() << std::endl; + } + provider->badLength(false); QPDFWriter w(pdf, "a.pdf"); w.setStaticID(true); w.setStreamDataMode(qpdf_s_preserve); |