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,6 +78,9 @@ namespace
78 Stream stream; 78 Stream stream;
79 qpdf_stream_decode_level_e decode_level; 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 } // namespace 84 } // namespace
82 85
83 std::string 86 std::string
@@ -110,16 +113,47 @@ QPDF_Stream::Members::expand_filter_name(std::string const&amp; name) const @@ -110,16 +113,47 @@ QPDF_Stream::Members::expand_filter_name(std::string const&amp; name) const
110 return name; 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 Stream::Stream( 158 Stream::Stream(
125 QPDF& qpdf, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length) : 159 QPDF& qpdf, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length) :
@@ -316,52 +350,43 @@ Stream::filterable( @@ -316,52 +350,43 @@ Stream::filterable(
316 auto s = stream(); 350 auto s = stream();
317 // Check filters 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 std::vector<std::string> filter_names; 355 std::vector<std::string> filter_names;
323 356
324 if (filter_obj.isNull()) { 357 if (filter_obj.isNull()) {
325 // No filters 358 // No filters
326 - } else if (filter_obj.isName()) { 359 + return true;
  360 + }
  361 + if (filter_obj.isName()) {
327 // One filter 362 // One filter
328 - filter_names.emplace_back(s->expand_filter_name(filter_obj.getName())); 363 + filter_names.emplace_back(filter_obj.getName());
329 } else if (filter_obj.isArray()) { 364 } else if (filter_obj.isArray()) {
330 // Potentially multiple filters 365 // Potentially multiple filters
331 int n = filter_obj.getArrayNItems(); 366 int n = filter_obj.getArrayNItems();
332 for (int i = 0; i < n; ++i) { 367 for (int i = 0; i < n; ++i) {
333 QPDFObjectHandle item = filter_obj.getArrayItem(i); 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 } else { 375 } else {
341 - filters_okay = false;  
342 - }  
343 -  
344 - if (!filters_okay) {  
345 - QTC::TC("qpdf", "QPDF_Stream invalid filter");  
346 warn("stream filter type is not name or array"); 376 warn("stream filter type is not name or array");
347 return false; 377 return false;
348 } 378 }
349 379
350 - bool filterable = true;  
351 -  
352 for (auto& filter_name: filter_names) { 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 return false; 385 return false;
363 } 386 }
364 387
  388 + bool filterable = true;
  389 +
365 // filters now contains a list of filters to be applied in order. See which ones we can support. 390 // filters now contains a list of filters to be applied in order. See which ones we can support.
366 391
367 // See if we can support any decode parameters that are specified. 392 // See if we can support any decode parameters that are specified.
libqpdf/qpdf/QPDFObjectHandle_private.hh
@@ -321,8 +321,6 @@ namespace qpdf @@ -321,8 +321,6 @@ namespace qpdf
321 void warn(std::string const& message); 321 void warn(std::string const& message);
322 322
323 static std::map<std::string, std::string> filter_abbreviations; 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 template <typename T> 326 template <typename T>
libqpdf/qpdf/QPDFObject_private.hh
@@ -227,6 +227,8 @@ class QPDF_Stream final @@ -227,6 +227,8 @@ class QPDF_Stream final
227 std::shared_ptr<QPDFObjectHandle::StreamDataProvider> stream_provider; 227 std::shared_ptr<QPDFObjectHandle::StreamDataProvider> stream_provider;
228 std::vector<std::shared_ptr<QPDFObjectHandle::TokenFilter>> token_filters; 228 std::vector<std::shared_ptr<QPDFObjectHandle::TokenFilter>> token_filters;
229 std::string expand_filter_name(std::string const& name) const; 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 friend class QPDFObject; 234 friend class QPDFObject;
qpdf/qpdf.testcov
@@ -77,7 +77,6 @@ QPDFTokenizer bad hexstring 2nd character 0 @@ -77,7 +77,6 @@ QPDFTokenizer bad hexstring 2nd character 0
77 QPDFTokenizer null in name 0 77 QPDFTokenizer null in name 0
78 QPDFTokenizer bad name 1 0 78 QPDFTokenizer bad name 1 0
79 QPDFTokenizer bad name 2 0 79 QPDFTokenizer bad name 2 0
80 -QPDF_Stream invalid filter 0  
81 QPDF UseOutlines but no Outlines 0 80 QPDF UseOutlines but no Outlines 0
82 QPDFObjectHandle makeDirect loop 0 81 QPDFObjectHandle makeDirect loop 0
83 QPDFObjectHandle copy stream 1 82 QPDFObjectHandle copy stream 1