Thursday, December 20, 2007

Getting started with iText - Part 14 (AddWatermarkPageNumbers.java)

The next installation in Getting started with iText translates the AddWatermarkPageNumbers example. As it's name implies, it demonstrates how to add watermarks and page numbers to an existing PDF file.

This example uses newer methods that are not available in the MX7/CF8 iText jar. So once again we will use Mark Mandel's JavaLoader.cfc to load the iText 2.0.7 jar.

What you will need for this example


1. A recent version of iText like 2.0.7. For instructions, see How to use a newer version of iText

2. Three (3) input files from the iText site. Download the sample files and place them in the same directory as your .cfm script: ChapterSection.pdf  SimpleAnnotations1.pdf  watermark.jpg

Getting started



As usual we start by initializing a few variables with the file paths used in this example. Then we get an instance of the JavaLoader.cfc for creating our iText objects. Next we create a PdfReader object that will read in our original PDF file. We then create a PdfStamper object that will be used to copy the original into a new PDF file.


UPDATE: To avoid confusion the attached code sample was updated to use an instance of the javaLoader stored in the server scope. This is necessary to prevent memory leaks as mentioned in the instructions.



<cfscript>
// cfSearching: by default all files are in current directory. change as needed
fullPathToInputFile = ExpandPath("./ChapterSection.pdf");
fullPathToWatermark = ExpandPath("./watermark.jpg");
fullPathToAnnotations = ExpandPath("./SimpleAnnotations1.pdf");
fullPathToOutputFile = ExpandPath("./Watermark_Pagenumbers.pdf");

/*
cfSearching: This example was updated to use an instance of
the javaLoader that is stored in the server scope. This prevents
memory leaks due to a bug in ColdFusion.

"Using a Java URLClassLoader in CFMX Can Cause a Memory Leak"
http://www.compoundtheory.com/?action=displayPost&ID=212
*/
javaLoader = server[YourUniqueKeyForTheJavaLoader];

// we create a reader for a certain document
pdfReader = javaLoader.create("com.lowagie.text.pdf.PdfReader").init(fullPathToInputFile);
totalPages = pdfReader.getNumberOfPages();

// we create a stamper that will copy the document to a new file
outStream = createObject("java", "java.io.FileOutputStream").init(fullPathToOutputFile);
pdfStamper = javaLoader.create("com.lowagie.text.pdf.PdfStamper").init(pdfReader, outStream);
</cfscript>


To make things interesting we add a bit of metadata to the new PDF file. We then load the watermark image and center it within the page using the setAbsolutePosition method. Next we create a BaseFont object that will be used for drawing the page numbers and watermark text.


<cfscript>
// adding some metadata
moreInfo = createObject("java", "java.util.HashMap").init();
moreInfo.put("Author", "Bruno Lowagie");
pdfStamper.setMoreInfo(moreInfo);

// cfSearching: create objects that will be used to in our loop to add content to each page
img = javaLoader.create("com.lowagie.text.Image").getInstance(fullPathToWatermark);
img.setAbsolutePosition( javacast("float", 200), javacast("float", 400) );
BaseFont = javaLoader.create("com.lowagie.text.pdf.BaseFont");
bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
Element = javaLoader.create("com.lowagie.text.Element");
PageSize = javaLoader.create("com.lowagie.text.PageSize");
</cfscript>


Using a while loop we stamp each page with the watermark image. You may notice the watermark is added to the underContent layer or beneath the existing text. We then stamp the word "DUPLICATE" diagonally across the watermark image. But this time using the overContent layer, or over the existing text layer. Still using the overContent layer we draw the page number at the bottom of the current page. You can read more about layers in the iText documentation.


<cfscript>
// adding content to each page
i = 0;
while (i LT totalPages) {
i = i + 1;

// watermark under the existing page
under = pdfStamper.getUnderContent( javacast("int", i) );
under.addImage(img);

// cfSearching: draw page number text over the existing page
over = pdfStamper.getOverContent( javacast("int", i) );
over.beginText();
over.setFontAndSize(bf, javacast("float", 18) );
over.setTextMatrix( javacast("float", 30), javacast("float", 30) );
over.showText("page " & i);
// cfSearching: draw the word "DUPLICATE" across the watermark image
// cfSearching: over the existing page content
over.setFontAndSize(bf, javacast("float", 32) );
over.showTextAligned(Element.ALIGN_LEFT, javacast("string", "DUPLICATE"),
javacast("float", 230),
javacast("float", 430),
javacast("float", 45) );
over.endText();
}
</cfscript>


Next we insert a new page at the beginning of our new PDF. Think of it as a cover page. The words "Duplicate of Existing PDF Document" are then written across the overContent. Finally we copy the content from another document and add it to the underContent layer. Why? To demonstrate we have merged the contents from two PDF files I suppose.


