From 4f3b89991be466b77d1be2ab4fe0dc3f9228a17e Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Fri, 3 Apr 2020 16:48:20 -0400 Subject: [PATCH] placeFormXObject: allow control of shrink/expand (fixes #409) --- ChangeLog | 8 + include/qpdf/QPDFPageObjectHelper.hh | 30 ++- libqpdf/QPDFPageObjectHelper.cc | 19 +- qpdf/qtest/qpdf.test | 15 +- qpdf/qtest/qpdf/fx-overlay-64.pdf | 389 +++++++++++++++++++++++++++ qpdf/qtest/qpdf/fx-overlay-65.pdf | 389 +++++++++++++++++++++++++++ qpdf/qtest/qpdf/fx-overlay-66.pdf | 389 +++++++++++++++++++++++++++ qpdf/qtest/qpdf/fx-overlay-67.pdf | 389 +++++++++++++++++++++++++++ qpdf/qtest/qpdf/fxo-bigsmall.pdf | 177 ++++++++++++ qpdf/qtest/qpdf/fxo-smallbig.pdf | 177 ++++++++++++ qpdf/test_driver.cc | 51 ++++ 11 files changed, 2016 insertions(+), 17 deletions(-) create mode 100644 qpdf/qtest/qpdf/fx-overlay-64.pdf create mode 100644 qpdf/qtest/qpdf/fx-overlay-65.pdf create mode 100644 qpdf/qtest/qpdf/fx-overlay-66.pdf create mode 100644 qpdf/qtest/qpdf/fx-overlay-67.pdf create mode 100644 qpdf/qtest/qpdf/fxo-bigsmall.pdf create mode 100644 qpdf/qtest/qpdf/fxo-smallbig.pdf diff --git a/ChangeLog b/ChangeLog index 77e31d89..94109b11 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2020-04-03 Jay Berkenbilt + + * Add two extra optional arguments to + QPDFPageObjectHelper::placeFormXObject to control whether the + placed item is allowed to be shrunk or expanded to fit within or + maximally fill the destination rectangle. Prior to this change, + placeFormXObject might shrink it but would never expand it. + 2020-04-02 Jay Berkenbilt * Add method QPDFObjectHandle::unsafeShallowCopy for copying only diff --git a/include/qpdf/QPDFPageObjectHelper.hh b/include/qpdf/QPDFPageObjectHelper.hh index 47794a5c..87dad8d4 100644 --- a/include/qpdf/QPDFPageObjectHelper.hh +++ b/include/qpdf/QPDFPageObjectHelper.hh @@ -209,27 +209,33 @@ class QPDFPageObjectHelper: public QPDFObjectHelper // Return content stream text that will place the given form // XObject (fo) using the resource name "name" on this page - // centered within the given rectangle and shrunk to fit if - // necessary. If invert_transformations is true, the effect of any - // rotation (/Rotate) and scaling (/UserUnit) applied to the - // current page will be inverted in the form XObject placement. - // This will cause the form XObject's absolute orientation to be - // preserved. You could overlay one page on another by calling - // getFormXObjectForPage on the original page, - // QPDFObjectHandle::getUniqueResourceName on the destination - // page's Resources dictionary to generate a name for the - // resulting object, and calling placeFormXObject on the + // centered within the given rectangle. If invert_transformations + // is true, the effect of any rotation (/Rotate) and scaling + // (/UserUnit) applied to the current page will be inverted in the + // form XObject placement. This will cause the form XObject's + // absolute orientation to be preserved. You could overlay one + // page on another by calling getFormXObjectForPage on the + // original page, QPDFObjectHandle::getUniqueResourceName on the + // destination page's Resources dictionary to generate a name for + // the resulting object, and calling placeFormXObject on the // destination page. Then insert the new fo (or, if it comes from // a different file, the result of calling copyForeignObject on // it) into the resources dictionary using name, and append or // prepend the content to the page's content streams. See the // overlay/underlay code in qpdf.cc or - // examples/pdf-overlay-page.cc for an example. + // examples/pdf-overlay-page.cc for an example. From qpdf 10.0.0, + // the allow_shrink and allow_expand parameters control whether + // the form XObject is allowed to be shrunk or expanded to stay + // within or maximally fill the destination rectangle. The default + // values are for backward compatibility with the pre-10.0.0 + // behavior. QPDF_DLL std::string placeFormXObject( QPDFObjectHandle fo, std::string const& name, QPDFObjectHandle::Rectangle rect, - bool invert_transformations = true); + bool invert_transformations = true, + bool allow_shrink = true, + bool allow_expand = false); private: static void diff --git a/libqpdf/QPDFPageObjectHelper.cc b/libqpdf/QPDFPageObjectHelper.cc index d6f1516e..8e0aac06 100644 --- a/libqpdf/QPDFPageObjectHelper.cc +++ b/libqpdf/QPDFPageObjectHelper.cc @@ -724,11 +724,12 @@ std::string QPDFPageObjectHelper::placeFormXObject( QPDFObjectHandle fo, std::string const& name, QPDFObjectHandle::Rectangle rect, - bool invert_transformations) + bool invert_transformations, + bool allow_shrink, bool allow_expand) { // Calculate the transformation matrix that will place the given - // form XObject fully inside the given rectangle, shrinking and - // centering if needed. + // form XObject fully inside the given rectangle, center and + // shrinking or expanding as needed if requested. // When rendering a form XObject, the transformation in the // graphics state (cm) is applied first (of course -- when it is @@ -797,7 +798,17 @@ QPDFPageObjectHelper::placeFormXObject( double scale = (xscale < yscale ? xscale : yscale); if (scale > 1.0) { - scale = 1.0; + if (! allow_expand) + { + scale = 1.0; + } + } + else if (scale < 1.0) + { + if (! allow_shrink) + { + scale = 1.0; + } } // Step 2: figure out what translation is required to get the diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 8d77b024..e1cd6bf4 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -412,7 +412,7 @@ foreach my $i (@choice_values) show_ntests(); # ---------- $td->notify("--- Form XObject, underlay, overlay ---"); -$n_tests += 10; +$n_tests += 18; $td->runtest("form xobject creation", {$td->COMMAND => "test_driver 55 fxo-red.pdf"}, @@ -433,6 +433,19 @@ foreach (my $i = 56; $i <= 59; ++$i) {$td->FILE => "a.pdf"}, {$td->FILE => "fx-overlay-$i.pdf"}); } +foreach (my $i = 64; $i <= 67; ++$i) +{ + # See comments in test_driver.cc for a verbal description of what + # the resulting files should look like. + $td->runtest("overlay shrink/expand", + {$td->COMMAND => + "test_driver $i fxo-bigsmall.pdf fxo-smallbig.pdf"}, + {$td->STRING => "test $i done\n", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + $td->runtest("compare files", + {$td->FILE => "a.pdf"}, + {$td->FILE => "fx-overlay-$i.pdf"}); +} my @uo_cases = ( '--underlay fxo-green.pdf --repeat=z --to=1-14 --' . diff --git a/qpdf/qtest/qpdf/fx-overlay-64.pdf b/qpdf/qtest/qpdf/fx-overlay-64.pdf new file mode 100644 index 00000000..eb27ad6d --- /dev/null +++ b/qpdf/qtest/qpdf/fx-overlay-64.pdf @@ -0,0 +1,389 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 2 + /Kids [ + 3 0 R + 4 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents [ + 5 0 R + 7 0 R + 9 0 R + ] + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 11 0 R + >> + /ProcSet 12 0 R + /XObject << + /Fx1 13 0 R + >> + >> + /Type /Page +>> +endobj + +%% Page 2 +%% Original object ID: 4 0 +4 0 obj +<< + /Contents [ + 15 0 R + 17 0 R + 19 0 R + ] + /MediaBox [ + 0 + 0 + 306 + 396 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 21 0 R + >> + /ProcSet 22 0 R + /XObject << + /Fx1 23 0 R + >> + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 16 0 +5 0 obj +<< + /Length 6 0 R +>> +stream +q +endstream +endobj + +6 0 obj +2 +endobj + +%% Contents for page 1 +%% Original object ID: 5 0 +7 0 obj +<< + /Length 8 0 R +>> +stream +1 .5 0 RG +1 .5 0 rg +BT + /F1 24 Tf + 72 690 Td + (big) Tj +ET +5 w +0 0 612 792 re s +246 396 60 60 re f +endstream +endobj + +8 0 obj +101 +endobj + +%% Contents for page 1 +%% Original object ID: 17 0 +9 0 obj +<< + /Length 10 0 R +>> +stream + +Q +q +1.00000 0.00000 0.00000 1.00000 153.00000 198.00000 cm +/Fx1 Do +Q +endstream +endobj + +10 0 obj +70 +endobj + +%% Original object ID: 7 0 +11 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 8 0 +12 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 13 0 +13 0 obj +<< + /BBox [ + 0 + 0 + 306 + 396 + ] + /Resources << + /Font << + /F1 25 0 R + >> + /ProcSet 26 0 R + >> + /Subtype /Form + /Type /XObject + /Length 14 0 R +>> +stream +.5 0 .5 RG +.5 0 .5 rg +BT + /F1 24 Tf + 36 345 Td + (small) Tj +ET +5 w +0 0 306 396 re s +153 198 30 30 re f +endstream +endobj + +14 0 obj +105 +endobj + +%% Contents for page 2 +%% Original object ID: 21 0 +15 0 obj +<< + /Length 16 0 R +>> +stream +q +endstream +endobj + +16 0 obj +2 +endobj + +%% Contents for page 2 +%% Original object ID: 9 0 +17 0 obj +<< + /Length 18 0 R +>> +stream +1 .5 0 RG +1 .5 0 rg +BT + /F1 24 Tf + 36 345 Td + (small) Tj +ET +5 w +0 0 306 396 re s +153 198 30 30 re f +endstream +endobj + +18 0 obj +103 +endobj + +%% Contents for page 2 +%% Original object ID: 22 0 +19 0 obj +<< + /Length 20 0 R +>> +stream + +Q +q +1.00000 0.00000 0.00000 1.00000 -153.00000 -198.00000 cm +/Fx1 Do +Q +endstream +endobj + +20 0 obj +72 +endobj + +%% Original object ID: 11 0 +21 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 12 0 +22 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 18 0 +23 0 obj +<< + /BBox [ + 0 + 0 + 612 + 792 + ] + /Resources << + /Font << + /F1 27 0 R + >> + /ProcSet 28 0 R + >> + /Subtype /Form + /Type /XObject + /Length 24 0 R +>> +stream +.5 0 .5 RG +.5 0 .5 rg +BT + /F1 24 Tf + 72 690 Td + (big) Tj +ET +5 w +0 0 612 792 re s +246 396 60 60 re f +endstream +endobj + +24 0 obj +103 +endobj + +%% Original object ID: 14 0 +25 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 15 0 +26 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 19 0 +27 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 20 0 +28 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 29 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000252 00000 n +0000000554 00000 n +0000000873 00000 n +0000000930 00000 n +0000000998 00000 n +0000001154 00000 n +0000001225 00000 n +0000001351 00000 n +0000001398 00000 n +0000001544 00000 n +0000001608 00000 n +0000001924 00000 n +0000001996 00000 n +0000002055 00000 n +0000002124 00000 n +0000002284 00000 n +0000002356 00000 n +0000002485 00000 n +0000002533 00000 n +0000002680 00000 n +0000002744 00000 n +0000003058 00000 n +0000003107 00000 n +0000003254 00000 n +0000003318 00000 n +0000003465 00000 n +trailer << + /Root 1 0 R + /Size 29 + /ID [<4866f3ccc81fb28dc4a27f0f976ce937><31415926535897932384626433832795>] +>> +startxref +3501 +%%EOF diff --git a/qpdf/qtest/qpdf/fx-overlay-65.pdf b/qpdf/qtest/qpdf/fx-overlay-65.pdf new file mode 100644 index 00000000..9a01f5d8 --- /dev/null +++ b/qpdf/qtest/qpdf/fx-overlay-65.pdf @@ -0,0 +1,389 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 2 + /Kids [ + 3 0 R + 4 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents [ + 5 0 R + 7 0 R + 9 0 R + ] + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 11 0 R + >> + /ProcSet 12 0 R + /XObject << + /Fx1 13 0 R + >> + >> + /Type /Page +>> +endobj + +%% Page 2 +%% Original object ID: 4 0 +4 0 obj +<< + /Contents [ + 15 0 R + 17 0 R + 19 0 R + ] + /MediaBox [ + 0 + 0 + 306 + 396 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 21 0 R + >> + /ProcSet 22 0 R + /XObject << + /Fx1 23 0 R + >> + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 16 0 +5 0 obj +<< + /Length 6 0 R +>> +stream +q +endstream +endobj + +6 0 obj +2 +endobj + +%% Contents for page 1 +%% Original object ID: 5 0 +7 0 obj +<< + /Length 8 0 R +>> +stream +1 .5 0 RG +1 .5 0 rg +BT + /F1 24 Tf + 72 690 Td + (big) Tj +ET +5 w +0 0 612 792 re s +246 396 60 60 re f +endstream +endobj + +8 0 obj +101 +endobj + +%% Contents for page 1 +%% Original object ID: 17 0 +9 0 obj +<< + /Length 10 0 R +>> +stream + +Q +q +1.00000 0.00000 0.00000 1.00000 153.00000 198.00000 cm +/Fx1 Do +Q +endstream +endobj + +10 0 obj +70 +endobj + +%% Original object ID: 7 0 +11 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 8 0 +12 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 13 0 +13 0 obj +<< + /BBox [ + 0 + 0 + 306 + 396 + ] + /Resources << + /Font << + /F1 25 0 R + >> + /ProcSet 26 0 R + >> + /Subtype /Form + /Type /XObject + /Length 14 0 R +>> +stream +.5 0 .5 RG +.5 0 .5 rg +BT + /F1 24 Tf + 36 345 Td + (small) Tj +ET +5 w +0 0 306 396 re s +153 198 30 30 re f +endstream +endobj + +14 0 obj +105 +endobj + +%% Contents for page 2 +%% Original object ID: 21 0 +15 0 obj +<< + /Length 16 0 R +>> +stream +q +endstream +endobj + +16 0 obj +2 +endobj + +%% Contents for page 2 +%% Original object ID: 9 0 +17 0 obj +<< + /Length 18 0 R +>> +stream +1 .5 0 RG +1 .5 0 rg +BT + /F1 24 Tf + 36 345 Td + (small) Tj +ET +5 w +0 0 306 396 re s +153 198 30 30 re f +endstream +endobj + +18 0 obj +103 +endobj + +%% Contents for page 2 +%% Original object ID: 22 0 +19 0 obj +<< + /Length 20 0 R +>> +stream + +Q +q +0.50000 0.00000 0.00000 0.50000 0.00000 0.00000 cm +/Fx1 Do +Q +endstream +endobj + +20 0 obj +66 +endobj + +%% Original object ID: 11 0 +21 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 12 0 +22 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 18 0 +23 0 obj +<< + /BBox [ + 0 + 0 + 612 + 792 + ] + /Resources << + /Font << + /F1 27 0 R + >> + /ProcSet 28 0 R + >> + /Subtype /Form + /Type /XObject + /Length 24 0 R +>> +stream +.5 0 .5 RG +.5 0 .5 rg +BT + /F1 24 Tf + 72 690 Td + (big) Tj +ET +5 w +0 0 612 792 re s +246 396 60 60 re f +endstream +endobj + +24 0 obj +103 +endobj + +%% Original object ID: 14 0 +25 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 15 0 +26 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 19 0 +27 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 20 0 +28 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 29 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000252 00000 n +0000000554 00000 n +0000000873 00000 n +0000000930 00000 n +0000000998 00000 n +0000001154 00000 n +0000001225 00000 n +0000001351 00000 n +0000001398 00000 n +0000001544 00000 n +0000001608 00000 n +0000001924 00000 n +0000001996 00000 n +0000002055 00000 n +0000002124 00000 n +0000002284 00000 n +0000002356 00000 n +0000002479 00000 n +0000002527 00000 n +0000002674 00000 n +0000002738 00000 n +0000003052 00000 n +0000003101 00000 n +0000003248 00000 n +0000003312 00000 n +0000003459 00000 n +trailer << + /Root 1 0 R + /Size 29 + /ID [<4866f3ccc81fb28dc4a27f0f976ce937><31415926535897932384626433832795>] +>> +startxref +3495 +%%EOF diff --git a/qpdf/qtest/qpdf/fx-overlay-66.pdf b/qpdf/qtest/qpdf/fx-overlay-66.pdf new file mode 100644 index 00000000..2b887848 --- /dev/null +++ b/qpdf/qtest/qpdf/fx-overlay-66.pdf @@ -0,0 +1,389 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 2 + /Kids [ + 3 0 R + 4 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents [ + 5 0 R + 7 0 R + 9 0 R + ] + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 11 0 R + >> + /ProcSet 12 0 R + /XObject << + /Fx1 13 0 R + >> + >> + /Type /Page +>> +endobj + +%% Page 2 +%% Original object ID: 4 0 +4 0 obj +<< + /Contents [ + 15 0 R + 17 0 R + 19 0 R + ] + /MediaBox [ + 0 + 0 + 306 + 396 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 21 0 R + >> + /ProcSet 22 0 R + /XObject << + /Fx1 23 0 R + >> + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 16 0 +5 0 obj +<< + /Length 6 0 R +>> +stream +q +endstream +endobj + +6 0 obj +2 +endobj + +%% Contents for page 1 +%% Original object ID: 5 0 +7 0 obj +<< + /Length 8 0 R +>> +stream +1 .5 0 RG +1 .5 0 rg +BT + /F1 24 Tf + 72 690 Td + (big) Tj +ET +5 w +0 0 612 792 re s +246 396 60 60 re f +endstream +endobj + +8 0 obj +101 +endobj + +%% Contents for page 1 +%% Original object ID: 17 0 +9 0 obj +<< + /Length 10 0 R +>> +stream + +Q +q +2.00000 0.00000 0.00000 2.00000 0.00000 0.00000 cm +/Fx1 Do +Q +endstream +endobj + +10 0 obj +66 +endobj + +%% Original object ID: 7 0 +11 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 8 0 +12 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 13 0 +13 0 obj +<< + /BBox [ + 0 + 0 + 306 + 396 + ] + /Resources << + /Font << + /F1 25 0 R + >> + /ProcSet 26 0 R + >> + /Subtype /Form + /Type /XObject + /Length 14 0 R +>> +stream +.5 0 .5 RG +.5 0 .5 rg +BT + /F1 24 Tf + 36 345 Td + (small) Tj +ET +5 w +0 0 306 396 re s +153 198 30 30 re f +endstream +endobj + +14 0 obj +105 +endobj + +%% Contents for page 2 +%% Original object ID: 21 0 +15 0 obj +<< + /Length 16 0 R +>> +stream +q +endstream +endobj + +16 0 obj +2 +endobj + +%% Contents for page 2 +%% Original object ID: 9 0 +17 0 obj +<< + /Length 18 0 R +>> +stream +1 .5 0 RG +1 .5 0 rg +BT + /F1 24 Tf + 36 345 Td + (small) Tj +ET +5 w +0 0 306 396 re s +153 198 30 30 re f +endstream +endobj + +18 0 obj +103 +endobj + +%% Contents for page 2 +%% Original object ID: 22 0 +19 0 obj +<< + /Length 20 0 R +>> +stream + +Q +q +1.00000 0.00000 0.00000 1.00000 -153.00000 -198.00000 cm +/Fx1 Do +Q +endstream +endobj + +20 0 obj +72 +endobj + +%% Original object ID: 11 0 +21 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 12 0 +22 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 18 0 +23 0 obj +<< + /BBox [ + 0 + 0 + 612 + 792 + ] + /Resources << + /Font << + /F1 27 0 R + >> + /ProcSet 28 0 R + >> + /Subtype /Form + /Type /XObject + /Length 24 0 R +>> +stream +.5 0 .5 RG +.5 0 .5 rg +BT + /F1 24 Tf + 72 690 Td + (big) Tj +ET +5 w +0 0 612 792 re s +246 396 60 60 re f +endstream +endobj + +24 0 obj +103 +endobj + +%% Original object ID: 14 0 +25 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 15 0 +26 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 19 0 +27 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 20 0 +28 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 29 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000252 00000 n +0000000554 00000 n +0000000873 00000 n +0000000930 00000 n +0000000998 00000 n +0000001154 00000 n +0000001225 00000 n +0000001347 00000 n +0000001394 00000 n +0000001540 00000 n +0000001604 00000 n +0000001920 00000 n +0000001992 00000 n +0000002051 00000 n +0000002120 00000 n +0000002280 00000 n +0000002352 00000 n +0000002481 00000 n +0000002529 00000 n +0000002676 00000 n +0000002740 00000 n +0000003054 00000 n +0000003103 00000 n +0000003250 00000 n +0000003314 00000 n +0000003461 00000 n +trailer << + /Root 1 0 R + /Size 29 + /ID [<4866f3ccc81fb28dc4a27f0f976ce937><31415926535897932384626433832795>] +>> +startxref +3497 +%%EOF diff --git a/qpdf/qtest/qpdf/fx-overlay-67.pdf b/qpdf/qtest/qpdf/fx-overlay-67.pdf new file mode 100644 index 00000000..8c09168f --- /dev/null +++ b/qpdf/qtest/qpdf/fx-overlay-67.pdf @@ -0,0 +1,389 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 2 + /Kids [ + 3 0 R + 4 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents [ + 5 0 R + 7 0 R + 9 0 R + ] + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 11 0 R + >> + /ProcSet 12 0 R + /XObject << + /Fx1 13 0 R + >> + >> + /Type /Page +>> +endobj + +%% Page 2 +%% Original object ID: 4 0 +4 0 obj +<< + /Contents [ + 15 0 R + 17 0 R + 19 0 R + ] + /MediaBox [ + 0 + 0 + 306 + 396 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 21 0 R + >> + /ProcSet 22 0 R + /XObject << + /Fx1 23 0 R + >> + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 16 0 +5 0 obj +<< + /Length 6 0 R +>> +stream +q +endstream +endobj + +6 0 obj +2 +endobj + +%% Contents for page 1 +%% Original object ID: 5 0 +7 0 obj +<< + /Length 8 0 R +>> +stream +1 .5 0 RG +1 .5 0 rg +BT + /F1 24 Tf + 72 690 Td + (big) Tj +ET +5 w +0 0 612 792 re s +246 396 60 60 re f +endstream +endobj + +8 0 obj +101 +endobj + +%% Contents for page 1 +%% Original object ID: 17 0 +9 0 obj +<< + /Length 10 0 R +>> +stream + +Q +q +2.00000 0.00000 0.00000 2.00000 0.00000 0.00000 cm +/Fx1 Do +Q +endstream +endobj + +10 0 obj +66 +endobj + +%% Original object ID: 7 0 +11 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 8 0 +12 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 13 0 +13 0 obj +<< + /BBox [ + 0 + 0 + 306 + 396 + ] + /Resources << + /Font << + /F1 25 0 R + >> + /ProcSet 26 0 R + >> + /Subtype /Form + /Type /XObject + /Length 14 0 R +>> +stream +.5 0 .5 RG +.5 0 .5 rg +BT + /F1 24 Tf + 36 345 Td + (small) Tj +ET +5 w +0 0 306 396 re s +153 198 30 30 re f +endstream +endobj + +14 0 obj +105 +endobj + +%% Contents for page 2 +%% Original object ID: 21 0 +15 0 obj +<< + /Length 16 0 R +>> +stream +q +endstream +endobj + +16 0 obj +2 +endobj + +%% Contents for page 2 +%% Original object ID: 9 0 +17 0 obj +<< + /Length 18 0 R +>> +stream +1 .5 0 RG +1 .5 0 rg +BT + /F1 24 Tf + 36 345 Td + (small) Tj +ET +5 w +0 0 306 396 re s +153 198 30 30 re f +endstream +endobj + +18 0 obj +103 +endobj + +%% Contents for page 2 +%% Original object ID: 22 0 +19 0 obj +<< + /Length 20 0 R +>> +stream + +Q +q +0.50000 0.00000 0.00000 0.50000 0.00000 0.00000 cm +/Fx1 Do +Q +endstream +endobj + +20 0 obj +66 +endobj + +%% Original object ID: 11 0 +21 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 12 0 +22 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 18 0 +23 0 obj +<< + /BBox [ + 0 + 0 + 612 + 792 + ] + /Resources << + /Font << + /F1 27 0 R + >> + /ProcSet 28 0 R + >> + /Subtype /Form + /Type /XObject + /Length 24 0 R +>> +stream +.5 0 .5 RG +.5 0 .5 rg +BT + /F1 24 Tf + 72 690 Td + (big) Tj +ET +5 w +0 0 612 792 re s +246 396 60 60 re f +endstream +endobj + +24 0 obj +103 +endobj + +%% Original object ID: 14 0 +25 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 15 0 +26 0 obj +[ + /PDF + /Text +] +endobj + +%% Original object ID: 19 0 +27 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 20 0 +28 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 29 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000252 00000 n +0000000554 00000 n +0000000873 00000 n +0000000930 00000 n +0000000998 00000 n +0000001154 00000 n +0000001225 00000 n +0000001347 00000 n +0000001394 00000 n +0000001540 00000 n +0000001604 00000 n +0000001920 00000 n +0000001992 00000 n +0000002051 00000 n +0000002120 00000 n +0000002280 00000 n +0000002352 00000 n +0000002475 00000 n +0000002523 00000 n +0000002670 00000 n +0000002734 00000 n +0000003048 00000 n +0000003097 00000 n +0000003244 00000 n +0000003308 00000 n +0000003455 00000 n +trailer << + /Root 1 0 R + /Size 29 + /ID [<4866f3ccc81fb28dc4a27f0f976ce937><31415926535897932384626433832795>] +>> +startxref +3491 +%%EOF diff --git a/qpdf/qtest/qpdf/fxo-bigsmall.pdf b/qpdf/qtest/qpdf/fxo-bigsmall.pdf new file mode 100644 index 00000000..ff8b108a --- /dev/null +++ b/qpdf/qtest/qpdf/fxo-bigsmall.pdf @@ -0,0 +1,177 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 2 + /Kids [ + 3 0 R + 4 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents 5 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 7 0 R + >> + /ProcSet 8 0 R + >> + /Type /Page +>> +endobj + +%% Page 2 +%% Original object ID: 8 0 +4 0 obj +<< + /Contents 9 0 R + /MediaBox [ + 0 + 0 + 306 + 396 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 11 0 R + >> + /ProcSet 12 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +5 0 obj +<< + /Length 6 0 R +>> +stream +1 .5 0 RG +1 .5 0 rg +BT + /F1 24 Tf + 72 690 Td + (big) Tj +ET +5 w +0 0 612 792 re s +246 396 60 60 re f +endstream +endobj + +6 0 obj +101 +endobj + +%% Original object ID: 6 0 +7 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 7 0 +8 0 obj +[ + /PDF + /Text +] +endobj + +%% Contents for page 2 +%% Original object ID: 9 0 +9 0 obj +<< + /Length 10 0 R +>> +stream +1 .5 0 RG +1 .5 0 rg +BT + /F1 24 Tf + 36 345 Td + (small) Tj +ET +5 w +0 0 306 396 re s +153 198 30 30 re f +endstream +endobj + +10 0 obj +103 +endobj + +%% Original object ID: 11 0 +11 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 12 0 +12 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 13 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000252 00000 n +0000000481 00000 n +0000000725 00000 n +0000000881 00000 n +0000000928 00000 n +0000001073 00000 n +0000001158 00000 n +0000001317 00000 n +0000001366 00000 n +0000001513 00000 n +trailer << + /Root 1 0 R + /Size 13 + /ID [<4866f3ccc81fb28dc4a27f0f976ce937><5b260ad1f0f4ccd7895374e61eff0d55>] +>> +startxref +1549 +%%EOF diff --git a/qpdf/qtest/qpdf/fxo-smallbig.pdf b/qpdf/qtest/qpdf/fxo-smallbig.pdf new file mode 100644 index 00000000..2e4061f9 --- /dev/null +++ b/qpdf/qtest/qpdf/fxo-smallbig.pdf @@ -0,0 +1,177 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 2 + /Kids [ + 3 0 R + 4 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents 5 0 R + /MediaBox [ + 0 + 0 + 306 + 396 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 7 0 R + >> + /ProcSet 8 0 R + >> + /Type /Page +>> +endobj + +%% Page 2 +%% Original object ID: 8 0 +4 0 obj +<< + /Contents 9 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 11 0 R + >> + /ProcSet 12 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +5 0 obj +<< + /Length 6 0 R +>> +stream +.5 0 .5 RG +.5 0 .5 rg +BT + /F1 24 Tf + 36 345 Td + (small) Tj +ET +5 w +0 0 306 396 re s +153 198 30 30 re f +endstream +endobj + +6 0 obj +105 +endobj + +%% Original object ID: 6 0 +7 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 7 0 +8 0 obj +[ + /PDF + /Text +] +endobj + +%% Contents for page 2 +%% Original object ID: 9 0 +9 0 obj +<< + /Length 10 0 R +>> +stream +.5 0 .5 RG +.5 0 .5 rg +BT + /F1 24 Tf + 72 690 Td + (big) Tj +ET +5 w +0 0 612 792 re s +246 396 60 60 re f +endstream +endobj + +10 0 obj +103 +endobj + +%% Original object ID: 11 0 +11 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 12 0 +12 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 13 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000252 00000 n +0000000481 00000 n +0000000725 00000 n +0000000885 00000 n +0000000932 00000 n +0000001077 00000 n +0000001162 00000 n +0000001321 00000 n +0000001370 00000 n +0000001517 00000 n +trailer << + /Root 1 0 R + /Size 13 + /ID [<4866f3ccc81fb28dc4a27f0f976ce937><7ac42e786a05e6c1a5adfc1c1f33e15c>] +>> +startxref +1553 +%%EOF diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index 2b1c710d..d3305377 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -2116,6 +2116,57 @@ void runtest(int n, char const* filename1, char const* arg2) w.setOutputFilename("a.pdf"); w.write(); } + else if ((n >= 64) && (n <= 67)) + { + // Placing form XObjects: expand, shrink + assert(arg2); + QPDF pdf2; + pdf2.processFile(arg2); + + // Overlay file2 on file1. + // 64: allow neither shrink nor shrink + // 65: allow shrink but not expand + // 66: allow expand but not shrink + // 67: allow both shrink and expand + bool allow_shrink = ((n == 65) || (n == 67)); + bool allow_expand = ((n == 66) || (n == 67)); + std::vector pages1 = + QPDFPageDocumentHelper(pdf).getAllPages(); + std::vector pages2 = + QPDFPageDocumentHelper(pdf2).getAllPages(); + size_t npages = (pages1.size() < pages2.size() + ? pages1.size() : pages2.size()); + for (size_t i = 0; i < npages; ++i) + { + QPDFPageObjectHelper& ph1 = pages1.at(i); + QPDFPageObjectHelper& ph2 = pages2.at(i); + QPDFObjectHandle fo = pdf.copyForeignObject( + ph2.getFormXObjectForPage()); + int min_suffix = 1; + QPDFObjectHandle resources = ph1.getAttribute("/Resources", true); + std::string name = resources.getUniqueResourceName( + "/Fx", min_suffix); + std::string content = + ph1.placeFormXObject( + fo, name, ph1.getTrimBox().getArrayAsRectangle(), + false, allow_shrink, allow_expand); + if (! content.empty()) + { + resources.mergeResources( + QPDFObjectHandle::parse("<< /XObject << >> >>")); + resources.getKey("/XObject").replaceKey(name, fo); + ph1.addPageContents( + QPDFObjectHandle::newStream(&pdf, "q\n"), true); + ph1.addPageContents( + QPDFObjectHandle::newStream(&pdf, "\nQ\n" + content), + false); + } + } + QPDFWriter w(pdf, "a.pdf"); + w.setQDFMode(true); + w.setStaticID(true); + w.write(); + } else { throw std::runtime_error(std::string("invalid test ") +