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:
<cfdocument name="pdf1" format="pdf">
<cfoutput>
This is PDF 1 at #timeFormat(now())#
</cfoutput>
</cfdocument>
<cfdocument name="pdf2" format="pdf">
<cfoutput>
This is PDF 2 at #timeFormat(now())#
</cfoutput>
</cfdocument>
<cfset savedFolder = expandPath("./pdfs")>
<cffile action="write" file="#savedFolder#/pdf1.pdf" output="#pdf1#">
<cffile action="write" file="#savedFolder#/pdf2.pdf" output="#pdf2#">
<cfpdf action="merge" directory="#savedFolder#" name="mergedpdf">
<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:
<cfpdf action="merge" directory="#savedFolder#" name="mergedpdf">
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:
<cfdocument name="pdf1" format="pdf">
<cfoutput>
This is PDF 1 at #timeFormat(now())#
</cfoutput>
</cfdocument>
<cfdocument name="pdf2" format="pdf">
<cfoutput>
This is PDF 2 at #timeFormat(now())#
</cfoutput>
</cfdocument>
<cfpdf action="merge" name="mergedpdf">
<cfpdfparam source="pdf1">
<cfpdfparam source="pdf2">
</cfpdf>
<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. :)
Archived Comments
While I may not be using the CFPDF tag anywhere, it's definitely useful to just read through your posts. There's usually something in there that will come in handy (even if I don't know it yet) :)
Ray,
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.
What about getting rid of certain elements? Such as images - I may want to print out a pdf but I'd like to get rid of that image on the top of the page so as to save my precious color ink (isn't it obscene how you're forced to pay lots of money for color cartridge??).
What about getting rid of certain elements? Such as images - I may want to print out a pdf but I'd like to get rid of that image on the top of the page so as to save my precious color ink (isn't it obscene how you're forced to pay lots of money for color cartridge??).
What about getting rid of certain elements? Such as images - I may want to print out a pdf but I'd like to get rid of that image on the top of the page so as to save my precious color ink (isn't it obscene how you're forced to pay lots of money for color cartridge??).
You can remove a watermark, but just removing an image - I don't believe you can do that. You could possibly "cheat" and put an image as a watermark, but just plain white with opacity set right and use it to cover it up.
When i use the cfpdf tag to merge documents, the data in the form is not merged.
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?
Not sure. I haven't done much at all with the forms yet. It was plan too - but life got busy. ;)
Finally this coding (cfpdf merge 2 documents in 1) solve my problem. Now my system can generate a pdf file with combination of landscape & potrait orientation.
I use cfdocument to print out personnel biographies. You select a user, click print, and bam, a .pdf is generated (sometimes they are 2 or 3 pages long, depending upon the content in their sql record). What I need to do is allow a user to pick a few names, and click print and generate one pdf with multiple biographies. The problem I have is dealing with page numbering. I need the page numbering to be based on the person and not the overall file page count. (If a person's bio is two pages, I need to print "continued" on their second page. The problem I have is that EVERY person after page one is essentially not the first page, so they all get the continue message)
The page numbering is easy. Just look at the docs for cfdocumentsection. It allows for page numbers based on the section.
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.
I've used this for page numbering before. My problem exists when I try to create a .pdf with more than just one person data. How do I page number based on a person instead of the whole .pdf? First person has two pages (so they are 1 and 2) but then second person in .pdf is page 3, instead of being page one of their own record.
Um - did you see my comment concerning documentsection? It lets you do per section page numbering. So just make each person a section. Please read the docs on the cfdocumentsection tag.
Sorry.. Guess I mis understood. Not sure how to make it loop thru and see each person as a section...
Doesn't your code already loop over people? Something like
<cfquery name="people">
#name#
</cfquery>
Just add cfdocumentsection tags in the loop.
Yes, code has two queries, and lots of outputs.. With more than one query, I don't know how to get it to loop thru without messing up the cfoutputs...
While it's great to see that Adobe has incorporated some PDF manipulation features into ColdFusion 8 I'm disappointed to see that they fell short in some areas an did not include more granular control like you can get with third party tools like ABCPDF, ActivePDF etc. Additionally there are large holes in their support such as when populating forms created with LiveCycle and using cfpdfformparam you cannot merge multiple PDF's without losing the embedded data.
I'm not sure I'd say they have large holes. Yes - there are holes, but since they sell another product that does everything, they had to make some cuts so as to know hurt their other businesses. Considering how difficult it is to make/work with PDFs in other languages, I think it's fair to give Adobe some props here.
Please tell me what i'm doing wrong, here is the code. I appreciate it. Thanks
<cfpdf action="merge" name="MergedPDF" directory="L:\aip\web\reports">
<cfpdfparam source="rpt1_pdf.cfr">
<cfpdfparam source="rpt1.cfr">
<cfpdfparam source="rpt1.cfr">
</cfpdf>
cfr files aren't pdfs.
But they are generating pdf report
But they aren't PDFs. Yes, they are used to generated PDFs (with CFREPORT), but you can't use them _as_ PDFs.
You would first need to run CFREPORT, generate the PDFs, and then use the cfpdf action to merge them.
Any idea on why the instructions for evaluating an odd or even pagenumber return an error for me: "coldfusion.runtime.Cast$NumberConversionException: The value {currentpagenumber} cannot be converted to a number"?
I haven't tried it myself. Can you share the code?
<cfdocument format="flashpaper">
<cfdocumentitem type="header">
<cfif (cfdocument.currentpagenumber mod 2) is 0>
<cfoutput>#sectionTitle#</cfoutput>
<cfelse>
<cfoutput>#chapterTitle#</cfoutput>
</cfif>
</cfdocumentitem>
...
</cfdocument>
Cool. I've said multiple times that there are -multiple- cool things fixed in 801 that people (including myself) miss because we typically stick with the main docs.
I have a pdf that ends with a table structure for a signature block - about 10 lines of text printed. I dont want the table to break with the pagebreak (ala Access 'keep together')
Can I protect the block from the pagebreak, causing the break to occur earlier?
Thanks in advance
I'm trying to figure out how to pass page number or nameddest (bookmark) to acrobat when use cfcontent to return a pdf document. We use cfcontent instead of direct links to the document because we secure them using permissions set within the application. Any ideas would be great.
You mean you want the PDF to open at a particular page?
If that -is- it, check this: http://kb2.adobe.com/cps/31...
Thanks Ray, that works if you go directly at the PDF in the browser URL. I'm using CFContent to server up the file, because to secure the documents they're not in a browser accessible directory.
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.
Looks like you can do this with DDX - this link is to the CF8 docs, but obviously it works in CF9.
http://livedocs.adobe.com/c...
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.
I had some issues with the example in the CF docs. I'm writing a blog entry now that shows a full demo.
Here's the blog entry I did:
http://www.raymondcamden.co...
Hi Ray, i do not know what to say but i have a situation here:
i have a text file whose contents are the name of the person say "M Randhawa"
Now i have another PDF which is full of text and images and i have a %firstname% in that PDF
I want the text contents to be added to the PDF and replace the %firstname% with the firstname from the textfile
what can i do to achieve this
You can't edit text in a pdf using cfpdf. You can make a new PDF dynamically with cfdocument.
btw there must some wa of doing this, because the pdf i already have is full of images and text, so it will cumbersome to convert that pdf to html and then again replace my text and regenrate it as pdf
seems very weird solution
There probably are other solutions out there - but if we are talking what is built into CF, the answer is no. You can easily do dynamic PDFs if your source is HTML you can wrap in cfdocument.