Cold Fusion Tips-N-Tricks
Dynamically creating a Microsoft Rich Text Document

The accompanied HTML/CFML Code contains a working template to create a RTF Word Document.

This code will allow you to create a multi-sectioned report. I was handed a printout of a report from a mainframe program and told to duplicate it (real fun, no specs) in Cold Fusion and they wanted it to look exactly the same. I came up with the following to get around of the headaches of forcing a pagebreak at a predefined point on a web page. I simply define the header, the footer, and pump all the data out and let Word handle the page breaks.

This program only generates text with the font Courier New though support for Times New Roman is also in there. Any formatting codes are acceptable and you'll see that I boldfaced the header on section two and used italics on the footer of section three. You can use "\f1 " (note the trailing space) to change the font to Times New Roman and "\f2 " for Courier New.

Microsoft has a white paper on RTF somewhere on their website but good luck finding it. Every time I had to search for it, it took on average of 30 minutes to find it.


Example HTML/CFML code:
<CFSETTING ENABLECFOUTPUTONLY="Yes">

<!--- Define the pysical directory to generate the report --->
<CFSET cf_report="C:\Internet\WWW\">

<!--- Define the URL directory --->
<CFSET cf_view="http://127.0.0.1/">

<!--- Amount of data to "cache" before writing the data to the rtf file.  20*1024 = 20k --->
<CFSET report_size=20*1024>

<!--- The character to represent a line break, must NOT be generated anywhere else in the report --->
<CFSET cr=Chr(3)>

<!--- Define special breaks and formatting --->
<CFSET page_break="\par \page ">
<CFSET section_break="\par \sect }\sectd \linex0\endnhere\sectdefaultcl ">
<CFSET new_header=section_break & "{\header \pard\plain \s15\widctlpar\tqc\tx4320\tqr\tx8640\adjustright \fs20\cgrid {\f2 ">
<CFSET new_footer="\par }}{\footer \pard\plain \s16\widctlpar\tqc\tx4320\tqr\tx8640\adjustright \fs20\cgrid {\f2 ">
<CFSET new_body="\par }}\pard\plain \widctlpar\adjustright \fs20\cgrid {\f2 ">
<CFSET new_line="\par ">
<CFSET eof="\par }}" & cr>

<!--- Initilize header and footer arrays --->
<CFSET head=ArrayNew(1)>
<CFSET foot=ArrayNew(1)>

<!--- The following four variables define the documents information, purely for informational sake --->
<CFSET title="Example RTF Report">
<CFSET author="John Bartlett">
<CFSET operator="">
<CFSET company="">

<!--- The next five variables define the margins.  "twips" defines how much space is in each tenth of
      an inch and should not be modifed.  To set a margin of 1.0", multiply twips by 10.  To set a
      margin of 0.5", multiply twips by 5. --->
<CFSET twips=144>
<CFSET top_margin=twips * 15>      <!--- Set top margin to 1.5 inches --->
<CFSET bottom_margin=twips * 15>   <!--- Set bottom margin to 1.5 inches --->
<CFSET left_margin=twips * 10>     <!--- Set left margin to 1 inch --->
<CFSET right_margin=twips * 10>    <!--- Set right margin to 1 inch --->

<!--- Page setup --->
<CFSET page_width=twips * 85>      <!--- Define the width of the paper to 8.5 inches --->
<CFSET page_height=twips * 110>    <!--- Define the height of the paper to 11 inches.  Use "140" for legal stock --->
<CFSET landscape="">  <!--- Set to "\landscape" for landscape reports, don't forget to reverse the page width and height --->

<!--- Create the text to display in the header field, must have at least two lines --->
<CFSET head[1]="Example Microsoft Word">
<CFSET head[2]="Rich Text Document Generator">
<CFSET head[3]="">
<CFSET numhead=3>

<!--- Create the text to display in the footer field --->
<CFSET foot[1]="">
<CFSET foot[2]="This is the footer field.">
<CFSET numfoot=2>

