Commit 2794bfb1a665cad93a38144bea0ba0daea7152e7

Authored by Jay Berkenbilt
1 parent dac0598b

Add flags to control zlib compression level (fixes #113)

ChangeLog
1 2019-08-23 Jay Berkenbilt <ejb@ql.org> 1 2019-08-23 Jay Berkenbilt <ejb@ql.org>
2 2
  3 + * Add --recompress-streams option to qpdf and
  4 + QPDFWriter::setRecompressFlate to cause QPDFWriter to recompress
  5 + streams that are already compressed with /FlateDecode.
  6 +
3 * Add option Pl_Flate::setCompressionLevel to globally set the 7 * Add option Pl_Flate::setCompressionLevel to globally set the
4 zlib compression level used by all Pl_Flate pipelines. 8 zlib compression level used by all Pl_Flate pipelines.
5 9
  10 + * Add --compression-level flag to qpdf to set the zlib compression
  11 + level. When combined with --recompress-flate, this will cause most
  12 + of qpdf's streams to use the maximum compression level. This
  13 + results in only a very small amount of savings in size that comes
  14 + at a fairly significant performance cost, but it could be useful
  15 + for archival files or other cases where every byte counts and
  16 + creation time doesn't matter so much. Note that using
  17 + --object-streams=generate in combination with these options gives
  18 + you the biggest advantage. Fixes #113.
  19 +
6 2019-08-22 Jay Berkenbilt <ejb@ql.org> 20 2019-08-22 Jay Berkenbilt <ejb@ql.org>
7 21
8 * In QPDFObjectHandle::ParserCallbacks, in addition to 22 * In QPDFObjectHandle::ParserCallbacks, in addition to
include/qpdf/QPDFWriter.hh
@@ -189,10 +189,11 @@ class QPDFWriter @@ -189,10 +189,11 @@ class QPDFWriter
189 // filters on the input. When combined with 189 // filters on the input. When combined with
190 // setCompressStreams(true), which the default, the effect of this 190 // setCompressStreams(true), which the default, the effect of this
191 // is that streams filtered with these older and less efficient 191 // is that streams filtered with these older and less efficient
192 - // filters will be recompressed with the Flate filter. As a  
193 - // special case, if a stream is already compressed with 192 + // filters will be recompressed with the Flate filter. By default,
  193 + // as a special case, if a stream is already compressed with
194 // FlateDecode and setCompressStreams is enabled, the original 194 // FlateDecode and setCompressStreams is enabled, the original
195 - // compressed data will be preserved. 195 + // compressed data will be preserved. This behavior can be
  196 + // overridden by calling setRecompressFlate(true).
196 // 197 //
197 // qpdf_dl_specialized: In addition to uncompressing the 198 // qpdf_dl_specialized: In addition to uncompressing the
198 // generalized compression formats, supported non-lossy 199 // generalized compression formats, supported non-lossy
@@ -209,6 +210,15 @@ class QPDFWriter @@ -209,6 +210,15 @@ class QPDFWriter
209 QPDF_DLL 210 QPDF_DLL
210 void setDecodeLevel(qpdf_stream_decode_level_e); 211 void setDecodeLevel(qpdf_stream_decode_level_e);
211 212
  213 + // By default, when both the input and output contents of a stream
  214 + // are compressed with Flate, qpdf does not uncompress and
  215 + // recompress the stream. Passing true here causes it to do so.
  216 + // This can be useful if recompressing all streams with a higher
  217 + // compression level, which can be set by calling the static
  218 + // method Pl_Flate::setCompressionLevel.
  219 + QPDF_DLL
  220 + void setRecompressFlate(bool);
  221 +
212 // Set value of content stream normalization. The default is 222 // Set value of content stream normalization. The default is
213 // "false". If true, we attempt to normalize newlines inside of 223 // "false". If true, we attempt to normalize newlines inside of
214 // content streams. Some constructs such as inline images may 224 // content streams. Some constructs such as inline images may
@@ -597,6 +607,7 @@ class QPDFWriter @@ -597,6 +607,7 @@ class QPDFWriter
597 bool compress_streams_set; 607 bool compress_streams_set;
598 qpdf_stream_decode_level_e stream_decode_level; 608 qpdf_stream_decode_level_e stream_decode_level;
599 bool stream_decode_level_set; 609 bool stream_decode_level_set;
  610 + bool recompress_flate;
600 bool qdf_mode; 611 bool qdf_mode;
601 bool preserve_unreferenced_objects; 612 bool preserve_unreferenced_objects;
602 bool newline_before_endstream; 613 bool newline_before_endstream;
libqpdf/QPDFWriter.cc
@@ -37,6 +37,7 @@ QPDFWriter::Members::Members(QPDF&amp; pdf) : @@ -37,6 +37,7 @@ QPDFWriter::Members::Members(QPDF&amp; pdf) :
37 compress_streams_set(false), 37 compress_streams_set(false),
38 stream_decode_level(qpdf_dl_none), 38 stream_decode_level(qpdf_dl_none),
39 stream_decode_level_set(false), 39 stream_decode_level_set(false),
  40 + recompress_flate(false),
40 qdf_mode(false), 41 qdf_mode(false),
41 preserve_unreferenced_objects(false), 42 preserve_unreferenced_objects(false),
42 newline_before_endstream(false), 43 newline_before_endstream(false),
@@ -207,6 +208,12 @@ QPDFWriter::setDecodeLevel(qpdf_stream_decode_level_e val) @@ -207,6 +208,12 @@ QPDFWriter::setDecodeLevel(qpdf_stream_decode_level_e val)
207 } 208 }
208 209
209 void 210 void
  211 +QPDFWriter::setRecompressFlate(bool val)
  212 +{
  213 + this->m->recompress_flate = val;
  214 +}
  215 +
  216 +void
210 QPDFWriter::setContentNormalization(bool val) 217 QPDFWriter::setContentNormalization(bool val)
211 { 218 {
212 this->m->normalize_content_set = true; 219 this->m->normalize_content_set = true;
@@ -1716,13 +1723,14 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, @@ -1716,13 +1723,14 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1716 if (this->m->compress_streams) 1723 if (this->m->compress_streams)
1717 { 1724 {
1718 // Don't filter if the stream is already compressed with 1725 // Don't filter if the stream is already compressed with
1719 - // FlateDecode. We don't want to make it worse by getting  
1720 - // rid of a predictor or otherwise messing with it. We  
1721 - // should also avoid messing with anything that's  
1722 - // compressed with a lossy compression scheme, but we  
1723 - // don't support any of those right now. 1726 + // FlateDecode. This way we don't make it worse if the
  1727 + // original file used a better Flate algorithm, and we
  1728 + // don't spend time and CPU cycles uncompressing and
  1729 + // recompressing stuff. This can be overridden with
  1730 + // setRecompressFlate(true).
1724 QPDFObjectHandle filter_obj = stream_dict.getKey("/Filter"); 1731 QPDFObjectHandle filter_obj = stream_dict.getKey("/Filter");
1725 - if ((! object.isDataModified()) && 1732 + if ((! this->m->recompress_flate) &&
  1733 + (! object.isDataModified()) &&
1726 filter_obj.isName() && 1734 filter_obj.isName() &&
1727 ((filter_obj.getName() == "/FlateDecode") || 1735 ((filter_obj.getName() == "/FlateDecode") ||
1728 (filter_obj.getName() == "/Fl"))) 1736 (filter_obj.getName() == "/Fl")))
manual/build.mk
@@ -26,7 +26,8 @@ endif @@ -26,7 +26,8 @@ endif
26 26
27 $(OUTDOC).pdf: $(OUTDOC).fo qpdf/build/qpdf 27 $(OUTDOC).pdf: $(OUTDOC).fo qpdf/build/qpdf
28 $(FOP) $< -pdf $@.tmp 28 $(FOP) $< -pdf $@.tmp
29 - qpdf/build/qpdf --linearize $@.tmp $@ 29 + qpdf/build/qpdf --linearize --object-streams=generate \
  30 + --recompress-flate --compression-level=9 $@.tmp $@
30 31
31 $(OUTDOC).html: $(INDOC).xml manual/html.xsl $(VALIDATE) 32 $(OUTDOC).html: $(INDOC).xml manual/html.xsl $(VALIDATE)
32 $(XSLTPROC) --output $@ manual/html.xsl $< 33 $(XSLTPROC) --output $@ manual/html.xsl $<
manual/qpdf-manual.xml
@@ -1433,27 +1433,32 @@ outfile.pdf&lt;/option&gt; @@ -1433,27 +1433,32 @@ outfile.pdf&lt;/option&gt;
1433 <listitem> 1433 <listitem>
1434 <para> 1434 <para>
1435 <option>generalized</option>: decode streams filtered with 1435 <option>generalized</option>: decode streams filtered with
1436 - supported generalized filters: <option>/LZWDecode</option>,  
1437 - <option>/FlateDecode</option>,  
1438 - <option>/ASCII85Decode</option>, and  
1439 - <option>/ASCIIHexDecode</option>. We define generalized 1436 + supported generalized filters:
  1437 + <literal>/LZWDecode</literal>,
  1438 + <literal>/FlateDecode</literal>,
  1439 + <literal>/ASCII85Decode</literal>, and
  1440 + <literal>/ASCIIHexDecode</literal>. We define generalized
1440 filters as those to be used for general-purpose compression 1441 filters as those to be used for general-purpose compression
1441 or encoding, as opposed to filters specifically designed 1442 or encoding, as opposed to filters specifically designed
1442 - for image data. 1443 + for image data. Note that, by default, streams already
  1444 + compressed with <literal>/FlateDecode</literal> are not
  1445 + uncompressed and recompressed unless you also specify
  1446 + <option>--recompress-flate</option>.
1443 </para> 1447 </para>
1444 </listitem> 1448 </listitem>
1445 <listitem> 1449 <listitem>
1446 <para> 1450 <para>
1447 <option>specialized</option>: in addition to generalized, 1451 <option>specialized</option>: in addition to generalized,
1448 decode streams with supported non-lossy specialized 1452 decode streams with supported non-lossy specialized
1449 - filters; currently this is just <option>/RunLengthDecode</option> 1453 + filters; currently this is just
  1454 + <literal>/RunLengthDecode</literal>
1450 </para> 1455 </para>
1451 </listitem> 1456 </listitem>
1452 <listitem> 1457 <listitem>
1453 <para> 1458 <para>
1454 <option>all</option>: in addition to generalized and 1459 <option>all</option>: in addition to generalized and
1455 specialized, decode streams with supported lossy filters; 1460 specialized, decode streams with supported lossy filters;
1456 - currently this is just <option>/DCTDecode</option> (JPEG) 1461 + currently this is just <literal>/DCTDecode</literal> (JPEG)
1457 </para> 1462 </para>
1458 </listitem> 1463 </listitem>
1459 </itemizedlist> 1464 </itemizedlist>
@@ -1476,7 +1481,10 @@ outfile.pdf&lt;/option&gt; @@ -1476,7 +1481,10 @@ outfile.pdf&lt;/option&gt;
1476 <option>compress</option>: recompress stream data when 1481 <option>compress</option>: recompress stream data when
1477 possible (default); equivalent to 1482 possible (default); equivalent to
1478 <option>--compress-streams=y</option> 1483 <option>--compress-streams=y</option>
1479 - <option>--decode-level=generalized</option> 1484 + <option>--decode-level=generalized</option>. Does not
  1485 + recompress streams already compressed with
  1486 + <literal>/FlateDecode</literal> unless
  1487 + <option>--recompress-flate</option> is also specified.
1480 </para> 1488 </para>
1481 </listitem> 1489 </listitem>
1482 <listitem> 1490 <listitem>
@@ -1499,6 +1507,37 @@ outfile.pdf&lt;/option&gt; @@ -1499,6 +1507,37 @@ outfile.pdf&lt;/option&gt;
1499 </listitem> 1507 </listitem>
1500 </varlistentry> 1508 </varlistentry>
1501 <varlistentry> 1509 <varlistentry>
  1510 + <term><option>--recompress-flate</option></term>
  1511 + <listitem>
  1512 + <para>
  1513 + By default, streams already compressed with
  1514 + <literal>/FlateDecode</literal> are left alone rather than
  1515 + being uncompressed and recompressed. This option causes qpdf
  1516 + to uncompress and recompress the streams. There is a
  1517 + significant performance cost to using this option, but you
  1518 + probably want to use it if you specify
  1519 + <option>--compression-level</option>.
  1520 + </para>
  1521 + </listitem>
  1522 + </varlistentry>
  1523 + <varlistentry>
  1524 + <term><option>--compression-level=<replaceable>level</replaceable></option></term>
  1525 + <listitem>
  1526 + <para>
  1527 + When writing new streams that are compressed with
  1528 + <literal>/FlateDecode</literal>, use the specified compression
  1529 + level. The value of <option>level</option> should be a number
  1530 + from 1 to 9 and is passed directly to zlib, which implements
  1531 + deflate compression. Note that qpdf doesn't uncompress and
  1532 + recompress streams by default. To have this option apply to
  1533 + already compressed streams, you should also specify
  1534 + <option>--recompress-flate</option>. If your goal is to shrink
  1535 + the size of PDF files, you should also use
  1536 + <option>--object-streams=generate</option>.
  1537 + </para>
  1538 + </listitem>
  1539 + </varlistentry>
  1540 + <varlistentry>
1502 <term><option>--normalize-content=[yn]</option></term> 1541 <term><option>--normalize-content=[yn]</option></term>
1503 <listitem> 1542 <listitem>
1504 <para> 1543 <para>
@@ -4449,7 +4488,7 @@ print &quot;\n&quot;; @@ -4449,7 +4488,7 @@ print &quot;\n&quot;;
4449 </listitem> 4488 </listitem>
4450 <listitem> 4489 <listitem>
4451 <para> 4490 <para>
4452 - Library Enhancements 4491 + Library and CLI Enhancements
4453 </para> 4492 </para>
4454 <itemizedlist> 4493 <itemizedlist>
4455 <listitem> 4494 <listitem>
@@ -4510,6 +4549,41 @@ print &quot;\n&quot;; @@ -4510,6 +4549,41 @@ print &quot;\n&quot;;
4510 </listitem> 4549 </listitem>
4511 <listitem> 4550 <listitem>
4512 <para> 4551 <para>
  4552 + Static method
  4553 + <function>Pl_Flate::setCompressionLevel</function> can be
  4554 + called to set the zlib compression level globally used by
  4555 + all instances of Pl_Flate in deflate mode.
  4556 + </para>
  4557 + </listitem>
  4558 + <listitem>
  4559 + <para>
  4560 + The method
  4561 + <function>QPDFWriter::setRecompressFlate</function> can be
  4562 + called to tell <classname>QPDFWriter</classname> to
  4563 + uncompress and recompress streams already compressed with
  4564 + <literal>/FlateDecode</literal>.
  4565 + </para>
  4566 + </listitem>
  4567 + <listitem>
  4568 + <para>
  4569 + CLI enhancement: the <option>--recompress-flate</option>
  4570 + instructs <command>qpdf</command> to recompress streams that
  4571 + are already compressed with <literal>/FlateDecode</literal>.
  4572 + Useful with <option>--compression-level</option>.
  4573 + </para>
  4574 + </listitem>
  4575 + <listitem>
  4576 + <para>
  4577 + CLI enhancement: the
  4578 + <option>--compression-level=<replaceable>level</replaceable></option>
  4579 + sets the zlib compression level used for any streams
  4580 + compressed by <literal>/FlateDecode</literal>. Most
  4581 + effective when combined with
  4582 + <option>--recompress-flate</option>.
  4583 + </para>
  4584 + </listitem>
  4585 + <listitem>
  4586 + <para>
4513 The underlying implementation of QPDF arrays has been 4587 The underlying implementation of QPDF arrays has been
4514 enhanced to be much more memory efficient when dealing with 4588 enhanced to be much more memory efficient when dealing with
4515 arrays with lots of nulls. This enables qpdf to use 4589 arrays with lots of nulls. This enables qpdf to use
@@ -5699,9 +5773,9 @@ print &quot;\n&quot;; @@ -5699,9 +5773,9 @@ print &quot;\n&quot;;
5699 <listitem> 5773 <listitem>
5700 <para> 5774 <para>
5701 Disregard data check errors when uncompressing 5775 Disregard data check errors when uncompressing
5702 - <option>/FlateDecode</option> streams. This is consistent with  
5703 - most other PDF readers and allows qpdf to recover data from  
5704 - another class of malformed PDF files. 5776 + <literal>/FlateDecode</literal> streams. This is consistent
  5777 + with most other PDF readers and allows qpdf to recover data
  5778 + from another class of malformed PDF files.
5705 </para> 5779 </para>
5706 </listitem> 5780 </listitem>
5707 <listitem> 5781 <listitem>
qpdf/qpdf.cc
@@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
13 #include <qpdf/Pl_Discard.hh> 13 #include <qpdf/Pl_Discard.hh>
14 #include <qpdf/Pl_DCT.hh> 14 #include <qpdf/Pl_DCT.hh>
15 #include <qpdf/Pl_Count.hh> 15 #include <qpdf/Pl_Count.hh>
  16 +#include <qpdf/Pl_Flate.hh>
16 #include <qpdf/PointerHolder.hh> 17 #include <qpdf/PointerHolder.hh>
17 18
18 #include <qpdf/QPDF.hh> 19 #include <qpdf/QPDF.hh>
@@ -124,6 +125,9 @@ struct Options @@ -124,6 +125,9 @@ struct Options
124 stream_data_mode(qpdf_s_compress), 125 stream_data_mode(qpdf_s_compress),
125 compress_streams(true), 126 compress_streams(true),
126 compress_streams_set(false), 127 compress_streams_set(false),
  128 + recompress_flate(false),
  129 + recompress_flate_set(false),
  130 + compression_level(-1),
127 decode_level(qpdf_dl_generalized), 131 decode_level(qpdf_dl_generalized),
128 decode_level_set(false), 132 decode_level_set(false),
129 normalize_set(false), 133 normalize_set(false),
@@ -217,6 +221,9 @@ struct Options @@ -217,6 +221,9 @@ struct Options
217 qpdf_stream_data_e stream_data_mode; 221 qpdf_stream_data_e stream_data_mode;
218 bool compress_streams; 222 bool compress_streams;
219 bool compress_streams_set; 223 bool compress_streams_set;
  224 + bool recompress_flate;
  225 + bool recompress_flate_set;
  226 + int compression_level;
220 qpdf_stream_decode_level_e decode_level; 227 qpdf_stream_decode_level_e decode_level;
221 bool decode_level_set; 228 bool decode_level_set;
222 bool normalize_set; 229 bool normalize_set;
@@ -632,6 +639,8 @@ class ArgParser @@ -632,6 +639,8 @@ class ArgParser
632 void argCollate(); 639 void argCollate();
633 void argStreamData(char* parameter); 640 void argStreamData(char* parameter);
634 void argCompressStreams(char* parameter); 641 void argCompressStreams(char* parameter);
  642 + void argRecompressFlate();
  643 + void argCompressionLevel(char* parameter);
635 void argDecodeLevel(char* parameter); 644 void argDecodeLevel(char* parameter);
636 void argNormalizeContent(char* parameter); 645 void argNormalizeContent(char* parameter);
637 void argSuppressRecovery(); 646 void argSuppressRecovery();
@@ -847,6 +856,9 @@ ArgParser::initOptionTable() @@ -847,6 +856,9 @@ ArgParser::initOptionTable()
847 &ArgParser::argStreamData, stream_data_choices); 856 &ArgParser::argStreamData, stream_data_choices);
848 (*t)["compress-streams"] = oe_requiredChoices( 857 (*t)["compress-streams"] = oe_requiredChoices(
849 &ArgParser::argCompressStreams, yn); 858 &ArgParser::argCompressStreams, yn);
  859 + (*t)["recompress-flate"] = oe_bare(&ArgParser::argRecompressFlate);
  860 + (*t)["compression-level"] = oe_requiredParameter(
  861 + &ArgParser::argCompressionLevel, "level");
850 char const* decode_level_choices[] = 862 char const* decode_level_choices[] =
851 {"none", "generalized", "specialized", "all", 0}; 863 {"none", "generalized", "specialized", "all", 0};
852 (*t)["decode-level"] = oe_requiredChoices( 864 (*t)["decode-level"] = oe_requiredChoices(
@@ -1328,6 +1340,9 @@ ArgParser::argHelp() @@ -1328,6 +1340,9 @@ ArgParser::argHelp()
1328 << "--stream-data=option controls transformation of stream data (below)\n" 1340 << "--stream-data=option controls transformation of stream data (below)\n"
1329 << "--compress-streams=[yn] controls whether to compress streams on output\n" 1341 << "--compress-streams=[yn] controls whether to compress streams on output\n"
1330 << "--decode-level=option controls how to filter streams from the input\n" 1342 << "--decode-level=option controls how to filter streams from the input\n"
  1343 + << "--recompress-flate recompress streams already compressed with Flate\n"
  1344 + << "--compression-level=n set zlib compression level; most effective with\n"
  1345 + << " --recompress-flate --object-streams=generate\n"
1331 << "--normalize-content=[yn] enables or disables normalization of content streams\n" 1346 << "--normalize-content=[yn] enables or disables normalization of content streams\n"
1332 << "--object-streams=mode controls handing of object streams\n" 1347 << "--object-streams=mode controls handing of object streams\n"
1333 << "--preserve-unreferenced preserve unreferenced objects\n" 1348 << "--preserve-unreferenced preserve unreferenced objects\n"
@@ -1725,6 +1740,19 @@ ArgParser::argCompressStreams(char* parameter) @@ -1725,6 +1740,19 @@ ArgParser::argCompressStreams(char* parameter)
1725 } 1740 }
1726 1741
1727 void 1742 void
  1743 +ArgParser::argRecompressFlate()
  1744 +{
  1745 + o.recompress_flate_set = true;
  1746 + o.recompress_flate = true;
  1747 +}
  1748 +
  1749 +void
  1750 +ArgParser::argCompressionLevel(char* parameter)
  1751 +{
  1752 + o.compression_level = QUtil::string_to_int(parameter);
  1753 +}
  1754 +
  1755 +void
1728 ArgParser::argDecodeLevel(char* parameter) 1756 ArgParser::argDecodeLevel(char* parameter)
1729 { 1757 {
1730 o.decode_level_set = true; 1758 o.decode_level_set = true;
@@ -4889,6 +4917,10 @@ static void set_encryption_options(QPDF&amp; pdf, Options&amp; o, QPDFWriter&amp; w) @@ -4889,6 +4917,10 @@ static void set_encryption_options(QPDF&amp; pdf, Options&amp; o, QPDFWriter&amp; w)
4889 4917
4890 static void set_writer_options(QPDF& pdf, Options& o, QPDFWriter& w) 4918 static void set_writer_options(QPDF& pdf, Options& o, QPDFWriter& w)
4891 { 4919 {
  4920 + if (o.compression_level >= 0)
  4921 + {
  4922 + Pl_Flate::setCompressionLevel(o.compression_level);
  4923 + }
4892 if (o.qdf_mode) 4924 if (o.qdf_mode)
4893 { 4925 {
4894 w.setQDFMode(true); 4926 w.setQDFMode(true);
@@ -4913,6 +4945,10 @@ static void set_writer_options(QPDF&amp; pdf, Options&amp; o, QPDFWriter&amp; w) @@ -4913,6 +4945,10 @@ static void set_writer_options(QPDF&amp; pdf, Options&amp; o, QPDFWriter&amp; w)
4913 { 4945 {
4914 w.setCompressStreams(o.compress_streams); 4946 w.setCompressStreams(o.compress_streams);
4915 } 4947 }
  4948 + if (o.recompress_flate_set)
  4949 + {
  4950 + w.setRecompressFlate(o.recompress_flate);
  4951 + }
4916 if (o.decode_level_set) 4952 if (o.decode_level_set)
4917 { 4953 {
4918 w.setDecodeLevel(o.decode_level); 4954 w.setDecodeLevel(o.decode_level);
qpdf/qtest/qpdf.test
@@ -3876,8 +3876,20 @@ $td-&gt;runtest(&quot;convert inline-images to qdf&quot;, @@ -3876,8 +3876,20 @@ $td-&gt;runtest(&quot;convert inline-images to qdf&quot;,
3876 compare_pdfs("inline-images.pdf", "a.pdf"); 3876 compare_pdfs("inline-images.pdf", "a.pdf");
3877 3877
3878 show_ntests(); 3878 show_ntests();
  3879 +# ----------
  3880 +$td->notify("--- Compression Level ---");
  3881 +$n_tests += 4;
3879 3882
  3883 +check_pdf("recompress with level",
  3884 + "qpdf --static-id --recompress-flate --compression-level=9" .
  3885 + " --object-streams=generate minimal.pdf",
  3886 + "minimal-9.pdf", 0);
  3887 +check_pdf("recompress with level",
  3888 + "qpdf --static-id --recompress-flate --compression-level=1" .
  3889 + " --object-streams=generate minimal.pdf",
  3890 + "minimal-1.pdf", 0);
3880 3891
  3892 +show_ntests();
3881 # ---------- 3893 # ----------
3882 $td->notify("--- Specialized filtering Tests ---"); 3894 $td->notify("--- Specialized filtering Tests ---");
3883 $n_tests += 3; 3895 $n_tests += 3;
qpdf/qtest/qpdf/minimal-1.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/minimal-9.pdf 0 → 100644
No preview for this file type