Monday, February 15, 2010

ColdFusion 9: CFPDF - Adding Headers and Footers (and a Few Quirks) - Part 1

With the explosion of new features in CFPDF, there is a lot to cover in the documentation. So it is understandably a little lacking on extra examples that go beyond the very basics. While belatedly exploring some of the extended features, I ran into a few quirks with addHeader / addFooter. Probably not news to some of you. But I thought I would share my examples and results in case it saves someone else a little time.


Text Headers and Footers
The basic concept of adding a textual headers and footers was simple enough. Just use the appropriate action, addHeader or addFooter, along with a simple text value.  Setting the text alignment is equally simple, just provide an align value (left,center, right) with the desired margins.  It is worth noting that if you omit the margin, CF uses its default.  While I would not swear to it, the default seems to be 1 inch. At least for letter sized pages.

Note: If there is too much text to display with the margin bounds, it may flow right off the page. So if working with a lot of text, you may need to adjust it with some html/css.

<!--- Align plain text header --->
<cfpdf  action="addHeader" 
    source="inputFile.pdf" 
    name="pdfOutput" 
    align="left"
    leftMargin="0.5"
    text="Left Aligned. One-half (0.5) inch from left side"
/>  

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />
Styling Text Headers and Footers
Applying html/css styles to the text is very similar to using a text watermark (added in CF 8.0.1). I did encounter one peculiarity with fonts. The names seem to be case sensitive. So on windows the font name "castellar" did nothing, but "Castellar" (with an uppercase "C") did produce the desired font.


<!--- 
Add Styled header 
--->
<cfsavecontent variable="headerText">
    <span style="font-size: 25pt; font-family: Castellar;">
       <b style="color: #0080ff;">Vancouver</b>
       <b style="color: #0000ff;">2010</b>
       <b style="color: #000080;">Olympics</b>
    </span>
</cfsavecontent>

<cfpdf action="addHeader" 
    source="inputFile.pdf" 
    name="pdfOutput" 
    topMargin="1"
    text="#headerText#"
/>

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

HTML/CSS Limitations
Not all html tags are supported, such as <div>, <a>, etcetera. I am assuming the same limitations apply to action="addWatermark". So whatever quirks one has, the others probably do as well. 

Now in case you see an error message complaining about an "Invalid Document..", check the stack trace for the real error. Most likely it will say something about either an invalid tag or syntax error in your html/css.


An error occurred during the ADDHEADER operation in the cfpdf tag.  
Error: Invalid Document C:\ColdFusion9\wwwroot\test\pdf\inputFile.pdf specified for source or directory.  
...

Stack Trace:
com.adobe.internal.pdftoolkit.core.exceptions.PDFInvalidDocumentException: Error during text formatting, internalProcessElement: InvalidInput: Illegal element tag "div".
....

Differences between Watermarks and Headers/Footers
One difference I have noticed is how text is sized. CF 8.0.1 added the width and height attributes to support text watermarks. From what I have observed, the width and height values seem to control the size of the watermark text, rather than any explicit font sizes in html/css.  Since the width and height attributes are not allowed with actions addHeader and addFooter,  that text can be controlled by css font sizes.

Text and Image Opacity
As with watermarks, you can also use the opacity attribute to change transparency of the header/footer. Simply use a value between 0 (transparent) and 10 (fully opaque). This example creates a styled footer whose text is 50% transparent. Opacity applies to both text and images.



<cfsavecontent variable="text">
    <span style="font-size: 18pt; font-family: Century Gothic;">
       <b style="color: #0080ff;">Vancouver</b>
       <b style="color: #0000ff;">2010</b>
       <b style="color: #000080;">Olympics</b>
    </span>
</cfsavecontent>

<cfpdf action="addFooter" 
    source="inputFile.pdf" 
    name="pdfOutput" 
    bottomMargin="0.5"
    text="#text#"
    align="center"
    opacity="5"
/>    

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

Image Headers and Footers
Using images for headers and footers is also supported. The image source can be a file path, url, CF image object, array of bytes, or a base64 string (using isBase64="true").

Figuring out image headers/footers took a little while. It is not that the syntax is particularly cryptic. But it was not really clear to me, from the documentation, what should happen in various cases, such as when using an over-sized or undersized image.