<!--- Generate the top of the Rich Text Document --->
<CFSET yr=DateFormat(Now(),"yyyy")>
<CFSET mo=Trim(DateFormat(Now(),"mm"))>
<CFSET dy=Trim(DateFormat(Now(),"dd"))>
<CFSET hr=Trim(TimeFormat(Now(),"HH"))>
<CFSET min=Trim(TimeFormat(Now(),"mm"))>
<CFSET tim="\yr" & yr & "\mo" & mo & "\dy" & dy & "\hr" & hr &
           "\min" & min & "}">
<CFSET header="{\rtf1\ansi\ansicpg1252\uc1 \deff0\deflang1033" &
              "\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2" &
              "{\*\panose 02020603050405020304}Times New Roman;}" &
              "{\f2\fmodern\fcharset0\fprq1{\*" &
              "\panose 02070309020205020404}Courier New;}}" &
              "{\colortbl;\red0\green0\blue0;" & cr>
<CFSET header=header & "\red0\green0\blue255;\red0\green255\blue255;" &
              "\red0\green255\blue0;\red255\green0\blue255;\red255" &
              "\green0\blue0;\red255\green255\blue0;\red255\green255" &
              "\blue255;\red0\green0\blue128;\red0\green128\blue128;" &
              "\red0\green128\blue0;\red128\green0\blue128;" & cr>
<CFSET header=header & "\red128\green0\blue0;\red128\green128\blue0;" &
              "\red128\green128\blue128;\red192\green192\blue192;}" &
              "{\stylesheet{\widctlpar\adjustright \fs20\cgrid " &
              "\snext0 Normal;}{\*\cs10 \additive Default Paragraph " &
              "Font;}{\s15\widctlpar\tqc\tx4320\tqr\tx8640\" &
              "adjustright " & cr>
<CFSET header=header & "\fs20\cgrid \sbasedon0 \snext15 header;}" &
              "{\s16\widctlpar\tqc\tx4320\tqr\tx8640\adjustright " &
              "\fs20\cgrid \sbasedon0 \snext16 footer;}}{\info" &
              "{\title " & title & "}" & cr>
<CFSET header=header & "{\author " & author & "}{\operator " &
              operator & "}" & cr>
<CFSET header=header & "{\creatim" & tim & cr>
<CFSET header=header & "{\revtim" & tim & cr>
<CFSET header=header & "{\printim" & tim & cr>
<CFSET header=header & "{\*\company " & company & "}{\nofcharsws0}" &
              "{\vern113}}" & cr>
<CFSET header=header & "\paperw" & page_width & "\paperh" &
              page_height & landscape & cr>
<CFSET header=header & "\margl" & left_margin & "\margr" &
              right_margin & "\margt" & top_margin & "\margb" &
              bottom_margin & " " & cr>
<CFSET header=header & "\widowctrl\ftnbj\aenddoc\hyphcaps0\formshade" &
              "\viewkind1\viewscale88\viewzk2\pgbrdrhead\pgbrdrfoot " &
              "\fet0\sectd \linex0\endnhere\sectdefaultcl {\header " &
              cr>
<CFSET header=header & "\pard\plain \s15\widctlpar\tqc\tx4320\tqr" &
              "\tx8640\pvpara\phpg\posx10944\posy0\adjustright " &
              "\fs20\cgrid {\field{\fldrslt {\cs17\f2\lang1024 1}}}" &
              "{\cs17\f2 " & cr>
<CFSET header=header & "\par }\pard \s15\ri360\widctlpar\tqc\tx4320" &
              "\tqr\tx8640\adjustright {\f2 " & head[1] & cr>
<CFSET header=header & "\par }\pard \s15\widctlpar\tqc\tx4320\tqr" &
              "\tx8640\adjustright {\f2 " & head[2] & cr>
<!--- Append the head variable --->
<CFIF numhead GREATER THAN 0>
  <CFLOOP INDEX="loop" FROM="3" TO="#numhead#">
    <CFIF loop GREATER THAN 1>
      <CFSET header=header & "\par ">
    </CFIF>
    <CFSET header=header & head[loop]>
  </CFLOOP>
</CFIF>

