ColdFusion 8: Working with PDFs (Part 3)

So today's CFPDF entry will detail how you can add a watermark to a PDF. Why would you do that? You may want to mark a PDF for security reasons. As an example, this weeks episode of Entourage featured M. Night Shyamalan (will his movies ever be good again?) giving Ari (a hollywood agent) a copy of his latest script. On every single page of the script, Ari's name was printed across the text. That way if the script was leaked to the Internet, M. Night would know exactly who to blame.

ColdFusion 8 gives up multiple ways to work with watermarks.

  • First off - you can use an existing PDF or an image for your watermark. I had some issues with images which I'll talk about at the very end.
  • You can set your watermark to be in the background or foreground.
  • You can make your watermark only show up on screen and not in print.
  • You can set a rotation.
  • You can set opacity.
  • You can position where the watermark is applied.
  • You can put the watermark only on certain pages.

So as you can see, you have a lot of options. Let's start with a simple example. First I'll create a PDF for my movie script:

<cfdocument format="pdf" name="mydocument"> <cfloop index="x" from="1" to="20"> <p> doloras lorem upsom doloras paris hilton is my hero loreum ipsom dsoio foom an to dht end of the world will anyone actually read this probably not but let me put more realtext in so it flows a bit nicely <cfloop index="y" from="1" to="#randRange(1,9)#">This sentence will appear a random amount of time.</cfloop> </p> </cfloop> </cfdocument>

Now I'll create a watermark. I'm going to use a PDF which means I'll need to save it to the file system as well:

<cfdocument format="pdf" name="watermark"> <h1>Raymond Camden</h1><br /> <h1><cfoutput>#dateformat(now(),"short")# #timeformat(now(), "short")#</cfoutput></h1> </cfdocument>

<cfset wfile = getTempFile(getTempDirectory(), "wfile")> <cffile action="write" file="#wfile#" output="#watermark#">

All I've done here is used my name and the current date and time. I then save it to a temporary file using ColdFusion's built in getTempFile/getTempDirectory functions.

Now let me add it as a watermark:

<cfpdf action="addWatermark" copyFrom="#wfile#" rotation="30" foreground="true" source="mydocument" name="mydocument" showonprint="true" overwrite="true" position="200,0">

The copyFrom attribute tells CFPDF to copy the watermark from a file. I've set a rotation of 30 degrees and put the watermark in the foreground. My source is a PDF in memory (the one built above). I also set the watermark to show up when printing as well. Lastly I've positioned where the PDF shows up. I've attached the PDF to this blog entry as an attachment. Pretty simple, eh? Here is the complete template.

<cfdocument format="pdf" name="mydocument"> <cfloop index="x" from="1" to="20"> <p> doloras lorem upsom doloras paris hilton is my hero loreum ipsom dsoio foom an to dht end of the world will anyone actually read this probably not but let me put more realtext in so it flows a bit nicely <cfloop index="y" from="1" to="#randRange(1,9)#">This sentence will appear a random amount of time.</cfloop> </p> </cfloop> </cfdocument>

<cfset wfile = getTempFile(getTempDirectory(), "wfile")> <cfdocument format="pdf" name="watermark"> <h1>Raymond Camden</h1><br /> <h1><cfoutput>#dateformat(now(),"short")# #timeformat(now(), "short")#</cfoutput></h1> </cfdocument>

<cffile action="write" file="#wfile#" output="#watermark#">

<cfpdf action="addWatermark" copyFrom="#wfile#" rotation="30" foreground="true" source="mydocument" name="mydocument" showonprint="true" overwrite="true" position="200,0">

<cfcontent type="application/pdf" reset="true" variable="#toBinary(mydocument)#">

So I mentioned that I had a few issues with images. Here is what I found, and I want to thank Adobe for their help in hashing this out. First - if you want to use an image you create on the fly, it must be grayscale. If you have an image on the file system, it can be any color model. Another problem I ran into was rotating the image using imageRotate. When I did it created a background that messed with the PDF. But I was able to switch to using CFPDF's rotate instead. I'm pasting the image example below for folks who want to see how that looks.

