Using ColdFusion to Generate a Custom Word Document

An oldie but a goodie, ColdFusion can be used to generate Word documents. Here is a quick example of writing to and opening a Word document.

Create Word Template

Create a Word template by adding “placeholders” in the document. This is done by wrapping a keyword with % (percent) signs as such: %firstname%. Put in as many as you want and add any necessary formatting in Word. Your replacement text will pick up the formatting. Save the file as an RTF (.rtf). For simplicity sake, put it in the same directory as the CF file used to generate it. Here an RTF file called homeworkpass.rtf is used.

Collect the Data

This example uses form fields to fill the document. Data from a database could be used just as easily. It’s a pretty standard form, nothing special.

<form action="index.cfm" method="post">
    <fieldset>
    <legend>Homework Pass Info</legend>
    <p><label for="expirydate">Expiration Date:</label><br />
    <input type="text" id="expirydate" name="expirydate" value="#DateFormat(DateAdd('d',10,Now()),'mm/dd/yy')#" /></p>

    <p><label for="points">Points:</label><br />
    <input type="text" id="points" name="points" value="100" /></p>

    <p><label for="studentname">Student Name:</label><br />
    <input type="text" id="studentname" name="studentname" /></p>

    <p><label for="subject">Subject:</label><br />
    <input type="text" id="subject" name="subject" /></p>

    <p><label for="datereceived">Date Received:</label><br />
    <input type="text" id="datereceived" name="datereceived" value="#DateFormat(Now(),'mm/dd/yy')#" /></p>
    </fieldset>

    <fieldset>
    <legend>Version of ColdFusion:</legend>
    <p><label><input type="radio" id="cfversion" name="cfversion" value="pre8" />CFMX or earlier</label><br />
    <label><input type="radio" id="cfversion" name="cfversion" value="post8" />CF8 or later</label></p>
    </fieldset>

    <p><input type="submit" name="submit" value="Generate Word" /></p>
    </form>

Replace Text with ColdFusion

After a little error checking, the file is opened and the placeholders are swapped out with the form field values using the Replace() function in the homeworkpass.rtf.

<cfset error = "" />

<cfif isDefined("form.submit")>

	<cfloop collection="#form#" item="i">
		<cfif NOT Len(form[i]) OR  (NOT isDefined("form.cfversion"))>
			<cfset error = "All fields must have a value" />
		</cfif>
	</cfloop>

	<cfif NOT Len(error)>

    	<cfset pathToRTF =  GetDirectoryFromPath(GetCurrentTemplatePath()) & "homeworkpass.rtf" />        

        <cflock name="homeworkpass" type="exclusive" timeout="30">

			<cfif form.cfversion EQ "pre8">
            <!--- CFMX7 or earlier --->
                <cffile action="read" file="#pathToRTF#" variable="rtf">
             <cfelse>
            <!--- CF8 or later --->
                <cfset rtf = FileRead(pathToRTF) />
            </cfif>

            <cfset rtf = Replace(rtf,"%expirydate%",form.expirydate) />
            <cfset rtf = Replace(rtf,"%points%",form.points) />
            <cfset rtf = Replace(rtf,"%studentname%",form.studentname) />
            <cfset rtf = Replace(rtf,"%subject%",form.subject) />
            <cfset rtf = Replace(rtf,"%datereceived%",form.datereceived) />

        </cflock>

        <cfheader name="content-disposition" value="filename=HomeworkPass.doc" />

    	<cfcontent type="application/msword"><cfoutput>#rtf#</cfoutput>
    	<cfabort>

	</cfif>

</cfif>

There is a section here to allow for ColdFusion version specification. For ColdFusion 8 the FileRead() function can be used. All other versions of CF will need to use cffile.

<cfif form.cfversion EQ "pre8">
    	<!--- CFMX7 or earlier --->
    		<cffile action="read" file="#pathToRTF#" variable="rtf">
   		 <cfelse>
    	<!--- CF8 or later --->
    		<cfset rtf = FileRead(pathToRTF) />
    	</cfif>

Offering a choice for ColdFusion will not be necessary in production; it’s here for illustrative purposes only. At this time all versions of Word can open .doc files.

Output the Word File

To output the Word file use cfcontent and stop processing the rest of the page.

<cfcontent type="application/msword"><cfoutput>#rtf#</cfoutput>
    	<cfabort>

Download Files (zip)

To create multiple Word documents in a batch-like process see the Using ColdFusion to Generate Multiple Word Documents (Batch Creation) post.

 

Further Reading:

  1. Using ColdFusion to Generate Multiple Word Documents (Batch Creation)

