Add an image dynamically to a PDF with CF and iText
For the last couple of weeks I’ve been in a runt because I have really been doing anything challenging in CF for quite sometime. So my partner called me up and wanted to revamp the way our software generates certificates for our clients. Right now we have separate certificates for each client and the only thing that’s really different about them is the logo that we place on them and some data being inserted into some form fields. What we wanted to do was find someway to add the logos dynamically to a master PDF and use form fields to enter in the copy that needed to be inserted as well.
Now CF8 has some really nice tags for doing the form stuff, but adding the logo dynamically was not supported. I’ve used iText before back in the CF5 and CF6 days to combine and generate PDFs, but I never added an image dynamically to an existing PDF before.
This is just the challenge that I’ve been waiting for!
Suffice to say that after scouring the internet and reading a slew of Java and ColdFusion blogs, I was finally able to piece together some ideas to get this things working. Below is the commented code that I used to achieve success.
// full path to PDF you want to add image to
readPDF = expandpath(“your.pdf”);
// full path to the PDF we will output. Using creatUUID() to create
// a unique file name so we can delete it afterwards
writePDF = expandpath(“#createUUID()#.pdf”);
// full path to the image you want to add
yourimage = expandpath(“dynamic_image.jpg”);
// JAVA STUFF!!!
// output buffer to write PDF
fileIO = createObject(“java”,”java.io.FileOutputStream”).init(writePDF);
// reader to read our PDF
reader = createObject(“java”,”com.lowagie.text.pdf.PdfReader”).init(readPDF);
// stamper so we can modify our existing PDF
stamper = createObject(“java”,”com.lowagie.text.pdf.PdfStamper”).init(reader, fileIO);
// get the content of our existing PDF
content = stamper.getOverContent(reader.getNumberOfPages());
// create an image object so we can add our dynamic image to our PDF
image = createobject(“java”, “com.lowagie.text.Image”);
// get the form fields
pdfForm = stamper.getAcroFields();
// setting a value to our form field
pdfForm.setField(“our_field”, “whatever you want to put here”);
// initalize our image
img = image.getInstance(yourimage);
// centering our image top center of our existing PDF with a little margin from the top
x = (reader.getPageSize(1).width() – img.scaledWidth()) – 50;
y = (reader.getPageSize(1).height() – img.scaledHeight()) / 2 ;
// now we assign the position to our image
img.setAbsolutePosition(javacast(“float”, y),javacast(“float”, x));
// add our image to the existing PDF
// flattern our form so our values show
// close the stamper and output our new PDF
// close the reader
<!— write out new PDF to the browser —>
<cfcontent type=”application/pdf” file = “#writePDF#” deleteFile = “yes”>
There you have it. The code above will insert a dynamic image into an existing PDF and fill in the form information as well.
One thing to note: In the finalized component I created to handle this, I was looping over a structure to populate the PDF form fields. Having spent about a half an hour wondering why my fields weren’t populating, it finally dawned on me: JAVA IS CASE SENSITIVE! How could I have forgotten. Anyways, in CF all the keys of a structure get converted to uppercase and the form fields in my PDF were lower case. After using wrapping the key references in lcase(), everything was working.