<cfdocument format="pdf" name="mydocument">

<cfloop index="x" from="1" to="20"> <p> doloras lorem upsom doloras paris hilton is my hero loreum ipsom dsoio foom an to dht end of the world will anyone actually read this probably not but let me put more realtext in so it flows a bit nicely <cfloop index="y" from="1" to="#randRange(1,9)#">This sentence will appear a random amount of time.</cfloop> </p> </cfloop>

</cfdocument>

<cfset textImage=ImageNew("",500,500,"grayscale","white")>

<cfset ImageSetDrawingColor(textImage,"black")>

<cfset attr=StructNew()> <cfset attr.size=50> <cfset attr.style="bold"> <cfset attr.font="ArialMT">

<cfset ImageSetAntialiasing(textImage, "on")>

<cfset ImageDrawText(textImage,"Raymond Camden",50,50,attr)>

<cfpdf action="addWatermark" image="#textImage#" rotation="30" foreground="true" source="mydocument" name="mydocument" showonprint="true" overwrite="true">

<cfcontent type="application/pdf" reset="true" variable="#toBinary(mydocument)#">

Download attached file.

Archived Comments

Comment 1 by Hugo Sombreireiro posted on 7/13/2007 at 8:41 PM

I didn't do much research on this but I keep getting this error while trying out the code:

The parameter 1 of function ToBinary, which is now coldfusion.pdf.PDFDocWrapper@11bb202 must be a Base-64 encoded string.

Comment 2 by Raymond Camden posted on 7/13/2007 at 8:48 PM

Can you paste in the entire code? Or did you run it exactly as I did?

Comment 3 by Hugo Sombreireiro posted on 7/13/2007 at 8:58 PM

Exactly as you did. Cut and paste from your code

Comment 4 by Hugo Sombreireiro posted on 7/13/2007 at 9:00 PM

Obviously I meant copy/paste :P

Comment 5 by Scott P posted on 7/14/2007 at 3:37 AM

you forgot spoiler alert in the title for the entourage leak...

Comment 6 by Raymond Camden posted on 7/14/2007 at 5:12 AM

Ack - sorry!

Comment 7 by ck posted on 7/16/2007 at 5:11 PM

@Hugo Support of converting pdfVar into byte[] through toBinary(#pdfVar#) was added after rc1. In case you are using a build of RC1 or before that wont work!

Comment 8 by Raymond Camden posted on 7/16/2007 at 5:20 PM

If folks keep running into this - just save your pdf to the file system first and modify the cfcontent. Just remember that tobinary will work for you in the final release.

Comment 9 by Charlie Arehart posted on 7/24/2007 at 5:21 AM

Seems a shame that the watermark options are only a PDF or image. Would be nice to have a simple text attribute, given the availability of the rotation attribute on the CFPDF. Of course, you've shown how one can create an image pretty easily in CFML in 8. Still, if all we want to do is add some text, it should be easier. Just filed a feature request, though I know it's too late for this release.

Comment 10 by Raymond Camden posted on 7/24/2007 at 8:01 AM

Charlie - I'm doing research on DDX for my next blog article. Turns out - you can do text base watermarks if you do it via DDX. While now _simple_, I may be able to write up a nice UDF.

Comment 11 by AndyC posted on 12/21/2007 at 1:01 AM

Did you ever cover removing a watermark?
I am getting this error

Check the page numbers 1 in the source.

<cfpdf action="removeWatermark" source="my.pdf" destination="myNew.pdf" pages="1" >

Cheers

Comment 12 by Raymond Camden posted on 12/21/2007 at 1:41 AM

Nope, I didn't play with it. All I can recommend is filing a bug report on it.

Comment 13 by AndyC posted on 12/21/2007 at 3:11 AM

I think it may be down to attempting to remove a watermark from a page written by a commercial program in trial mode

When I create my own watermark in CF and then attempt to remove it, everything works fine

Comment 14 by Kevin Hall posted on 3/4/2008 at 9:13 PM

Ray, I swear that every time I think "How could this be done in ColdFusion?" I visit your site and you have an article written about it. You appear to be some sort of future version of my coding self, doing all of my work a few days or weeks ahead of me. It's really quite eerie.

Comment 15 by Raymond Camden posted on 3/4/2008 at 9:23 PM

It's called "Not knowing when to shut up and not blog." ;)

Comment 16 by Charlie Arehart posted on 3/4/2008 at 9:38 PM

Kevin, you're not the first to experience that feeling. I've had others ask me (seriously) if Ray's a real person. :-) He's just that prolific!

But maybe instead he really IS going ahead in time--or it's you who's stuck in the middle. Before the confusion makes you lose your mind, I can give you this advice: when you see Ray next, tell him to "set the device to 2.342 and oscillating at 11 hertz". That's for Ray and all the other Lost fans. :-)

Comment 17 by Raymond Camden posted on 3/4/2008 at 9:43 PM

Heh, or even better...

"1.21 gigawatts? 1.21 gigawatts? Great Scott!"

Comment 18 by Charlie Arehart posted on 3/4/2008 at 10:02 PM

"What the hell is a gigawatt?"

(For those not following, he was quoting Dr Emmet Brown in "Back to the Future". Mine is the reply that George McFly said to him next.)

Comment 19 by Marty McFly posted on 4/3/2008 at 7:32 PM

I believe you are quoting Marty McFly, not George... no?

Comment 20 by Raymond Camden posted on 4/3/2008 at 7:39 PM

Good catch there.

Comment 21 by Dave posted on 9/18/2008 at 2:41 AM

Is it possible to overlay text and a single image to an exisitng pdf document created by CF?

Comment 22 by Raymond Camden posted on 9/18/2008 at 4:51 AM

Um, that's what I did above. I used an image, and drew text on it. My image was just text, but you could use another picture.

Comment 23 by Damo posted on 8/24/2009 at 6:15 PM

Hi
Im trying to get a picture attached as a watermark, instead of text and its not working out as I expected, any tips

Comment 24 by Raymond Camden posted on 8/25/2009 at 2:34 AM

I guess I'd say - how exactly is it not working for you?

Comment 25 by Damo posted on 8/25/2009 at 11:37 AM

Well im using your code only amending it so it will put a image in the background of a PDF (which is a text document)
all im getting is one error after another, but when i copy your code exactly like it is, it works fine

Comment 26 by Raymond Camden posted on 8/25/2009 at 4:48 PM

Well what error though? Also, what if you try making one change at a time - ie, take baby steps.

Comment 27 by Damo posted on 8/26/2009 at 11:59 AM

Well ive now managed to get half of it working with just the 1 error message,
(Attribute validation error for the CFPDF tag.
When the value of the ACTION attribute is ADDWATERMARK, the tag requires the attribute(s): SOURCE.)

heres the code as I have it now
<cfpdf action="Addwatermark"
destination="#request.maindrivePath#temppdf\#pdfname#"
overwrite="yes">
<cfpdfparam source="#request.maindrivePath#testfiles\#qgetinvoice.INVOICE_PDFFile#">
<cfpdfparam source="#request.maindrivePath#templates\#qgettemplate.COMPANY_Template#">
</cfpdf>

<cflocation url="temppdf/#pdfname#">

Comment 28 by Raymond Camden posted on 8/26/2009 at 5:37 PM

In your code below, what are you trying to use as the watermark? I don't believe you can use cfpdfparam that way.

Comment 29 by Damo posted on 8/26/2009 at 5:47 PM

Im trying to use an Image as a watermark saved as a JPG. This is where my Image is located
#request.maindrivePath#templates\#qgettemplate.COMPANY_Template#

Comment 30 by Raymond Camden posted on 8/26/2009 at 5:50 PM

So the source attribute is the PDF you want to use - that needs to be in the tag. And Image="" should point to your JPG. Review the docs for cfpdf (that's where I got this info :)

Comment 31 by Damo posted on 8/26/2009 at 6:13 PM

Thanks for that
ive tried it and now i get this wonderful error message:

The pdf image format is not supported on this operating system.
Use GetReadableImageFormats() and GetWriteableImageFormats() to see which image formats are supported.

Comment 32 by Raymond Camden posted on 8/26/2009 at 6:21 PM

It sounds like you used the PDF for the image though and not the JPG. Care to share the code again?

Comment 33 by Damo posted on 8/26/2009 at 6:24 PM

The code as it is now

<cfpdf
action = "addwatermark"
source = "#request.maindrivePath#testfiles\#qgetinvoice.INVOICE_PDFFile#"
image = "#request.maindrivePath#templates\#qgettemplate.COMPANY_Template#"
foreground = "No"
overwrite = "yes"
pages = "1"
rotation = "45"
showonprint = "yes">
destination = "#request.maindrivePath#temppdf\#pdfname#"
<cflocation url="temppdf/#pdfname#">

Comment 34 by Raymond Camden posted on 8/26/2009 at 6:25 PM

So are you sure this:

image = "#request.maindrivePath#templates\#qgettemplate.COMPANY_Template#"

points to a valid image? How about hard coding that to a jpg temporarily.

Comment 35 by Damo posted on 8/26/2009 at 6:50 PM

Now The page cannot be found
The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.

I dont know what im doing wrong with it now

Comment 36 by Raymond Camden posted on 8/26/2009 at 6:51 PM

Remove your cflocation.

Biggest tip I can tell folks when debugging is to run the LEAST amount of code possible.

Comment 37 by Damo posted on 8/26/2009 at 6:58 PM

This is now posting on the screen

destination = "#request.maindrivePath#temppdf\#pdfname#"

but when i go to this location its not there ;-(

Comment 38 by Raymond Camden posted on 8/26/2009 at 6:59 PM

You left the destination out of the tag. See the > ?

Comment 39 by Damo posted on 8/26/2009 at 7:03 PM

What do you mean by that?

Comment 40 by Raymond Camden posted on 8/26/2009 at 7:07 PM

Look at your code:

avatar
The code as it is now

<cfpdf
action = "addwatermark"
source = "#request.maindrivePath#testfiles\#qgetinvoice.INVOICE_PDFFile#"
image = "#request.maindrivePath#templates\#qgettemplate.COMPANY_Template#"
foreground = "No"
overwrite = "yes"
pages = "1"
rotation = "45"
showonprint = "yes">
destination = "#request.maindrivePath#temppdf\#pdfname#

See the > after showonprint?

Comment 41 by Damo posted on 8/26/2009 at 7:35 PM

Perfect job Raymond, that was the problem, so when i put it in the correct place and put back in my <cflocation url="temppdf/#pdfname#"> all is working fine now,
all I have to do now is make the image smaller to fit on the screen

Thanks for all your help

Comment 42 by Pushpendra posted on 11/13/2009 at 5:59 PM

Is it possible to read the content of pdf file and print it or store it to database table.
<cfpdf action = "read" name = "test" source = "D:\Dummy.pdf" >
<cfoutput>#test#</cfoutput>
The above lines of code on execution returns coldfusion.pdf.PDFDocWrapper@3c4c33
I assume this is some sort of wrapper, but then how can i get the text written in pdf?

Comment 43 by Raymond Camden posted on 11/13/2009 at 6:48 PM

To get the text from a PDF...

In CF8, you have to use DDX, or use my pdfutils CFC (http://pdfutils.riaforge.org)

In CF9, Adobe added it as a feature to the cfpdf tag.

Comment 44 by Rawdyn posted on 9/5/2011 at 10:19 AM

Thanks again for another great article Ray.
I used the watermark tecnique to create several templates containing logos, background and some text common to a range of certificates our system produces.
The system just adds that template to the individual cert data (pdf of the students info and cert uniqueID etc).
Doing it this way changed the processing time from 17 seconds (it was getting hung up on a tiled background image) to less that 400 ms.
The certificates look a million times better too as I created the source PDF directly from illustrator so they now print in HD.
Awesome.

Comment 45 by LAKSHMI posted on 10/11/2011 at 5:50 PM

While searching
Existing system: contains PDFs which consists of statistical data in Tabular format when i search for a particular Criteria it displays the contents of the statistical data(i.e; numbers).

what i want / Proposed system: same pdfs (consists of description in their properties) when i search for particular criteria it must show the summary/contents in the form of characters (reason is it will be helpful to the users to choose among the selected topics)given in the metadata or properties .

Comment 46 by Raymond Camden posted on 10/11/2011 at 5:51 PM

You do not get to control the summary/context. It's driven by SOLR. If you want to display something in particular, you would need to supply that yourself when indexing the PDFs. We provide 4 custom columns you can use for this purpose.

Comment 47 by LAKSHMI posted on 10/11/2011 at 6:23 PM

I will explain the Something particular that i want
when we do searching we will see the list of search results along with some little description(the description must contain the "CRITERIA" such that i can understand whether the search item is useful to me or not)
regarding indexing i had done it using the admin page can u please tell me how to get to my requirement using the 4 custom column and where can i get it

Comment 48 by Raymond Camden posted on 10/11/2011 at 6:57 PM

Well the context _should_ contain the criteria.

As for how to use custom fields, please read the documentation on cfindex.

Comment 49 by LAKSHMI posted on 10/11/2011 at 7:31 PM

on your previous comment u mentioned about the 4 custom column what is it? and how can it will be useful to me?

Comment 50 by Raymond Camden posted on 10/11/2011 at 7:35 PM

When you index data, you have the ability to supply 4 separate custom fields. The data there can be whatever you want.

Please. Read. The. Docs. Seriously - look at cfindex. Also, I have a presentation you can watch on using Solr in CF in general.

Comment 51 by LAKSHMI posted on 10/11/2011 at 9:34 PM

please update the links of the presentation and where can i find those docs

Comment 52 by LAKSHMI posted on 10/11/2011 at 10:17 PM

what about this idea
1.i will search in the collection and get the set of pdfs containing CRITERIA
2.now script for accessing PDF by PDF and reading the lines that contains CRITERIA
But it looks complicated can u suggest whether its worthdoing.

Comment 53 by Raymond Camden posted on 10/11/2011 at 10:38 PM

http://slidesix.com/view/Co...

As to the docs, they are in the Adobe CF docs.

As to your second comment, I do not understand what you are saying unfortunately.

Comment 54 by LAKSHMI posted on 10/11/2011 at 11:32 PM

how can we get the Additional properties of the Pdf file. i know upto properties.please help me in this.

Comment 55 by Raymond Camden posted on 10/11/2011 at 11:33 PM

Look at CFPDF. It gets info about PDFs.

Comment 56 by LAKSHMI posted on 10/12/2011 at 6:09 PM

I am unable to get those Additional metadata of a document i am accessing the MetaData only.
so please give me the information or where can i get the information.

Comment 57 by Raymond Camden posted on 10/12/2011 at 8:57 PM

I'm sorry, but I do not know what you mean by "Additional" metadata. I can tell you that there is an open source project called pdfutils (http://pdfutils.riaforge.org) that has a utility to get additional info (I forget the type) from PDFs, so you can try that as well.

Comment 58 by LAKSHMI posted on 10/12/2011 at 11:57 PM

Thanks alot for your support.