33 Comments

  • March 5, 2010 - 1:41 pm | Permalink

    Convert an RTF or Word doc to PDF with CF? Not that I know of. Neither cfdocument nor cfpdf will do that. This whole method is clunky at best and may not work as Word and CF evolve. Actually, I was kinda surprised it can still be done at all.

    BTW, I LOVE the anti-spam because I no longer spend an hour a day deleting links to porn.

  • March 5, 2010 - 2:15 pm | Permalink

    @Gary

    You may be running into a caching issue. I was able to put variables in on the second page without a problem.

  • March 5, 2010 - 2:20 pm | Permalink

    What I really need I think is to be able to open the docs in WordPad. Can't find any documentation on how to instantiate and use a WordPad object tho.

    Leave the porn! It makes your site more popular. lol

    And I keep guessing wrong anyway.

  • March 5, 2010 - 2:25 pm | Permalink

    @Don

    "Leave the porn! It makes your site more popular. lol"

    That actually crossed my mind…

  • Joe
    June 28, 2010 - 2:07 pm | Permalink

    I bet the person wanting to know how to get variables on page 2 or beyond may be using the same variable on those pages that they used on page 1. Therefore, they simply need the "All" parameter at the end of the replace() function.
    However, I will tell you that there may be some sort of limitation on the number of replacements in a single document because I'm fine up to maybe 100 statements or so and then it just stops replacing. It could be a memory limitation.

  • June 28, 2010 - 4:20 pm | Permalink

    @Joe
    Thanks for the info. It is a kludge at best no matter how it is done. But, it can be done, like you said, with limitations.

  • Joe
    June 29, 2010 - 11:15 am | Permalink

    Just a note for Michael (above). You can also simply write html to a file with a .doc extension and Word will open it and it will have the html formatting. And if you have Word on your server, you can issue a read of the file and then issue a "save as" (via COM — you'll have to good that) with an rtf extension and it will save it. (It creates an instance of Word on the server though, so you wouldn't want this to be high volume.)
    Also, ActivePDF will convert from html to PDF or Word to PDF. http://www.activepdf.com/products/serverproducts/docconverter/overview.cfm

  • September 22, 2010 - 6:33 pm | Permalink

    Jen, is there a way to loop over the contents of the word file? For example, let's say you wanted to generate several mailing labels based on a query output. Would you have to create a single file for each query row, or can you create a single placeholder and loop over it as many times as there are rows in a query? My intention is to generate address labels. The number will always vary, but I would like to output them to the same file.

  • dsam6891
    October 6, 2010 - 4:29 pm | Permalink

    Hi, I am new to CF.. I am trying to generate labels (basic first name and last name) from MS Access. Here is the code I am using :

    <!— Get Names from the database—>
    <cfquery name ="GetNames" datasource = "playdb">
    Select FirstName, LastName
    From Instructors
    </cfquery>

    <!— Location of our RTF template —>
    <cfset pathToRTF = GetDirectoryFromPath(GetCurrentTemplatePath()) & "nametags.rtf" />

    <!— Read RTF template into variable called “RTF” —>
    <cfset rtf = FileRead(pathToRTF) />

    <!— Replace “placeholders” with specific information —>
    <cfset rtf = Replace(rtf, "%First Name%", GetNames.FirstName)>

    <cfset rtf = Replace(rtf, "%Last Name%",GetNames.LastName)>

    <!— Default filename for document —>
    <cfheader name="Content-Disposition" value="filename=WordNametag.doc">

    <!— Set the content-type so Word is invoked —>
    <cfcontent type="application/msword">
    <cfoutput>#rtf#</cfoutput>
    _____________________________________________________

    The code does invoke word (with this error message: 'We cannot Start the converter mswrd632'). After clicking OK twice, a 12 page document pops up with a bunch of 0's and 1's and some formatting code. What am I missing here?

    I would really appreciate any help !

    Thanks

  • Carlos
    October 18, 2010 - 11:15 am | Permalink

    Is there any way to create word documents ala <cfdocument>?

    as in with headers, pagebreak, footers, etc.
    im using CF 9

    thanks for your time.

  • Joe
    October 18, 2010 - 1:26 pm | Permalink

    Carlos,
    I would suggest that you create the document in Word that has the headers/footers, page breaks, etc. and use variables in it like ~CompanyName~, ~FirstName~, ~LastName~, etc. Save it as an rtf.
    Then copy the file to your server. Have Coldfusion read the file and do the replacements (replace()). Then serve the file back to the user via the cfcontent method that Jen describes above. Make sure you use the "all" parameter of the replace statement so that all variables are changed on every page (not just the first instance of the variable).

  • Joe
    October 18, 2010 - 1:40 pm | Permalink

    dsam,

    http://forums.techarena.in/office-update-service/1283466.htm

    Looks like there's not necessarily a solution as you have it because of security patches. So use <cfcontent type="application/rtf"> instead.

  • Joe
    October 18, 2010 - 1:44 pm | Permalink

    Mohammed,

    I would seriously consider using cfreport's label feature instead and make the format="rtf".

  • Comments are closed.