Commit 772cd8d5d2449025d0b0b000e054ddb8a5346d0a

Authored by m-holger
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.
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&amp; 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
... ...