Commit 6f2bd7eb3a64ae6ffbdf9ae256d822056ddcb7b0
1 parent
11df7809
newStream
git-svn-id: svn+q:///qpdf/trunk@991 71b93d88-0707-0410-a8cf-f5a4172ac649
Showing
11 changed files
with
290 additions
and
30 deletions
include/qpdf/QPDFObjectHandle.hh
| @@ -28,6 +28,34 @@ class QPDF_Array; | @@ -28,6 +28,34 @@ class QPDF_Array; | ||
| 28 | class QPDFObjectHandle | 28 | class QPDFObjectHandle |
| 29 | { | 29 | { |
| 30 | public: | 30 | public: |
| 31 | + // This class is used by replaceStreamData. It provides an | ||
| 32 | + // alternative way of associating stream data with a stream. See | ||
| 33 | + // comments on replaceStreamData and newStream for additional | ||
| 34 | + // details. | ||
| 35 | + class StreamDataProvider | ||
| 36 | + { | ||
| 37 | + public: | ||
| 38 | + QPDF_DLL | ||
| 39 | + virtual ~StreamDataProvider() | ||
| 40 | + { | ||
| 41 | + } | ||
| 42 | + // The implementation of this function must write the | ||
| 43 | + // unencrypted, raw stream data to the given pipeline. Every | ||
| 44 | + // call to provideStreamData for a given stream must write the | ||
| 45 | + // same data. The number of bytes written must agree with the | ||
| 46 | + // length provided at the time the StreamDataProvider object | ||
| 47 | + // was associated with the stream. The object ID and | ||
| 48 | + // generation passed to this method are those that belong to | ||
| 49 | + // the stream on behalf of which the provider is called. They | ||
| 50 | + // may be ignored or used by the implementation for indexing | ||
| 51 | + // or other purposes. This information is made available just | ||
| 52 | + // to make it more convenient to use a single | ||
| 53 | + // StreamDataProvider object to provide data for multiple | ||
| 54 | + // streams. | ||
| 55 | + virtual void provideStreamData(int objid, int generation, | ||
| 56 | + Pipeline* pipeline) = 0; | ||
| 57 | + }; | ||
| 58 | + | ||
| 31 | QPDF_DLL | 59 | QPDF_DLL |
| 32 | QPDFObjectHandle(); | 60 | QPDFObjectHandle(); |
| 33 | QPDF_DLL | 61 | QPDF_DLL |
| @@ -83,6 +111,30 @@ class QPDFObjectHandle | @@ -83,6 +111,30 @@ class QPDFObjectHandle | ||
| 83 | static QPDFObjectHandle newDictionary( | 111 | static QPDFObjectHandle newDictionary( |
| 84 | std::map<std::string, QPDFObjectHandle> const& items); | 112 | std::map<std::string, QPDFObjectHandle> const& items); |
| 85 | 113 | ||
| 114 | + // Create a new stream and associate it with the given qpdf | ||
| 115 | + // object. A subsequent call must be made to replaceStreamData() | ||
| 116 | + // to provide data for the stream. The stream's dictionary may be | ||
| 117 | + // retrieved by calling getDict(), and the resulting dictionary | ||
| 118 | + // may be modified. | ||
| 119 | + QPDF_DLL | ||
| 120 | + static QPDFObjectHandle newStream(QPDF* qpdf); | ||
| 121 | + | ||
| 122 | + // Create a new stream and associate it with the given qpdf | ||
| 123 | + // object. Use the given buffer as the stream data. The stream | ||
| 124 | + // dictionary's /Length key will automatically be set to the size | ||
| 125 | + // of the data buffer. If additional keys are required, the | ||
| 126 | + // stream's dictionary may be retrieved by calling getDict(), and | ||
| 127 | + // the resulting dictionary may be modified. This method is just | ||
| 128 | + // a convient wrapper around the newStream() and | ||
| 129 | + // replaceStreamData(). It is a convenience methods for streams | ||
| 130 | + // that require no parameters beyond the stream length. Note that | ||
| 131 | + // you don't have to deal with compression yourself if you use | ||
| 132 | + // QPDFWriter. By default, QPDFWriter will automatically compress | ||
| 133 | + // uncompressed stream data. Example programs are provided that | ||
| 134 | + // illustrate this. | ||
| 135 | + QPDF_DLL | ||
| 136 | + static QPDFObjectHandle newStream(QPDF* qpdf, PointerHolder<Buffer> data); | ||
| 137 | + | ||
| 86 | // Accessor methods. If an accessor method that is valid for only | 138 | // Accessor methods. If an accessor method that is valid for only |
| 87 | // a particular object type is called on an object of the wrong | 139 | // a particular object type is called on an object of the wrong |
| 88 | // type, an exception is thrown. | 140 | // type, an exception is thrown. |
| @@ -198,34 +250,17 @@ class QPDFObjectHandle | @@ -198,34 +250,17 @@ class QPDFObjectHandle | ||
| 198 | QPDFObjectHandle const& filter, | 250 | QPDFObjectHandle const& filter, |
| 199 | QPDFObjectHandle const& decode_parms); | 251 | QPDFObjectHandle const& decode_parms); |
| 200 | 252 | ||
| 201 | - class StreamDataProvider | ||
| 202 | - { | ||
| 203 | - public: | ||
| 204 | - QPDF_DLL | ||
| 205 | - virtual ~StreamDataProvider() | ||
| 206 | - { | ||
| 207 | - } | ||
| 208 | - // See replaceStreamData below for details on how to override | ||
| 209 | - // this method. | ||
| 210 | - virtual void provideStreamData(int objid, int generation, | ||
| 211 | - Pipeline* pipeline) = 0; | ||
| 212 | - }; | ||
| 213 | // As above, replace this stream's stream data. Instead of | 253 | // As above, replace this stream's stream data. Instead of |
| 214 | // directly providing a buffer with the stream data, call the | 254 | // directly providing a buffer with the stream data, call the |
| 215 | - // given provider's provideStreamData method. The method is to | ||
| 216 | - // write the unencrypted, raw stream data to the provided | ||
| 217 | - // pipeline. The stream's /Length key will be set to the length | ||
| 218 | - // as provided. This must match the number of bytes written to | ||
| 219 | - // the pipeline. The provider must write exactly the same data to | ||
| 220 | - // the pipeline every time it is called. The method is invoked | ||
| 221 | - // with the object ID and generation number, which are just there | ||
| 222 | - // to be available to the handler in case it is useful for | ||
| 223 | - // indexing purposes. This makes it easier to reuse the same | ||
| 224 | - // StreamDataProvider object for multiple streams. Although it is | ||
| 225 | - // more complex to use this form of replaceStreamData, it makes it | ||
| 226 | - // possible to avoid allocating memory for the stream data. | ||
| 227 | - // Example programs are provided that use both forms of | ||
| 228 | - // replaceStreamData. | 255 | + // given provider's provideStreamData method. See comments on the |
| 256 | + // StreamDataProvider class (defined above) for details on the | ||
| 257 | + // method. The provider must write the number of bytes as | ||
| 258 | + // indicated by the length parameter, and the data must be | ||
| 259 | + // consistent with filter and decode_parms as provided. Although | ||
| 260 | + // it is more complex to use this form of replaceStreamData than | ||
| 261 | + // the one that takes a buffer, it makes it possible to avoid | ||
| 262 | + // allocating memory for the stream data. Example programs are | ||
| 263 | + // provided that use both forms of replaceStreamData. | ||
| 229 | QPDF_DLL | 264 | QPDF_DLL |
| 230 | void replaceStreamData(PointerHolder<StreamDataProvider> provider, | 265 | void replaceStreamData(PointerHolder<StreamDataProvider> provider, |
| 231 | QPDFObjectHandle const& filter, | 266 | QPDFObjectHandle const& filter, |
libqpdf/QPDF.cc
| @@ -1779,7 +1779,11 @@ QPDF::resolveObjectsInStream(int obj_stream_number) | @@ -1779,7 +1779,11 @@ QPDF::resolveObjectsInStream(int obj_stream_number) | ||
| 1779 | QPDFObjectHandle | 1779 | QPDFObjectHandle |
| 1780 | QPDF::makeIndirectObject(QPDFObjectHandle oh) | 1780 | QPDF::makeIndirectObject(QPDFObjectHandle oh) |
| 1781 | { | 1781 | { |
| 1782 | - ObjGen o1 = (*(this->obj_cache.rbegin())).first; | 1782 | + ObjGen o1(0, 0); |
| 1783 | + if (! this->obj_cache.empty()) | ||
| 1784 | + { | ||
| 1785 | + o1 = (*(this->obj_cache.rbegin())).first; | ||
| 1786 | + } | ||
| 1783 | ObjGen o2 = (*(this->xref_table.rbegin())).first; | 1787 | ObjGen o2 = (*(this->xref_table.rbegin())).first; |
| 1784 | QTC::TC("qpdf", "QPDF indirect last obj from xref", | 1788 | QTC::TC("qpdf", "QPDF indirect last obj from xref", |
| 1785 | (o2.obj > o1.obj) ? 1 : 0); | 1789 | (o2.obj > o1.obj) ? 1 : 0); |
libqpdf/QPDFObjectHandle.cc
| @@ -561,6 +561,30 @@ QPDFObjectHandle::newStream(QPDF* qpdf, int objid, int generation, | @@ -561,6 +561,30 @@ QPDFObjectHandle::newStream(QPDF* qpdf, int objid, int generation, | ||
| 561 | stream_dict, offset, length)); | 561 | stream_dict, offset, length)); |
| 562 | } | 562 | } |
| 563 | 563 | ||
| 564 | +QPDFObjectHandle | ||
| 565 | +QPDFObjectHandle::newStream(QPDF* qpdf) | ||
| 566 | +{ | ||
| 567 | + QTC::TC("qpdf", "QPDFObjectHandle newStream"); | ||
| 568 | + std::map<std::string, QPDFObjectHandle> keys; | ||
| 569 | + QPDFObjectHandle stream_dict = newDictionary(keys); | ||
| 570 | + QPDFObjectHandle result = qpdf->makeIndirectObject( | ||
| 571 | + QPDFObjectHandle( | ||
| 572 | + new QPDF_Stream(qpdf, 0, 0, stream_dict, 0, 0))); | ||
| 573 | + result.dereference(); | ||
| 574 | + QPDF_Stream* stream = dynamic_cast<QPDF_Stream*>(result.obj.getPointer()); | ||
| 575 | + stream->setObjGen(result.getObjectID(), result.getGeneration()); | ||
| 576 | + return result; | ||
| 577 | +} | ||
| 578 | + | ||
| 579 | +QPDFObjectHandle | ||
| 580 | +QPDFObjectHandle::newStream(QPDF* qpdf, PointerHolder<Buffer> data) | ||
| 581 | +{ | ||
| 582 | + QTC::TC("qpdf", "QPDFObjectHandle newStream with data"); | ||
| 583 | + QPDFObjectHandle result = newStream(qpdf); | ||
| 584 | + result.replaceStreamData(data, newNull(), newNull()); | ||
| 585 | + return result; | ||
| 586 | +} | ||
| 587 | + | ||
| 564 | void | 588 | void |
| 565 | QPDFObjectHandle::makeDirectInternal(std::set<int>& visited) | 589 | QPDFObjectHandle::makeDirectInternal(std::set<int>& visited) |
| 566 | { | 590 | { |
| @@ -649,7 +673,7 @@ QPDFObjectHandle::makeDirectInternal(std::set<int>& visited) | @@ -649,7 +673,7 @@ QPDFObjectHandle::makeDirectInternal(std::set<int>& visited) | ||
| 649 | } | 673 | } |
| 650 | else | 674 | else |
| 651 | { | 675 | { |
| 652 | - throw std::logic_error("QPDFObjectHandle::makeIndirect: " | 676 | + throw std::logic_error("QPDFObjectHandle::makeDirectInternal: " |
| 653 | "unknown object type"); | 677 | "unknown object type"); |
| 654 | } | 678 | } |
| 655 | 679 |
libqpdf/QPDF_Stream.cc
| @@ -40,6 +40,19 @@ QPDF_Stream::~QPDF_Stream() | @@ -40,6 +40,19 @@ QPDF_Stream::~QPDF_Stream() | ||
| 40 | { | 40 | { |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | +void | ||
| 44 | +QPDF_Stream::setObjGen(int objid, int generation) | ||
| 45 | +{ | ||
| 46 | + if (! ((this->objid == 0) && (this->generation == 0))) | ||
| 47 | + { | ||
| 48 | + throw std::logic_error( | ||
| 49 | + "attempt to set object ID and generation of a stream" | ||
| 50 | + " that already has them"); | ||
| 51 | + } | ||
| 52 | + this->objid = objid; | ||
| 53 | + this->generation = generation; | ||
| 54 | +} | ||
| 55 | + | ||
| 43 | std::string | 56 | std::string |
| 44 | QPDF_Stream::unparse() | 57 | QPDF_Stream::unparse() |
| 45 | { | 58 | { |
| @@ -353,6 +366,12 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool filter, | @@ -353,6 +366,12 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool filter, | ||
| 353 | QUtil::int_to_string(desired_length) + " bytes"); | 366 | QUtil::int_to_string(desired_length) + " bytes"); |
| 354 | } | 367 | } |
| 355 | } | 368 | } |
| 369 | + else if (this->offset == 0) | ||
| 370 | + { | ||
| 371 | + QTC::TC("qpdf", "QPDF_Stream pipe no stream data"); | ||
| 372 | + throw std::logic_error( | ||
| 373 | + "pipeStreamData called for stream with no data"); | ||
| 374 | + } | ||
| 356 | else | 375 | else |
| 357 | { | 376 | { |
| 358 | QTC::TC("qpdf", "QPDF_Stream pipe original stream data"); | 377 | QTC::TC("qpdf", "QPDF_Stream pipe original stream data"); |
libqpdf/qpdf/QPDF_Stream.hh
| @@ -31,6 +31,11 @@ class QPDF_Stream: public QPDFObject | @@ -31,6 +31,11 @@ class QPDF_Stream: public QPDFObject | ||
| 31 | QPDFObjectHandle const& decode_parms, | 31 | QPDFObjectHandle const& decode_parms, |
| 32 | size_t length); | 32 | size_t length); |
| 33 | 33 | ||
| 34 | + // Replace object ID and generation. This may only be called if | ||
| 35 | + // object ID and generation are 0. It is used by QPDFObjectHandle | ||
| 36 | + // when adding streams to files. | ||
| 37 | + void setObjGen(int objid, int generation); | ||
| 38 | + | ||
| 34 | private: | 39 | private: |
| 35 | void replaceFilterData(QPDFObjectHandle const& filter, | 40 | void replaceFilterData(QPDFObjectHandle const& filter, |
| 36 | QPDFObjectHandle const& decode_parms, | 41 | QPDFObjectHandle const& decode_parms, |
qpdf/qpdf.testcov
| @@ -178,3 +178,6 @@ QPDF_Stream pipe original stream data 0 | @@ -178,3 +178,6 @@ QPDF_Stream pipe original stream data 0 | ||
| 178 | QPDF_Stream pipe replaced stream data 0 | 178 | QPDF_Stream pipe replaced stream data 0 |
| 179 | QPDF_Stream pipe use stream provider 0 | 179 | QPDF_Stream pipe use stream provider 0 |
| 180 | QPDF_Stream provider length mismatch 0 | 180 | QPDF_Stream provider length mismatch 0 |
| 181 | +QPDFObjectHandle newStream 0 | ||
| 182 | +QPDFObjectHandle newStream with data 0 | ||
| 183 | +QPDF_Stream pipe no stream data 0 |
qpdf/qtest/qpdf.test
| @@ -77,7 +77,7 @@ flush_tiff_cache(); | @@ -77,7 +77,7 @@ flush_tiff_cache(); | ||
| 77 | show_ntests(); | 77 | show_ntests(); |
| 78 | # ---------- | 78 | # ---------- |
| 79 | $td->notify("--- Miscellaneous Tests ---"); | 79 | $td->notify("--- Miscellaneous Tests ---"); |
| 80 | -$n_tests += 26; | 80 | +$n_tests += 28; |
| 81 | 81 | ||
| 82 | $td->runtest("qpdf version", | 82 | $td->runtest("qpdf version", |
| 83 | {$td->COMMAND => "qpdf --version"}, | 83 | {$td->COMMAND => "qpdf --version"}, |
| @@ -104,7 +104,6 @@ $td->runtest("replace stream data", | @@ -104,7 +104,6 @@ $td->runtest("replace stream data", | ||
| 104 | $td->runtest("check output", | 104 | $td->runtest("check output", |
| 105 | {$td->FILE => "a.pdf"}, | 105 | {$td->FILE => "a.pdf"}, |
| 106 | {$td->FILE => "replaced-stream-data.out"}); | 106 | {$td->FILE => "replaced-stream-data.out"}); |
| 107 | - | ||
| 108 | $td->runtest("replace stream data compressed", | 107 | $td->runtest("replace stream data compressed", |
| 109 | {$td->COMMAND => "test_driver 8 qstream.pdf"}, | 108 | {$td->COMMAND => "test_driver 8 qstream.pdf"}, |
| 110 | {$td->FILE => "test8.out", $td->EXIT_STATUS => 0}, | 109 | {$td->FILE => "test8.out", $td->EXIT_STATUS => 0}, |
| @@ -112,6 +111,13 @@ $td->runtest("replace stream data compressed", | @@ -112,6 +111,13 @@ $td->runtest("replace stream data compressed", | ||
| 112 | $td->runtest("check output", | 111 | $td->runtest("check output", |
| 113 | {$td->FILE => "a.pdf"}, | 112 | {$td->FILE => "a.pdf"}, |
| 114 | {$td->FILE => "replaced-stream-data-flate.out"}); | 113 | {$td->FILE => "replaced-stream-data-flate.out"}); |
| 114 | +$td->runtest("new streams", | ||
| 115 | + {$td->COMMAND => "test_driver 9 minimal.pdf"}, | ||
| 116 | + {$td->FILE => "test9.out", $td->EXIT_STATUS => 0}, | ||
| 117 | + $td->NORMALIZE_NEWLINES); | ||
| 118 | +$td->runtest("new stream", | ||
| 119 | + {$td->FILE => "a.pdf"}, | ||
| 120 | + {$td->FILE => "new-streams.pdf"}); | ||
| 115 | 121 | ||
| 116 | # Make sure we ignore decode parameters that we don't understand | 122 | # Make sure we ignore decode parameters that we don't understand |
| 117 | $td->runtest("unknown decode parameters", | 123 | $td->runtest("unknown decode parameters", |
qpdf/qtest/qpdf/minimal.pdf
0 → 100644
| 1 | +%PDF-1.3 | ||
| 2 | +1 0 obj | ||
| 3 | +<< | ||
| 4 | + /Type /Catalog | ||
| 5 | + /Pages 2 0 R | ||
| 6 | +>> | ||
| 7 | +endobj | ||
| 8 | + | ||
| 9 | +2 0 obj | ||
| 10 | +<< | ||
| 11 | + /Type /Pages | ||
| 12 | + /Kids [ | ||
| 13 | + 3 0 R | ||
| 14 | + ] | ||
| 15 | + /Count 1 | ||
| 16 | +>> | ||
| 17 | +endobj | ||
| 18 | + | ||
| 19 | +3 0 obj | ||
| 20 | +<< | ||
| 21 | + /Type /Page | ||
| 22 | + /Parent 2 0 R | ||
| 23 | + /MediaBox [0 0 612 792] | ||
| 24 | + /Contents 4 0 R | ||
| 25 | + /Resources << | ||
| 26 | + /ProcSet 5 0 R | ||
| 27 | + /Font << | ||
| 28 | + /F1 6 0 R | ||
| 29 | + >> | ||
| 30 | + >> | ||
| 31 | +>> | ||
| 32 | +endobj | ||
| 33 | + | ||
| 34 | +4 0 obj | ||
| 35 | +<< | ||
| 36 | + /Length 44 | ||
| 37 | +>> | ||
| 38 | +stream | ||
| 39 | +BT | ||
| 40 | + /F1 24 Tf | ||
| 41 | + 72 720 Td | ||
| 42 | + (Potato) Tj | ||
| 43 | +ET | ||
| 44 | +endstream | ||
| 45 | +endobj | ||
| 46 | + | ||
| 47 | +5 0 obj | ||
| 48 | +[ | ||
| 49 | |||
| 50 | + /Text | ||
| 51 | +] | ||
| 52 | +endobj | ||
| 53 | + | ||
| 54 | +6 0 obj | ||
| 55 | +<< | ||
| 56 | + /Type /Font | ||
| 57 | + /Subtype /Type1 | ||
| 58 | + /Name /F1 | ||
| 59 | + /BaseFont /Helvetica | ||
| 60 | + /Encoding /WinAnsiEncoding | ||
| 61 | +>> | ||
| 62 | +endobj | ||
| 63 | + | ||
| 64 | +xref | ||
| 65 | +0 7 | ||
| 66 | +0000000000 65535 f | ||
| 67 | +0000000009 00000 n | ||
| 68 | +0000000063 00000 n | ||
| 69 | +0000000135 00000 n | ||
| 70 | +0000000307 00000 n | ||
| 71 | +0000000403 00000 n | ||
| 72 | +0000000438 00000 n | ||
| 73 | +trailer << | ||
| 74 | + /Size 7 | ||
| 75 | + /Root 1 0 R | ||
| 76 | +>> | ||
| 77 | +startxref | ||
| 78 | +556 | ||
| 79 | +%%EOF |
qpdf/qtest/qpdf/new-streams.pdf
0 → 100644
| 1 | +%PDF-1.3 | ||
| 2 | +%¿÷¢þ | ||
| 3 | +1 0 obj | ||
| 4 | +<< /Pages 2 0 R /QStream 3 0 R /RStream 4 0 R /Type /Catalog >> | ||
| 5 | +endobj | ||
| 6 | +2 0 obj | ||
| 7 | +<< /Count 1 /Kids [ 5 0 R ] /Type /Pages >> | ||
| 8 | +endobj | ||
| 9 | +3 0 obj | ||
| 10 | +<< /Length 20 >> | ||
| 11 | +stream | ||
| 12 | +data for new stream | ||
| 13 | +endstream | ||
| 14 | +endobj | ||
| 15 | +4 0 obj | ||
| 16 | +<< /Length 22 >> | ||
| 17 | +stream | ||
| 18 | +data for other stream | ||
| 19 | +endstream | ||
| 20 | +endobj | ||
| 21 | +5 0 obj | ||
| 22 | +<< /Contents 6 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 7 0 R >> /ProcSet 8 0 R >> /Type /Page >> | ||
| 23 | +endobj | ||
| 24 | +6 0 obj | ||
| 25 | +<< /Length 44 >> | ||
| 26 | +stream | ||
| 27 | +BT | ||
| 28 | + /F1 24 Tf | ||
| 29 | + 72 720 Td | ||
| 30 | + (Potato) Tj | ||
| 31 | +ET | ||
| 32 | +endstream | ||
| 33 | +endobj | ||
| 34 | +7 0 obj | ||
| 35 | +<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >> | ||
| 36 | +endobj | ||
| 37 | +8 0 obj | ||
| 38 | +[ /PDF /Text ] | ||
| 39 | +endobj | ||
| 40 | +xref | ||
| 41 | +0 9 | ||
| 42 | +0000000000 65535 f | ||
| 43 | +0000000015 00000 n | ||
| 44 | +0000000094 00000 n | ||
| 45 | +0000000153 00000 n | ||
| 46 | +0000000222 00000 n | ||
| 47 | +0000000293 00000 n | ||
| 48 | +0000000436 00000 n | ||
| 49 | +0000000529 00000 n | ||
| 50 | +0000000636 00000 n | ||
| 51 | +trailer << /Root 1 0 R /Size 9 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >> | ||
| 52 | +startxref | ||
| 53 | +666 | ||
| 54 | +%%EOF |
qpdf/qtest/qpdf/test9.out
0 → 100644
qpdf/test_driver.cc
| @@ -398,6 +398,35 @@ void runtest(int n, char const* filename) | @@ -398,6 +398,35 @@ void runtest(int n, char const* filename) | ||
| 398 | w.setStreamDataMode(qpdf_s_preserve); | 398 | w.setStreamDataMode(qpdf_s_preserve); |
| 399 | w.write(); | 399 | w.write(); |
| 400 | } | 400 | } |
| 401 | + else if (n == 9) | ||
| 402 | + { | ||
| 403 | + QPDFObjectHandle root = pdf.getRoot(); | ||
| 404 | + PointerHolder<Buffer> b1 = new Buffer(20); | ||
| 405 | + unsigned char* bp = b1.getPointer()->getBuffer(); | ||
| 406 | + memcpy(bp, (char*)"data for new stream\n", 20); // no null! | ||
| 407 | + QPDFObjectHandle qstream = QPDFObjectHandle::newStream(&pdf, b1); | ||
| 408 | + QPDFObjectHandle rstream = QPDFObjectHandle::newStream(&pdf); | ||
| 409 | + try | ||
| 410 | + { | ||
| 411 | + rstream.getStreamData(); | ||
| 412 | + std::cout << "oops -- getStreamData didn't throw" << std::endl; | ||
| 413 | + } | ||
| 414 | + catch (std::logic_error const& e) | ||
| 415 | + { | ||
| 416 | + std::cout << "exception: " << e.what() << std::endl; | ||
| 417 | + } | ||
| 418 | + PointerHolder<Buffer> b2 = new Buffer(22); | ||
| 419 | + bp = b2.getPointer()->getBuffer(); | ||
| 420 | + memcpy(bp, (char*)"data for other stream\n", 22); // no null! | ||
| 421 | + rstream.replaceStreamData( | ||
| 422 | + b2, QPDFObjectHandle::newNull(), QPDFObjectHandle::newNull()); | ||
| 423 | + root.replaceKey("/QStream", qstream); | ||
| 424 | + root.replaceKey("/RStream", rstream); | ||
| 425 | + QPDFWriter w(pdf, "a.pdf"); | ||
| 426 | + w.setStaticID(true); | ||
| 427 | + w.setStreamDataMode(qpdf_s_preserve); | ||
| 428 | + w.write(); | ||
| 429 | + } | ||
| 401 | else | 430 | else |
| 402 | { | 431 | { |
| 403 | throw std::runtime_error(std::string("invalid test ") + | 432 | throw std::runtime_error(std::string("invalid test ") + |