Rip's Domain

Add an image dynamically to a PDF with CF and iText

Posted in ColdFusion by rip747 on March 26, 2009

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.

<cfscript>
// 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
content.addImage(img);
// flattern our form so our values show
stamper.setFormFlattening(true);
// close the stamper and output our new PDF
stamper.close();
// close the reader
reader.close();
</cfscript>
<!— 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.

8 Responses

Subscribe to comments with RSS.

  1. Aaron Presuhn said, on September 14, 2009 at 11:46 am

    Hello…I’ve attempted to get your code to work. I’ve been looking into the exact same problem, and haven’t found much until I stumbled onto this. However, I keep getting this error…was wondering if you have any idea what could be causing this? Any help is appreciated! I’m using Coldfusion 8.

    An exception occurred when instantiating a Java object. The class must not be an interface or an abstract class. Error: ”.

    The error occurred in C:\Inetpub\webs\STCNETDEV2\stcedispute\includes\display.cfm: line 194

    192 : fileIO = createObject(“java”,”java.io.FileOutputStream”).init(writePDF);
    193 : // reader to read our PDF
    194 : reader = createObject(“java”,”com.lowagie.text.pdf.PdfReader”).init(readPDF);
    195 : // stamper so we can modify our existing PDF
    196 : stamper = createObject(“java”,”com.lowagie.text.pdf.PdfStamper”).init(reader, fileIO);

  2. rip747 said, on September 15, 2009 at 12:07 pm

    make sure that you are using 8.0.1 and have all the updates installed. there were some weird thing that went on with 8.0.0.

  3. Aaron Presuhn said, on September 16, 2009 at 10:30 am

    Ok thanks. I got this working, but ran into one more snag. I’m filling out form fields from a query.

    The setField method was not found.
    Either there are no methods with the specified method name and argument types, or the setField method is overloaded with argument types that ColdFusion cannot decipher reliably. ColdFusion found 0 methods that matched the provided arguments. If this is a Java object and you verified that the method exists, you may need to use the javacast function to reduce ambiguity.

    The error occurred in C:\Inetpub\webs\STCNETDEV2\stcedispute\includes\display.cfm: line 73

    71 : pdfForm = stamper.getAcroFields();
    72 : pdfForm.setField(“name”, “#pdfquery.name#”);
    73 : pdfForm.setField(“amount”, “#pdfquery.amount#”);
    74 : pdfForm.setField(“hphone”, “#pdfquery.hphone#”);
    75 : pdfForm.setField(“wphone”, “#pdfquery.wphone#”);

    It’s triggering on the ‘amount’ field which in the MySQL database is a Decimal type. It’s just a text field in the PDF however. Do I need to use some type of casting function to get it to work with this type? Otherwise this works as intended.

  4. nic said, on April 22, 2010 at 12:02 pm

    Hey Rip

    Thank you so much!!! You made my day!
    Coldfusion with the JVM as fundament is so incredibly powerful…

    nic

  5. rip747 said, on April 22, 2010 at 2:39 pm

    your very welcome 🙂

  6. Eric Belair said, on June 28, 2011 at 12:35 pm

    Are the x/y coordinates in the setAbsolutePosition() call transposed? Doesn’t x come first?

  7. Eric Belair said, on June 28, 2011 at 1:18 pm

    You should also add that the page number can be set in

    content = stamper.getOverContent(reader.getNumberOfPages());

    in order to specify what page to place the image in.

  8. Ben said, on June 22, 2012 at 6:55 am

    You are 100% right Eric – the x and y are transposed.


Leave a comment