Posted in ColdFusion | Posted on 07-17-2007 | 12,498 views
Today's PDF entry is all about merging. ColdFusion 8 allows us to merge any number of PDFs, whether from files or directly in memory. What are some usage examples? Your site could have a standard disclaimer that you want added to the front of each PDF you create. You may have a standard credits page you want to add to the end. Whatever the need - ColdFusion makes it pretty simple, so let's take a look.
As I mentioned above, you can work with PDFs on the file system or with PDFs in memory. Let's first take a look at PDFs on the file system. The CFPDF tag takes a directory attribute. This directory consists of the PDF files you want to merge. By default ColdFusion will merge all files in the folder. There are three things to consider when working with a folder:
- CFPDF will sort your PDFs by timestamp first. You can supply the order attribute to change this to name.
- CFPDF will sort your PDFs in reverse order. So if you use name, PDFs will be sorted Z to A. You can change this by using the ascending attribute. The default value is no.
- CFPDF will try to merge every file in a folder. If a folder contains non-PDF files, ColdFusion will ignore it. If you do not want ColdFusion to ignore non-PDF files, use stopOnError=true.
So let's look at a simple example:
2<cfoutput>
3This is PDF 1 at #timeFormat(now())#
4</cfoutput>
5</cfdocument>
6
7<cfdocument name="pdf2" format="pdf">
8<cfoutput>
9This is PDF 2 at #timeFormat(now())#
10</cfoutput>
11</cfdocument>
12
13<cfset savedFolder = expandPath("./pdfs")>
14
15<cffile action="write" file="#savedFolder#/pdf1.pdf" output="#pdf1#">
16<cffile action="write" file="#savedFolder#/pdf2.pdf" output="#pdf2#">
17
18<cfpdf action="merge" directory="#savedFolder#" name="mergedpdf">
19
20<cfcontent type="application/pdf" reset="true" variable="#toBinary(mergedpdf)#">
The code begins by simply creating two PDFs. These PDFs are stored to the file system in a subfolder named pdfs. The important line is here:
I simply specify a directory and in my case, a name variable to store the result in memory. Lastly I serve up the PDF with the cfcontent tag. If you run this you will notice that the PDF seems backwards. PDF2 is on page 1, and PDF1 is on page 2. This makes sense if you remember the above notes. The default order is by time, descending, and PDF2 was written out first.
Now let's take it up a notch and introduce a new tag, cfpdfparam. The cfpdfparam tag is only used with merging PDFs. It lets you do all kinds of fun things. It gives you the power to provide more control over the order. It lets you specify a page range for each PDF. (So for example, merge pages 1-10 in pdf 1, pages 13-19 in pdf 2, and pages 90-100 in pdf 3.) You can also supply passwords for individual PDFs that need them. Pretty cool, eh? Here is a simple example:
2<cfoutput>
3This is PDF 1 at #timeFormat(now())#
4</cfoutput>
5</cfdocument>
6
7<cfdocument name="pdf2" format="pdf">
8<cfoutput>
9This is PDF 2 at #timeFormat(now())#
10</cfoutput>
11</cfdocument>
12
13<cfpdf action="merge" name="mergedpdf">
14 <cfpdfparam source="pdf1">
15 <cfpdfparam source="pdf2">
16</cfpdf>
17
18<cfcontent type="application/pdf" reset="true" variable="#toBinary(mergedpdf)#">
This example is much like the first one. I create two PDFs with cfdocument. This time though I don't bother saving them to the file system. I then do the merge operation, but note the use of cfpdfparam. Now my order will work correctly because I explicitly specified the proper order. I could have used filenames as well. (And let me thank Adobe again for supporting relative paths!)
One final note - another option for merging PDFs is "keepBookmark". This tells CFPDF to keep the bookmarks in the source PDF files. I'll be talking about bookmarks more in the next entry.
Please let me know if you are enjoying this series. The last entry didn't get any comments so I want to make sure folks are still getting it. :)


This is definatly useful. I have to admit, back in 2000 I was working with technology XSL:FO to create PDF. I took about a day of work to create a single PDF and lay it out pixel-perfect. It was major pain in the... Since then, I'm kinda scared of generating PDF and after reading your series, I'm starting to reconsider.
Thanks again.
I previously created the documents with cfpdfform. I verified that the documents are stored with the data in the form, however when i merge the docuemtns and look at the new form the docuemtns are merged, but the data is gone.
Is there a way to merge the documents and keep the data?
Your second issue (if page 2 for person) is not something I know how to do. You cna obviously force a page break, but there is no way to say - if this is page 2, add X.
Wait! I'm wrong. The docs give a good example of this - even and odd numbering. The following text is from the cf ref.
ColdFusion 8 lets you use the scope variables inside any expression within a cfdocumentitem tag. For example, you
can use the currentpagenumber variable to place the section name on even pages and the chapter name on odd
pages in the header, as follows:
<cfdocument format="flashpaper">
<cfdocumentitem type="header">
<cfif (cfdocument.currentpagenumber mod 2) is 0>
<cfoutput>#sectionTitle#</cfoutput>
<cfelse>
<cfoutput>#chapterTitle#</cfoutput>
</cfif>
</cfdocumentitem>
...
</cfdocument>
That should give you what you need.
<cfquery name="people">
#name#
</cfquery>
Just add cfdocumentsection tags in the loop.
<cfpdf action="merge" name="MergedPDF" directory="L:\aip\web\reports">
<cfpdfparam source="rpt1_pdf.cfr">
<cfpdfparam source="rpt1.cfr">
<cfpdfparam source="rpt1.cfr">
</cfpdf>
You would first need to run CFREPORT, generate the PDFs, and then use the cfpdf action to merge them.
<cfdocumentitem type="header">
<cfif (cfdocument.currentpagenumber mod 2) is 0>
<cfoutput>#sectionTitle#</cfoutput>
<cfelse>
<cfoutput>#chapterTitle#</cfoutput>
</cfif>
</cfdocumentitem>
...
</cfdocument>
The gist of the fix is that you have to add the new "evalAtPrint" attribute to the cfdocumentitem tag and set it to true:
<cfdocumentitem type="header" evalAtPrint="true">
Can I protect the block from the pagebreak, causing the break to occur earlier?
Thanks in advance
Here's the bit of code that returns the pdf to the browser:
<cfheader name="content-disposition" value="inline; filename=#filename#" />
<cfcontent file="#rootfolder##documentfolder##filename#" type="#sttCFG.structFileCFContentType[fileExt]#" reset="yes">
I tried adding &page=3 to the file parameter of the cfcontent tag, but that didn't work, I tried the same thing for the cfheader thinking that that might be passed to acrobat when the local client was opening the file, but that didn't work either. Any ideas? Thanks again.
http://livedocs.adobe.com/coldfusion/8/cfpdf_18.ht...
Now - to be clear, a DDX operation will be slow (relatively slow), so I'd recommend - perhaps - keeping a copy of the PDF that opens to page N so you can serve it up next time.
http://www.raymondcamden.com/index.cfm/2011/12/23/...
[Add Comment] [Subscribe to Comments]