Thursday, July 19, 2007

Enhancements to CFDocument in ColdFusion 8

As you all know, CFDocument tag is used to easily create pdf or flashpaper documents from HTML/CFML content. ColdFusion 8 has added lot of enhancements to it and in this post we will talk about those enhancements.

1. Bookmark : You can create bookmarks for each section of the pdf using "bookmark" attribute. The bookmark created is of only one level and its name is set to the documentsection's name. Here is a sample code for creating pdf with bookmarks.

<cfdocument format="PDF" bookmark="yes">
<cfdocumentsection name="Introduction">
<p>The introduction goes here.</p>
<cfdocumentsection name="Chapter 1">
<h3>Chapter 1: Getting Started</h3>
<p>Chapter 1 goes here.</p>
<cfdocumentsection name="Chapter 2">
<h3>Chapter 2: Building Applications</h3>
<p>Chapter 2 goes here.</p>
<cfdocumentsection name="Conclusion">
<p>The conclusion goes here.</p>

2. Proxy Support : With ColdFusion 8, you can now provide proxy configuration to cfdocument for retrieving external content like images. This will be useful in situation where the machine hosting ColdFusion is connected to the external world via a proxy. The new attributes added for this (which are self explanatory) are listed below

  • proxyHost
  • proxyPort
  • proxyUser
  • proxyPort

3. Content from URL : Though this was added in 7.0.2, I think it makes sense to add here because it was not there in 7. :-) Have you ever created or wanted to create a pdf from a web page? If yes, then the new attribute "src" in cfdocument and cfdocumentsection tag makes it very easy to do this. Here is an example to do this.

<cfdocument format="pdf" src="" />

4. Basic Authentication : If the CFDocument body contains a resource (e.g; image or URL) that is protected with basic authentication, ColdFusion 7 can not retrieve it and it was one of the reason for getting "red-x" for images. (See my old post on this). ColdFusion 8 addresses this by adding these two attributes to cfdocument and cfdocumentsection tag.

  • authuser - user name to be used for basic authentication.
  • authpassword - password to be used for basic authentication.

5. User Agent : There are some cases where the web server is configured to allow requests only from a certain set of browsers (User agents to be precise) to prevent spiders and bots from overloading the server. In ColdFusion 7, when CFDocument creates a URLConnection for an image, it sends a "User-Agent" header, that looks like "User-Agent:Java/1.4.2_07", in the HTTP request. If the web server does not recognize "Java" user-agent, it returns a status code of 404 (resource not found) and hence the images can not be displayed. ColdFusion 8 adds a new attribute "user-agent" to address this.

  • user-agent : User agent to be used for making http connection. The default value will now be "ColdFusion". If this also does not work, you can use the same string that browsers like IE or firefox use. This attribute has been added to <cfdocument> and <cfdocumentsection> tag.

6. PDF name : When a pdf generated by cfdocument is sent to the browser and you try to save it, the browser will prompt with the cfm name for the pdf which is generally not desirable. With ColdFusion 8, you can provide the appropriate name in "saveAsName" attribute of cfdocument.

  • saveAsName - Name that appears in the 'saveAs' dialog when you try to save the pdf from the browser (Generally File -> saveAs). This name will not work if you use "save" dialog of the pdf plugin.

Here is a sample code for the same.

<cfdocument format="pdf" saveAsName="mypdf">
<p>This is a PDF document.</p>

7. Local URLs : When CFDocument body contains a relative URL, ColdFusion will resolve the relative URL to an absolute URL and will send an HTTP request for this url. A side effect of this is - Server ends up sending HTTP request even for local URL or images that are lying on the local file system which obviously hurts the performance. In ColdFusion 8, we have added a new attribute "localURL" to cfdocument tag which if enabled, will try to resolve the relative URLs as file on the local machine.

  • localURL : "true" | "false" - It should be enabled if the images used in cfdocument body are on the local machine. This would make the cfdocument engine retrieve the images directly from the file system rather then asking the server for it over http.

This attribute helps reducing the load from the server so that the same web server thread can now serve user request instead of serving local images to CFDocument. This also addresses some of the "missing image" problems which I mentioned here. Here is a sample code using this attribute.

<cfdocument format="PDF" localUrl="true">
<td><image src="images/bird.jpg"></td>
<td><image src="images/rose.jpg"></td>

8. Section Page Counts : CFDOCUMENT scope contains two new variables which give you the page counts for document section.

  • TOTALSECTIONPAGECOUNT - total no of pages in the current section
  • CURRENTSECTIONPAGENUMBER - Current page number in the current section.

9. Dynamic header and footer : CFDOCUMENT scope variables can now be used in expressions inside <cfdocumentitem> which makes it possible to have dynamic header and footers. You can now build logic for header/footer content based on the page number. Here is a sample code which prints section title if the page is even and prints the page no otherwise. Below is a code snippet which creates a dynamic header.

    <cfdocumentitem type="header">
<cfif (cfdocument.currentpagenumber mod 2) is 0>
<cfoutput>#cfdocument.currentpagenumber# of #cfdocument.TOTALPAGECOUNT#</cfoutput>

10. We have also fixed most of the CFDocument related bugs e.g text chopping, image cropping or red-x, image scaling, "Document has no pages" etc.


Dan King said...

RE:This name will not work if you use "save" dialog of the pdf plugin.

So how do you specify the name when using the pdf plugin (like most people do these days)? It sucks that the save button in the plug-in shows the entire URL as the default "save a copy" name.

Anonymous said...

very useful post, amazing!
one question: what happens with cfchart in cfdocument, while using localUrl="true"? I just see red crosses, could this be solved?

Paul Barbeau said...

I am trying to use a different footer and your code as is does not work. The error i get is.

The cause of this exception was that: coldfusion.runtime.Cast$NumberConversionException: The value {currentpagenumber} cannot be converted to a number..

Any ideas?

Anonymous said...

I know this is very very late, but to @Anonymous
to use a cfchart in a cfdocument, you have to save the cfchart as a jpg file (use cffile) in the disk and then insert it into the cfdocument as a document item.