<!--- Generate the footer part of Rich Text Document --->
<CFSET footer="\par }}{\footer \pard\plain \s16\widctlpar\tqc" &
              "\tx4320\tqr\tx8640\adjustright \fs20\cgrid {\f2 " & cr>
<CFIF footer GREATER THAN 0>
  <CFLOOP INDEX="loop" FROM="1" TO="#numfoot#">
    <CFIF loop GREATER THAN 1>
      <CFSET header=header & "\par ">
    </CFIF>
    <CFSET footer=footer & foot[loop]>
  </CFLOOP>
</CFIF>

<!---
The physical path and file name on the server for the report being
generated.
NOTE: Make sure that you use some unique filename (ie: user id) so
that if two users generate the same report at the same time, it
doesn't trip over each other.
--->
<CFSET file="Example.rtf">
<CFSET file_name=cf_report & file>

<!--- Delete this report if it already exists, prevents this report from simply being appended onto the last report --->
<CFDIRECTORY ACTION="LIST"
             DIRECTORY="#cf_report#"
             NAME="dir">
<CFLOOP QUERY="dir">
  <CFIF dir.name[CurrentRow] EQUAL file>
    <CFFILE ACTION="DELETE" FILE="#file_name#">
    <CFBREAK>
  </CFIF>
</CFLOOP>

<!--- Generate the start of the actual report --->
<CFSET report=header & footer>

<CFSET report=report & "\par }}{\*\pnseclvl1\pnucrm\pnstart1\" &
              "pnindent720\pnhang{\pntxta .}}{\*\pnseclvl2\pnucltr" &
              "\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl3" &
              "\pndec\pnstart1\pnindent720\pnhang{\pntxta .}}" &
              "{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang" &
              "{\pntxta )}}" & cr>
<CFSET report=report & "{\*\pnseclvl5\pndec\pnstart1\pnindent720" &
              "\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl6" &
              "\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}" &
              "{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720" &
              "\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl8" & cr>
<CFSET report=report & "\pnlcltr\pnstart1\pnindent720\pnhang" &
              "{\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1" &
              "\pnindent720\pnhang{\pntxtb (}{\pntxta )}}\pard" &
              "\plain \widctlpar\adjustright \fs20\cgrid {\f2 ">

<!--- Insert your report code here --->

<CFLOOP index="loop1" from="1" to="10">
  <CFLOOP index="loop2" from="1" to="20">
    <CFSET report=report & "#loop2# ">
  </CFLOOP>
  <CFSET report=report & cr & new_line>

  <!---
  Write the generated report to the file if report generated so far
  is in excess of our defined size to keep the memory allocated
  to this task to a reasonable amount.
  --->
  <CFIF Len(report) GREATER THAN report_size>
    <CFSET ck=0>
    <CFSET data=0>
    <CFLOOP CONDITION="ck equal 0">
      <CFSET data=data+1>
      <CFSET out=GetToken(report,data,cr)>
      <CFIF out EQUAL "">
        <CFSET ck=1>
      <CFELSE>
        <CFFILE ACTION="Append" 
                FILE="#file_name#"
                OUTPUT="#out#">
     </CFIF>
    </CFLOOP>
    <CFSET report="">
  </CFIF>

</CFLOOP>



<!--- Generate Section Two --->


<!--- Redefine the header and don't generate a footer.  Note that the header is flushed with the top of the page
      and the report won't display until after the margin so you may need to padd the header as I have done here. --->
<CFSET head[1]="Section Two Header">
<CFSET head[2]="">
<CFSET head[3]="">
<CFSET head[4]="">
<CFSET head[5]="\b Line      Random Number\b0">
<CFSET head[6]="\b ====      =============\b0">
<CFSET numhead=6>
<CFSET numfoot=0>

<CFSET report=report & new_header>
<CFLOOP INDEX="loop" FROM="1" TO="#numhead#">
  <CFIF loop GREATER THAN 1>
    <CFSET report=report & "\par ">
  </CFIF>
  <CFSET report=report & head[loop] & cr>
</CFLOOP>