Based on my tests, if an image is too large to fit within the given margin dimensions, it is automatically re-sized. So if the margin is too small, you can end up with a skewed or squished image. I also observed some strange alignment issues in a few of my tests. Though I am not yet sure why. In any case, you may want to ensure your images are correctly sized beforehand, to avoid unexpected resizing behavior by cfpdf.

If you view the image url below, its size should be around 7.4 x 2.4 inches. To center align the image, I used the code below. The top margin is set to "2.6" (inches). Tall enough to fit the image without resizing, plus a small amount of padding at the top. Both the left and right margin are set to zero (0) so the image is centered across the entire page width.

Now you may notice that visually this image does not appear completely centered. But that is due to the layout of the underlying graphic. But you can adjust the position by changing the alignment and leftMargin.


<!---
    Sample Document
--->
<cfdocument format="pdf" fileName="inputFile.pdf" pageType="Letter" marginTop="2.6" overwrite="true">
    <style type="text/css" media="screen">
        div { font-family: Castellar; padding: 10px; color: 204a64; } 
    </style>
    <cfloop from="1" to="10" index="x">
        <div> 
            Alpine Skiing * Biathlon * BobSleigh * Cross-Country Skiing * Figure Skating * 
            Ice Hockey * Luge * Curling * Snowboard * Speed Skating
        </div>
        <cfif x lt 10>
            <cfdocumentitem type="pagebreak" />
        </cfif>
    </cfloop>
</cfdocument>

<!--- 
    Add image header (Note: Using image URL just for demonstration purposes)
--->
<cfpdf action="addHeader" 
    source="inputFile.pdf" 
    destination="pdfWithImageHeader.pdf" 
    topMargin="2.6"
    leftMargin="0"
    rightMargin="0"
    align="center"
    image="http://www.google.com/logos/olympics10-prsskating-hp.png"
    overwrite="true"
/>

Multiple Headers / Footers
It is also possible to layer headers/footers by adding text on top of an existing image. To demonstrate, read in the file created in the previous code snippet and adjust the margin size so the text is placed in the desired position. The results should look something like this



<!--- 
Add text header on top of existing image header
--->
<cfsavecontent variable="headerText">
    <span style="font-size: 25pt; font-family: Castellar;">
       <b style="color: #0080ff;">Vancouver</b>
       <b style="color: #0000ff;">2010</b>
       <b style="color: #000080;">Olympics</b>
    </span>
</cfsavecontent>

<cfpdf action="addHeader" 
    source="pdfWithImageHeader.pdf" 
    name="pdfOutput" 
    topMargin="1.7"
    leftMargin="0"
    rightMargin="0"
    align="center"
    text="#headerText#"
/>

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

Remove Headers/Footers
In the process of running all of these tests, I discovered something worth noting about images.  Using an image obviously increases the file size. In some cases substantially (I have not gotten around to doing any size comparisons between cfpdf and iText yet). Since adding a new header/footer does not remove any existing ones, if you mistakenly add an image twice, it will increase the file size .. twice. But you can always use the action="removeHeaderFooter" first. (Yes, it does remove both headers and footers).


<!--- 
    Remove headers and footers
--->
<cfpdf action="removeHeaderFooter" 
        source="pdfWithImageHeader.pdf" 
        name="pdfOutput" 
    />

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

On a side note, if you use cfdocument to generate header and footers, as opposed to the new cfpdf actions, be aware there is a "Known Issue" mentioned in the CF9 release notes.


Bug 76078:
Headers or footers added while generating PDF using cfdocument are not marked and are not recognized as headers or footers. Therefore, cfpdf action="removeheaderfooter" will not be able to remove them from source PDF.

ColdFusion 9: CFPDF - Adding Headers and Footers (and a Few Quirks) - Part 2

2 comments:

Julian Halliwell December 14, 2010 at 7:20 AM  

Great couple of posts. Just to note another quirk I've encountered: cfpdf addFooter will apparently not allow the copyright symbol. Tried the HTML entity, ascii character value using chr() and copying and pasting, but the parser kept throwing errors.

Bit of shame since footers are the ideal place to place copyright details.

cfSearching December 14, 2010 at 1:52 PM  

@Julian,

May not be the best way, but using &#169; seems to work.

-Leigh

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep