ColdFusion PDF Generation - other options to cfdocument

1k Views Asked by At

Im just wondering whether or not there are other options to using <cfdocument> that may leverage the web-kit API's for ColdFusion.

Or if one is available in Java but has a nice ColdFusion wrapper on it to make it easier than working out all the intricacies of java.

2

There are 2 best solutions below

1
On

I go under the hood and use iText. Doing this I have created a very robust printing framework (for printing forms) and reporting framework (tabular reports). I've fine tuned it over the years, it can generate PDFs with hundreds of thousands of rows no problem. It can go 4 children deep and has many of features/settings. Sorry so broad. If you go this route I could help with specifics.

I use JavaLoader (http://javaloader.riaforge.org/) in my onApplicationStart to load the iText (and other jars I need in my app)...

<!--- note, this is actually a harcoded UUID value --->
<cfset MyUniqueKeyForJavaLoader = "1111-2222-3333444455556666">
<!--- if we have not already created the javaLoader --->
<cfif not structKeyExists(server, MyUniqueKeyForJavaLoader)>
    <!--- construct an array containing the full path to the jars you wish to load --->
    <cfset pathArray = arrayNew(1)>
    <cfset arrayAppend(pathArray, expandpath('jars/iText-2.1.3.jar'))>
    <!---<cfset arrayAppend(pathArray, expandpath('jars/iText-5.0.6.jar'))>--->
    <cfset arrayAppend(pathArray, expandpath('jars/IDADataMatrix.jar'))>
    <cfset arrayAppend(pathArray, expandpath('jars/LinearBarCode.jar'))>
    <cfset arrayAppend(pathArray, expandpath('jars/barcodeencoder.jar'))>
    <cfset arrayAppend(pathArray, expandpath('jars/sshcommandexecutor.jar'))>
    <!--- <cfset arrayAppend(pathArray, expandpath('jars/jsch-0.1.40.jar'))> --->
    <cflock scope="server" type="exclusive" timeout="10">
        <!--- verify again the javaloader was not already created --->
        <cfif not StructKeyExists(server, MyUniqueKeyForJavaLoader)>
            <cfset server[MyUniqueKeyForJavaLoader] = createObject("component", "javaloader.JavaLoader").init(pathArray)>
        </cfif>
    </cflock>
</cfif>

Then, in the report framework, this is how you would start to initialize everything. (this is just a small snippet, there is much much more than goes into this, but it should be enough for you to start fooling around with if you decide to).

<cfscript>
    pathAndFile      = request.reportDir.fpath&request.reportDir.pdfFile;
    session.reports[libRptId].pathAndFile = pathAndFile;
    loader           = server['1111-2222-3333444455556666']; //Our iText version which currently is 2.1.3 (yes, we need to update soon)
    document         = loader.create("com.lowagie.text.Document");
    PdfWriter        = loader.create("com.lowagie.text.pdf.PdfWriter");
    FileOutputStream = createObject("java", "java.io.FileOutputStream");
    myFile           = CreateObject("java","java.io.File").init(pathAndFile); //Only used for getting the file size to display on screen in real-time as the pdf is being generated
    Color            = createObject("java", "java.awt.Color");
    element          = loader.create("com.lowagie.text.Element");
    Chunk            = loader.create("com.lowagie.text.Chunk");
    PageSize         = loader.create("com.lowagie.text.PageSize");
    HeaderFooter     = loader.create("com.lowagie.text.HeaderFooter");
    Rectangle        = loader.create("com.lowagie.text.Rectangle");
    Paragraph        = loader.create("com.lowagie.text.Paragraph");
    PdfPCell         = loader.create("com.lowagie.text.pdf.PdfPCell");
    PdfPTable        = loader.create("com.lowagie.text.pdf.PdfPTable");
    Phrase           = loader.create("com.lowagie.text.Phrase");
    Font             = loader.create("com.lowagie.text.Font");
    FontFactory      = loader.create("com.lowagie.text.FontFactory");
</cfscript>

<!--- Define our different fonts --->
<cfset fontTitle       = FontFactory.getFont("Arial", 10,                 Font.BOLD,       Color.BLACK)>
<cfset fontSubRptTitle = FontFactory.getFont("Arial", 10,                 Font.BOLDITALIC, Color.BLACK)>
<cfset fontStandard    = FontFactory.getFont("Arial", 8,                  Font.NORMAL,     Color.BLACK)>
<cfset fontHeader      = FontFactory.getFont("Arial", 9,                  Font.NORMAL,     Color.GRAY)>
<cfset fontFooter      = FontFactory.getFont("Arial", 9,                  Font.NORMAL,     Color.GRAY)>
<cfset fontColHeader   = FontFactory.getFont("Arial", rpt.fontsizeLabel,  Font.BOLD,       Color.BLACK)>
<cfset fontData        = FontFactory.getFont("Arial", rpt.fontsize,       Font.NORMAL,     Color.BLACK)>

<cfset marginTop    =  round(72 / (100 / (rpt.marginTop * 100))) >
<cfset marginright  =  round(72 / (100 / (rpt.marginright * 100))) >
<cfset marginbottom =  round(72 / (100 / (rpt.marginbottom * 100))) >
<cfset marginleft   =  round(72 / (100 / (rpt.marginleft * 100))) >

<!--- Page Setup (PageSize and Margins)  For the margins 18 = .25inches --->
<cfif rpt.pagetype eq "letter">
    <cfif rpt.orientation eq "portrait">
        <cfset document = document.init(PageSize.LETTER, marginleft, marginright, marginTop, marginbottom)>
    <cfelse><!--- Landscape --->
        <cfset document = document.init(PageSize.LETTER.rotate(), marginleft, marginright, marginTop, marginbottom)>
    </cfif>
<cfelseif rpt.pagetype eq "legal">
    <cfif rpt.orientation eq "portrait">
        <cfset document = document.init(PageSize.LEGAL, marginleft, marginright, marginTop, marginbottom)>
    <cfelse><!--- Landscape --->
        <cfset document = document.init(PageSize.LEGAL.rotate(), marginleft, marginright, marginTop, marginbottom)>
    </cfif>
<cfelseif rpt.pagetype eq "ledger">
<!--- We found that ledger logic is backward from letter and legal. Long edge is portrait 11 X 17 and landscape mode = 17 X 11 --->
    <cfif rpt.orientation eq "portrait">
        <cfset document = document.init(PageSize.LEDGER.rotate(), marginleft, marginright, marginTop, marginbottom)>
    <cfelse><!--- Landscape --->
        <cfset document = document.init(PageSize.LEDGER, marginleft, marginright, marginTop, marginbottom)>
    </cfif>
</cfif>
<cfset writer = PdfWriter.getInstance(document, FileOutputStream.init(pathAndFile))> <!--- Init the variable "document" that we write to --->
0
On

We use a little application called wkhtmltopdf, which lets you create a PDF from any HTML page. It works with a commandline, so you can use <cfexecute> to run it. I also use the following code to make sure the PDF is fully formed before outputting it:

<cfset truePDF = "false">
<cfloop condition="NOT truePDF">
    <cfif fileExists("#pathname##filename#")>
        <cffile action="read" file="#pathname##filename#" variable="pdfContent">
        <cfif findNoCase('%%EOF',right(pdfContent,1024)) GT 0>
            <cfset truePDF = "true">
        </cfif>
    </cfif>
</cfloop>
<cfoutput>#pdfContent#</cfoutput>