Sunday, December 16, 2007

Creating a wicked Film Festival VIP Pass with iText - Part 1 (CODE)

Here is the full code for Creating a wicked Film Festival VIP Pass with iText - Part 1



UPDATE: I wanted to re-emphasize an issue that is mentioned in the instructions for this entry. MX users that are running the JavaLoader.cfc must read the article Using a Java URLClassLoader in CFMX Can Cause a Memory Leak.

In summary it is recommended that you store a single instance of the javaLoader in the server scope, rather than creating a new object each time (as in the code below). This prevents a memory leak caused by a bug in ColdFusion MX. I do not know if this bug exists in ColdFusion 8.

The code for this example creates a new instance of the JavaLoader on each request. This was intended only to demonstrate how to use the JavaLoader. To avoid any confusion, my future code examples will use a server scoped object instead.


Stay tuned for Part 2


<h1>Accreditation Card Example - Step 1 (CreateForm)</h1>
<cfscript>
savedErrorMessage = "";

// cfSearching: All file paths are relative to the current directory
// cfSearching: Your file paths may be different
fullPathToOutputFile = ExpandPath("./accreditation_form.pdf");
fullPathToKubrikImage = ExpandPath("./kubrick.jpg");
fullPathToFFLogoImage = ExpandPath("./fflogo.jpg");
fullPathToYourImage = ExpandPath("./yourPersonImage.jpg");
fullPathToITextJar = ExpandPath("./iText-2.0.7.jar");
dotNotationPathToJavaLoader = "javaloader.JavaLoader";

// cfSearching: page width and height in millimeters
WIDTH = mmToPoints(53.98);
HEIGHT = mmToPoints(85.60);

pathsForJavaLoader = arrayNew(1);
arrayAppend(pathsForJavaLoader, fullPathToITextJar);
javaLoader = createObject("component", dotNotationPathToJavaLoader).init(pathsForJavaLoader);

// cfSearching: create colors used in the form template
color = createObject("java", "java.awt.Color");
RED = color.init( javacast("int", 255), javacast("int", 0), javacast("int", 80) );
GREEN = color.init(javacast("int", 192), javacast("int", 222), javacast("int", 30) );
BLUE = color.init(javacast("int", 0), javacast("int", 173), javacast("int", 205) );
ORANGE = color.init(javacast("int", 241), javacast("int", 165), javacast("int", 1) );
GRAY = color.init(javacast("int", 192), javacast("int", 192), javacast("int", 192) );
BLACK = color.init(javacast("int", 0), javacast("int", 0), javacast("int", 0) );
WHITE = color.init(javacast("int", 255), javacast("int", 255), javacast("int", 255) );
BaseFont = javaLoader.create("com.lowagie.text.pdf.BaseFont");
FONT = javaLoader.create("com.lowagie.text.FontFactory").getFont(BaseFont.HELVETICA,
BaseFont.WINANSI, BaseFont.NOT_EMBEDDED).getBaseFont();




try {
// cfSearching: create a document with the preset page dimensions
pageDimensions = javaLoader.create("com.lowagie.text.Rectangle").init( WIDTH, HEIGHT );
document = javaLoader.create("com.lowagie.text.Document").init(pageDimensions);

// we create a writer that listens to the document and directs a PDF-stream to a file
outStream = createObject("java", "java.io.FileOutputStream").init(fullPathToOutputFile);
writer = javaLoader.create("com.lowagie.text.pdf.PdfWriter").getInstance(document, outStream);

// step 3
document.open();
// step 4
directContent = writer.getDirectContent();

// top part of the card
// cfSearching: create a red band across the top portion of the document
directContent.setColorFill(RED);
directContent.rectangle(javacast("float", 0), mmToPoints(57), WIDTH, HEIGHT - mmToPoints(57));
directContent.fill();

// cfSearching: overlay a smaller black band in the middle
directContent.setColorFill(BLACK);
directContent.rectangle(javacast("float", 0), mmToPoints(61), WIDTH, mmToPoints(21));
directContent.fill();

// cfSearching: for the bottom of the card use a white background
directContent.setColorFill(WHITE);

// cfSearching: add the kubrick photo and logo to the top section
Image = javaLoader.create("com.lowagie.text.Image");
kubrick = Image.getInstance(fullPathToKubrikImage);
kubrick.scaleToFit(WIDTH, mmToPoints(21));
kubrick.setAbsolutePosition( javacast("float", 0), mmToPoints(61));
directContent.addImage(kubrick);

logo = Image.getInstance(fullPathToFFLogoImage);
logo.scaleToFit(mmToPoints(14), mmToPoints(14));
logo.setAbsolutePosition(mmToPoints(37), mmToPoints(65));
directContent.addImage(logo);

// cfSearching: add the festival name just beneath the photo and logo
Element = javaLoader.create("com.lowagie.text.Element");
directContent.beginText();
directContent.setFontAndSize(FONT, javacast("float", 7));
directContent.showTextAligned(Element.ALIGN_CENTER,
"Flanders International Film Festival-Ghent", WIDTH / 2,
mmToPoints(58.5), javacast("float", 0) );
directContent.endText();

// bottom part of the card
Rectangle = javaLoader.create("com.lowagie.text.Rectangle");
TextField = javaLoader.create("com.lowagie.text.pdf.TextField");

// cfSearching: add the gray footer
filmfestival = TextField.init(writer, rectangle.init( javacast("float", 0),
javacast("float", 0), WIDTH, mmToPoints(9)),
"filmfestival");
filmfestival.setBackgroundColor(GRAY);
filmfestival.setTextColor(WHITE);
filmfestival.setText("www.filmfestival.be");
filmfestival.setFontSize( javacast("float", 14) );
filmfestival.setOptions(TextField.READ_ONLY);
filmfestival.setAlignment(Element.ALIGN_CENTER);
writer.addAnnotation(filmfestival.getTextField());

// cfSearching: add the festival date just above the footer
directContent.beginText();
directContent.moveText(mmToPoints(1.5), mmToPoints(12));
directContent.setFontAndSize(FONT, javacast("float", 21) );
directContent.setColorFill(RED);
directContent.showText("10");
directContent.setColorFill(BLUE);
directContent.showText("/");
directContent.setColorFill(RED);
directContent.showText("21");
directContent.setColorFill(BLUE);
directContent.showText("OCT");
directContent.setColorFill(GREEN);
directContent.showText("2006");
directContent.endText();
directContent.setColorStroke(BLUE);
directContent.moveTo(mmToPoints(24.5), mmToPoints(29));
directContent.lineTo(mmToPoints(24.5), mmToPoints(36));
directContent.moveTo(mmToPoints(24.5), mmToPoints(48));
directContent.lineTo(mmToPoints(24.5), mmToPoints(54));
directContent.stroke();


// central part of the card
PushbuttonField = javaLoader.create("com.lowagie.text.pdf.PushbuttonField");
photo = PushbuttonField.init(writer,
rectangle.init(mmToPoints(3), mmToPoints(29), mmToPoints(24), mmToPoints(54)),
"photo");
t1 = directContent.createTemplate(mmToPoints(21), mmToPoints(25));
t1.setColorFill(GRAY);
t1.rectangle( javacast("float", 0), javacast("float", 0), mmToPoints(21), mmToPoints(25));
t1.fill();
photo.setTemplate(t1);
photo.setLayout(PushbuttonField.LAYOUT_ICON_ONLY);
writer.addAnnotation(photo.getField());
type = TextField.init(writer, rectangle.init( mmToPoints(26), mmToPoints(46),
WIDTH - mmToPoints(1.5), mmToPoints(54)), "type");
type.setTextColor(GRAY);
type.setText("TYPE");
type.setFontSize(0);
writer.addAnnotation(type.getTextField());

number = TextField.init(writer, rectangle.init( mmToPoints(26),
mmToPoints(44), WIDTH - mmToPoints(1.5), mmToPoints(48)), "number");
number.setText("N° 0000000");
number.setFontSize(8);
writer.addAnnotation(number.getTextField());

name = TextField.init(writer, rectangle.init( mmToPoints(26),
mmToPoints(28), WIDTH - mmToPoints(1.5), mmToPoints(40)), "name");
name.setText("Name");
name.setFontSize(8);
name.setOptions(TextField.MULTILINE);
writer.addAnnotation(name.getTextField());

barcode = PushbuttonField.init(writer, rectangle.init(mmToPoints(3),
mmToPoints(23), WIDTH - mmToPoints(3), mmToPoints(28)), "barcode");

t2 = directContent.createTemplate(WIDTH - mmToPoints(6), mmToPoints(5));
t2.setColorFill(GRAY);
t2.rectangle(0, 0, WIDTH - mmToPoints(6), mmToPoints(5));
t2.fill();
barcode.setTemplate(t2);
barcode.setLayout(PushbuttonField.LAYOUT_ICON_ONLY);
writer.addAnnotation(barcode.getField());
}
catch (com.lowagie.text.DocumentException de) {
savedErrorMessage = de;
}
catch (java.io.IOException ioe) {
savedErrorMessage = ioe;
}

document.close();
outStream.close();
</cfscript>

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

<cffunction name="mmToPoints" returntype="numeric" access="public" output="false"
hint="Converts millimeters to PostScript points">

<cfargument name="millimeters" type="numeric" required="true">
<cfreturn javacast("float", (arguments.millimeters * 2.8346456)) />
</cffunction>

2 comments:

Anonymous,  August 24, 2009 at 6:07 AM  

Hello,

I am developing a PDF whereby I am populating some pre-existing form fields in the PDF from the browser.

I have a COMMENTS box where I wish to be able to leave comments by the user, however unlike the other fields, I need it to be MULTILINE and READ_ONLY but when I attempt to add name.setOptions(TextField.MULTILINE | TextField.READ_ONLY); in the CFSCRIPT it throws an error :

"coldfusion.compiler.TokenMgrError: Invalid token &nbsp found on line 859 at column 46.

cfSearching August 24, 2009 at 7:25 AM  

@Anonymous,

Use the CF BitOr() function instead of the java bitwise operator "|".

-Leigh

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep