From 1bc8abfdd3eb9b5a6af5d274c85cd1708bdb9e0c Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 7 May 2022 11:12:15 -0400 Subject: Implement JSON v2 for Stream Not fully exercised in this commit --- libqpdf/QPDFObjectHandle.cc | 13 +++++ libqpdf/QPDF_Stream.cc | 118 +++++++++++++++++++++++++++++++++++++++++++- libqpdf/qpdf/QPDF_Stream.hh | 6 +++ 3 files changed, 135 insertions(+), 2 deletions(-) (limited to 'libqpdf') diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 33155097..1d6a9ccf 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -1797,6 +1797,19 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) } } +JSON +QPDFObjectHandle::getStreamJSON( + int json_version, + qpdf_stream_data_json_e json_data, + qpdf_stream_decode_level_e decode_level, + Pipeline* p, + std::string const& data_filename) +{ + assertStream(); + return dynamic_cast(obj.get())->getStreamJSON( + json_version, json_data, decode_level, p, data_filename); +} + QPDFObjectHandle QPDFObjectHandle::wrapInArray() { diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 8940b7cf..67a3ad0d 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -2,8 +2,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -54,6 +56,18 @@ namespace return nullptr; } }; + + class StreamBlobProvider + { + public: + StreamBlobProvider( + QPDF_Stream* stream, qpdf_stream_decode_level_e decode_level); + void operator()(Pipeline*); + + private: + QPDF_Stream* stream; + qpdf_stream_decode_level_e decode_level; + }; } // namespace std::map QPDF_Stream::filter_abbreviations = { @@ -81,6 +95,19 @@ std::map()>> {"/ASCIIHexDecode", SF_ASCIIHexDecode::factory}, }; +StreamBlobProvider::StreamBlobProvider( + QPDF_Stream* stream, qpdf_stream_decode_level_e decode_level) : + stream(stream), + decode_level(decode_level) +{ +} + +void +StreamBlobProvider::operator()(Pipeline* p) +{ + this->stream->pipeStreamData(p, nullptr, 0, decode_level, false, false); +} + QPDF_Stream::QPDF_Stream( QPDF* qpdf, int objid, @@ -153,8 +180,95 @@ QPDF_Stream::unparse() JSON QPDF_Stream::getJSON(int json_version) { - // QXXXQ - return this->stream_dict.getJSON(json_version); + if (json_version == 1) { + return this->stream_dict.getJSON(json_version); + } + return getStreamJSON(json_version, qpdf_sj_none, qpdf_dl_none, nullptr, ""); +} + +JSON +QPDF_Stream::getStreamJSON( + int json_version, + qpdf_stream_data_json_e json_data, + qpdf_stream_decode_level_e decode_level, + Pipeline* p, + std::string const& data_filename) +{ + switch (json_data) { + case qpdf_sj_none: + case qpdf_sj_inline: + if (p != nullptr) { + throw std::logic_error("QPDF_Stream::getStreamJSON: pipline should " + "only be suppiled json_data is file"); + } + break; + case qpdf_sj_file: + if (p == nullptr) { + throw std::logic_error("QPDF_Stream::getStreamJSON: pipline must " + "be be suppiled json_data is file"); + } + if (data_filename.empty()) { + throw std::logic_error("QPDF_Stream::getStreamJSON: data_filename " + "must be supplied when json_data is file"); + } + break; + } + + auto dict = this->stream_dict; + JSON result = JSON::makeDictionary(); + if (json_data != qpdf_sj_none) { + std::shared_ptr buf; + bool filtered = false; + bool filter = (decode_level != qpdf_dl_none); + for (int attempt = 1; attempt <= 2; ++attempt) { + Pl_Discard discard; + std::shared_ptr buf_pl; + Pipeline* data_pipeline = nullptr; + if (json_data == qpdf_sj_file) { + // We need to capture the data to write + buf_pl = std::make_shared("stream data"); + data_pipeline = buf_pl.get(); + } else { + data_pipeline = &discard; + } + filtered = pipeStreamData( + data_pipeline, nullptr, 0, decode_level, false, (attempt == 1)); + if (filter && (!filtered)) { + // Try again + filter = false; + } else { + if (buf_pl.get()) { + buf = buf_pl->getBufferSharedPointer(); + } + break; + } + } + // We can use unsafeShallowCopy because we are only + // touching top-level keys. + dict = this->stream_dict.unsafeShallowCopy(); + dict.removeKey("/Length"); + if (filtered) { + dict.removeKey("/Filter"); + dict.removeKey("/DecodeParms"); + } + if (json_data == qpdf_sj_file) { + result.addDictionaryMember( + "datafile", JSON::makeString(data_filename)); + if (!buf.get()) { + throw std::logic_error( + "QPDF_Stream: failed to get stream data in json file mode"); + } + p->write(buf->getBuffer(), buf->getSize()); + } else if (json_data == qpdf_sj_inline) { + result.addDictionaryMember( + "data", JSON::makeBlob(StreamBlobProvider(this, decode_level))); + } else { + throw std::logic_error( + "QPDF_Stream: unexpected value of json_data"); + } + } + result.addDictionaryMember("dict", dict.getJSON(json_version)); + return result; } QPDFObject::object_type_e diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh index 5d8de669..fcf98ffa 100644 --- a/libqpdf/qpdf/QPDF_Stream.hh +++ b/libqpdf/qpdf/QPDF_Stream.hh @@ -61,6 +61,12 @@ class QPDF_Stream: public QPDFObject QPDFObjectHandle const& decode_parms); void addTokenFilter(std::shared_ptr token_filter); + JSON getStreamJSON( + int json_version, + qpdf_stream_data_json_e json_data, + qpdf_stream_decode_level_e decode_level, + Pipeline* p, + std::string const& data_filename); void replaceDict(QPDFObjectHandle const& new_dict); -- cgit v1.2.3-54-g00ecf