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)#">

Comments

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.
# Posted By Hugo Sombreireiro | 7/13/07 11:41 AM
Can you paste in the entire code? Or did you run it exactly as I did?
# Posted By Raymond Camden | 7/13/07 11:48 AM
Exactly as you did. Cut and paste from your code
# Posted By Hugo Sombreireiro | 7/13/07 11:58 AM
Obviously I meant copy/paste :P
# Posted By Hugo Sombreireiro | 7/13/07 12:00 PM
you forgot spoiler alert in the title for the entourage leak...
# Posted By Scott P | 7/13/07 6:37 PM
Ack - sorry!
# Posted By Raymond Camden | 7/13/07 8:12 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!
# Posted By ck | 7/16/07 8:11 AM
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.
# Posted By Raymond Camden | 7/16/07 8:20 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.
# Posted By Charlie Arehart | 7/23/07 8:21 PM
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.
# Posted By Raymond Camden | 7/23/07 11:01 PM
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
# Posted By AndyC | 12/20/07 2:01 PM
Nope, I didn't play with it. All I can recommend is filing a bug report on it.
# Posted By Raymond Camden | 12/20/07 2:41 PM
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
# Posted By AndyC | 12/20/07 4:11 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.
# Posted By Kevin Hall | 3/4/08 10:13 AM
It's called "Not knowing when to shut up and not blog." ;)
# Posted By Raymond Camden | 3/4/08 10:23 AM
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. :-)
# Posted By Charlie Arehart | 3/4/08 10:38 AM
Heh, or even better...

"1.21 gigawatts? 1.21 gigawatts? Great Scott!"
# Posted By Raymond Camden | 3/4/08 10:43 AM
"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.)
# Posted By Charlie Arehart | 3/4/08 11:02 AM
I believe you are quoting Marty McFly, not George... no?
# Posted By Marty McFly | 4/3/08 10:32 AM
Good catch there.
# Posted By Raymond Camden | 4/3/08 10:39 AM
Is it possible to overlay text and a single image to an exisitng pdf document created by CF?
# Posted By Dave | 9/17/08 5:41 PM
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.
# Posted By Raymond Camden | 9/17/08 7:51 PM