Commit 98da4fd83527f47a28132ff4a120bc043d9e58f6

Authored by Jay Berkenbilt
1 parent bedf35d6

Externalize inline images now includes form XObjects

ChangeLog
  1 +2021-01-02 Jay Berkenbilt <ejb@ql.org>
  2 +
  3 + * QPDFPageObjectHelper::externalizeInlineImages can be called with
  4 + form XObjects as well as pages.
  5 +
  6 + * Bug fix: QPDFPageObjectHelper::externalizeInlineImages was not
  7 + descending into form XObjects on a page. It now does this by
  8 + default. In the extremely unlikely event that anyone was actually
  9 + depending on the old behavior, it is available by passing
  10 + shallow=true to the externalizeInlineImages call.
  11 +
  12 + * Bug fix: QPDFObjectHandle::filterPageContents was broken for
  13 + pages with an array of content streams. This caused
  14 + externalize-inline-images to also be broken for this case.
  15 +
1 16 2021-01-01 Jay Berkenbilt <ejb@ql.org>
2 17  
3 18 * Add methods to QPDFPageObjectHelper: forEachXObject,
... ...
... ... @@ -21,18 +21,6 @@ Candidates for upcoming release
21 21 * big page even with --remove-unreferenced-resources=yes, even with --empty
22 22 * optimize image failure because of colorspace
23 23  
24   -* Externalize inline images doesn't walk into form XObjects. In
25   - general:
26   -
27   - * Check QPDFPageObjectHelper and see what can be applied to form
28   - XObjects. Maybe think about generalizing it to work with form
29   - XObjects.
30   -
31   - * There is an increasing amount of logic in qpdf.cc that should
32   - probably move into the library. This includes externalizing inline
33   - images and page splitting as those operations become more
34   - elaborate, particularly with handling of form XObjects.
35   -
36 24 * See if the tokenizer is a performance bottleneck and, if so,
37 25 optimize it. We might end up with a high-performance tokenizer that
38 26 has a different interface but still ultimately creates the same
... ...
include/qpdf/QPDFPageObjectHelper.hh
... ... @@ -123,8 +123,15 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
123 123 QPDF_DLL
124 124 std::map<std::string, QPDFObjectHandle> getFormXObjects();
125 125  
126   - // Convert each inline image to an external (normal) image if the
127   - // size is at least the specified number of bytes.
  126 + // Converts each inline image to an external (normal) image if the
  127 + // size is at least the specified number of bytes. This method
  128 + // works with pages or form XObjects. By default, it recursively
  129 + // processes nested form XObjects. Pass true as shallow to avoid
  130 + // this behavior. Prior to qpdf 10.1, form XObjects were ignored,
  131 + // but this was considered a bug.
  132 + QPDF_DLL
  133 + void externalizeInlineImages(size_t min_size, bool shallow);
  134 + // ABI: make shallow optional (default false) and merge