<cfscript>
// adding an extra page
pdfStamper.insertPage( javacast("int", 1), PageSize.A4);
over = pdfStamper.getOverContent(javacast("int", 1));
over.beginText();
over.setFontAndSize(bf, javacast("float", 18) );
over.showTextAligned(Element.ALIGN_LEFT, javacast("string", "DUPLICATE OF AN EXISTING PDF DOCUMENT"),
javacast("float", 30),
javacast("float", 600),
javacast("float", 0) );
over.endText();
// adding a page from another document
reader2 = javaLoader.create("com.lowagie.text.pdf.PdfReader").init( fullPathToAnnotations );
under = pdfStamper.getUnderContent( javacast("int", 1));
under.addTemplate(pdfStamper.getImportedPage(reader2, javacast("int", 3)),
javacast("float", 1),
javacast("float", 0),
javacast("float", 0),
javacast("float", 1),
javacast("float", 0),
javacast("float", 0) );
</cfscript>


All that is left is to close the PdfStamper object and our new PDF will be created. Well that is it for now. It was interesting learning how to draw an image or text on the different layers. Until next time.

Documentation: Manipulating existing PDF documents
Source: AddWatermarkPageNumbers.java


Complete Code

<h1>Add Watermark and Page Numbers Example</h1>
<cfscript>
savedErrorMessage = "";

// cfSearching: by default all files are in current directory. change as needed
fullPathToInputFile = ExpandPath("./ChapterSection.pdf");
fullPathToWatermark = ExpandPath("./watermark.jpg");
fullPathToAnnotations = ExpandPath("./SimpleAnnotations1.pdf");
fullPathToOutputFile = ExpandPath("./Watermark_Pagenumbers.pdf");

/*
cfSearching: This example was updated to use an instance of
the javaLoader that is stored in the server scope. This prevents
memory leaks due to a bug in ColdFusion.

"Using a Java URLClassLoader in CFMX Can Cause a Memory Leak"
http://www.compoundtheory.com/?action=displayPost&ID=212
*/
javaLoader = server[YourUniqueKeyForTheJavaLoader];

try {
// we create a reader for a certain document
pdfReader = javaLoader.create("com.lowagie.text.pdf.PdfReader").init(fullPathToInputFile);
totalPages = pdfReader.getNumberOfPages();

// we create a stamper that will copy the document to a new file
outStream = createObject("java", "java.io.FileOutputStream").init(fullPathToOutputFile);
pdfStamper = javaLoader.create("com.lowagie.text.pdf.PdfStamper").init(pdfReader, outStream);

// adding some metadata
moreInfo = createObject("java", "java.util.HashMap").init();
moreInfo.put("Author", "Bruno Lowagie");
pdfStamper.setMoreInfo(moreInfo);

// cfSearching: create objects that will be used to in our loop to add content to each page
img = javaLoader.create("com.lowagie.text.Image").getInstance(fullPathToWatermark);
img.setAbsolutePosition( javacast("float", 200), javacast("float", 400) );
BaseFont = javaLoader.create("com.lowagie.text.pdf.BaseFont");
bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
Element = javaLoader.create("com.lowagie.text.Element");
PageSize = javaLoader.create("com.lowagie.text.PageSize");

// adding content to each page
i = 0;
while (i LT totalPages) {
i = i + 1;

// watermark under the existing page
under = pdfStamper.getUnderContent( javacast("int", i) );
under.addImage(img);
// text over the existing page
over = pdfStamper.getOverContent( javacast("int", i) );
over.beginText();
over.setFontAndSize(bf, javacast("float", 18) );
over.setTextMatrix( javacast("float", 30), javacast("float", 30) );
over.showText("page " & i);
over.setFontAndSize(bf, javacast("float", 32) );
over.showTextAligned(Element.ALIGN_LEFT, javacast("string", "DUPLICATE"),
javacast("float", 230),
javacast("float", 430),
javacast("float", 45) );
over.endText();
}

// adding an extra page
pdfStamper.insertPage( javacast("int", 1), PageSize.A4);
over = pdfStamper.getOverContent(javacast("int", 1));
over.beginText();
over.setFontAndSize(bf, javacast("float", 18) );
over.showTextAligned(Element.ALIGN_LEFT, javacast("string", "DUPLICATE OF AN EXISTING PDF DOCUMENT"),
javacast("float", 30),
javacast("float", 600),
javacast("float", 0) );
over.endText();
// adding a page from another document
reader2 = javaLoader.create("com.lowagie.text.pdf.PdfReader").init( fullPathToAnnotations );
under = pdfStamper.getUnderContent( javacast("int", 1));
under.addTemplate(pdfStamper.getImportedPage(reader2, javacast("int", 3)),
javacast("float", 1),
javacast("float", 0),
javacast("float", 0),
javacast("float", 1),
javacast("float", 0),
javacast("float", 0) );

WriteOutput("Finished!");
}
catch (java.lang.Exception e) {
savedErrorMessage = e;
}
// closing PdfStamper will generate the new PDF file
if (IsDefined("pdfStamper")) {
pdfStamper.close();
}
if (IsDefined("outStream")) {
outStream.close();
}
</cfscript>

<!--- show any errors --->
<cfif len(savedErrorMessage) gt 0>
Error - unable to create document
<cfdump var="#savedErrorMessage#">
</cfif>

0 comments:

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep