ColdFusion makes it relatively trivial to create a PDF and password protect it. But how do you edit a PDF to remove that password? Let me begin this blog entry by saying that I am convinced that the solution I am about to provide is the wrong way to solve the problem. I truly can't believe that the awesome CFPDF tag, which does so much, doesn't provide for this simple functionality. I'll be happy to be proved right when someone posts the one line solution. Until then, here is how I solved it using DDX and the power of magical unicorns.

Let's begin with a simple example of creating a PDF with password protection. This is documented of course, but I just wanted to show a quick reminder of how easy ColdFusion makes it.

<cfdocument format="pdf" name="secret" userpassword="paris" encryption="128-bit"> <img src="http://www.magicunicorns.us/Index3/aunirainbow.jpg" align="left"> This is the PDF that I'll protect. My bank atm is 5318008. </cfdocument>

<cfheader name="Content-Disposition" value="inline; filename=foo.pdf"> <cfcontent type="application/pdf" variable="#toBinary(secret)#">

This code makes a simple one line PDF, adds the password 'paris', and encrypts the document. I then serve it up witha filename of foo. Here is a quick screen shot of how it comes out in Preview (btw Mac users, you definitely want to assign Preview for PDF files and not Acrobat):

Before the password...

After the password...

Alright, so that's how we can create the password protected PDF, but how do we remove the password? Well I began by trying to use various combinations of cfpdf attributes. I tried to set newUserPassword to a blank string, but that returned an error. I tried setting encrypt to None, which is documented, but was told I didn't have permission. (This was after opening the PDF using the password option which worked just fine.) I spent a good hour just playing around and no combination worked for me.

I was about to give up when I looked to DDX. I've blogged on DDX a few times now (my first entry may be found here) and it is truly a very powerful tool. It can be a bit hard to use at times though. You have to be sure you read the docs carefully and make use of isDDX() to verify your XML is valid. Using DDX to solve this problem involves two steps really.

The first thing we need to do is find out how to simply even work with a password protected PDF. Who cares about removing a password - we just want to be sure we can even use a password protected document to do other tasks. I did some searching in the DDX Reference and discovered that you can provide a PasswordAccessProfile element. This element lets you do a few things, one of which is a password for opening the document. Consider:

<!--- Make and save the pdf ---> <cfdocument format="pdf" name="secret" userpassword="paris" encryption="128-bit"> <img src="http://www.magicunicorns.us/Index3/aunirainbow.jpg" align="left"> This is the PDF that I'll protect. My bank atm is 5318008. </cfdocument>

<cfset fileWrite(expandPath('private.pdf'),secret)>

<cfset pdfpath = expandPath('./private.pdf')> <cfset outfile = expandPath('./notprivate.pdf')>

<cfsavecontent variable="myddx"> <?xml version="1.0" encoding="UTF-8"?> <DDX xmlns="http://ns.adobe.com/DDX/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ns.adobe.com/DDX/1.0/ coldfusion_ddx.xsd"> <PDF result="Out1"> <PDF source="In1" access="mainP"/> </PDF> <PasswordAccessProfile name="mainP"> <Password>paris</Password> </PasswordAccessProfile> </DDX> </cfsavecontent> <cfset myddx = trim(myddx)>

<cfset inputStruct = {In1="#pdfpath#"}> <cfset outputStruct = {Out1="#outfile#"}>

<cfpdf action="processddx" ddxfile="#myddx#" inputfiles="#inputStruct#" outputfiles="#outputStruct#" name="ddxVar"> <cfdump var="#ddxVar#">

This script recreates the PDF from the previous version and saves it to the file system. DDX requires all actions to be performed at the file system and can't work with PDFs in ColdFusion variables. The DDX I created makes use of PasswordAccessProfile. It has a name for the profile, mainP, and a Password element with the password I supplied when creating the PDF. Notice than that my PDF source uses access="mainP" to say, "When accessing this PDF, use this profile." That's it really - I don't actually perform any actions on the PDF, I just open it and return it. It's the BAT file equivalent of reading a file and saving it to a new file name.

The next step is to remove the password. That turns out to be rather simple as well. All we need to do is modify the PDF result tag and supply an encryption value:

<?xml version="1.0" encoding="UTF-8"?> <DDX xmlns="http://ns.adobe.com/DDX/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ns.adobe.com/DDX/1.0/ coldfusion_ddx.xsd"> <PDF result="Out1" encryption="None"> <PDF source="In1" access="mainP"/> </PDF> <PasswordAccessProfile name="mainP"> <Password>paris</Password> </PasswordAccessProfile> </DDX>

I've supplied the None value which basically just strips out the encryption. Note - remember how I said DDX was a bit anal? If you use "none", as I did at first, it will not work. But it will actually pass the isDDX test, so you have to be careful.

So that's it. If I'm wrong and this is the only way to remove passwords from PDF, I'll make this a bit cleaner and add it to my pdfUtils utility CFC.

Comments? I've included the test code to this blog entry.

Download attached file.