From 0a470d2daf8ec8a1ba0abfea053af4b4d0955ff6 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Thu, 31 Jan 2019 16:07:33 -0500 Subject: Don't optimize non-8-bit images Also add test cases for additional coverage on image optimization. --- ChangeLog | 4 ++ TODO | 5 --- manual/qpdf-manual.xml | 7 +++ qpdf/qpdf.cc | 13 ++++++ qpdf/qpdf.testcov | 2 + qpdf/qtest/qpdf.test | 2 + .../qpdf/optimize-images-unsupported-json.out | 49 +++++++++++++++++++++ qpdf/qtest/qpdf/optimize-images-unsupported.out | 3 ++ qpdf/qtest/qpdf/unsupported-optimization.pdf | Bin 0 -> 1621 bytes 9 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 qpdf/qtest/qpdf/optimize-images-unsupported-json.out create mode 100644 qpdf/qtest/qpdf/optimize-images-unsupported.out create mode 100644 qpdf/qtest/qpdf/unsupported-optimization.pdf diff --git a/ChangeLog b/ChangeLog index 120ecd2f..01b98a93 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2019-01-31 Jay Berkenbilt + * Bug fix: do better pre-checks on images before optimizing; + refuse to optimize images that can't be converted to JPEG because + of colorspace or depth. + * Add new options --externalize-inline-images, which converts inline images larger than a specified size to regular images, and --ii-min-bytes, which tweaks that size. diff --git a/TODO b/TODO index 451069d9..0bd28ad7 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,3 @@ -Now -=== - -* Deal with compiler warnings - Soon ==== diff --git a/manual/qpdf-manual.xml b/manual/qpdf-manual.xml index 834942ff..1b4dd31b 100644 --- a/manual/qpdf-manual.xml +++ b/manual/qpdf-manual.xml @@ -4454,6 +4454,13 @@ print "\n"; suite and properly handled. + + + When optimizing images, detect and refuse to optimize + images that can't be converted to JPEG because of bit depth + or color space. + + Linearization and page manipulation APIs now detect and diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc index 12672e21..6db07432 100644 --- a/qpdf/qpdf.cc +++ b/qpdf/qpdf.cc @@ -3816,6 +3816,18 @@ ImageOptimizer::makePipeline(std::string const& description, Pipeline* next) } return result; } + QPDFObjectHandle components_obj = dict.getKey("/BitsPerComponent"); + if (! (components_obj.isInteger() && (components_obj.getIntValue() == 8))) + { + QTC::TC("qpdf", "qpdf image optimize bits per component"); + if (o.verbose && (! description.empty())) + { + std::cout << whoami << ": " << description + << ": not optimizing because image has other than" + << " 8 bits per component" << std::endl; + } + return result; + } // Files have been seen in the wild whose width and height are // floating point, which is goofy, but we can deal with it. JDIMENSION w = static_cast( @@ -3844,6 +3856,7 @@ ImageOptimizer::makePipeline(std::string const& description, Pipeline* next) } else { + QTC::TC("qpdf", "qpdf image optimize colorspace"); if (o.verbose && (! description.empty())) { std::cout << whoami << ": " << description diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index b7d2b704..b62dc663 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -437,3 +437,5 @@ QPDFTokenizer inline image at EOF the old way 0 QPDFTokenizer found EI after more than one try 0 QPDFPageObjectHelper externalize inline image 0 QPDFPageObjectHelper keep inline image 0 +qpdf image optimize colorspace 0 +qpdf image optimize bits per component 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 4862f02f..762c3fc5 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -2088,6 +2088,8 @@ my @image_opt = ( '--oi-min-width=0 --oi-min-height=0 --oi-min-area=0 --ii-min-bytes=0'], ['large-inline-image', 'inline-images-keep-some', ''], ['large-inline-image', 'inline-images-keep-all', '--keep-inline-images'], + ['unsupported-optimization', 'unsupported', + '--oi-min-width=0 --oi-min-height=0 --oi-min-area=0'], ); $n_tests += 2 * scalar(@image_opt); diff --git a/qpdf/qtest/qpdf/optimize-images-unsupported-json.out b/qpdf/qtest/qpdf/optimize-images-unsupported-json.out new file mode 100644 index 00000000..78026973 --- /dev/null +++ b/qpdf/qtest/qpdf/optimize-images-unsupported-json.out @@ -0,0 +1,49 @@ +{ + "pages": [ + { + "contents": [ + "4 0 R" + ], + "images": [ + { + "bitspercomponent": 1, + "colorspace": "/DeviceGray", + "decodeparms": [ + null + ], + "filter": [ + "/FlateDecode" + ], + "filterable": true, + "height": 200, + "name": "/Im1", + "object": "6 0 R", + "width": 100 + }, + { + "bitspercomponent": 8, + "colorspace": "/XeviceGray", + "decodeparms": [ + null + ], + "filter": [ + "/FlateDecode" + ], + "filterable": true, + "height": 200, + "name": "/Im2", + "object": "7 0 R", + "width": 200 + } + ], + "label": null, + "object": "3 0 R", + "outlines": [], + "pageposfrom1": 1 + } + ], + "parameters": { + "decodelevel": "generalized" + }, + "version": 1 +} diff --git a/qpdf/qtest/qpdf/optimize-images-unsupported.out b/qpdf/qtest/qpdf/optimize-images-unsupported.out new file mode 100644 index 00000000..2b281039 --- /dev/null +++ b/qpdf/qtest/qpdf/optimize-images-unsupported.out @@ -0,0 +1,3 @@ +qpdf: image /Im1 on page 1: not optimizing because image has other than 8 bits per component +qpdf: image /Im2 on page 1: not optimizing because qpdf can't optimize images with this colorspace +qpdf: wrote file a.pdf diff --git a/qpdf/qtest/qpdf/unsupported-optimization.pdf b/qpdf/qtest/qpdf/unsupported-optimization.pdf new file mode 100644 index 00000000..51816b34 Binary files /dev/null and b/qpdf/qtest/qpdf/unsupported-optimization.pdf differ -- cgit v1.2.3-54-g00ecf