It's long been a hassle to easily generate printable content from within Web pages, and this is a source of serious aggravation for Web application developers (all developers, not just ColdFusion developers). Considering that a very significant chunk of Web application development tends to be data reporting and presentation type applications, this is a big problem.
The truth is, Web browsers just don't print Web pages properly, so developers have had to resort to complex and painful work-arounds to put content in a printable format.
ColdFusion solves this problem simply with the <cfdocument> family of tags.
We'll start with a really simple example. Listing 16.8 contains simple text wrapped within a pair of <cfdocument> tags. The generated output is seen in Figure 16.8.
<!--- Name: Print1.cfm Author: Ben Forta Description: Simple printable output Created: 01/10/05 ---> <cfdocument format="pdf"> Hello world! </cfdocument>
The code in Listing 16.8 couldn't be simpler. By wrapping text in between <cfdocument> and </cfdocument> tags, content between those tags is converted into a PDF file on the fly, and embedded in the page.
In addition to PDF, ColdFusion can generate FlashPaper, a lightweight alternative to PDF, and one that only requires that the Flash Player be present on the browser. To generate FlashPaper, simply change format="pdf" to format="flashpaper" to generate a page like the one seen in Figure 16.9.
<!--- Name: Print2.cfm Author: Ben Forta Description: Data driven printable output Created: 01/10/05 ---> <!--- Get budget data ---> <cfinvoke component="ChartData" method="GetBudgetData" returnvariable="BudgetData"> <!--- Generate document ---> <cfdocument format="pdf"> <!--- Header ---> <table align="center"> <tr> <td> <img src="../images/logo_c.gif" > </td> <td align="center"> <font size="+2">Orange Whip Studios<br>Movies</font> </td> </tr> </table> <!--- Title ---> <div align="center"> <h2>Budget Data</h2> </div> <!--- Details ---> <table> <tr> <th>Movie</th> <th>Budget</th> </tr> <cfoutput query="BudgetData"> <tr> <td><strong>#MovieTitle#</strong></td> <td>#LSCurrencyFormat(AmountBudgeted)#</td> </tr> </cfoutput> </table> </cfdocument>
You'll notice that the code in <font>, which generally should be avoided), an image, tables, CFML expressions, and more. The <cfdocument> tag supports all of the following:
<271>271>
XML 1
DOM level 1 and 2
CSS1 and CSS2
In other words, <cfdocument> should be more than able to convert all sorts of pages into printable PDF or FlashPaper.
You will likely often need to create printable versions of existing pages. It would be tempting to try and simply conditionally include <cfdocument> tags in existing pages, but unfortunately that won't work: ColdFusion won't parse the page correctly because it thinks your tags aren't properly paired.
The solution to this problem is to create a wrapper page, one that defines the printable document and includes the original page. Chapter 11, it simply displays movie details. The modified page is seen in Figure 16.11.
<!--- Name: details.cfm Author: Ben Forta (ben@forta.com) Description: CFC driven data drill-down details with complete validation Created: 12/15/04 ---> <!--- Movie list page ---> <cfset list_page="movies.cfm"> <!--- Make sure FilmID was passed ---> <cfif not IsDefined("URL.filmid")> <!--- it wasn't, send to movie list ---> <cflocation url="#list_page#"> </cfif> <!--- Get movie details ---> <cfinvoke component="ows.11.movies" method="GetDetails" returnvariable="movie" FilmID="#URL.filmid#"> <!--- Make sure have a movie ---> <cfif movie.RecordCount IS 0> <!--- It wasn't, send to movie list ---> <cflocation url="#list_page#"> </cfif> <!--- Build image paths ---> <cfset image_src="../images/f#movie.FilmID#.gif"> <cfset image_path=ExpandPath(image_src)> <!--- Creat260 page ---> &l275> <head> <title>Orange Whip Studios - Movie Details</title> </head> <body> <!--- Display movie details ---> <cfoutput query="movie"> <table> <tr> <td colspan="2"> <!--- Check of image file exists ---> <cfif FileExists(image_path)> <!--- If it does, display it ---> <img src="../images/f#filmid#.gif" align="middle"> </cfif> <b>#MovieTitle#</b> </td> </tr> <tr valign="top"> <th align="right">Tag line:</th> <td>#PitchText#</td> </tr> <tr valign="top"> <th align="right">Summary:</th> <td>#Summary#</td> </tr> <tr valign="top"> <th align="right">Released:</th> <td>#DateFormat(DateInTheaters)#</td> </tr> <tr valign="top"> <th align="right">Budget:</th> <td>#DollarFormat(AmountBudgeted)#</td> </tr> </table> <p> <!--- Links ---> [<a href="detailsprint.cfm?FilmID=#URL.FilmID#">Printable page</a>] [<a href="#list_page#">Movie list</a>] </cfoutput> </body> <l>
The big change to this page is a line added to the links section at the bottom. A new link to a Printable page has been created; when clicked, it opens detailsprint.cfm passing the FilmID to that page. The code for that page is remarkably simple, as seen in Listing 16.11.
<!--- Name: detailsprint.cfm Author: Ben Forta (ben@forta.com) Description: Printable version of details pahge Created: 12/15/04 ---> <cfdocument format="pdf"> <cfinclude template="details.cfm"> </cfdocument>
Listing 16.11 creates a document using <cfdocument> tags, and includes the existing details page to generate the printable output seen in Figure 16.12.
The links in details.cfm work in the generated printable output.
In addition to the format attribute used here, <cfdocument> supports a whole series of attributes to give you greater control over printed output. Table 16.9 lists the supported attributes.
ATTRIBUTE | DESCRIPTION |
---|---|
backgroundvisible | Whether or not to display page background; default is no. |
encryption | Optional encryption, 128-bit or 40-bit (used by PDF only). |
filename | Optional file name, if specified document is saved to disk instead of being served in the browser. |
fontembed | Whether or not to embed used fonts, yes, no, or selective (default). |
format | pdf or flashpaper, this attribute is required. |
marginbottom | Page bottom margin, use unit to specify unit of measure. |
marginleft | Page left margin, use unit to specify unit of measure. |
Page right margin, use unit to specify unit of measure. | |
margintop | Page top margin, use unit to specify unit of measure. |
name | Optional variable name to contain generated output. |
orientation | Page orientation, portrait (the default) or landscape. |
overwrite | Whether or not to overwrite existing documents (if using filename). |
ownerpassword | Optional owner password (used by PDF only). |
pageheight | Page height (used if pagetype="custom"), use unit to specify unit of measure. |
pagetype | Page size, supports legal, letter, A4, A5, B5, and custom. |
pagewidth | Page width (used if pagetype="custom"), use unit to specify unit of measure. |
scale | Scaling factor, default is calculated by ColdFusion automatically. |
unit | Unit of measure, in (inches) or cm (centimeters). |
userpassword | Optional user password (used by PDF only). |
<cfdocument> embeds generated output in your Web page. You may opt to save the generated files to disk instead of serving them in real time. Reasons to do this include:
Caching, so as to not have to regenerate pages unnecessarily
Emailing generated content
Generating pages that can be served statically
To save generated output, simply provide a filename in the filename attribute.
<cfdocumentitem> is used within a <cfdocument> tag set to embed additional items. <cfdocumentitem> requires that a type be specified. Table 16.10 lists the supported types.
TYPE | DESCRIPTION |
---|---|
footer | Page footer. |
header | Page header. |
pagebreak | Embed a page break, this type takes no body. |
Page breaks are calculated automatically by ColdFusion. Use <cfdocumentitem type="pagebreak"> to embed manual breaks.
Listing 16.12 is a revised printable movie listing, the output of which is seen in Figure 16.13.
<!--- Name: Print3.cfm Author: Ben Forta Description: Printable output with additional options Created: 01/10/05 ---> <!--- Get budget data ---> <cfinvoke component="ChartData" method="GetBudgetData" returnvariable="BudgetData"> <!--- Generate document ---> <cfdocument format="pdf"> <!--- Header ---> <cfdocumentitem type="header"> OWS Budget Report </cfdocumentitem> <!--- Footer ---> <cfdocumentitem type="footer"> <p align="center"> <cfoutput> #CFDOCUMENT.currentpagenumber# of #CFDOCUMENT.totalpagecount# </cfoutput> </p> </cfdocumentitem> <!--- Header ---> <table align="center"> <tr> <td><img src="../images/logo_c.gif" ></td> <td align="center"><font size="+2">Orange Whip Studios<br>Movies</font> </td> </tr> </table> <!--- Title ---> <div align="center"> <h2>Budget Data</h2> </div> <!--- Page break ---> <cfdocumentitem type="pagebreak" /> <!--- Details ---> <table> <tr> <th>Movie</th> <th>Budget</th> </tr> <cfoutput query="BudgetData"> <tr> <td><strong>#MovieTitle#</strong></td> <td>#LSCurrencyFormat(AmountBudgeted)#</td> </tr> </cfoutput> </table> </cfdocument>
Listing 16.12 warrants some explanation. The <cfdocument> content now contains the following code:
<!--- Header ---> <cfdocumentitem type="header"> OWS Budget Report </cfdocumentitem>
This code defines a page header, text that will be placed at the top of each page. A footer is also defined as follows:
<!--- Footer ---> <cfdocumentitem type="footer"> <p align="center"> <cfoutput> #CFDOCUMENT.currentpagenumber# of #CFDOCUMENT.totalpagecount# </cfoutput> </p> </cfdocumentitem>
This page footer contains two special variables. Within a <cfdocument> tag, a special scope exists named CFDOCUMENT. It contains two variables, as listed in Table 16.11. These variables may be used in headers and footers, as used in this example.
TYPE | DESCRIPTION |
---|---|
currentpagenumber | Current page number. |
totalpagecount | Total number of generated pages. |
In addition, the code in Listing 16.12 embeds a manual page break using this code:
<!--- Page break ---> <cfdocumentitem type="pagebreak" />
<cfdocumentitem> must always have an end tag, even when no body is used. The trailing / is a shortcut that you can use. In other words, the above tag is functionally identical to:
<!--- Page break ---> <cfdocumentitem type="pagebreak"></cfdocumentitem>
As you have seen, you have a lot of control over generated pages using the <cfdocument> and <cfdocumentitem> tags. But sometimes you may want different options in different parts of the same document. For example, you may want a title page to have different margins than other pages. Or you may want different headers and footers in different parts of the document.
To do this, you use <cfdocumentsection> tags. A <cfdocument> tag pair may contain one or more sections, each defined using <cfdocumentsection> tags. Within each section you can specify alternate margins, and can use <cfdocumentitem> tags to specify headers and footers for each section.