<CFSET report=report & new_footer>
<CFLOOP INDEX="loop" FROM="1" TO="#numfoot#">
  <CFIF loop GREATER THAN 1>
    <CFSET report=report & "\par ">
  </CFIF>
  <CFSET report=report & foot[loop] & cr>
</CFLOOP>

<CFSET report=report & new_body>

<!--- Insert your report code here for section 2 --->

<CFLOOP index="loop" from="1" to="60">
  <CFIF loop GREATER THAN 1>
    <CFSET report=report & new_line>
  </CFIF>
  <CFSET report=report & "#RJustify(loop,4)#      #RJustify(RandRange(1,99999999),13)#" & cr>

  <!---
  Write the generated report to the file if report generated so far
  is in excess of our defined size to keep the memory allocated
  to this task to a reasonable amount.
  --->
  <CFIF Len(report) GREATER THAN report_size>
    <CFSET ck=0>
    <CFSET data=0>
    <CFLOOP CONDITION="ck equal 0">
      <CFSET data=data+1>
      <CFSET out=GetToken(report,data,cr)>
      <CFIF out EQUAL "">
        <CFSET ck=1>
      <CFELSE>
        <CFFILE ACTION="Append" 
                FILE="#file_name#"
                OUTPUT="#out#">
     </CFIF>
    </CFLOOP>
    <CFSET report="">
  </CFIF>

</CFLOOP>


<!--- Generate Section Three --->



<CFSET head[1]="Section Three">
<CFSET head[2]="">
<CFSET numhead=2>
<CFSET foot[1]="">
<CFSET foot[2]="\i WooHoo!\i0">
<CFSET numfoot=2>

<CFSET report=report & new_header>
<CFLOOP INDEX="loop" FROM="1" TO="#numhead#">
  <CFIF loop GREATER THAN 1>
    <CFSET report=report & "\par ">
  </CFIF>
  <CFSET report=report & head[loop] & cr>
</CFLOOP>

<CFSET report=report & new_footer>
<CFLOOP INDEX="loop" FROM="1" TO="#numfoot#">
  <CFIF loop GREATER THAN 1>
    <CFSET report=report & "\par ">
  </CFIF>
  <CFSET report=report & foot[loop] & cr>
</CFLOOP>

<CFSET report=report & new_body>

<!--- Insert your report code here for section 3 --->

<CFLOOP index="loop" from="0" to="25">
  <CFIF loop GREATER THAN 0>
    <CFSET report=report & cr & new_line>
  </CFIF>
  <CFLOOP index="alpha" from="65" to="90">
    <CFSET char=alpha + loop>
    <CFIF char GREATER THAN 90>
      <CFSET char=char - 26>
    </CFIF>
    <CFSET report=report & Chr(char)>
  </CFLOOP>

  <!---
  Write the generated report to the file if report generated so far
  is in excess of our defined size to keep the memory allocated
  to this task to a reasonable amount.
  --->
  <CFIF Len(report) GREATER THAN report_size>
    <CFSET ck=0>
    <CFSET data=0>
    <CFLOOP CONDITION="ck equal 0">
      <CFSET data=data+1>
      <CFSET out=GetToken(report,data,cr)>
      <CFIF out EQUAL "">
        <CFSET ck=1>
      <CFELSE>
        <CFFILE ACTION="Append" 
                FILE="#file_name#"
                OUTPUT="#out#">
     </CFIF>
    </CFLOOP>
    <CFSET report="">
  </CFIF>

</CFLOOP>


<!--- END OF REPORT --->

<CFSET report=report & eof>

<!--- Write the rest of the generated report to the file --->
<CFSET ck=0>
<CFSET loop=0>
<CFLOOP CONDITION="ck equal 0">
  <CFSET loop=loop+1>
  <CFSET out=GetToken(report,loop,cr)>
  <CFIF out EQUAL "">
    <CFSET ck=1>
  <CFELSE>
    <CFFILE ACTION="Append" 
            FILE="#file_name#"
            OUTPUT="#out#">
  </CFIF>
</CFLOOP>


<CFOUTPUT>
<a href="#CF_VIEW##file#">View report</a>
</CFOUTPUT>


Return to the Cold Fusion Tips-N-Tricks topic list