Commit 2a4495dd8a8bf9f578fc43626a9b039e510aad17

Authored by m-holger
Committed by GitHub
2 parents e9359c72 5c1a5d43

Merge pull request #1457 from m-holger/stream

Refactor Stream::filterable and Stream::pipeStreamData
libqpdf/ContentNormalizer.cc
@@ -71,15 +71,3 @@ ContentNormalizer::handleToken(QPDFTokenizer::Token const& token) @@ -71,15 +71,3 @@ ContentNormalizer::handleToken(QPDFTokenizer::Token const& token)
71 write("\n"); 71 write("\n");
72 } 72 }
73 } 73 }
74 -  
75 -bool  
76 -ContentNormalizer::anyBadTokens() const  
77 -{  
78 - return this->any_bad_tokens;  
79 -}  
80 -  
81 -bool  
82 -ContentNormalizer::lastTokenWasBad() const  
83 -{  
84 - return this->last_token_was_bad;  
85 -}  
libqpdf/QPDF_Stream.cc
@@ -27,33 +27,37 @@ using namespace qpdf; @@ -27,33 +27,37 @@ using namespace qpdf;
27 27
28 namespace 28 namespace
29 { 29 {
30 - class SF_Crypt: public QPDFStreamFilter 30 + class SF_Crypt final: public QPDFStreamFilter
31 { 31 {
32 public: 32 public:
33 SF_Crypt() = default; 33 SF_Crypt() = default;
34 - ~SF_Crypt() override = default; 34 + ~SF_Crypt() final = default;
35 35
36 bool 36 bool
37 - setDecodeParms(QPDFObjectHandle decode_parms) override 37 + setDecodeParms(QPDFObjectHandle decode_parms) final
38 { 38 {
39 - if (decode_parms.isNull()) {  
40 - return true;  
41 - }  
42 - bool filterable = true;  
43 - for (auto const& key: decode_parms.getKeys()) {  
44 - if (((key == "/Type") || (key == "/Name")) &&  
45 - ((!decode_parms.hasKey("/Type")) ||  
46 - decode_parms.isDictionaryOfType("/CryptFilterDecodeParms"))) {  
47 - // we handle this in decryptStream  
48 - } else {  
49 - filterable = false; 39 + // we only validate here - processing happens in decryptStream
  40 + if (auto dict = decode_parms.as_dictionary(optional)) {
  41 + for (auto const& [key, value]: dict) {
  42 + if (key == "/Type" &&
  43 + (value.null() ||
  44 + (value.isName() && value.getName() == "/CryptFilterDecodeParms"))) {
  45 + continue;
  46 + }
  47 + if (key == "/Name") {
  48 + continue;
  49 + }
  50 + if (!value.null()) {
  51 + return false;
  52 + }
50 } 53 }
  54 + return true;
51 } 55 }
52 - return filterable; 56 + return false;
53 } 57 }
54 58
55 Pipeline* 59 Pipeline*
56 - getDecodePipeline(Pipeline*) override 60 + getDecodePipeline(Pipeline*) final
57 { 61 {
58 // Not used -- handled by pipeStreamData 62 // Not used -- handled by pipeStreamData
59 return nullptr; 63 return nullptr;
@@ -78,31 +82,67 @@ namespace @@ -78,31 +82,67 @@ namespace
78 Stream stream; 82 Stream stream;
79 qpdf_stream_decode_level_e decode_level; 83 qpdf_stream_decode_level_e decode_level;
80 }; 84 };
  85 +
  86 + /// User defined streamfilter factories
  87 + std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>> filter_factories;
81 } // namespace 88 } // namespace
82 89
83 -std::map<std::string, std::string> Stream::filter_abbreviations = { 90 +std::function<std::shared_ptr<QPDFStreamFilter>()>
  91 +QPDF_Stream::Members::filter_factory(std::string const& name) const
  92 +{
  93 + if (name == "/FlateDecode") {
  94 + return SF_FlateLzwDecode::flate_factory;
  95 + }
  96 + if (name == "/Crypt") {
  97 + return []() { return std::make_shared<SF_Crypt>(); };
  98 + }
  99 + if (name == "/LZWDecode") {
  100 + return SF_FlateLzwDecode::lzw_factory;
  101 + }
  102 + if (name == "/RunLengthDecode") {
  103 + return SF_RunLengthDecode::factory;
  104 + }
  105 + if (name == "/DCTDecode") {
  106 + return SF_DCTDecode::factory;
  107 + }
  108 + if (name == "/ASCII85Decode") {
  109 + return SF_ASCII85Decode::factory;
  110 + }
  111 + if (name == "/ASCIIHexDecode") {
  112 + return SF_ASCIIHexDecode::factory;
  113 + }
84 // The PDF specification provides these filter abbreviations for use in inline images, but 114 // The PDF specification provides these filter abbreviations for use in inline images, but
85 // according to table H.1 in the pre-ISO versions of the PDF specification, Adobe Reader also 115 // according to table H.1 in the pre-ISO versions of the PDF specification, Adobe Reader also
86 // accepts them for stream filters. 116 // accepts them for stream filters.
87 - {"/AHx", "/ASCIIHexDecode"},  
88 - {"/A85", "/ASCII85Decode"},  
89 - {"/LZW", "/LZWDecode"},  
90 - {"/Fl", "/FlateDecode"},  
91 - {"/RL", "/RunLengthDecode"},  
92 - {"/CCF", "/CCITTFaxDecode"},  
93 - {"/DCT", "/DCTDecode"},  
94 -};  
95 -  
96 -std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>> Stream::filter_factories =  
97 - {  
98 - {"/Crypt", []() { return std::make_shared<SF_Crypt>(); }},  
99 - {"/FlateDecode", SF_FlateLzwDecode::flate_factory},  
100 - {"/LZWDecode", SF_FlateLzwDecode::lzw_factory},  
101 - {"/RunLengthDecode", SF_RunLengthDecode::factory},  
102 - {"/DCTDecode", SF_DCTDecode::factory},  
103 - {"/ASCII85Decode", SF_ASCII85Decode::factory},  
104 - {"/ASCIIHexDecode", SF_ASCIIHexDecode::factory},  
105 -}; 117 +
  118 + if (name == "/Fl") {
  119 + return SF_FlateLzwDecode::flate_factory;
  120 + }
  121 + if (name == "/AHx") {
  122 + return SF_ASCIIHexDecode::factory;
  123 + }
  124 + if (name == "/A85") {
  125 + return SF_ASCII85Decode::factory;
  126 + }
  127 + if (name == "/LZW") {
  128 + return SF_FlateLzwDecode::lzw_factory;
  129 + }
  130 + if (name == "/RL") {
  131 + return SF_RunLengthDecode::factory;
  132 + }
  133 + if (name == "/DCT") {
  134 + return SF_DCTDecode::factory;
  135 + }
  136 + if (filter_factories.empty()) {
  137 + return nullptr;
  138 + }
  139 + auto ff =
  140 + name == "/CCF" ? filter_factories.find("/CCITTFaxDecode") : filter_factories.find(name);
  141 + if (ff == filter_factories.end()) {
  142 + return nullptr;
  143 + }
  144 + return ff->second;
  145 +}
106 146
107 Stream::Stream( 147 Stream::Stream(
108 QPDF& qpdf, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length) : 148 QPDF& qpdf, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length) :
@@ -292,112 +332,88 @@ Stream::isRootMetadata() const @@ -292,112 +332,88 @@ Stream::isRootMetadata() const
292 332
293 bool 333 bool
294 Stream::filterable( 334 Stream::filterable(
295 - std::vector<std::shared_ptr<QPDFStreamFilter>>& filters,  
296 - bool& specialized_compression,  
297 - bool& lossy_compression) 335 + qpdf_stream_decode_level_e decode_level,
  336 + std::vector<std::shared_ptr<QPDFStreamFilter>>& filters)
298 { 337 {
299 auto s = stream(); 338 auto s = stream();
300 // Check filters 339 // Check filters
301 340
302 - QPDFObjectHandle filter_obj = s->stream_dict.getKey("/Filter");  
303 - bool filters_okay = true;  
304 -  
305 - std::vector<std::string> filter_names; 341 + auto filter_obj = s->stream_dict.getKey("/Filter");
306 342
307 if (filter_obj.isNull()) { 343 if (filter_obj.isNull()) {
308 // No filters 344 // No filters
309 - } else if (filter_obj.isName()) { 345 + return true;
  346 + }
  347 + if (filter_obj.isName()) {
310 // One filter 348 // One filter
311 - filter_names.push_back(filter_obj.getName());  
312 - } else if (filter_obj.isArray()) { 349 + auto ff = s->filter_factory(filter_obj.getName());
  350 + if (!ff) {
  351 + return false;
  352 + }
  353 + filters.emplace_back(ff());
  354 + } else if (auto array = filter_obj.as_array(strict)) {
313 // Potentially multiple filters 355 // Potentially multiple filters
314 - int n = filter_obj.getArrayNItems();  
315 - for (int i = 0; i < n; ++i) {  
316 - QPDFObjectHandle item = filter_obj.getArrayItem(i);  
317 - if (item.isName()) {  
318 - filter_names.push_back(item.getName());  
319 - } else {  
320 - filters_okay = false; 356 + for (auto const& item: array) {
  357 + if (!item.isName()) {
  358 + warn("stream filter type is not name or array");
  359 + return false;
321 } 360 }
  361 + auto ff = s->filter_factory(item.getName());
  362 + if (!ff) {
  363 + filters.clear();
  364 + return false;
  365 + }
  366 + filters.emplace_back(ff());
322 } 367 }
323 } else { 368 } else {
324 - filters_okay = false;  
325 - }  
326 -  
327 - if (!filters_okay) {  
328 - QTC::TC("qpdf", "QPDF_Stream invalid filter");  
329 warn("stream filter type is not name or array"); 369 warn("stream filter type is not name or array");
330 return false; 370 return false;
331 } 371 }
332 372
333 - bool filterable = true; 373 + // filters now contains a list of filters to be applied in order. See which ones we can support.
  374 + // See if we can support any decode parameters that are specified.
334 375
335 - for (auto& filter_name: filter_names) {  
336 - if (filter_abbreviations.count(filter_name)) {  
337 - QTC::TC("qpdf", "QPDF_Stream expand filter abbreviation");  
338 - filter_name = filter_abbreviations[filter_name];  
339 - } 376 + auto decode_obj = s->stream_dict.getKey("/DecodeParms");
340 377
341 - auto ff = filter_factories.find(filter_name);  
342 - if (ff == filter_factories.end()) {  
343 - filterable = false;  
344 - } else {  
345 - filters.push_back((ff->second)()); 378 + auto can_filter = // linebreak
  379 + [](auto d_level, auto& filter, auto& d_obj) -> bool {
  380 + if (!filter.setDecodeParms(d_obj) ||
  381 + (d_level < qpdf_dl_all && filter.isLossyCompression()) ||
  382 + (d_level < qpdf_dl_specialized && filter.isSpecializedCompression())) {
  383 + return false;
346 } 384 }
347 - }  
348 -  
349 - if (!filterable) {  
350 - return false;  
351 - }  
352 -  
353 - // filters now contains a list of filters to be applied in order. See which ones we can support. 385 + return true;
  386 + };
354 387
355 - // See if we can support any decode parameters that are specified. 388 + auto decode_array = decode_obj.as_array(strict);
  389 + if (!decode_array || decode_array.size() == 0) {
  390 + if (decode_array) {
  391 + decode_obj = QPDFObjectHandle::newNull();
  392 + }
356 393
357 - QPDFObjectHandle decode_obj = s->stream_dict.getKey("/DecodeParms");  
358 - std::vector<QPDFObjectHandle> decode_parms;  
359 - if (decode_obj.isArray() && (decode_obj.getArrayNItems() == 0)) {  
360 - decode_obj = QPDFObjectHandle::newNull();  
361 - }  
362 - if (decode_obj.isArray()) {  
363 - for (int i = 0; i < decode_obj.getArrayNItems(); ++i) {  
364 - decode_parms.push_back(decode_obj.getArrayItem(i)); 394 + for (auto& filter: filters) {
  395 + if (!can_filter(decode_level, *filter, decode_obj)) {
  396 + return false;
  397 + }
365 } 398 }
366 } else { 399 } else {
367 - for (unsigned int i = 0; i < filter_names.size(); ++i) {  
368 - decode_parms.push_back(decode_obj); 400 + // Ignore /DecodeParms entirely if /Filters is empty. At least one case of a file whose
  401 + // /DecodeParms was [ << >> ] when /Filters was empty has been seen in the wild.
  402 + if (!filters.empty() && QIntC::to_size(decode_array.size()) != filters.size()) {
  403 + warn("stream /DecodeParms length is inconsistent with filters");
  404 + return false;
369 } 405 }
370 - }  
371 -  
372 - // Ignore /DecodeParms entirely if /Filters is empty. At least one case of a file whose  
373 - // /DecodeParms was [ << >> ] when /Filters was empty has been seen in the wild.  
374 - if ((filters.size() != 0) && (decode_parms.size() != filters.size())) {  
375 - warn("stream /DecodeParms length is inconsistent with filters");  
376 - filterable = false;  
377 - }  
378 -  
379 - if (!filterable) {  
380 - return false;  
381 - }  
382 406
383 - for (size_t i = 0; i < filters.size(); ++i) {  
384 - auto filter = filters.at(i);  
385 - auto decode_item = decode_parms.at(i);  
386 -  
387 - if (filter->setDecodeParms(decode_item)) {  
388 - if (filter->isSpecializedCompression()) {  
389 - specialized_compression = true;  
390 - }  
391 - if (filter->isLossyCompression()) {  
392 - specialized_compression = true;  
393 - lossy_compression = true; 407 + int i = -1;
  408 + for (auto& filter: filters) {
  409 + auto d_obj = decode_array.at(++i).second;
  410 + if (!can_filter(decode_level, *filter, d_obj)) {
  411 + return false;
394 } 412 }
395 - } else {  
396 - filterable = false;  
397 } 413 }
398 } 414 }
399 415
400 - return filterable; 416 + return true;
401 } 417 }
402 418
403 bool 419 bool
@@ -411,33 +427,17 @@ Stream::pipeStreamData( @@ -411,33 +427,17 @@ Stream::pipeStreamData(
411 { 427 {
412 auto s = stream(); 428 auto s = stream();
413 std::vector<std::shared_ptr<QPDFStreamFilter>> filters; 429 std::vector<std::shared_ptr<QPDFStreamFilter>> filters;
414 - bool specialized_compression = false;  
415 - bool lossy_compression = false;  
416 bool ignored; 430 bool ignored;
417 - if (filterp == nullptr) { 431 + if (!filterp) {
418 filterp = &ignored; 432 filterp = &ignored;
419 } 433 }
420 bool& filter = *filterp; 434 bool& filter = *filterp;
421 - filter = (!((encode_flags == 0) && (decode_level == qpdf_dl_none)));  
422 - bool success = true; 435 + filter = encode_flags || decode_level != qpdf_dl_none;
423 if (filter) { 436 if (filter) {
424 - filter = filterable(filters, specialized_compression, lossy_compression);  
425 - if ((decode_level < qpdf_dl_all) && lossy_compression) {  
426 - filter = false;  
427 - }  
428 - if ((decode_level < qpdf_dl_specialized) && specialized_compression) {  
429 - filter = false;  
430 - }  
431 - QTC::TC(  
432 - "qpdf",  
433 - "QPDF_Stream special filters",  
434 - (!filter) ? 0  
435 - : lossy_compression ? 1  
436 - : specialized_compression ? 2  
437 - : 3); 437 + filter = filterable(decode_level, filters);
438 } 438 }
439 439
440 - if (pipeline == nullptr) { 440 + if (!pipeline) {
441 QTC::TC("qpdf", "QPDF_Stream pipeStreamData with null pipeline"); 441 QTC::TC("qpdf", "QPDF_Stream pipeStreamData with null pipeline");
442 // Return value is whether we can filter in this case. 442 // Return value is whether we can filter in this case.
443 return filter; 443 return filter;
@@ -446,40 +446,37 @@ Stream::pipeStreamData( @@ -446,40 +446,37 @@ Stream::pipeStreamData(
446 // Construct the pipeline in reverse order. Force pipelines we create to be deleted when this 446 // Construct the pipeline in reverse order. Force pipelines we create to be deleted when this
447 // function finishes. Pipelines created by QPDFStreamFilter objects will be deleted by those 447 // function finishes. Pipelines created by QPDFStreamFilter objects will be deleted by those
448 // objects. 448 // objects.
449 - std::vector<std::shared_ptr<Pipeline>> to_delete; 449 + std::vector<std::unique_ptr<Pipeline>> to_delete;
450 450
451 - std::shared_ptr<ContentNormalizer> normalizer;  
452 - std::shared_ptr<Pipeline> new_pipeline; 451 + ContentNormalizer normalizer;
453 if (filter) { 452 if (filter) {
454 if (encode_flags & qpdf_ef_compress) { 453 if (encode_flags & qpdf_ef_compress) {
455 - new_pipeline =  
456 - std::make_shared<Pl_Flate>("compress stream", pipeline, Pl_Flate::a_deflate);  
457 - to_delete.push_back(new_pipeline); 454 + auto new_pipeline =
  455 + std::make_unique<Pl_Flate>("compress stream", pipeline, Pl_Flate::a_deflate);
458 pipeline = new_pipeline.get(); 456 pipeline = new_pipeline.get();
  457 + to_delete.push_back(std::move(new_pipeline));
459 } 458 }
460 459
461 if (encode_flags & qpdf_ef_normalize) { 460 if (encode_flags & qpdf_ef_normalize) {
462 - normalizer = std::make_shared<ContentNormalizer>();  
463 - new_pipeline =  
464 - std::make_shared<Pl_QPDFTokenizer>("normalizer", normalizer.get(), pipeline);  
465 - to_delete.push_back(new_pipeline); 461 + auto new_pipeline =
  462 + std::make_unique<Pl_QPDFTokenizer>("normalizer", &normalizer, pipeline);
466 pipeline = new_pipeline.get(); 463 pipeline = new_pipeline.get();
  464 + to_delete.push_back(std::move(new_pipeline));
467 } 465 }
468 466
469 for (auto iter = s->token_filters.rbegin(); iter != s->token_filters.rend(); ++iter) { 467 for (auto iter = s->token_filters.rbegin(); iter != s->token_filters.rend(); ++iter) {
470 - new_pipeline =  
471 - std::make_shared<Pl_QPDFTokenizer>("token filter", (*iter).get(), pipeline);  
472 - to_delete.push_back(new_pipeline); 468 + auto new_pipeline =
  469 + std::make_unique<Pl_QPDFTokenizer>("token filter", (*iter).get(), pipeline);
473 pipeline = new_pipeline.get(); 470 pipeline = new_pipeline.get();
  471 + to_delete.push_back(std::move(new_pipeline));
474 } 472 }
475 473
476 for (auto f_iter = filters.rbegin(); f_iter != filters.rend(); ++f_iter) { 474 for (auto f_iter = filters.rbegin(); f_iter != filters.rend(); ++f_iter) {
477 - auto decode_pipeline = (*f_iter)->getDecodePipeline(pipeline);  
478 - if (decode_pipeline) { 475 + if (auto decode_pipeline = (*f_iter)->getDecodePipeline(pipeline)) {
479 pipeline = decode_pipeline; 476 pipeline = decode_pipeline;
480 } 477 }
481 auto* flate = dynamic_cast<Pl_Flate*>(pipeline); 478 auto* flate = dynamic_cast<Pl_Flate*>(pipeline);
482 - if (flate != nullptr) { 479 + if (flate) {
483 flate->setWarnCallback([this](char const* msg, int code) { warn(msg); }); 480 flate->setWarnCallback([this](char const* msg, int code) { warn(msg); });
484 } 481 }
485 } 482 }
@@ -495,18 +492,15 @@ Stream::pipeStreamData( @@ -495,18 +492,15 @@ Stream::pipeStreamData(
495 if (!s->stream_provider->provideStreamData( 492 if (!s->stream_provider->provideStreamData(
496 obj->getObjGen(), &count, suppress_warnings, will_retry)) { 493 obj->getObjGen(), &count, suppress_warnings, will_retry)) {
497 filter = false; 494 filter = false;
498 - success = false; 495 + return false;
499 } 496 }
500 } else { 497 } else {
501 s->stream_provider->provideStreamData(obj->getObjGen(), &count); 498 s->stream_provider->provideStreamData(obj->getObjGen(), &count);
502 } 499 }
503 qpdf_offset_t actual_length = count.getCount(); 500 qpdf_offset_t actual_length = count.getCount();
504 - qpdf_offset_t desired_length = 0;  
505 - if (success && s->stream_dict.hasKey("/Length")) {  
506 - desired_length = s->stream_dict.getKey("/Length").getIntValue();  
507 - if (actual_length == desired_length) {  
508 - QTC::TC("qpdf", "QPDF_Stream pipe use stream provider");  
509 - } else { 501 + if (s->stream_dict.hasKey("/Length")) {
  502 + auto desired_length = s->stream_dict.getKey("/Length").getIntValue();
  503 + if (actual_length != desired_length) {
510 QTC::TC("qpdf", "QPDF_Stream provider length mismatch"); 504 QTC::TC("qpdf", "QPDF_Stream provider length mismatch");
511 // This would be caused by programmer error on the part of a library user, not by 505 // This would be caused by programmer error on the part of a library user, not by
512 // invalid input data. 506 // invalid input data.
@@ -515,14 +509,15 @@ Stream::pipeStreamData( @@ -515,14 +509,15 @@ Stream::pipeStreamData(
515 std::to_string(actual_length) + " bytes instead of expected " + 509 std::to_string(actual_length) + " bytes instead of expected " +
516 std::to_string(desired_length) + " bytes"); 510 std::to_string(desired_length) + " bytes");
517 } 511 }
518 - } else if (success) { 512 + } else {
519 QTC::TC("qpdf", "QPDF_Stream provider length not provided"); 513 QTC::TC("qpdf", "QPDF_Stream provider length not provided");
520 s->stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(actual_length)); 514 s->stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(actual_length));
521 } 515 }
522 - } else if (obj->getParsedOffset() == 0) {  
523 - QTC::TC("qpdf", "QPDF_Stream pipe no stream data");  
524 - throw std::logic_error("pipeStreamData called for stream with no data");  
525 } else { 516 } else {
  517 + if (obj->getParsedOffset() == 0) {
  518 + QTC::TC("qpdf", "QPDF_Stream pipe no stream data");
  519 + throw std::logic_error("pipeStreamData called for stream with no data");
  520 + }
526 QTC::TC("qpdf", "QPDF_Stream pipe original stream data"); 521 QTC::TC("qpdf", "QPDF_Stream pipe original stream data");
527 if (!QPDF::Pipe::pipeStreamData( 522 if (!QPDF::Pipe::pipeStreamData(
528 obj->getQPDF(), 523 obj->getQPDF(),
@@ -535,13 +530,13 @@ Stream::pipeStreamData( @@ -535,13 +530,13 @@ Stream::pipeStreamData(
535 suppress_warnings, 530 suppress_warnings,
536 will_retry)) { 531 will_retry)) {
537 filter = false; 532 filter = false;
538 - success = false; 533 + return false;
539 } 534 }
540 } 535 }
541 536
542 - if (filter && (!suppress_warnings) && normalizer.get() && normalizer->anyBadTokens()) { 537 + if (filter && !suppress_warnings && normalizer.anyBadTokens()) {
543 warn("content normalization encountered bad tokens"); 538 warn("content normalization encountered bad tokens");
544 - if (normalizer->lastTokenWasBad()) { 539 + if (normalizer.lastTokenWasBad()) {
545 QTC::TC("qpdf", "QPDF_Stream bad token at end during normalize"); 540 QTC::TC("qpdf", "QPDF_Stream bad token at end during normalize");
546 warn( 541 warn(
547 "normalized content ended with a bad token; you may be able to resolve this by " 542 "normalized content ended with a bad token; you may be able to resolve this by "
@@ -554,7 +549,7 @@ Stream::pipeStreamData( @@ -554,7 +549,7 @@ Stream::pipeStreamData(
554 "in the manual."); 549 "in the manual.");
555 } 550 }
556 551
557 - return success; 552 + return true;
558 } 553 }
559 554
560 void 555 void
libqpdf/SF_FlateLzwDecode.cc
@@ -69,48 +69,37 @@ SF_FlateLzwDecode::setDecodeParms(QPDFObjectHandle decode_parms) @@ -69,48 +69,37 @@ SF_FlateLzwDecode::setDecodeParms(QPDFObjectHandle decode_parms)
69 Pipeline* 69 Pipeline*
70 SF_FlateLzwDecode::getDecodePipeline(Pipeline* next) 70 SF_FlateLzwDecode::getDecodePipeline(Pipeline* next)
71 { 71 {
72 - std::shared_ptr<Pipeline> pipeline; 72 + std::unique_ptr<Pipeline> pipeline;
73 if (predictor >= 10 && predictor <= 15) { 73 if (predictor >= 10 && predictor <= 15) {
74 QTC::TC("qpdf", "SF_FlateLzwDecode PNG filter"); 74 QTC::TC("qpdf", "SF_FlateLzwDecode PNG filter");
75 - pipeline = std::make_shared<Pl_PNGFilter>( 75 + pipeline = std::make_unique<Pl_PNGFilter>(
76 "png decode", 76 "png decode",
77 next, 77 next,
78 Pl_PNGFilter::a_decode, 78 Pl_PNGFilter::a_decode,
79 QIntC::to_uint(columns), 79 QIntC::to_uint(columns),
80 QIntC::to_uint(colors), 80 QIntC::to_uint(colors),
81 QIntC::to_uint(bits_per_component)); 81 QIntC::to_uint(bits_per_component));
82 - pipelines.push_back(pipeline);  
83 next = pipeline.get(); 82 next = pipeline.get();
  83 + pipelines.push_back(std::move(pipeline));
84 } else if (predictor == 2) { 84 } else if (predictor == 2) {
85 QTC::TC("qpdf", "SF_FlateLzwDecode TIFF predictor"); 85 QTC::TC("qpdf", "SF_FlateLzwDecode TIFF predictor");
86 - pipeline = std::make_shared<Pl_TIFFPredictor>( 86 + pipeline = std::make_unique<Pl_TIFFPredictor>(
87 "tiff decode", 87 "tiff decode",
88 next, 88 next,
89 Pl_TIFFPredictor::a_decode, 89 Pl_TIFFPredictor::a_decode,
90 QIntC::to_uint(columns), 90 QIntC::to_uint(columns),
91 QIntC::to_uint(colors), 91 QIntC::to_uint(colors),
92 QIntC::to_uint(bits_per_component)); 92 QIntC::to_uint(bits_per_component));
93 - pipelines.push_back(pipeline);  
94 next = pipeline.get(); 93 next = pipeline.get();
  94 + pipelines.push_back(std::move(pipeline));
95 } 95 }
96 96
97 if (lzw) { 97 if (lzw) {
98 - pipeline = std::make_shared<Pl_LZWDecoder>("lzw decode", next, early_code_change); 98 + pipeline = std::make_unique<Pl_LZWDecoder>("lzw decode", next, early_code_change);
99 } else { 99 } else {
100 - pipeline = std::make_shared<Pl_Flate>("stream inflate", next, Pl_Flate::a_inflate); 100 + pipeline = std::make_unique<Pl_Flate>("stream inflate", next, Pl_Flate::a_inflate);
101 } 101 }
102 - pipelines.push_back(pipeline);  
103 - return pipeline.get();  
104 -}  
105 -  
106 -std::shared_ptr<QPDFStreamFilter>  
107 -SF_FlateLzwDecode::flate_factory()  
108 -{  
109 - return std::make_shared<SF_FlateLzwDecode>(false);  
110 -}  
111 -  
112 -std::shared_ptr<QPDFStreamFilter>  
113 -SF_FlateLzwDecode::lzw_factory()  
114 -{  
115 - return std::make_shared<SF_FlateLzwDecode>(true); 102 + next = pipeline.get();
  103 + pipelines.push_back(std::move(pipeline));
  104 + return next;
116 } 105 }
libqpdf/qpdf/ContentNormalizer.hh
@@ -3,15 +3,23 @@ @@ -3,15 +3,23 @@
3 3
4 #include <qpdf/QPDFObjectHandle.hh> 4 #include <qpdf/QPDFObjectHandle.hh>
5 5
6 -class ContentNormalizer: public QPDFObjectHandle::TokenFilter 6 +class ContentNormalizer final: public QPDFObjectHandle::TokenFilter
7 { 7 {
8 public: 8 public:
9 ContentNormalizer(); 9 ContentNormalizer();
10 - ~ContentNormalizer() override = default;  
11 - void handleToken(QPDFTokenizer::Token const&) override; 10 + ~ContentNormalizer() final = default;
  11 + void handleToken(QPDFTokenizer::Token const&) final;
12 12
13 - bool anyBadTokens() const;  
14 - bool lastTokenWasBad() const; 13 + bool
  14 + anyBadTokens() const
  15 + {
  16 + return any_bad_tokens;
  17 + }
  18 + bool
  19 + lastTokenWasBad() const
  20 + {
  21 + return last_token_was_bad;
  22 + }
15 23
16 private: 24 private:
17 bool any_bad_tokens; 25 bool any_bad_tokens;
libqpdf/qpdf/QPDFObjectHandle_private.hh
@@ -312,17 +312,14 @@ namespace qpdf @@ -312,17 +312,14 @@ namespace qpdf
312 return nullptr; // unreachable 312 return nullptr; // unreachable
313 } 313 }
314 bool filterable( 314 bool filterable(
315 - std::vector<std::shared_ptr<QPDFStreamFilter>>& filters,  
316 - bool& specialized_compression,  
317 - bool& lossy_compression); 315 + qpdf_stream_decode_level_e decode_level,
  316 + std::vector<std::shared_ptr<QPDFStreamFilter>>& filters);
318 void replaceFilterData( 317 void replaceFilterData(
319 QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length); 318 QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length);
320 319
321 void warn(std::string const& message); 320 void warn(std::string const& message);
322 321
323 static std::map<std::string, std::string> filter_abbreviations; 322 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 }; 323 };
327 324
328 template <typename T> 325 template <typename T>
libqpdf/qpdf/QPDFObject_private.hh
@@ -226,6 +226,8 @@ class QPDF_Stream final @@ -226,6 +226,8 @@ class QPDF_Stream final
226 std::shared_ptr<Buffer> stream_data; 226 std::shared_ptr<Buffer> stream_data;
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::function<std::shared_ptr<QPDFStreamFilter>()>
  230 + filter_factory(std::string const& name) const;
229 }; 231 };
230 232
231 friend class QPDFObject; 233 friend class QPDFObject;
libqpdf/qpdf/SF_FlateLzwDecode.hh
@@ -17,8 +17,16 @@ class SF_FlateLzwDecode final: public QPDFStreamFilter @@ -17,8 +17,16 @@ class SF_FlateLzwDecode final: public QPDFStreamFilter
17 bool setDecodeParms(QPDFObjectHandle decode_parms) final; 17 bool setDecodeParms(QPDFObjectHandle decode_parms) final;
18 Pipeline* getDecodePipeline(Pipeline* next) final; 18 Pipeline* getDecodePipeline(Pipeline* next) final;
19 19
20 - static std::shared_ptr<QPDFStreamFilter> flate_factory();  
21 - static std::shared_ptr<QPDFStreamFilter> lzw_factory(); 20 + static std::shared_ptr<QPDFStreamFilter>
  21 + flate_factory()
  22 + {
  23 + return std::make_shared<SF_FlateLzwDecode>(false);
  24 + }
  25 + static std::shared_ptr<QPDFStreamFilter>
  26 + lzw_factory()
  27 + {
  28 + return std::make_shared<SF_FlateLzwDecode>(true);
  29 + }
22 30
23 private: 31 private:
24 bool lzw{}; 32 bool lzw{};
@@ -28,7 +36,7 @@ class SF_FlateLzwDecode final: public QPDFStreamFilter @@ -28,7 +36,7 @@ class SF_FlateLzwDecode final: public QPDFStreamFilter
28 int colors{1}; 36 int colors{1};
29 int bits_per_component{8}; 37 int bits_per_component{8};
30 bool early_code_change{true}; 38 bool early_code_change{true};
31 - std::vector<std::shared_ptr<Pipeline>> pipelines; 39 + std::vector<std::unique_ptr<Pipeline>> pipelines;
32 }; 40 };
33 41
34 #endif // SF_FLATELZWDECODE_HH 42 #endif // SF_FLATELZWDECODE_HH
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
@@ -168,7 +167,6 @@ qpdf-c called qpdf_has_error 0 @@ -168,7 +167,6 @@ qpdf-c called qpdf_has_error 0
168 qpdf-c called qpdf_get_qpdf_version 0 167 qpdf-c called qpdf_get_qpdf_version 0
169 QPDF_Stream pipe original stream data 0 168 QPDF_Stream pipe original stream data 0
170 QPDF_Stream pipe replaced stream data 0 169 QPDF_Stream pipe replaced stream data 0
171 -QPDF_Stream pipe use stream provider 0  
172 QPDF_Stream provider length mismatch 0 170 QPDF_Stream provider length mismatch 0
173 QPDFObjectHandle newStream 0 171 QPDFObjectHandle newStream 0
174 QPDFObjectHandle newStream with data 0 172 QPDFObjectHandle newStream with data 0
@@ -177,7 +175,6 @@ QPDFObjectHandle prepend page contents 0 @@ -177,7 +175,6 @@ QPDFObjectHandle prepend page contents 0
177 QPDFObjectHandle append page contents 0 175 QPDFObjectHandle append page contents 0
178 QPDF_Stream getRawStreamData 0 176 QPDF_Stream getRawStreamData 0
179 QPDF_Stream getStreamData 0 177 QPDF_Stream getStreamData 0
180 -QPDF_Stream expand filter abbreviation 0  
181 qpdf-c called qpdf_read_memory 0 178 qpdf-c called qpdf_read_memory 0
182 QPDF stream without newline 0 179 QPDF stream without newline 0
183 QPDF stream with CR only 0 180 QPDF stream with CR only 0
@@ -281,7 +278,6 @@ QPDF ignore second extra space in xref entry 0 @@ -281,7 +278,6 @@ QPDF ignore second extra space in xref entry 0
281 QPDF ignore length error xref entry 0 278 QPDF ignore length error xref entry 0
282 QPDF_encryption pad short parameter 0 279 QPDF_encryption pad short parameter 0
283 QPDFObjectHandle found old angle 1 280 QPDFObjectHandle found old angle 1
284 -QPDF_Stream special filters 3  
285 QPDFTokenizer block long token 0 281 QPDFTokenizer block long token 0
286 qpdf-c called qpdf_set_decode_level 0 282 qpdf-c called qpdf_set_decode_level 0
287 qpdf-c called qpdf_set_compress_streams 0 283 qpdf-c called qpdf_set_compress_streams 0