Commit 0a470d2daf8ec8a1ba0abfea053af4b4d0955ff6

Authored by Jay Berkenbilt
1 parent eb49e07c

Don't optimize non-8-bit images

Also add test cases for additional coverage on image optimization.
ChangeLog
1 2019-01-31 Jay Berkenbilt <ejb@ql.org> 1 2019-01-31 Jay Berkenbilt <ejb@ql.org>
2 2
  3 + * Bug fix: do better pre-checks on images before optimizing;
  4 + refuse to optimize images that can't be converted to JPEG because
  5 + of colorspace or depth.
  6 +
3 * Add new options --externalize-inline-images, which converts 7 * Add new options --externalize-inline-images, which converts
4 inline images larger than a specified size to regular images, and 8 inline images larger than a specified size to regular images, and
5 --ii-min-bytes, which tweaks that size. 9 --ii-min-bytes, which tweaks that size.
1 -Now  
2 -===  
3 -  
4 -* Deal with compiler warnings  
5 -  
6 Soon 1 Soon
7 ==== 2 ====
8 3
manual/qpdf-manual.xml
@@ -4456,6 +4456,13 @@ print &quot;\n&quot;; @@ -4456,6 +4456,13 @@ print &quot;\n&quot;;
4456 </listitem> 4456 </listitem>
4457 <listitem> 4457 <listitem>
4458 <para> 4458 <para>
  4459 + When optimizing images, detect and refuse to optimize
  4460 + images that can't be converted to JPEG because of bit depth
  4461 + or color space.
  4462 + </para>
  4463 + </listitem>
  4464 + <listitem>
  4465 + <para>
4459 Linearization and page manipulation APIs now detect and 4466 Linearization and page manipulation APIs now detect and
4460 recover from files that have duplicate Page objects in the 4467 recover from files that have duplicate Page objects in the
4461 pages tree. 4468 pages tree.
qpdf/qpdf.cc
@@ -3816,6 +3816,18 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next) @@ -3816,6 +3816,18 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
3816 } 3816 }
3817 return result; 3817 return result;
3818 } 3818 }
  3819 + QPDFObjectHandle components_obj = dict.getKey("/BitsPerComponent");
  3820 + if (! (components_obj.isInteger() && (components_obj.getIntValue() == 8)))
  3821 + {
  3822 + QTC::TC("qpdf", "qpdf image optimize bits per component");
  3823 + if (o.verbose && (! description.empty()))
  3824 + {
  3825 + std::cout << whoami << ": " << description
  3826 + << ": not optimizing because image has other than"
  3827 + << " 8 bits per component" << std::endl;
  3828 + }
  3829 + return result;
  3830 + }
3819 // Files have been seen in the wild whose width and height are 3831 // Files have been seen in the wild whose width and height are
3820 // floating point, which is goofy, but we can deal with it. 3832 // floating point, which is goofy, but we can deal with it.
3821 JDIMENSION w = static_cast<JDIMENSION>( 3833 JDIMENSION w = static_cast<JDIMENSION>(
@@ -3844,6 +3856,7 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next) @@ -3844,6 +3856,7 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
3844 } 3856 }
3845 else 3857 else
3846 { 3858 {
  3859 + QTC::TC("qpdf", "qpdf image optimize colorspace");
3847 if (o.verbose && (! description.empty())) 3860 if (o.verbose && (! description.empty()))
3848 { 3861 {
3849 std::cout << whoami << ": " << description 3862 std::cout << whoami << ": " << description
qpdf/qpdf.testcov
@@ -437,3 +437,5 @@ QPDFTokenizer inline image at EOF the old way 0 @@ -437,3 +437,5 @@ QPDFTokenizer inline image at EOF the old way 0
437 QPDFTokenizer found EI after more than one try 0 437 QPDFTokenizer found EI after more than one try 0
438 QPDFPageObjectHelper externalize inline image 0 438 QPDFPageObjectHelper externalize inline image 0
439 QPDFPageObjectHelper keep inline image 0 439 QPDFPageObjectHelper keep inline image 0
  440 +qpdf image optimize colorspace 0
  441 +qpdf image optimize bits per component 0
qpdf/qtest/qpdf.test
@@ -2088,6 +2088,8 @@ my @image_opt = ( @@ -2088,6 +2088,8 @@ my @image_opt = (
2088 '--oi-min-width=0 --oi-min-height=0 --oi-min-area=0 --ii-min-bytes=0'], 2088 '--oi-min-width=0 --oi-min-height=0 --oi-min-area=0 --ii-min-bytes=0'],
2089 ['large-inline-image', 'inline-images-keep-some', ''], 2089 ['large-inline-image', 'inline-images-keep-some', ''],
2090 ['large-inline-image', 'inline-images-keep-all', '--keep-inline-images'], 2090 ['large-inline-image', 'inline-images-keep-all', '--keep-inline-images'],
  2091 + ['unsupported-optimization', 'unsupported',
  2092 + '--oi-min-width=0 --oi-min-height=0 --oi-min-area=0'],
2091 ); 2093 );
2092 2094
2093 $n_tests += 2 * scalar(@image_opt); 2095 $n_tests += 2 * scalar(@image_opt);
qpdf/qtest/qpdf/optimize-images-unsupported-json.out 0 โ†’ 100644
  1 +{
  2 + "pages": [
  3 + {
  4 + "contents": [
  5 + "4 0 R"
  6 + ],
  7 + "images": [
  8 + {
  9 + "bitspercomponent": 1,
  10 + "colorspace": "/DeviceGray",
  11 + "decodeparms": [
  12 + null
  13 + ],
  14 + "filter": [
  15 + "/FlateDecode"
  16 + ],
  17 + "filterable": true,
  18 + "height": 200,
  19 + "name": "/Im1",
  20 + "object": "6 0 R",
  21 + "width": 100
  22 + },
  23 + {
  24 + "bitspercomponent": 8,
  25 + "colorspace": "/XeviceGray",
  26 + "decodeparms": [
  27 + null
  28 + ],
  29 + "filter": [
  30 + "/FlateDecode"
  31 + ],
  32 + "filterable": true,
  33 + "height": 200,
  34 + "name": "/Im2",
  35 + "object": "7 0 R",
  36 + "width": 200
  37 + }
  38 + ],
  39 + "label": null,
  40 + "object": "3 0 R",
  41 + "outlines": [],
  42 + "pageposfrom1": 1
  43 + }
  44 + ],
  45 + "parameters": {
  46 + "decodelevel": "generalized"
  47 + },
  48 + "version": 1
  49 +}
qpdf/qtest/qpdf/optimize-images-unsupported.out 0 โ†’ 100644
  1 +qpdf: image /Im1 on page 1: not optimizing because image has other than 8 bits per component
  2 +qpdf: image /Im2 on page 1: not optimizing because qpdf can't optimize images with this colorspace
  3 +qpdf: wrote file a.pdf
qpdf/qtest/qpdf/unsupported-optimization.pdf 0 โ†’ 100644
No preview for this file type