Skip to content

Commit

Permalink
Overlay/underlay: capture origial page as form XObject (fixes qpdf#904)
Browse files Browse the repository at this point in the history
  • Loading branch information
jberkenbilt committed Feb 25, 2023
1 parent 48bacbf commit 78f7dc9
Show file tree
Hide file tree
Showing 19 changed files with 40 additions and 14 deletions.
9 changes: 9 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
2023-02-25 Jay Berkenbilt <[email protected]>

* When performing overlay or underlay operations, convert the
origianal page to a form XObject instead of simply isolating its
contents with q/Q operators. This prevents unbalanced q/Q
operators in any of the original pages from messing up the
graphics state of anything that is overlaid on top of it. Fixes
#904.

2023-02-18 Jay Berkenbilt <[email protected]>

* Treat all linearization errors and warnings as warnings, and
Expand Down
39 changes: 26 additions & 13 deletions libqpdf/QPDFJob.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2082,11 +2082,6 @@ QPDFJob::doUnderOverlayForPage(
std::string content;
int min_suffix = 1;
QPDFObjectHandle resources = dest_page.getAttribute("/Resources", true);
if (!resources.isDictionary()) {
QTC::TC("qpdf", "QPDFJob overlay page with no resources");
resources = dest_page.getObjectHandle().replaceKeyAndGetNew(
"/Resources", QPDFObjectHandle::newDictionary());
}
for (int from_pageno: pagenos[pageno]) {
doIfVerbose([&](Pipeline& v, std::string const& prefix) {
v << " " << uo.which << " " << from_pageno << "\n";
Expand Down Expand Up @@ -2179,7 +2174,24 @@ QPDFJob::handleUnderOverlay(QPDF& pdf)
overlay_pagenos.count(pageno))) {
continue;
}
// This code converts the original page, any underlays, and
// any overlays to form XObjects. Then it concatenates display
// of all underlays, the original page, and all overlays.
// Prior to 11.3.0, the original page contents were wrapped in
// q/Q, but this didin't work if the original page had
// unbalanced q/Q operators. See github issue #904.
auto& dest_page = main_pages.at(i);
auto dest_page_oh = dest_page.getObjectHandle();
auto this_page_fo = dest_page.getFormXObjectForPage();
// The resulting form xobject lazily reads the content from
// the original page, which we are going to replace. Therefore
// we have to explicitly copy it.
auto content_data = this_page_fo.getRawStreamData();
this_page_fo.replaceStreamData(
content_data, QPDFObjectHandle(), QPDFObjectHandle());
auto resources = dest_page_oh.replaceKeyAndGetNew(
"/Resources", "<< /XObject << >> >>"_qpdf);
resources.getKey("/XObject").replaceKeyAndGetNew("/Fx0", this_page_fo);
auto content = doUnderOverlayForPage(
pdf,
m->underlay,
Expand All @@ -2188,15 +2200,16 @@ QPDFJob::handleUnderOverlay(QPDF& pdf)
underlay_fo,
upages,
dest_page);
if (!content.empty()) {
dest_page.addPageContents(pdf.newStream(content), true);
}
content = doUnderOverlayForPage(
content += dest_page.placeFormXObject(
this_page_fo,
"/Fx0",
dest_page.getMediaBox().getArrayAsRectangle(),
true,
false,
false);
content += doUnderOverlayForPage(
pdf, m->overlay, overlay_pagenos, i, overlay_fo, opages, dest_page);
if (!content.empty()) {
dest_page.addPageContents(pdf.newStream("q\n"), true);
dest_page.addPageContents(pdf.newStream("\nQ\n" + content), false);
}
dest_page_oh.replaceKey("/Contents", pdf.newStream(content));
}
}

Expand Down
5 changes: 5 additions & 0 deletions manual/release-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ For a detailed list of changes, please see the file
- New option :qpdf:ref:`--remove-restrictions` removes security
restrictions from digitally signed files.

- Improve overlay/underlay so that the content a page with
unbalanced graphics state operators (``q``/``Q``) doesn't affect
the way subsequent pages are displayed. This changes the output
of all overlay/underlay operations.

- Library enhancements

- New method ``QPDF::removeSecurityRestrictions`` removes security
Expand Down
1 change: 0 additions & 1 deletion qpdf/qpdf.testcov
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,6 @@ QPDF resolve duplicated page in insert 0
QPDFWriter preserve object streams 1
QPDFWriter exclude from object stream 0
QPDF_pages findPage not found 0
QPDFJob overlay page with no resources 0
QPDFObjectHandle check ownership 0
QPDFJob weak crypto error 0
qpdf-c called qpdf_oh_is_initialized 0
Expand Down
Binary file modified qpdf/qtest/qpdf/job-json-underlay-overlay-password.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/job-json-underlay-overlay.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/overlay-copy-annotations-p1.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/overlay-copy-annotations-p2.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/overlay-copy-annotations-p5.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/overlay-copy-annotations-p6.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/overlay-copy-annotations.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/overlay-no-resources.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/uo-1.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/uo-2.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/uo-3.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/uo-4.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/uo-5.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/uo-6.pdf
Binary file not shown.
Binary file modified qpdf/qtest/qpdf/uo-7.pdf
Binary file not shown.

0 comments on commit 78f7dc9

Please sign in to comment.