128 135 QPDF_DLL
129 136 void externalizeInlineImages(size_t min_size = 0);
130 137  
... ...
libqpdf/QPDFPageObjectHelper.cc
... ... @@ -486,20 +486,50 @@ QPDFPageObjectHelper::getFormXObjects()
486 486 void
487 487 QPDFPageObjectHelper::externalizeInlineImages(size_t min_size)
488 488 {
489   - QPDFObjectHandle resources = getAttribute("/Resources", true);
490   - // Calling mergeResources also ensures that /XObject becomes
491   - // direct and is not shared with other pages.
492   - resources.mergeResources(
493   - QPDFObjectHandle::parse("<< /XObject << >> >>"));
494   - InlineImageTracker iit(this->oh.getOwningQPDF(), min_size, resources);
495   - Pl_Buffer b("new page content");
496   - filterContents(&iit, &b);
497   - if (iit.any_images)
  489 + externalizeInlineImages(min_size, false);
  490 +}
  491 +
  492 +void
  493 +QPDFPageObjectHelper::externalizeInlineImages(size_t min_size, bool shallow)
  494 +{
  495 + if (shallow)
  496 + {
  497 + QPDFObjectHandle resources = getAttribute("/Resources", true);
  498 + // Calling mergeResources also ensures that /XObject becomes
  499 + // direct and is not shared with other pages.
  500 + resources.mergeResources(
  501 + QPDFObjectHandle::parse("<< /XObject << >> >>"));
  502 + InlineImageTracker iit(this->oh.getOwningQPDF(), min_size, resources);
  503 + Pl_Buffer b("new page content");
  504 + filterContents(&iit, &b);
  505 + if (iit.any_images)
  506 + {
  507 + if (this->oh.isFormXObject())
  508 + {
  509 + this->oh.replaceStreamData(
  510 + b.getBuffer(),
  511 + QPDFObjectHandle::newNull(),
  512 + QPDFObjectHandle::newNull());
  513 + }
  514 + else
  515 + {
  516 + this->oh.replaceKey(
  517 + "/Contents",
  518 + QPDFObjectHandle::newStream(
  519 + this->oh.getOwningQPDF(), b.getBuffer()));
  520 + }
  521 + }
  522 + }
  523 + else
498 524 {
499   - getObjectHandle().replaceKey(
500   - "/Contents",
501   - QPDFObjectHandle::newStream(
502   - this->oh.getOwningQPDF(), b.getBuffer()));
  525 + externalizeInlineImages(min_size, true);
  526 + forEachFormXObject(
  527 + true,
  528 + [min_size](QPDFObjectHandle& obj,
  529 + QPDFObjectHandle&, std::string const&) {
  530 + QPDFPageObjectHelper(obj).externalizeInlineImages(
  531 + min_size, true);
  532 + });
503 533 }
504 534 }
505 535  
... ...
manual/qpdf-manual.xml
... ... @@ -4991,6 +4991,26 @@ print &quot;\n&quot;;
4991 4991 </listitem>
4992 4992 </itemizedlist>
4993 4993 </listitem>
  4994 + <listitem>
  4995 + <para>
  4996 + Bug Fixes
  4997 + </para>
  4998 + <itemizedlist>
  4999 + <listitem>
  5000 + <para>
  5001 + <function>QPDFPageObjectHelper::externalizeInlineImages</function>
  5002 + was not externalizing images referenced from form XObjects
  5003 + that appeared on the page.
  5004 + </para>
  5005 + </listitem>
  5006 + <listitem>
  5007 + <para>
  5008 + <function>QPDFObjectHandle::filterPageContents</function>
  5009 + was broken for pages with multiple content streams.
  5010 + </para>
  5011 + </listitem>
  5012 + </itemizedlist>
  5013 + </listitem>
4994 5014 </itemizedlist>
4995 5015 </listitem>
4996 5016 </varlistentry>
... ...
qpdf/qtest/qpdf.test
... ... @@ -905,6 +905,7 @@ $td-&gt;runtest(&quot;check output&quot;,
905 905 my @eii_tests = (
906 906 ['inline-images', 80],
907 907 ['large-inline-image', 1024],
  908 + ['nested-form-xobjects-inline-images', 20],
908 909 );
909 910 $n_tests += 4 * scalar(@eii_tests);
910 911 $n_compare_pdfs += 2 * scalar(@eii_tests);
... ...
qpdf/qtest/qpdf/nested-form-xobjects-inline-images-ii-all.pdf 0 → 100644
  1 +%PDF-1.3
  2 +%¿÷¢þ
  3 +%QDF-1.0
  4 +
  5 +%% Original object ID: 1 0
  6 +1 0 obj
  7 +<<
  8 + /Pages 2 0 R
  9 + /Type /Catalog
  10 +>>
  11 +endobj
  12 +
  13 +%% Original object ID: 2 0
  14 +2 0 obj
  15 +<<
  16 + /Count 1
  17 + /Kids [
  18 + 3 0 R
  19 + ]
  20 + /Type /Pages
  21 +>>
  22 +endobj
  23 +
  24 +%% Page 1
  25 +%% Original object ID: 3 0
  26 +3 0 obj
  27 +<<
  28 + /Contents 4 0 R
  29 + /MediaBox [
  30 + 0
  31 + 0
  32 + 612
  33 + 792
  34 + ]
  35 + /Parent 2 0 R
  36 + /Resources <<
  37 + /Font <<
  38 + /F1 6 0 R
  39 + >>
  40 + /ProcSet 7 0 R
  41 + /XObject <<
  42 + /Fx1 8 0 R
  43 + /IIm1 10 0 R
  44 + /IIm2 12 0 R
  45 + >>
  46 + >>
  47 + /Type /Page
  48 +>>
  49 +endobj
  50 +
  51 +%% Contents for page 1
  52 +%% Original object ID: 20 0
  53 +4 0 obj
  54 +<<
  55 + /Length 5 0 R
  56 +>>
  57 +stream
  58 +q
  59 +BT
  60 + /F1 24 Tf
  61 + 72 720 Td
  62 + (Page) Tj
  63 +ET
  64 +q
  65 +100 0 0 100 72 600 cm
  66 +/IIm1 Do
  67 +
  68 +Q
  69 +q
  70 +100 0 0 100 192 600 cm
  71 +/IIm2 Do
  72 +
  73 +Q
  74 +
  75 +Q
  76 +q
  77 +1.00000 0.00000 0.00000 1.00000 72.00000 200.00000 cm
  78 +/Fx1 Do
  79 +Q
  80 +endstream
  81 +endobj
  82 +
  83 +5 0 obj
  84 +186
  85 +endobj
  86 +
  87 +%% Original object ID: 10 0
  88 +6 0 obj
  89 +<<
  90 + /BaseFont /Helvetica
  91 + /Encoding /WinAnsiEncoding
  92 + /Name /F1
  93 + /Subtype /Type1
  94 + /Type /Font
  95 +>>
  96 +endobj
  97 +
  98 +%% Original object ID: 11 0
  99 +7 0 obj
  100 +[
  101 + /PDF
  102 + /Text
  103 + /ImageC
  104 +]
  105 +endobj
  106 +
  107 +%% Original object ID: 12 0
  108 +8 0 obj
  109 +<<
  110 + /BBox [
  111 + 0
  112 + 0
  113 + 300
  114 + 500
  115 + ]
  116 + /Resources <<
  117 + /Font <<
  118 + /F1 14 0 R
  119 + >>
  120 + /ProcSet 15 0 R
  121 + /XObject <<
  122 + /Fx1 16 0 R
  123 + /IIm1 18 0 R
  124 + /IIm2 20 0 R
  125 + >>
  126 + >>
  127 + /Subtype /Form
  128 + /Type /XObject
  129 + /Length 9 0 R
  130 +>>
  131 +stream
  132 +BT
  133 + /F1 24 Tf
  134 + 0 320 Td
  135 + (FX1) Tj
  136 +ET
  137 +q
  138 +100 0 0 100 000 200 cm
  139 +/IIm1 Do
  140 +
  141 +Q
  142 +q
  143 +100 0 0 100 120 200 cm
  144 +/IIm2 Do
  145 +
  146 +Q
  147 +q
  148 +1.00000 0.00000 0.00000 1.00000 0.00000 0.00000 cm
  149 +/Fx1 Do
  150 +Q
  151 +endstream
  152 +endobj
  153 +
  154 +9 0 obj
  155 +177
  156 +endobj
  157 +
  158 +%% Original object ID: 18 0
  159 +10 0 obj
  160 +<<
  161 + /BitsPerComponent 8
  162 + /ColorSpace /DeviceGray
  163 + /Height 15
  164 + /Subtype /Image
  165 + /Type /XObject
  166 + /Width 15
  167 + /Length 11 0 R
  168 +>>
  169 +stream
  170 +`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  171 +endstream
  172 +endobj
  173 +
  174 +%QDF: ignore_newline
  175 +11 0 obj
  176 +225
  177 +endobj
  178 +
  179 +%% Original object ID: 19 0
  180 +12 0 obj
  181 +<<
  182 + /BitsPerComponent 8
  183 + /ColorSpace /DeviceGray
  184 + /Height 15
  185 + /Subtype /Image
  186 + /Type /XObject
  187 + /Width 15
  188 + /Length 13 0 R
  189 +>>
  190 +stream
  191 +@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  192 +endstream
  193 +endobj
  194 +
  195 +%QDF: ignore_newline
  196 +13 0 obj
  197 +225
  198 +endobj
  199 +
  200 +%% Original object ID: 14 0
  201 +14 0 obj
  202 +<<
  203 + /BaseFont /Helvetica
  204 + /Encoding /WinAnsiEncoding
  205 + /Name /F1
  206 + /Subtype /Type1
  207 + /Type /Font
  208 +>>
  209 +endobj
  210 +
  211 +%% Original object ID: 15 0
  212 +15 0 obj
  213 +[
  214 + /PDF
  215 + /Text
  216 + /ImageC
  217 +]
  218 +endobj
  219 +
  220 +%% Original object ID: 16 0
  221 +16 0 obj
  222 +<<
  223 + /BBox [
  224 + 0
  225 + 0
  226 + 300
  227 + 200
  228 + ]
  229 + /Resources <<
  230 + /Font <<
  231 + /F1 14 0 R
  232 + >>
  233 + /ProcSet 15 0 R
  234 + /XObject <<
  235 + /IIm1 22 0 R
  236 + /IIm2 24 0 R
  237 + >>
  238 + >>
  239 + /Subtype /Form
  240 + /Type /XObject
  241 + /Length 17 0 R
  242 +>>
  243 +stream
  244 +BT
  245 + /F1 24 Tf
  246 + 0 120 Td
  247 + (FX2) Tj
  248 +ET
  249 +q
  250 +100 0 0 100 0 0 cm
  251 +/IIm1 Do
  252 +
  253 +Q
  254 +q
  255 +100 0 0 100 120 0 cm
  256 +/IIm2 Do
  257 +
  258 +Q
  259 +endstream
  260 +endobj
  261 +
  262 +17 0 obj
  263 +108
  264 +endobj
  265 +
  266 +%% Original object ID: 21 0
  267 +18 0 obj
  268 +<<
  269 + /BitsPerComponent 8
  270 + /ColorSpace /DeviceGray
  271 + /Height 15
  272 + /Subtype /Image
  273 + /Type /XObject
  274 + /Width 15
  275 + /Length 19 0 R
  276 +>>
  277 +stream
  278 +@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  279 +endstream
  280 +endobj
  281 +
  282 +%QDF: ignore_newline
  283 +19 0 obj
  284 +225
  285 +endobj
  286 +
  287 +%% Original object ID: 22 0
  288 +20 0 obj
  289 +<<
  290 + /BitsPerComponent 8
  291 + /ColorSpace /DeviceGray
  292 + /Height 15
  293 + /Subtype /Image
  294 + /Type /XObject
  295 + /Width 15
  296 + /Length 21 0 R
  297 +>>
  298 +stream
  299 +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  300 +endstream
  301 +endobj
  302 +
  303 +%QDF: ignore_newline
  304 +21 0 obj
  305 +225
  306 +endobj
  307 +
  308 +%% Original object ID: 23 0
  309 +22 0 obj
  310 +<<
  311 + /BitsPerComponent 8
  312 + /ColorSpace /DeviceGray
  313 + /Height 15
  314 + /Subtype /Image
  315 + /Type /XObject
  316 + /Width 15
  317 + /Length 23 0 R
  318 +>>
  319 +stream
  320 +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  321 +endstream
  322 +endobj
  323 +
  324 +%QDF: ignore_newline
  325 +23 0 obj
  326 +225
  327 +endobj
  328 +
  329 +%% Original object ID: 24 0
  330 +24 0 obj
  331 +<<
  332 + /BitsPerComponent 8
  333 + /ColorSpace /DeviceGray
  334 + /Height 15
  335 + /Subtype /Image
  336 + /Type /XObject
  337 + /Width 15
  338 + /Length 25 0 R
  339 +>>
  340 +stream
  341 +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  342 +endstream
  343 +endobj
  344 +
  345 +%QDF: ignore_newline
  346 +25 0 obj
  347 +225
  348 +endobj
  349 +
  350 +xref
  351 +0 26
  352 +0000000000 65535 f
  353 +0000000052 00000 n
  354 +0000000133 00000 n
  355 +0000000242 00000 n
  356 +0000000563 00000 n
  357 +0000000804 00000 n
  358 +0000000852 00000 n
  359 +0000000998 00000 n
  360 +0000001071 00000 n
  361 +0000001536 00000 n
  362 +0000001584 00000 n
  363 +0000001996 00000 n
  364 +0000002045 00000 n
  365 +0000002457 00000 n
  366 +0000002506 00000 n
  367 +0000002653 00000 n
  368 +0000002727 00000 n
  369 +0000003107 00000 n
  370 +0000003156 00000 n
  371 +0000003568 00000 n
  372 +0000003617 00000 n
  373 +0000004029 00000 n
  374 +0000004078 00000 n
  375 +0000004490 00000 n
  376 +0000004539 00000 n
  377 +0000004951 00000 n
  378 +trailer <<
  379 + /Root 1 0 R
  380 + /Size 26
  381 + /ID [<55269d37282af9edc76855e4cb859987><31415926535897932384626433832795>]
  382 +>>
  383 +startxref
  384 +4972
  385 +%%EOF
... ...
qpdf/qtest/qpdf/nested-form-xobjects-inline-images-ii-some.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/nested-form-xobjects-inline-images.pdf 0 → 100644
No preview for this file type