Commit 09c3dc5f92e1512ba1e3fdf221bd3be08f6654fa

Authored by m-holger
1 parent 796913e9

Refactor QPDF_Stream

Move all stream-specific methods to new class qpdf::Stream
include/qpdf/ObjectHandle.hh
... ... @@ -35,8 +35,9 @@ class QPDFObjectHandle;
35 35 namespace qpdf
36 36 {
37 37 class Array;
38   - class Dictionary;
39 38 class BaseDictionary;
  39 + class Dictionary;
  40 + class Stream;
40 41  
41 42 enum typed : std::uint8_t { strict = 0, any_flag = 1, optional = 2, any = 3, error = 4};
42 43  
... ...
include/qpdf/QPDF.hh
... ... @@ -854,6 +854,7 @@ class QPDF
854 854 class Pipe
855 855 {
856 856 friend class QPDF_Stream;
  857 + friend class qpdf::Stream;
857 858  
858 859 private:
859 860 static bool
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -1357,6 +1357,7 @@ class QPDFObjectHandle final: public qpdf::BaseHandle
1357 1357  
1358 1358 inline qpdf::Array as_array(qpdf::typed options = qpdf::typed::any) const;
1359 1359 inline qpdf::Dictionary as_dictionary(qpdf::typed options = qpdf::typed::any) const;
  1360 + inline qpdf::Stream as_stream(qpdf::typed options = qpdf::typed::strict) const;
1360 1361  
1361 1362 private:
1362 1363 QPDF_Array* asArray() const;
... ...
libqpdf/QPDF.cc
... ... @@ -17,9 +17,9 @@
17 17 #include <qpdf/Pipeline.hh>
18 18 #include <qpdf/QPDFExc.hh>
19 19 #include <qpdf/QPDFLogger.hh>
  20 +#include <qpdf/QPDFObjectHandle_private.hh>
20 21 #include <qpdf/QPDFObject_private.hh>
21 22 #include <qpdf/QPDFParser.hh>
22   -#include <qpdf/QPDF_Array.hh>
23 23 #include <qpdf/QPDF_Dictionary.hh>
24 24 #include <qpdf/QPDF_Null.hh>
25 25 #include <qpdf/QPDF_Reserved.hh>
... ... @@ -298,7 +298,7 @@ void
298 298 QPDF::registerStreamFilter(
299 299 std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory)
300 300 {
301   - QPDF_Stream::registerStreamFilter(filter_name, factory);
  301 + qpdf::Stream::registerStreamFilter(filter_name, factory);
302 302 }
303 303  
304 304 void
... ... @@ -2442,13 +2442,11 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2442 2442 QPDF& foreign_stream_qpdf =
2443 2443 foreign.getQPDF("unable to retrieve owning qpdf from foreign stream");
2444 2444  
2445   - auto stream = foreign.getObjectPtr()->as<QPDF_Stream>();
2446   - if (stream == nullptr) {
2447   - throw std::logic_error(
2448   - "unable to retrieve underlying"
2449   - " stream object from foreign stream");
  2445 + auto stream = foreign.as_stream();
  2446 + if (!stream) {
  2447 + throw std::logic_error("unable to retrieve underlying stream object from foreign stream");
2450 2448 }
2451   - std::shared_ptr<Buffer> stream_buffer = stream->getStreamDataBuffer();
  2449 + std::shared_ptr<Buffer> stream_buffer = stream.getStreamDataBuffer();
2452 2450 if ((foreign_stream_qpdf.m->immediate_copy_from) && (stream_buffer == nullptr)) {
2453 2451 // Pull the stream data into a buffer before attempting the copy operation. Do it on the
2454 2452 // source stream so that if the source stream is copied multiple times, we don't have to
... ... @@ -2458,10 +2456,10 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2458 2456 foreign.getRawStreamData(),
2459 2457 old_dict.getKey("/Filter"),
2460 2458 old_dict.getKey("/DecodeParms"));
2461   - stream_buffer = stream->getStreamDataBuffer();
  2459 + stream_buffer = stream.getStreamDataBuffer();
2462 2460 }
2463 2461 std::shared_ptr<QPDFObjectHandle::StreamDataProvider> stream_provider =
2464   - stream->getStreamDataProvider();
  2462 + stream.getStreamDataProvider();
2465 2463 if (stream_buffer.get()) {
2466 2464 QTC::TC("qpdf", "QPDF copy foreign stream with buffer");
2467 2465 result.replaceStreamData(
... ... @@ -2476,9 +2474,9 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2476 2474 auto foreign_stream_data = std::make_shared<ForeignStreamData>(
2477 2475 foreign_stream_qpdf.m->encp,
2478 2476 foreign_stream_qpdf.m->file,
2479   - foreign.getObjGen(),
2480   - stream->getParsedOffset(),
2481   - stream->getLength(),
  2477 + foreign,
  2478 + foreign.getParsedOffset(),
  2479 + stream.getLength(),
2482 2480 dict);
2483 2481 m->copied_stream_data_provider->registerForeignStream(local_og, foreign_stream_data);
2484 2482 result.replaceStreamData(
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -962,43 +962,43 @@ QPDFObjectHandle::getUniqueResourceName(
962 962 QPDFObjectHandle
963 963 QPDFObjectHandle::getDict() const
964 964 {
965   - return asStreamWithAssert()->getDict();
  965 + return as_stream(error).getDict();
966 966 }
967 967  
968 968 void
969 969 QPDFObjectHandle::setFilterOnWrite(bool val)
970 970 {
971   - asStreamWithAssert()->setFilterOnWrite(val);
  971 + as_stream(error).setFilterOnWrite(val);
972 972 }
973 973  
974 974 bool
975 975 QPDFObjectHandle::getFilterOnWrite()
976 976 {
977   - return asStreamWithAssert()->getFilterOnWrite();
  977 + return as_stream(error).getFilterOnWrite();
978 978 }
979 979  
980 980 bool
981 981 QPDFObjectHandle::isDataModified()
982 982 {
983   - return asStreamWithAssert()->isDataModified();
  983 + return as_stream(error).isDataModified();
984 984 }
985 985  
986 986 void
987 987 QPDFObjectHandle::replaceDict(QPDFObjectHandle const& new_dict)
988 988 {
989   - asStreamWithAssert()->replaceDict(new_dict);
  989 + as_stream(error).replaceDict(new_dict);
990 990 }
991 991  
992 992 std::shared_ptr<Buffer>
993 993 QPDFObjectHandle::getStreamData(qpdf_stream_decode_level_e level)
994 994 {
995   - return asStreamWithAssert()->getStreamData(level);
  995 + return as_stream(error).getStreamData(level);
996 996 }
997 997  
998 998 std::shared_ptr<Buffer>
999 999 QPDFObjectHandle::getRawStreamData()
1000 1000 {
1001   - return asStreamWithAssert()->getRawStreamData();
  1001 + return as_stream(error).getRawStreamData();
1002 1002 }
1003 1003  
1004 1004 bool
... ... @@ -1010,7 +1010,7 @@ QPDFObjectHandle::pipeStreamData(
1010 1010 bool suppress_warnings,
1011 1011 bool will_retry)
1012 1012 {
1013   - return asStreamWithAssert()->pipeStreamData(
  1013 + return as_stream(error).pipeStreamData(
1014 1014 p, filtering_attempted, encode_flags, decode_level, suppress_warnings, will_retry);
1015 1015 }
1016 1016  
... ... @@ -1023,7 +1023,7 @@ QPDFObjectHandle::pipeStreamData(
1023 1023 bool will_retry)
1024 1024 {
1025 1025 bool filtering_attempted;
1026   - asStreamWithAssert()->pipeStreamData(
  1026 + as_stream(error).pipeStreamData(
1027 1027 p, &filtering_attempted, encode_flags, decode_level, suppress_warnings, will_retry);
1028 1028 return filtering_attempted;
1029 1029 }
... ... @@ -1051,7 +1051,7 @@ QPDFObjectHandle::replaceStreamData(
1051 1051 QPDFObjectHandle const& filter,
1052 1052 QPDFObjectHandle const& decode_parms)
1053 1053 {
1054   - asStreamWithAssert()->replaceStreamData(data, filter, decode_parms);
  1054 + as_stream(error).replaceStreamData(data, filter, decode_parms);
1055 1055 }
1056 1056  
1057 1057 void
... ... @@ -1063,7 +1063,7 @@ QPDFObjectHandle::replaceStreamData(
1063 1063 if (bp) {
1064 1064 memcpy(bp, data.c_str(), data.length());
1065 1065 }
1066   - asStreamWithAssert()->replaceStreamData(b, filter, decode_parms);
  1066 + as_stream(error).replaceStreamData(b, filter, decode_parms);
1067 1067 }
1068 1068  
1069 1069 void
... ... @@ -1072,7 +1072,7 @@ QPDFObjectHandle::replaceStreamData(
1072 1072 QPDFObjectHandle const& filter,
1073 1073 QPDFObjectHandle const& decode_parms)
1074 1074 {
1075   - asStreamWithAssert()->replaceStreamData(provider, filter, decode_parms);
  1075 + as_stream(error).replaceStreamData(provider, filter, decode_parms);
1076 1076 }
1077 1077  
1078 1078 namespace
... ... @@ -1119,7 +1119,7 @@ QPDFObjectHandle::replaceStreamData(
1119 1119 QPDFObjectHandle const& decode_parms)
1120 1120 {
1121 1121 auto sdp = std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider));
1122   - asStreamWithAssert()->replaceStreamData(sdp, filter, decode_parms);
  1122 + as_stream(error).replaceStreamData(sdp, filter, decode_parms);
1123 1123 }
1124 1124  
1125 1125 void
... ... @@ -1129,7 +1129,7 @@ QPDFObjectHandle::replaceStreamData(
1129 1129 QPDFObjectHandle const& decode_parms)
1130 1130 {
1131 1131 auto sdp = std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider));
1132   - asStreamWithAssert()->replaceStreamData(sdp, filter, decode_parms);
  1132 + as_stream(error).replaceStreamData(sdp, filter, decode_parms);
1133 1133 }
1134 1134  
1135 1135 std::map<std::string, QPDFObjectHandle>
... ... @@ -1348,8 +1348,7 @@ QPDFObjectHandle::getStreamJSON(
1348 1348 Pipeline* p,
1349 1349 std::string const& data_filename)
1350 1350 {
1351   - return asStreamWithAssert()->getStreamJSON(
1352   - json_version, json_data, decode_level, p, data_filename);
  1351 + return as_stream(error).getStreamJSON(json_version, json_data, decode_level, p, data_filename);
1353 1352 }
1354 1353  
1355 1354 QPDFObjectHandle
... ... @@ -1552,7 +1551,7 @@ QPDFObjectHandle::addContentTokenFilter(std::shared_ptr&lt;TokenFilter&gt; filter)
1552 1551 void
1553 1552 QPDFObjectHandle::addTokenFilter(std::shared_ptr<TokenFilter> filter)
1554 1553 {
1555   - return asStreamWithAssert()->addTokenFilter(filter);
  1554 + return as_stream(error).addTokenFilter(filter);
1556 1555 }
1557 1556  
1558 1557 QPDFObjectHandle
... ...
libqpdf/QPDF_Stream.cc
... ... @@ -12,6 +12,7 @@
12 12 #include <qpdf/QIntC.hh>
13 13 #include <qpdf/QPDF.hh>
14 14 #include <qpdf/QPDFExc.hh>
  15 +#include <qpdf/QPDFObjectHandle_private.hh>
15 16 #include <qpdf/QTC.hh>
16 17 #include <qpdf/QUtil.hh>
17 18 #include <qpdf/SF_ASCII85Decode.hh>
... ... @@ -22,6 +23,9 @@
22 23  
23 24 #include <stdexcept>
24 25  
  26 +using namespace std::literals;
  27 +using namespace qpdf;
  28 +
25 29 namespace
26 30 {
27 31 class SF_Crypt: public QPDFStreamFilter
... ... @@ -60,16 +64,24 @@ namespace
60 64 class StreamBlobProvider
61 65 {
62 66 public:
63   - StreamBlobProvider(QPDF_Stream* stream, qpdf_stream_decode_level_e decode_level);
64   - void operator()(Pipeline*);
  67 + StreamBlobProvider(Stream stream, qpdf_stream_decode_level_e decode_level) :
  68 + stream(stream),
  69 + decode_level(decode_level)
  70 + {
  71 + }
  72 + void
  73 + operator()(Pipeline* p)
  74 + {
  75 + stream.pipeStreamData(p, nullptr, 0, decode_level, false, false);
  76 + }
65 77  
66 78 private:
67   - QPDF_Stream* stream;
  79 + Stream stream;
68 80 qpdf_stream_decode_level_e decode_level;
69 81 };
70 82 } // namespace
71 83  
72   -std::map<std::string, std::string> QPDF_Stream::filter_abbreviations = {
  84 +std::map<std::string, std::string> Stream::filter_abbreviations = {
73 85 // The PDF specification provides these filter abbreviations for use in inline images, but
74 86 // according to table H.1 in the pre-ISO versions of the PDF specification, Adobe Reader also
75 87 // accepts them for stream filters.
... ... @@ -82,8 +94,8 @@ std::map&lt;std::string, std::string&gt; QPDF_Stream::filter_abbreviations = {
82 94 {"/DCT", "/DCTDecode"},
83 95 };
84 96  
85   -std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>>
86   - QPDF_Stream::filter_factories = {
  97 +std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>> Stream::filter_factories =
  98 + {
87 99 {"/Crypt", []() { return std::make_shared<SF_Crypt>(); }},
88 100 {"/FlateDecode", SF_FlateLzwDecode::flate_factory},
89 101 {"/LZWDecode", SF_FlateLzwDecode::lzw_factory},
... ... @@ -93,19 +105,6 @@ std::map&lt;std::string, std::function&lt;std::shared_ptr&lt;QPDFStreamFilter&gt;()&gt;&gt;
93 105 {"/ASCIIHexDecode", SF_ASCIIHexDecode::factory},
94 106 };
95 107  
96   -StreamBlobProvider::StreamBlobProvider(
97   - QPDF_Stream* stream, qpdf_stream_decode_level_e decode_level) :
98   - stream(stream),
99   - decode_level(decode_level)
100   -{
101   -}
102   -
103   -void
104   -StreamBlobProvider::operator()(Pipeline* p)
105   -{
106   - this->stream->pipeStreamData(p, nullptr, 0, decode_level, false, false);
107   -}
108   -
109 108 QPDF_Stream::QPDF_Stream(
110 109 QPDF* qpdf, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length) :
111 110 QPDFValue(::ot_stream, qpdf, og),
... ... @@ -137,25 +136,13 @@ QPDF_Stream::copy(bool shallow)
137 136 }
138 137  
139 138 void
140   -QPDF_Stream::registerStreamFilter(
  139 +Stream::registerStreamFilter(
141 140 std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory)
142 141 {
143 142 filter_factories[filter_name] = factory;
144 143 }
145 144  
146 145 void
147   -QPDF_Stream::setFilterOnWrite(bool val)
148   -{
149   - this->filter_on_write = val;
150   -}
151   -
152   -bool
153   -QPDF_Stream::getFilterOnWrite() const
154   -{
155   - return this->filter_on_write;
156   -}
157   -
158   -void
159 146 QPDF_Stream::disconnect()
160 147 {
161 148 this->stream_provider = nullptr;
... ... @@ -175,8 +162,20 @@ QPDF_Stream::writeJSON(int json_version, JSON::Writer&amp; jw)
175 162 stream_dict.writeJSON(json_version, jw);
176 163 }
177 164  
  165 +QPDF_Stream*
  166 +Stream::stream() const
  167 +{
  168 + if (obj) {
  169 + if (auto s = obj->as<QPDF_Stream>()) {
  170 + return s;
  171 + }
  172 + }
  173 + throw std::runtime_error("operation for stream attempted on object of type dictionary");
  174 + return nullptr; // unreachable
  175 +}
  176 +
178 177 JSON
179   -QPDF_Stream::getStreamJSON(
  178 +Stream::getStreamJSON(
180 179 int json_version,
181 180 qpdf_json_stream_data_e json_data,
182 181 qpdf_stream_decode_level_e decode_level,
... ... @@ -190,13 +189,13 @@ QPDF_Stream::getStreamJSON(
190 189 pb.finish();
191 190 auto result = JSON::parse(pb.getString());
192 191 if (json_data == qpdf_sj_inline) {
193   - result.addDictionaryMember("data", JSON::makeBlob(StreamBlobProvider(this, decode_level)));
  192 + result.addDictionaryMember("data", JSON::makeBlob(StreamBlobProvider(*this, decode_level)));
194 193 }
195 194 return result;
196 195 }
197 196  
198 197 qpdf_stream_decode_level_e
199   -QPDF_Stream::writeStreamJSON(
  198 +Stream::writeStreamJSON(
200 199 int json_version,
201 200 JSON::Writer& jw,
202 201 qpdf_json_stream_data_e json_data,
... ... @@ -205,6 +204,7 @@ QPDF_Stream::writeStreamJSON(
205 204 std::string const& data_filename,
206 205 bool no_data_key)
207 206 {
  207 + auto s = stream();
208 208 switch (json_data) {
209 209 case qpdf_sj_none:
210 210 case qpdf_sj_inline:
... ... @@ -232,7 +232,7 @@ QPDF_Stream::writeStreamJSON(
232 232 if (json_data == qpdf_sj_none) {
233 233 jw.writeNext();
234 234 jw << R"("dict": )";
235   - stream_dict.writeJSON(json_version, jw);
  235 + s->stream_dict.writeJSON(json_version, jw);
236 236 jw.writeEnd('}');
237 237 return decode_level;
238 238 }
... ... @@ -264,7 +264,7 @@ QPDF_Stream::writeStreamJSON(
264 264 throw std::logic_error("QPDF_Stream: failed to get stream data");
265 265 }
266 266 // We can use unsafeShallowCopy because we are only touching top-level keys.
267   - auto dict = stream_dict.unsafeShallowCopy();
  267 + auto dict = s->stream_dict.unsafeShallowCopy();
268 268 dict.removeKey("/Length");
269 269 if (filter && filtered) {
270 270 dict.removeKey("/Filter");
... ... @@ -305,48 +305,19 @@ QPDF_Stream::setDictDescription()
305 305 }
306 306 }
307 307  
308   -QPDFObjectHandle
309   -QPDF_Stream::getDict() const
310   -{
311   - return this->stream_dict;
312   -}
313   -
314   -bool
315   -QPDF_Stream::isDataModified() const
316   -{
317   - return (!this->token_filters.empty());
318   -}
319   -
320   -size_t
321   -QPDF_Stream::getLength() const
322   -{
323   - return this->length;
324   -}
325   -
326   -std::shared_ptr<Buffer>
327   -QPDF_Stream::getStreamDataBuffer() const
328   -{
329   - return this->stream_data;
330   -}
331   -
332   -std::shared_ptr<QPDFObjectHandle::StreamDataProvider>
333   -QPDF_Stream::getStreamDataProvider() const
334   -{
335   - return this->stream_provider;
336   -}
337   -
338 308 std::shared_ptr<Buffer>
339   -QPDF_Stream::getStreamData(qpdf_stream_decode_level_e decode_level)
  309 +Stream::getStreamData(qpdf_stream_decode_level_e decode_level)
340 310 {
  311 + auto s = stream();
341 312 Pl_Buffer buf("stream data buffer");
342 313 bool filtered;
343 314 pipeStreamData(&buf, &filtered, 0, decode_level, false, false);
344 315 if (!filtered) {
345 316 throw QPDFExc(
346 317 qpdf_e_unsupported,
347   - qpdf->getFilename(),
  318 + s->qpdf->getFilename(),
348 319 "",
349   - this->parsed_offset,
  320 + s->parsed_offset,
350 321 "getStreamData called on unfilterable stream");
351 322 }
352 323 QTC::TC("qpdf", "QPDF_Stream getStreamData");
... ... @@ -354,15 +325,16 @@ QPDF_Stream::getStreamData(qpdf_stream_decode_level_e decode_level)
354 325 }
355 326  
356 327 std::shared_ptr<Buffer>
357   -QPDF_Stream::getRawStreamData()
  328 +Stream::getRawStreamData()
358 329 {
  330 + auto s = stream();
359 331 Pl_Buffer buf("stream data buffer");
360 332 if (!pipeStreamData(&buf, nullptr, 0, qpdf_dl_none, false, false)) {
361 333 throw QPDFExc(
362 334 qpdf_e_unsupported,
363   - qpdf->getFilename(),
  335 + s->qpdf->getFilename(),
364 336 "",
365   - this->parsed_offset,
  337 + s->parsed_offset,
366 338 "error getting raw stream data");
367 339 }
368 340 QTC::TC("qpdf", "QPDF_Stream getRawStreamData");
... ... @@ -370,14 +342,15 @@ QPDF_Stream::getRawStreamData()
370 342 }
371 343  
372 344 bool
373   -QPDF_Stream::filterable(
  345 +Stream::filterable(
374 346 std::vector<std::shared_ptr<QPDFStreamFilter>>& filters,
375 347 bool& specialized_compression,
376 348 bool& lossy_compression)
377 349 {
  350 + auto s = stream();
378 351 // Check filters
379 352  
380   - QPDFObjectHandle filter_obj = this->stream_dict.getKey("/Filter");
  353 + QPDFObjectHandle filter_obj = s->stream_dict.getKey("/Filter");
381 354 bool filters_okay = true;
382 355  
383 356 std::vector<std::string> filter_names;
... ... @@ -432,7 +405,7 @@ QPDF_Stream::filterable(
432 405  
433 406 // See if we can support any decode parameters that are specified.
434 407  
435   - QPDFObjectHandle decode_obj = this->stream_dict.getKey("/DecodeParms");
  408 + QPDFObjectHandle decode_obj = s->stream_dict.getKey("/DecodeParms");
436 409 std::vector<QPDFObjectHandle> decode_parms;
437 410 if (decode_obj.isArray() && (decode_obj.getArrayNItems() == 0)) {
438 411 decode_obj = QPDFObjectHandle::newNull();
... ... @@ -479,7 +452,7 @@ QPDF_Stream::filterable(
479 452 }
480 453  
481 454 bool
482   -QPDF_Stream::pipeStreamData(
  455 +Stream::pipeStreamData(
483 456 Pipeline* pipeline,
484 457 bool* filterp,
485 458 int encode_flags,
... ... @@ -487,6 +460,7 @@ QPDF_Stream::pipeStreamData(
487 460 bool suppress_warnings,
488 461 bool will_retry)
489 462 {
  463 + auto s = stream();
490 464 std::vector<std::shared_ptr<QPDFStreamFilter>> filters;
491 465 bool specialized_compression = false;
492 466 bool lossy_compression = false;
... ... @@ -543,7 +517,7 @@ QPDF_Stream::pipeStreamData(
543 517 pipeline = new_pipeline.get();
544 518 }
545 519  
546   - for (auto iter = this->token_filters.rbegin(); iter != this->token_filters.rend(); ++iter) {
  520 + for (auto iter = s->token_filters.rbegin(); iter != s->token_filters.rend(); ++iter) {
547 521 new_pipeline =
548 522 std::make_shared<Pl_QPDFTokenizer>("token filter", (*iter).get(), pipeline);
549 523 to_delete.push_back(new_pipeline);
... ... @@ -562,25 +536,25 @@ QPDF_Stream::pipeStreamData(
562 536 }
563 537 }
564 538  
565   - if (this->stream_data.get()) {
  539 + if (s->stream_data.get()) {
566 540 QTC::TC("qpdf", "QPDF_Stream pipe replaced stream data");
567   - pipeline->write(this->stream_data->getBuffer(), this->stream_data->getSize());
  541 + pipeline->write(s->stream_data->getBuffer(), s->stream_data->getSize());
568 542 pipeline->finish();
569   - } else if (this->stream_provider.get()) {
  543 + } else if (s->stream_provider.get()) {
570 544 Pl_Count count("stream provider count", pipeline);
571   - if (this->stream_provider->supportsRetry()) {
572   - if (!this->stream_provider->provideStreamData(
573   - og, &count, suppress_warnings, will_retry)) {
  545 + if (s->stream_provider->supportsRetry()) {
  546 + if (!s->stream_provider->provideStreamData(
  547 + s->og, &count, suppress_warnings, will_retry)) {
574 548 filter = false;
575 549 success = false;
576 550 }
577 551 } else {
578   - this->stream_provider->provideStreamData(og, &count);
  552 + s->stream_provider->provideStreamData(s->og, &count);
579 553 }
580 554 qpdf_offset_t actual_length = count.getCount();
581 555 qpdf_offset_t desired_length = 0;
582   - if (success && this->stream_dict.hasKey("/Length")) {
583   - desired_length = this->stream_dict.getKey("/Length").getIntValue();
  556 + if (success && s->stream_dict.hasKey("/Length")) {
  557 + desired_length = s->stream_dict.getKey("/Length").getIntValue();
584 558 if (actual_length == desired_length) {
585 559 QTC::TC("qpdf", "QPDF_Stream pipe use stream provider");
586 560 } else {
... ... @@ -588,25 +562,25 @@ QPDF_Stream::pipeStreamData(
588 562 // This would be caused by programmer error on the part of a library user, not by
589 563 // invalid input data.
590 564 throw std::runtime_error(
591   - "stream data provider for " + og.unparse(' ') + " provided " +
  565 + "stream data provider for " + s->og.unparse(' ') + " provided " +
592 566 std::to_string(actual_length) + " bytes instead of expected " +
593 567 std::to_string(desired_length) + " bytes");
594 568 }
595 569 } else if (success) {
596 570 QTC::TC("qpdf", "QPDF_Stream provider length not provided");
597   - this->stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(actual_length));
  571 + s->stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(actual_length));
598 572 }
599   - } else if (this->parsed_offset == 0) {
  573 + } else if (s->parsed_offset == 0) {
600 574 QTC::TC("qpdf", "QPDF_Stream pipe no stream data");
601 575 throw std::logic_error("pipeStreamData called for stream with no data");
602 576 } else {
603 577 QTC::TC("qpdf", "QPDF_Stream pipe original stream data");
604 578 if (!QPDF::Pipe::pipeStreamData(
605   - this->qpdf,
606   - og,
607   - this->parsed_offset,
608   - this->length,
609   - this->stream_dict,
  579 + s->qpdf,
  580 + s->og,
  581 + s->parsed_offset,
  582 + s->length,
  583 + s->stream_dict,
610 584 pipeline,
611 585 suppress_warnings,
612 586 will_retry)) {
... ... @@ -634,60 +608,52 @@ QPDF_Stream::pipeStreamData(
634 608 }
635 609  
636 610 void
637   -QPDF_Stream::replaceStreamData(
  611 +Stream::replaceStreamData(
638 612 std::shared_ptr<Buffer> data,
639 613 QPDFObjectHandle const& filter,
640 614 QPDFObjectHandle const& decode_parms)
641 615 {
642   - this->stream_data = data;
643   - this->stream_provider = nullptr;
  616 + auto s = stream();
  617 + s->stream_data = data;
  618 + s->stream_provider = nullptr;
644 619 replaceFilterData(filter, decode_parms, data->getSize());
645 620 }
646 621  
647 622 void
648   -QPDF_Stream::replaceStreamData(
  623 +Stream::replaceStreamData(
649 624 std::shared_ptr<QPDFObjectHandle::StreamDataProvider> provider,
650 625 QPDFObjectHandle const& filter,
651 626 QPDFObjectHandle const& decode_parms)
652 627 {
653   - this->stream_provider = provider;
654   - this->stream_data = nullptr;
  628 + auto s = stream();
  629 + s->stream_provider = provider;
  630 + s->stream_data = nullptr;
655 631 replaceFilterData(filter, decode_parms, 0);
656 632 }
657 633  
658 634 void
659   -QPDF_Stream::addTokenFilter(std::shared_ptr<QPDFObjectHandle::TokenFilter> token_filter)
660   -{
661   - this->token_filters.push_back(token_filter);
662   -}
663   -
664   -void
665   -QPDF_Stream::replaceFilterData(
  635 +Stream::replaceFilterData(
666 636 QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length)
667 637 {
  638 + auto s = stream();
668 639 if (filter) {
669   - stream_dict.replaceKey("/Filter", filter);
  640 + s->stream_dict.replaceKey("/Filter", filter);
670 641 }
671 642 if (decode_parms) {
672   - stream_dict.replaceKey("/DecodeParms", decode_parms);
  643 + s->stream_dict.replaceKey("/DecodeParms", decode_parms);
673 644 }
674 645 if (length == 0) {
675 646 QTC::TC("qpdf", "QPDF_Stream unknown stream length");
676   - stream_dict.removeKey("/Length");
  647 + s->stream_dict.removeKey("/Length");
677 648 } else {
678   - stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(QIntC::to_longlong(length)));
  649 + s->stream_dict.replaceKey(
  650 + "/Length", QPDFObjectHandle::newInteger(QIntC::to_longlong(length)));
679 651 }
680 652 }
681 653  
682 654 void
683   -QPDF_Stream::replaceDict(QPDFObjectHandle const& new_dict)
684   -{
685   - this->stream_dict = new_dict;
686   - setDictDescription();
687   -}
688   -
689   -void
690   -QPDF_Stream::warn(std::string const& message)
  655 +Stream::warn(std::string const& message)
691 656 {
692   - this->qpdf->warn(qpdf_e_damaged_pdf, "", this->parsed_offset, message);
  657 + auto s = stream();
  658 + s->qpdf->warn(qpdf_e_damaged_pdf, "", s->parsed_offset, message);
693 659 }
... ...
libqpdf/QPDF_json.cc
... ... @@ -5,6 +5,7 @@
5 5 #include <qpdf/Pl_Base64.hh>
6 6 #include <qpdf/Pl_StdioFile.hh>
7 7 #include <qpdf/QIntC.hh>
  8 +#include <qpdf/QPDFObjectHandle_private.hh>
8 9 #include <qpdf/QPDFObject_private.hh>
9 10 #include <qpdf/QPDFValue.hh>
10 11 #include <qpdf/QPDF_Null.hh>
... ... @@ -821,7 +822,7 @@ void
821 822 writeJSONStreamFile(
822 823 int version,
823 824 JSON::Writer& jw,
824   - QPDF_Stream& stream,
  825 + qpdf::Stream& stream,
825 826 int id,
826 827 qpdf_stream_decode_level_e decode_level,
827 828 std::string const& file_prefix)
... ... @@ -894,13 +895,13 @@ QPDF::writeJSON(
894 895 } else {
895 896 jw << "\n },\n \"" << key;
896 897 }
897   - if (auto* stream = obj.getObjectPtr()->as<QPDF_Stream>()) {
  898 + if (auto stream = obj.as_stream()) {
898 899 jw << "\": {\n \"stream\": ";
899 900 if (json_stream_data == qpdf_sj_file) {
900 901 writeJSONStreamFile(
901   - version, jw, *stream, og.getObj(), decode_level, file_prefix);
  902 + version, jw, stream, og.getObj(), decode_level, file_prefix);
902 903 } else {
903   - stream->writeStreamJSON(
  904 + stream.writeStreamJSON(
904 905 version, jw, json_stream_data, decode_level, nullptr, "");
905 906 }
906 907 } else {
... ...
libqpdf/qpdf/QPDFObjectHandle_private.hh
... ... @@ -6,6 +6,7 @@
6 6 #include <qpdf/QPDFObject_private.hh>
7 7 #include <qpdf/QPDF_Array.hh>
8 8 #include <qpdf/QPDF_Dictionary.hh>
  9 +#include <qpdf/QPDF_Stream.hh>
9 10  
10 11 namespace qpdf
11 12 {
... ... @@ -80,6 +81,123 @@ namespace qpdf
80 81 }
81 82 };
82 83  
  84 + class Stream final: public BaseHandle
  85 + {
  86 + public:
  87 + explicit Stream(std::shared_ptr<QPDFObject> const& obj) :
  88 + BaseHandle(obj)
  89 + {
  90 + }
  91 +
  92 + explicit Stream(std::shared_ptr<QPDFObject>&& obj) :
  93 + BaseHandle(std::move(obj))
  94 + {
  95 + }
  96 +
  97 + QPDFObjectHandle
  98 + getDict() const
  99 + {
  100 + return stream()->stream_dict;
  101 + }
  102 + bool
  103 + isDataModified() const
  104 + {
  105 + return !stream()->token_filters.empty();
  106 + }
  107 + void
  108 + setFilterOnWrite(bool val)
  109 + {
  110 + stream()->filter_on_write = val;
  111 + }
  112 + bool
  113 + getFilterOnWrite() const
  114 + {
  115 + return stream()->filter_on_write;
  116 + }
  117 +
  118 + // Methods to help QPDF copy foreign streams
  119 + size_t
  120 + getLength() const
  121 + {
  122 + return stream()->length;
  123 + }
  124 + std::shared_ptr<Buffer>
  125 + getStreamDataBuffer() const
  126 + {
  127 + return stream()->stream_data;
  128 + }
  129 + std::shared_ptr<QPDFObjectHandle::StreamDataProvider>
  130 + getStreamDataProvider() const
  131 + {
  132 + return stream()->stream_provider;
  133 + }
  134 +
  135 + // See comments in QPDFObjectHandle.hh for these methods.
  136 + bool pipeStreamData(
  137 + Pipeline* p,
  138 + bool* tried_filtering,
  139 + int encode_flags,
  140 + qpdf_stream_decode_level_e decode_level,
  141 + bool suppress_warnings,
  142 + bool will_retry);
  143 + std::shared_ptr<Buffer> getStreamData(qpdf_stream_decode_level_e level);
  144 + std::shared_ptr<Buffer> getRawStreamData();
  145 + void replaceStreamData(
  146 + std::shared_ptr<Buffer> data,
  147 + QPDFObjectHandle const& filter,
  148 + QPDFObjectHandle const& decode_parms);
  149 + void replaceStreamData(
  150 + std::shared_ptr<QPDFObjectHandle::StreamDataProvider> provider,
  151 + QPDFObjectHandle const& filter,
  152 + QPDFObjectHandle const& decode_parms);
  153 + void
  154 + addTokenFilter(std::shared_ptr<QPDFObjectHandle::TokenFilter> token_filter)
  155 + {
  156 + stream()->token_filters.emplace_back(token_filter);
  157 + }
  158 + JSON getStreamJSON(
  159 + int json_version,
  160 + qpdf_json_stream_data_e json_data,
  161 + qpdf_stream_decode_level_e decode_level,
  162 + Pipeline* p,
  163 + std::string const& data_filename);
  164 + qpdf_stream_decode_level_e writeStreamJSON(
  165 + int json_version,
  166 + JSON::Writer& jw,
  167 + qpdf_json_stream_data_e json_data,
  168 + qpdf_stream_decode_level_e decode_level,
  169 + Pipeline* p,
  170 + std::string const& data_filename,
  171 + bool no_data_key = false);
  172 + void
  173 + replaceDict(QPDFObjectHandle const& new_dict)
  174 + {
  175 + auto s = stream();
  176 + s->stream_dict = new_dict;
  177 + s->setDictDescription();
  178 + }
  179 +
  180 + static void registerStreamFilter(
  181 + std::string const& filter_name,
  182 + std::function<std::shared_ptr<QPDFStreamFilter>()> factory);
  183 +
  184 + private:
  185 + QPDF_Stream* stream() const;
  186 +
  187 + bool filterable(
  188 + std::vector<std::shared_ptr<QPDFStreamFilter>>& filters,
  189 + bool& specialized_compression,
  190 + bool& lossy_compression);
  191 + void replaceFilterData(
  192 + QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length);
  193 +
  194 + void warn(std::string const& message);
  195 +
  196 + static std::map<std::string, std::string> filter_abbreviations;
  197 + static std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>>
  198 + filter_factories;
  199 + };
  200 +
83 201 inline qpdf_object_type_e
84 202 BaseHandle::type_code() const
85 203 {
... ... @@ -98,7 +216,7 @@ QPDFObjectHandle::as_array(qpdf::typed options) const
98 216 (options & qpdf::optional && type_code() == ::ot_null)) {
99 217 return qpdf::Array(obj);
100 218 }
101   - return qpdf::Array({});
  219 + return qpdf::Array(std::shared_ptr<QPDFObject>());
102 220 }
103 221  
104 222 inline qpdf::Dictionary
... ... @@ -114,4 +232,17 @@ QPDFObjectHandle::as_dictionary(qpdf::typed options) const
114 232 return qpdf::Dictionary(std::shared_ptr<QPDFObject>());
115 233 }
116 234  
  235 +inline qpdf::Stream
  236 +QPDFObjectHandle::as_stream(qpdf::typed options) const
  237 +{
  238 + if (options & qpdf::any_flag || type_code() == ::ot_stream ||
  239 + (options & qpdf::optional && type_code() == ::ot_null)) {
  240 + return qpdf::Stream(obj);
  241 + }
  242 + if (options & qpdf::error) {
  243 + assertType("stream", false);
  244 + }
  245 + return qpdf::Stream(std::shared_ptr<QPDFObject>());
  246 +}
  247 +
117 248 #endif // OBJECTHANDLE_PRIVATE_HH
... ...
libqpdf/qpdf/QPDF_Stream.hh
... ... @@ -4,6 +4,7 @@
4 4 #include <qpdf/Types.h>
5 5  
6 6 #include <qpdf/QPDFObjectHandle.hh>
  7 +#include <qpdf/QPDFObject_private.hh>
7 8 #include <qpdf/QPDFStreamFilter.hh>
8 9 #include <qpdf/QPDFValue.hh>
9 10  
... ... @@ -25,69 +26,15 @@ class QPDF_Stream final: public QPDFValue
25 26 void setDescription(
26 27 QPDF*, std::shared_ptr<QPDFValue::Description>& description, qpdf_offset_t offset) final;
27 28 void disconnect() final;
28   - QPDFObjectHandle getDict() const;
29   - bool isDataModified() const;
30   - void setFilterOnWrite(bool);
31   - bool getFilterOnWrite() const;
32   -
33   - // Methods to help QPDF copy foreign streams
34   - size_t getLength() const;
35   - std::shared_ptr<Buffer> getStreamDataBuffer() const;
36   - std::shared_ptr<QPDFObjectHandle::StreamDataProvider> getStreamDataProvider() const;
37   -
38   - // See comments in QPDFObjectHandle.hh for these methods.
39   - bool pipeStreamData(
40   - Pipeline*,
41   - bool* tried_filtering,
42   - int encode_flags,
43   - qpdf_stream_decode_level_e decode_level,
44   - bool suppress_warnings,
45   - bool will_retry);
46   - std::shared_ptr<Buffer> getStreamData(qpdf_stream_decode_level_e);
47   - std::shared_ptr<Buffer> getRawStreamData();
48   - void replaceStreamData(
49   - std::shared_ptr<Buffer> data,
50   - QPDFObjectHandle const& filter,
51   - QPDFObjectHandle const& decode_parms);
52   - void replaceStreamData(
53   - std::shared_ptr<QPDFObjectHandle::StreamDataProvider> provider,
54   - QPDFObjectHandle const& filter,
55   - QPDFObjectHandle const& decode_parms);
56   - void addTokenFilter(std::shared_ptr<QPDFObjectHandle::TokenFilter> token_filter);
57   - JSON getStreamJSON(
58   - int json_version,
59   - qpdf_json_stream_data_e json_data,
60   - qpdf_stream_decode_level_e decode_level,
61   - Pipeline* p,
62   - std::string const& data_filename);
63   - qpdf_stream_decode_level_e writeStreamJSON(
64   - int json_version,
65   - JSON::Writer& jw,
66   - qpdf_json_stream_data_e json_data,
67   - qpdf_stream_decode_level_e decode_level,
68   - Pipeline* p,
69   - std::string const& data_filename,
70   - bool no_data_key = false);
71   -
72   - void replaceDict(QPDFObjectHandle const& new_dict);
73   -
74   - static void registerStreamFilter(
75   - std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory);
76 29  
77 30 private:
  31 + friend class qpdf::Stream;
  32 +
78 33 QPDF_Stream(
79 34 QPDF*, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length);
80   - static std::map<std::string, std::string> filter_abbreviations;
81   - static std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>>
82   - filter_factories;
83 35  
84 36 void replaceFilterData(
85 37 QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length);
86   - bool filterable(
87   - std::vector<std::shared_ptr<QPDFStreamFilter>>& filters,
88   - bool& specialized_compression,
89   - bool& lossy_compression);
90   - void warn(std::string const& message);
91 38 void setDictDescription();
92 39  
93 40 bool filter_on_write;
... ...