summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO16
-rw-r--r--include/qpdf/QPDFObjectHandle.hh38
-rw-r--r--libqpdf/QPDFObjectHandle.cc15
-rw-r--r--libqpdf/QPDF_Stream.cc55
-rw-r--r--libqpdf/qpdf/QPDF_Stream.hh13
-rw-r--r--qpdf/qpdf.testcov2
-rw-r--r--qpdf/qtest/qpdf.test2
-rw-r--r--qpdf/qtest/qpdf/test8.out2
-rw-r--r--qpdf/test_driver.cc53
9 files changed, 167 insertions, 29 deletions
diff --git a/TODO b/TODO
index f20c44c0..20b4652b 100644
--- a/TODO
+++ b/TODO
@@ -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);