Commit 772cd8d5d2449025d0b0b000e054ddb8a5346d0a
1 parent
ab806259
Refactor stream filter handling for improved modularity
Moved stream filter factory logic into `Members::filter_factory` for better encapsulation and maintainability. Removed redundant static filter factory map and improved error handling for invalid stream filter configurations.
Showing
4 changed files
with
60 additions
and
36 deletions
libqpdf/QPDF_Stream.cc
| ... | ... | @@ -78,6 +78,9 @@ namespace |
| 78 | 78 | Stream stream; |
| 79 | 79 | qpdf_stream_decode_level_e decode_level; |
| 80 | 80 | }; |
| 81 | + | |
| 82 | + /// User defined streamfilter factories | |
| 83 | + std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>> filter_factories; | |
| 81 | 84 | } // namespace |
| 82 | 85 | |
| 83 | 86 | std::string |
| ... | ... | @@ -110,16 +113,47 @@ QPDF_Stream::Members::expand_filter_name(std::string const& name) const |
| 110 | 113 | return name; |
| 111 | 114 | }; |
| 112 | 115 | |
| 113 | -std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>> Stream::filter_factories = | |
| 114 | - { | |
| 115 | - {"/Crypt", []() { return std::make_shared<SF_Crypt>(); }}, | |
| 116 | - {"/FlateDecode", SF_FlateLzwDecode::flate_factory}, | |
| 117 | - {"/LZWDecode", SF_FlateLzwDecode::lzw_factory}, | |
| 118 | - {"/RunLengthDecode", SF_RunLengthDecode::factory}, | |
| 119 | - {"/DCTDecode", SF_DCTDecode::factory}, | |
| 120 | - {"/ASCII85Decode", SF_ASCII85Decode::factory}, | |
| 121 | - {"/ASCIIHexDecode", SF_ASCIIHexDecode::factory}, | |
| 122 | -}; | |
| 116 | +std::function<std::shared_ptr<QPDFStreamFilter>()> | |
| 117 | +QPDF_Stream::Members::filter_factory(std::string const& name) const | |
| 118 | +{ | |
| 119 | + auto e_name = expand_filter_name(name); | |
| 120 | + if (e_name == "/Crypt") { | |
| 121 | + return []() { return std::make_shared<SF_Crypt>(); }; | |
| 122 | + } | |
| 123 | + if (e_name == "/FlateDecode") { | |
| 124 | + return SF_FlateLzwDecode::flate_factory; | |
| 125 | + } | |
| 126 | + if (e_name == "/LZWDecode") { | |
| 127 | + return SF_FlateLzwDecode::lzw_factory; | |
| 128 | + } | |
| 129 | + if (e_name == "/RunLengthDecode") { | |
| 130 | + return SF_RunLengthDecode::factory; | |
| 131 | + } | |
| 132 | + if (e_name == "/DCTDecode") { | |
| 133 | + return SF_DCTDecode::factory; | |
| 134 | + } | |
| 135 | + if (e_name == "/ASCII85Decode") { | |
| 136 | + return SF_ASCII85Decode::factory; | |
| 137 | + } | |
| 138 | + if (e_name == "/RunLengthDecode") { | |
| 139 | + return SF_RunLengthDecode::factory; | |
| 140 | + } | |
| 141 | + if (e_name == "/DCTDecode") { | |
| 142 | + return SF_DCTDecode::factory; | |
| 143 | + } | |
| 144 | + if (e_name == "/ASCII85Decode") { | |
| 145 | + return SF_ASCII85Decode::factory; | |
| 146 | + } | |
| 147 | + if (e_name == "/ASCIIHexDecode") { | |
| 148 | + return SF_ASCIIHexDecode::factory; | |
| 149 | + } | |
| 150 | + | |
| 151 | + auto ff = filter_factories.find(e_name); | |
| 152 | + if (ff == filter_factories.end()) { | |
| 153 | + return nullptr; | |
| 154 | + } | |
| 155 | + return ff->second; | |
| 156 | +} | |
| 123 | 157 | |
| 124 | 158 | Stream::Stream( |
| 125 | 159 | QPDF& qpdf, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length) : |
| ... | ... | @@ -316,52 +350,43 @@ Stream::filterable( |
| 316 | 350 | auto s = stream(); |
| 317 | 351 | // Check filters |
| 318 | 352 | |
| 319 | - QPDFObjectHandle filter_obj = s->stream_dict.getKey("/Filter"); | |
| 320 | - bool filters_okay = true; | |
| 353 | + auto filter_obj = s->stream_dict.getKey("/Filter"); | |
| 321 | 354 | |
| 322 | 355 | std::vector<std::string> filter_names; |
| 323 | 356 | |
| 324 | 357 | if (filter_obj.isNull()) { |
| 325 | 358 | // No filters |
| 326 | - } else if (filter_obj.isName()) { | |
| 359 | + return true; | |
| 360 | + } | |
| 361 | + if (filter_obj.isName()) { | |
| 327 | 362 | // One filter |
| 328 | - filter_names.emplace_back(s->expand_filter_name(filter_obj.getName())); | |
| 363 | + filter_names.emplace_back(filter_obj.getName()); | |
| 329 | 364 | } else if (filter_obj.isArray()) { |
| 330 | 365 | // Potentially multiple filters |
| 331 | 366 | int n = filter_obj.getArrayNItems(); |
| 332 | 367 | for (int i = 0; i < n; ++i) { |
| 333 | 368 | QPDFObjectHandle item = filter_obj.getArrayItem(i); |
| 334 | - if (item.isName()) { | |
| 335 | - filter_names.emplace_back(s->expand_filter_name(filter_obj.getName())); | |
| 336 | - } else { | |
| 337 | - filters_okay = false; | |
| 369 | + if (!item.isName()) { | |
| 370 | + warn("stream filter type is not name or array"); | |
| 371 | + return false; | |
| 338 | 372 | } |
| 373 | + filter_names.emplace_back(item.getName()); | |
| 339 | 374 | } |
| 340 | 375 | } else { |
| 341 | - filters_okay = false; | |
| 342 | - } | |
| 343 | - | |
| 344 | - if (!filters_okay) { | |
| 345 | - QTC::TC("qpdf", "QPDF_Stream invalid filter"); | |
| 346 | 376 | warn("stream filter type is not name or array"); |
| 347 | 377 | return false; |
| 348 | 378 | } |
| 349 | 379 | |
| 350 | - bool filterable = true; | |
| 351 | - | |
| 352 | 380 | for (auto& filter_name: filter_names) { |
| 353 | - auto ff = filter_factories.find(filter_name); | |
| 354 | - if (ff == filter_factories.end()) { | |
| 355 | - filterable = false; | |
| 356 | - } else { | |
| 357 | - filters.push_back((ff->second)()); | |
| 381 | + if (auto ff = s->filter_factory(filter_name)) { | |
| 382 | + filters.emplace_back(ff()); | |
| 383 | + continue; | |
| 358 | 384 | } |
| 359 | - } | |
| 360 | - | |
| 361 | - if (!filterable) { | |
| 362 | 385 | return false; |
| 363 | 386 | } |
| 364 | 387 | |
| 388 | + bool filterable = true; | |
| 389 | + | |
| 365 | 390 | // filters now contains a list of filters to be applied in order. See which ones we can support. |
| 366 | 391 | |
| 367 | 392 | // See if we can support any decode parameters that are specified. | ... | ... |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| ... | ... | @@ -321,8 +321,6 @@ namespace qpdf |
| 321 | 321 | void warn(std::string const& message); |
| 322 | 322 | |
| 323 | 323 | static std::map<std::string, std::string> filter_abbreviations; |
| 324 | - static std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>> | |
| 325 | - filter_factories; | |
| 326 | 324 | }; |
| 327 | 325 | |
| 328 | 326 | template <typename T> | ... | ... |
libqpdf/qpdf/QPDFObject_private.hh
| ... | ... | @@ -227,6 +227,8 @@ class QPDF_Stream final |
| 227 | 227 | std::shared_ptr<QPDFObjectHandle::StreamDataProvider> stream_provider; |
| 228 | 228 | std::vector<std::shared_ptr<QPDFObjectHandle::TokenFilter>> token_filters; |
| 229 | 229 | std::string expand_filter_name(std::string const& name) const; |
| 230 | + std::function<std::shared_ptr<QPDFStreamFilter>()> | |
| 231 | + filter_factory(std::string const& name) const; | |
| 230 | 232 | }; |
| 231 | 233 | |
| 232 | 234 | friend class QPDFObject; | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -77,7 +77,6 @@ QPDFTokenizer bad hexstring 2nd character 0 |
| 77 | 77 | QPDFTokenizer null in name 0 |
| 78 | 78 | QPDFTokenizer bad name 1 0 |
| 79 | 79 | QPDFTokenizer bad name 2 0 |
| 80 | -QPDF_Stream invalid filter 0 | |
| 81 | 80 | QPDF UseOutlines but no Outlines 0 |
| 82 | 81 | QPDFObjectHandle makeDirect loop 0 |
| 83 | 82 | QPDFObjectHandle copy stream 1 | ... | ... |