Monday, July 27, 2009

ColdFusion: Basic example - Positioning buttons with iText

Recently, a poster named Brook asked about positioning PushButtonFields with iText. Positioning is relatively simple once you understand the default coordinate system. However, if you are like me, that may initially require a slight adjustment in thinking. Unlike top-down systems, the pdf coordinates work from the bottom up, where 0,0 signifies the lower left corner. Also, the default measurement is in points. So you must take both of these things into account in order to position elements.


I threw together a very simple example to demonstrate. The example adds a few buttons to a blank pdf. Each button is placed on a separate page, in the lower right corner. In terms of coordinates, the key part of the code is the section below. It uses a Rectangle object to determine the placement and dimensions of the buttons. The four (4) values passed into the Rectangle represent: the lower left x, lower left y, upper right x and upper right y coordinates.


<cfscript>
box = Rectangle.init( document.right()-72, // llx = lower left x position
document.bottom(), // lly = lower left y position
document.right(), // urx = upper right x position
document.bottom() + 36 // ury = upper right y position
);
// ...
</cfscript>


If you look at the blue angular brackets in the image below, you can easily see how these values combine to determine both size and position of the buttons.



Note, this example uses the JavaLoader.cfc to load a newer version of iText.

<cfscript>
javaLoader = server[MyUniqueKeyForJavaLoader];
fullPathToOutputFile = ExpandPath("./Buttonapalooza.pdf");

PageSize = javaLoader.create("com.lowagie.text.PageSize");
document = javaLoader.create("com.lowagie.text.Document").init(PageSize.LETTER);
outStream = javaLoader.create("java.io.FileOutputStream").init(fullPathToOutputFile);
writer = javaLoader.create("com.lowagie.text.pdf.PdfWriter").getInstance(document, outStream);
Color = javaLoader.create("java.awt.Color");

PushbuttonField = javaloader.create("com.lowagie.text.pdf.PushbuttonField");
Rectangle = javaloader.create("com.lowagie.text.Rectangle");
// the coordinates define both position and size of the field
box = Rectangle.init( document.right()-72, // lower left
document.bottom(), // lower right
document.right(), // upper x
document.bottom() + 36 // upper y
);

document.open();
for (i = 1; i lte 5; i = i+1)
{
button = PushbuttonField.init(writer, box, "buttonField"& i);
button.setText("Button "& i);
button.setBackgroundColor(Color.GRAY);
document.newPage();
writer.addAnnotation(button.getField());
}

document.close();
WriteOutput("Finished!");
</cfscript>


Related articles

6 comments:

Unknown July 27, 2009 at 10:15 AM  

Your Awesome! Thanks for explaining this in such detail!!

Unknown July 28, 2009 at 1:15 PM  

I'm trying to get some extended properties about an existing button, and also to set properties of a button (as you know ;)). I found this reference:

http://www.mail-archive.com/itext-questions@lists.sourceforge.net/msg20916.html

But how do you do this with CF/Itext? Any quick pointers?

cfSearching July 30, 2009 at 9:53 AM  

@Brook,

(I sent a message offline). For anyone else the concept is basically what is described in the archived message. You can access the desired properties using the underlying dictionaries:

<cfscript>
formData = stamper.getAcroFields();
field = formData.getFields().get(buttonFieldName);
// for later versions, use field.getWidgetRef() instead
widgetRef = field.widget_refs.iterator().next();
obj = reader.getPdfObject( widgetRef.getNumber() );
// get button action (may be null)
action = obj.get(PdfName.A);
</cfscript>

Then extract the properties needed by key name (ie PdfName.A -action, etcetera). In many ways, PdfDictionary objects are similar to CF structures. Except you use get(PdfName keyName) to access the "values".

You can read more about the meaning of they key names in: Portable document format – Part 1: PDF 1.7, First Edition (July, 2008)
http://www.adobe.com/devnet/pdf/pdf_reference.html
* Section 12 Interactive Features

-Leigh

johnnylawson September 28, 2009 at 12:36 PM  

Leigh -

I have been racking my brain on one little situation - Of most of your examples I see the output file is dynamically generated. I have been working with iText and Coldfusion on this project and have had some success. My question is - I am trying to add columntext to an existing PDF (right now I am stamping dynamic images and text) on the pdf. I need to make it so that the text obeys my width and height coordinates I have stored in a database. Could you point me in the right direction? I'm having a hard time converting what I see with itext's documentation into coldfusion. I can generally translate cfscript in to cfml which is how I have my project layed out in right now. I have a url - http://67.216.80.60/services/Pdfgeneratorgateway.cfc?method=PdfGenerator&job_id=69 - you can generally see my issue there.

Thanks for any help in advance!

cfSearching October 1, 2009 at 10:27 AM  

@johnnylawson,

I tried the url, but right now it is throwing an exception. So I guess you are working on it. I will check it again later.

-Leigh

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep