aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2020-04-03 22:48:20 +0200
committerJay Berkenbilt <ejb@ql.org>2020-04-04 03:39:17 +0200
commit4f3b89991be466b77d1be2ab4fe0dc3f9228a17e (patch)
tree30381f6344548f26a3e2b2b29e57a4ea53c23001
parentb76b73b229c0d8111c13c9179ad97d3bfd1e1c7c (diff)
downloadqpdf-4f3b89991be466b77d1be2ab4fe0dc3f9228a17e.tar.zst
placeFormXObject: allow control of shrink/expand (fixes #409)
-rw-r--r--ChangeLog8
-rw-r--r--include/qpdf/QPDFPageObjectHelper.hh30
-rw-r--r--libqpdf/QPDFPageObjectHelper.cc19
-rw-r--r--qpdf/qtest/qpdf.test15
-rw-r--r--qpdf/qtest/qpdf/fx-overlay-64.pdf389
-rw-r--r--qpdf/qtest/qpdf/fx-overlay-65.pdf389
-rw-r--r--qpdf/qtest/qpdf/fx-overlay-66.pdf389
-rw-r--r--qpdf/qtest/qpdf/fx-overlay-67.pdf389
-rw-r--r--qpdf/qtest/qpdf/fxo-bigsmall.pdf177
-rw-r--r--qpdf/qtest/qpdf/fxo-smallbig.pdf177
-rw-r--r--qpdf/test_driver.cc51
11 files changed, 2016 insertions, 17 deletions
diff --git a/ChangeLog b/ChangeLog
index 77e31d89..94109b11 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2020-04-03 Jay Berkenbilt <ejb@ql.org>
+
+ * 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 <ejb@ql.org>
* 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<QPDFPageObjectHelper> pages1 =
+ QPDFPageDocumentHelper(pdf).getAllPages();
+ std::vector<QPDFPageObjectHelper> 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 ") +