From 2d1db060422eabb82aaa1764926a3f9e881116e2 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 26 Jan 2019 17:33:06 -0500 Subject: [PATCH] Example of form XObject, page overlay --- ChangeLog | 3 + examples/build.mk | 3 +- examples/pdf-overlay-page.cc | 110 ++++++++++++++++++++++++++ examples/qtest/overlay-page.test | 28 +++++++ examples/qtest/overlay-page/in.pdf | Bin 0 -> 1840 bytes examples/qtest/overlay-page/out.pdf | Bin 0 -> 3361 bytes examples/qtest/overlay-page/stamp.pdf | Bin 0 -> 799 bytes 7 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 examples/pdf-overlay-page.cc create mode 100644 examples/qtest/overlay-page.test create mode 100644 examples/qtest/overlay-page/in.pdf create mode 100644 examples/qtest/overlay-page/out.pdf create mode 100644 examples/qtest/overlay-page/stamp.pdf diff --git a/ChangeLog b/ChangeLog index cacd3592..3f267404 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2019-01-26 Jay Berkenbilt + * Create examples/pdf-overlay-page.cc to demonstrate use of + page/form XObject interaction + * Add new methods QPDFPageObjectHelper::getFormXObjectForPage, which creates a form XObject equivalent to a page, and QPDFObjectHandle::placeFormXObject, which generates content stream diff --git a/examples/build.mk b/examples/build.mk index 031601ff..8033d7d2 100644 --- a/examples/build.mk +++ b/examples/build.mk @@ -9,7 +9,8 @@ BINS_examples = \ pdf-split-pages \ pdf-filter-tokens \ pdf-count-strings \ - pdf-set-form-values + pdf-set-form-values \ + pdf-overlay-page CBINS_examples = pdf-linearize TARGETS_examples = $(foreach B,$(BINS_examples) $(CBINS_examples),examples/$(OUTPUT_DIR)/$(call binname,$(B))) diff --git a/examples/pdf-overlay-page.cc b/examples/pdf-overlay-page.cc new file mode 100644 index 00000000..88722352 --- /dev/null +++ b/examples/pdf-overlay-page.cc @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// This program demonstrates use of form XObjects to overlay a page +// from one file onto all pages of another file. The qpdf program's +// --overlay and --underlay options provide a more general version of +// this capability. + +static char const* whoami = 0; + +void usage() +{ + std::cerr << "Usage: " << whoami << " infile pagefile outfile" + << std::endl + << "Stamp page 1 of pagefile on every page of infile," + << " writing to outfile" + << std::endl; + exit(2); +} + +static void stamp_page(char const* infile, + char const* stampfile, + char const* outfile) +{ + QPDF inpdf; + inpdf.processFile(infile); + QPDF stamppdf; + stamppdf.processFile(stampfile); + + // Get first page from other file + QPDFPageObjectHelper stamp_page_1 = + QPDFPageDocumentHelper(stamppdf).getAllPages().at(0); + // Convert page to a form XObject + QPDFObjectHandle foreign_fo = stamp_page_1.getFormXObjectForPage(); + // Copy form XObject to the input file + QPDFObjectHandle stamp_fo = inpdf.copyForeignObject(foreign_fo); + + // For each page... + std::vector pages = + QPDFPageDocumentHelper(inpdf).getAllPages(); + for (std::vector::iterator iter = pages.begin(); + iter != pages.end(); ++iter) + { + QPDFPageObjectHelper& ph = *iter; + + // Find a unique resource name for the new form XObject + QPDFObjectHandle resources = ph.getAttribute("/Resources", true); + int min_suffix = 1; + std::string name = resources.getUniqueResourceName("/Fx", min_suffix); + + // Generate content to place the form XObject centered within + // destination page's trim box. + std::string content = + ph.placeFormXObject( + stamp_fo, name, ph.getTrimBox().getArrayAsRectangle()); + if (! content.empty()) + { + // Append the content to the page's content. Surround the + // original content with q...Q to the new content from the + // page's original content. + resources.mergeResources( + QPDFObjectHandle::parse("<< /XObject << >> >>")); + resources.getKey("/XObject").replaceKey(name, stamp_fo); + ph.addPageContents( + QPDFObjectHandle::newStream(&inpdf, "q\n"), true); + ph.addPageContents( + QPDFObjectHandle::newStream(&inpdf, "\nQ\n" + content), false); + } + } + + QPDFWriter w(inpdf, outfile); + w.setStaticID(true); // for testing only + w.write(); +} + +int main(int argc, char* argv[]) +{ + whoami = QUtil::getWhoami(argv[0]); + + // For libtool's sake.... + if (strncmp(whoami, "lt-", 3) == 0) + { + whoami += 3; + } + + if (argc != 4) + { + usage(); + } + char const* infile = argv[1]; + char const* stampfile = argv[2]; + char const* outfile = argv[3]; + + try + { + stamp_page(infile, stampfile, outfile); + } + catch (std::exception &e) + { + std::cerr << whoami << ": " << e.what() << std::endl; + exit(2); + } + return 0; +} diff --git a/examples/qtest/overlay-page.test b/examples/qtest/overlay-page.test new file mode 100644 index 00000000..68e695d3 --- /dev/null +++ b/examples/qtest/overlay-page.test @@ -0,0 +1,28 @@ +#!/usr/bin/env perl +require 5.008; +use warnings; +use strict; + +chdir("overlay-page"); + +require TestDriver; + +my $td = new TestDriver('overlay-page'); + +cleanup(); + +$td->runtest("overlay-page", + {$td->COMMAND => "pdf-overlay-page in.pdf stamp.pdf a.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); +$td->runtest("compare files", + {$td->FILE => "a.pdf"}, + {$td->FILE => "out.pdf"}); + +cleanup(); + +$td->report(2); + +sub cleanup +{ + unlink("a.pdf"); +} diff --git a/examples/qtest/overlay-page/in.pdf b/examples/qtest/overlay-page/in.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f94c739a9504502e02775a60f336025c30aa62b5 GIT binary patch literal 1840 zcmc&!&ubGw6c(i*OB6xTi-(62Xrb-S%w~T?LZD3=C|ayZqzCaZyE}<1=}g(3VDu>T z-ao)o{|>=Rk3D$vA1QcK-)ytpn7tM{3&ZgC&3oT_^L?+r-|cPC&4yL~@%!|TMS*|{ zKU%F8*!z4eGH}q+2YdMWNPykpnorai+HFh7*f_F<3!PMOohaGmTEJ@e1tXLZpW#Lr z_17?Bs^V!RQdr$O5;MTm2Wg^3y0#F|8vFW>!qe_!Cc zgj}LC7s_B#3#l^*@;mmPh!ei8W*C>?=TQcJlL5xs=P5cCmG?!arfGz&Fiq(x{F5Ij zH1k01c3xOC5E?_-SQMfWGmVkVugk_6U<*Z>5-PZ;fq|{QNnoa{RbYk{Hw?@?bQPFk z-wgvZY$|~Dk_oaINisnNbVa0M)XXa($NB@fj(Pt~qec#`*|*Gk^YfE4B#pOscL&73 z{p`;1$)lBfe^1XZ&cB~MJ^p%jF+${CIx22{eXc#jzasTE&qU5m zdzc)GY@@FZxdeMhB8Wte!9GajmdujUFqnoq7qww1Er^=OC<30QZcd!P{$%y3q03#To}QXjp7V33uI~Y zaNaH1Xrt=K7*@CB=lIpTE<@EV`FR0OsU=(B;Lu*OHC-Gf|Jx{WD_AJ?k;O}X4q1q& zQ=UvvRA%Px%8`P(=KD%%m=C5w@7%Q6m}a~DvLpth^T}(43tO>G})b*{Y92V6W25cdOkE z7{mcp-&We*$cW#)bJee>S8 zZ(*a^TEfeww(!e85C7FLGEmgLt<~#D-(b5uK@=iwq;GzHha-KJrK}(AqDDjGVNWb1 zr46ewXK4_HO_p+WsrfE3FfkkgFN0ub5i-V6Z_np3x^(3ZA0mL>ii4ELi=~7DFF(O* zv=^qxMEdohm!NIeDssO@ir6DeC8jb8Cadwk9yP8u> zL!5IOjjWnTR!t-;gi}mI=$zB|fQP&3CkQ%0Zv}nmNBG-^9&U0!>d8t>QfTdgHhl2T z(b-Dn@5-0Z{iE2(2L$~*-?Sw92*jeIvd8%n?dGG;=%!(QbZ)I7%00ao+ASN8wr05pp^!Q~l`w9f*wWM3}8~Ad>lnu$M7@D~bm>eC|m3 z4VmuqrAxFw+Z*!Y$jx(Wb3cB2Sevh{fAwG2bezU%QvGQyEg{mrJ z+2m(YebNMT&1PMG7mM<{P+L;Tl>PaH%dhOAGtsT=X0z7KFLXn-WmUH+7Iac=TXv^e z=q7Fcr0VUJLbt`5uIMJeEjjkd)|+&b9ICLpYrOv{PXnJl)9gNE1DF_FU~^;Jd6w-- zn-W;ZC#HPP9L9V{GY~N~WBef7vZ#f2&^RWhAn~BEh}ke8O~kONt|Nl%#5xm0;Y`GA zF``VwTrs^Ik6{Btvrfi5HAYknOo_VAoD#F)YdVSHP=zmcE#=JEvlR`9VJf&VxWh)Jk}tjoCtk74`1->6e;V#_0-7r;9nCDb)-Vw;q@lsKN%xRp&M SEKX&i5r??i!ot-XE$u(r5V}PG literal 0 HcmV?d00001 diff --git a/examples/qtest/overlay-page/stamp.pdf b/examples/qtest/overlay-page/stamp.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a86f43442b41704df465425759fa239218140e81 GIT binary patch literal 799 zcmZWn&2G~`5Jo~su=Lmm7z8JOWR_4KeG~F$(L0?QiFs`R1D$jE2cwe$QruFTX$iVH`}5=}Q(xV2s2Z z8?Xo&gK_fa48hnJN-X3Y;+SET>p*?o5%%S>QozA@T;>f-!EULuj=av-Q}uOEtrS+O z0cTzA2=h|x$rWWW>G8M)erN&Z8i|?$J6U6F7Yb{7FNDG&W-@O{jjBgSI?c6N7;-ktBl!`s2PpZ@*HtvesSyYFva`5N80My_2lFa6UpyQ+Jl!L|kC5iVY# zDl-AbK}99YY7WNpvU*T8We+?Rr!03c{6w zSvSa4jRiBoXXfUB=eo8F1#B=*a`l0#$2>A_3*#2eb_=sSs=tNV!T($*q#f+yR4vK{ zxvytBmQpWQyN#3bErJim;Si=#m^-;!_-W|*mLI0MZKWRiS>SRn&x%~cQEqY}vLH1b l5fq^?3rl1o2tu^cLEkIlncjm&iCVQbxE5!F!NGCD{sJJ9+t2_2 literal 0 HcmV?d00001