From 772cd8d5d2449025d0b0b000e054ddb8a5346d0a Mon Sep 17 00:00:00 2001 From: m-holger Date: Fri, 9 May 2025 11:45:04 +0100 Subject: [PATCH] Refactor stream filter handling for improved modularity --- libqpdf/QPDF_Stream.cc | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------- libqpdf/qpdf/QPDFObjectHandle_private.hh | 2 -- libqpdf/qpdf/QPDFObject_private.hh | 2 ++ qpdf/qpdf.testcov | 1 - 4 files changed, 60 insertions(+), 36 deletions(-) diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 6b8c774..4aa570b 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -78,6 +78,9 @@ namespace Stream stream; qpdf_stream_decode_level_e decode_level; }; + + /// User defined streamfilter factories + std::map()>> filter_factories; } // namespace std::string @@ -110,16 +113,47 @@ QPDF_Stream::Members::expand_filter_name(std::string const& name) const return name; }; -std::map()>> Stream::filter_factories = - { - {"/Crypt", []() { return std::make_shared(); }}, - {"/FlateDecode", SF_FlateLzwDecode::flate_factory}, - {"/LZWDecode", SF_FlateLzwDecode::lzw_factory}, - {"/RunLengthDecode", SF_RunLengthDecode::factory}, - {"/DCTDecode", SF_DCTDecode::factory}, - {"/ASCII85Decode", SF_ASCII85Decode::factory}, - {"/ASCIIHexDecode", SF_ASCIIHexDecode::factory}, -}; +std::function()> +QPDF_Stream::Members::filter_factory(std::string const& name) const +{ + auto e_name = expand_filter_name(name); + if (e_name == "/Crypt") { + return []() { return std::make_shared(); }; + } + if (e_name == "/FlateDecode") { + return SF_FlateLzwDecode::flate_factory; + } + if (e_name == "/LZWDecode") { + return SF_FlateLzwDecode::lzw_factory; + } + if (e_name == "/RunLengthDecode") { + return SF_RunLengthDecode::factory; + } + if (e_name == "/DCTDecode") { + return SF_DCTDecode::factory; + } + if (e_name == "/ASCII85Decode") { + return SF_ASCII85Decode::factory; + } + if (e_name == "/RunLengthDecode") { + return SF_RunLengthDecode::factory; + } + if (e_name == "/DCTDecode") { + return SF_DCTDecode::factory; + } + if (e_name == "/ASCII85Decode") { + return SF_ASCII85Decode::factory; + } + if (e_name == "/ASCIIHexDecode") { + return SF_ASCIIHexDecode::factory; + } + + auto ff = filter_factories.find(e_name); + if (ff == filter_factories.end()) { + return nullptr; + } + return ff->second; +} Stream::Stream( QPDF& qpdf, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length) : @@ -316,52 +350,43 @@ Stream::filterable( auto s = stream(); // Check filters - QPDFObjectHandle filter_obj = s->stream_dict.getKey("/Filter"); - bool filters_okay = true; + auto filter_obj = s->stream_dict.getKey("/Filter"); std::vector filter_names; if (filter_obj.isNull()) { // No filters - } else if (filter_obj.isName()) { + return true; + } + if (filter_obj.isName()) { // One filter - filter_names.emplace_back(s->expand_filter_name(filter_obj.getName())); + filter_names.emplace_back(filter_obj.getName()); } else if (filter_obj.isArray()) { // Potentially multiple filters int n = filter_obj.getArrayNItems(); for (int i = 0; i < n; ++i) { QPDFObjectHandle item = filter_obj.getArrayItem(i); - if (item.isName()) { - filter_names.emplace_back(s->expand_filter_name(filter_obj.getName())); - } else { - filters_okay = false; + if (!item.isName()) { + warn("stream filter type is not name or array"); + return false; } + filter_names.emplace_back(item.getName()); } } else { - filters_okay = false; - } - - if (!filters_okay) { - QTC::TC("qpdf", "QPDF_Stream invalid filter"); warn("stream filter type is not name or array"); return false; } - bool filterable = true; - for (auto& filter_name: filter_names) { - auto ff = filter_factories.find(filter_name); - if (ff == filter_factories.end()) { - filterable = false; - } else { - filters.push_back((ff->second)()); + if (auto ff = s->filter_factory(filter_name)) { + filters.emplace_back(ff()); + continue; } - } - - if (!filterable) { return false; } + bool filterable = true; + // filters now contains a list of filters to be applied in order. See which ones we can support. // See if we can support any decode parameters that are specified. diff --git a/libqpdf/qpdf/QPDFObjectHandle_private.hh b/libqpdf/qpdf/QPDFObjectHandle_private.hh index a26d54f..9b4b536 100644 --- a/libqpdf/qpdf/QPDFObjectHandle_private.hh +++ b/libqpdf/qpdf/QPDFObjectHandle_private.hh @@ -321,8 +321,6 @@ namespace qpdf void warn(std::string const& message); static std::map filter_abbreviations; - static std::map()>> - filter_factories; }; template diff --git a/libqpdf/qpdf/QPDFObject_private.hh b/libqpdf/qpdf/QPDFObject_private.hh index fb08200..8518665 100644 --- a/libqpdf/qpdf/QPDFObject_private.hh +++ b/libqpdf/qpdf/QPDFObject_private.hh @@ -227,6 +227,8 @@ class QPDF_Stream final std::shared_ptr stream_provider; std::vector> token_filters; std::string expand_filter_name(std::string const& name) const; + std::function()> + filter_factory(std::string const& name) const; }; friend class QPDFObject; diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index 0d4fa26..42f4555 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -77,7 +77,6 @@ QPDFTokenizer bad hexstring 2nd character 0 QPDFTokenizer null in name 0 QPDFTokenizer bad name 1 0 QPDFTokenizer bad name 2 0 -QPDF_Stream invalid filter 0 QPDF UseOutlines but no Outlines 0 QPDFObjectHandle makeDirect loop 0 QPDFObjectHandle copy stream 1 -- libgit2 0.21.4