ColdFusion Sample - Turning a form input into a PDF for email

This post is more than 2 years old.

This idea came from a discussion today on the cf-newbie mail list. Zelda (real name, or real name in the sig, either way, cool) described a situation where she need to...

  • Process a form
  • Turn the form data into a PDF
  • Email the PDF

This isn't a complex process, but it's an interesting example of where you can combine simple tasks in ColdFusion into something a bit more complex. I thought it would make an excellent entry in my ColdFusion Sample series. What follows is my take on how one could do this. Let's begin by creating a simple form.

<form method="post"> <h2>Order Request / Kuat Drive Yards</h2>

<p> Thank you for your interest in ordering form Kuat Drive Yards. Please fill out the form below and be as complete as possible with your needs. </p>

<cfoutput> <cfif structKeyExists(variables, "errors")> <p> <b>Please correct these errors:<br> #errors# </b> </p> </cfif>

<p> <label for="name">Your Name:</label> <input type="text" name="name" id="name" value="#form.name#"><br/> </p>

<p> <label for="email">Your Email:</label> <input type="text" name="email" id="email" value="#form.email#"><br/> </p>

<p> <label for="orderrequest">Your Request:</label><br/> <textarea name="orderrequest" id="orderrequest">#form.orderrequest#</textarea> </p> </cfoutput>

<p> <input type="submit" name="submit" value="Send Request"> </p> </form>

Nothing too complex here. I've got 3 main form fields and a bit of logic to handle errors. Where that variable comes from, and the form fields themselves, will get to in a minute. With a bit of styling, this is our result.

Ok, now let's actually build in the form processing logic. In this version, I've got everything done except what we want to happen with the form when everything is entered correctly.

<cfparam name="form.name" default=""> <cfparam name="form.email" default=""> <cfparam name="form.orderrequest" default="">

<cfset showForm = true> <cfif structKeyExists(form, "submit")> <cfset errors = ""> <cfset form.name = trim(htmlEditFormat(form.name))> <cfset form.email = trim(htmlEditFormat(form.email))> <cfset form.orderrequest = trim(htmlEditFormat(form.orderrequest))>

&lt;cfif form.name is ""&gt;
	&lt;cfset errors = errors & "Include your name.&lt;br/&gt;"&gt;
&lt;/cfif&gt;
&lt;cfif form.email is "" or not isValid("email", form.email)&gt;
	&lt;cfset errors = errors & "Include your email address.&lt;br/&gt;"&gt;
&lt;/cfif&gt;
&lt;cfif form.orderrequest is ""&gt;
	&lt;cfset errors = errors & "Include your order request.&lt;br/&gt;"&gt;
&lt;/cfif&gt;

&lt;cfif errors is ""&gt;
	&lt;cfset showForm = false&gt;
&lt;/cfif&gt;	

</cfif>

<html>

<head> <title>Order Request / Kuat Drive Yards</title> <style> #orderForm { width: 500px; margin-left: auto; margin-right: auto; margin-top: 10px; background-color: white; padding: 10px; }

input[type='text'] { 
	width: 250px;
	float: right;
}
textarea {
	width: 100%;
	height: 200px;
}

body {
	background-color: #c0c0c0;;	
}

</style> </head>

<body>

<div id="orderForm">

&lt;cfif showForm&gt;
	&lt;form method="post"&gt;
	
		&lt;h2&gt;Order Request / Kuat Drive Yards&lt;/h2&gt;
		
		&lt;p&gt;
			Thank you for your interest in ordering form Kuat Drive Yards. Please fill out the
			form below and be as complete as possible with your needs.
		&lt;/p&gt;
		
		&lt;cfoutput&gt;
		&lt;cfif structKeyExists(variables, "errors")&gt;
			&lt;p&gt;
				&lt;b&gt;Please correct these errors:&lt;br&gt;
				#errors#
				&lt;/b&gt;
			&lt;/p&gt;
		&lt;/cfif&gt;
		
		&lt;p&gt;
		&lt;label for="name"&gt;Your Name:&lt;/label&gt;
		&lt;input type="text" name="name" id="name" value="#form.name#"&gt;&lt;br/&gt;
		&lt;/p&gt;
		
		&lt;p&gt;
		&lt;label for="email"&gt;Your Email:&lt;/label&gt;
		&lt;input type="text" name="email" id="email" value="#form.email#"&gt;&lt;br/&gt;
		&lt;/p&gt;
		
		&lt;p&gt;
		&lt;label for="orderrequest"&gt;Your Request:&lt;/label&gt;&lt;br/&gt;
		&lt;textarea name="orderrequest" id="orderrequest"&gt;#form.orderrequest#&lt;/textarea&gt;
		&lt;/p&gt;
		
		&lt;/cfoutput&gt;
		&lt;p&gt;
		&lt;input type="submit" name="submit" value="Send Request"&gt;
		&lt;/p&gt;
	&lt;/form&gt;
	
&lt;cfelse&gt;
	&lt;h2&gt;Thank you&lt;/h2&gt;
	&lt;p&gt;
	Your order request has been received. Thank you.
	&lt;/p&gt;
&lt;/cfif&gt;

</div>

</body> </html>

Scrolling from the top to the bottom, you can see I've paramed my form fields first. This allows me to always assume they exist and use them in the form right away. Whenever an error occurs and we redisplay the form, this allows for keeping their previous data in the form. If you don't do this, your users will hate you. I'm using a simple variable, showForm, that will keep track of whether or not we need to display the form.

My form processing logic is rather simple. Notice I trim and htmlEditFormat all the fields. Then I simply ensure they are all not blank. Only the email field gets a bit of extra love with the isValid function. Now I should be able to test my form. I can try leaving a few fields blank, hitting submit, and ensuring I get my error messages. The error message should change based on what I did wrong and the form should remember the fields I entered. Nice. Ok, now for the last bits.

First - generating a PDF is incredibly simple. I create my PDF from the form input using the cfdocument tag.

<!--- create a PDF from the request: ---> <cfdocument format="pdf" name="pdfData"> <cfoutput> <h2>Order Request</h2> <p> Order made by #form.name# (#form.email#) on #dateFormat(now(), "mm/dd/yy")# at #timeFormat(now(), "h:mm tt")#. </p> <p> The request was for: </p> <p> #form.orderrequest# </p> </cfoutput> </cfdocument>

While you've probably seen cfdocument before, make note of the name attribute. This tells ColdFusion to store the PDF bits in a variable instead of saving it or writing it out to screen. Now let's send our email:

<cfmail to="raymondcamden@gmail.com" from="#form.email#" subject="Order Request"> <cfmailparam disposition="attachment" file="request.pdf" type="application/pdf" content="#pdfData#" > An order request has been filed. See the attached PDF for details. </cfmail>

Nothing too fancy here. I send the email and attach the document. Make note of the content attribute of the cfmailparam tag. This allows me to attach the PDF and skip saving it to the file system. This is not in the PDF for the CFML 9 reference but is in the online version.

And that's it. If you're curious about the PDF I've attached it to the blog entry. And yes - this is a bit of a silly example. I didn't really need to create a PDF for 3 simple fields, but if your business process requires a PDF to be generated and emailed, hopefully this demonstrates how simple it is in ColdFusion. The full code of the template I used may be found below.

<cfparam name="form.name" default=""> <cfparam name="form.email" default=""> <cfparam name="form.orderrequest" default="">

<cfset showForm = true> <cfif structKeyExists(form, "submit")> <cfset errors = ""> <cfset form.name = trim(htmlEditFormat(form.name))> <cfset form.email = trim(htmlEditFormat(form.email))> <cfset form.orderrequest = trim(htmlEditFormat(form.orderrequest))>

&lt;cfif form.name is ""&gt;
	&lt;cfset errors = errors & "Include your name.&lt;br/&gt;"&gt;
&lt;/cfif&gt;
&lt;cfif form.email is "" or not isValid("email", form.email)&gt;
	&lt;cfset errors = errors & "Include your email address.&lt;br/&gt;"&gt;
&lt;/cfif&gt;
&lt;cfif form.orderrequest is ""&gt;
	&lt;cfset errors = errors & "Include your order request.&lt;br/&gt;"&gt;
&lt;/cfif&gt;

&lt;cfif errors is ""&gt;

	&lt;!--- create a PDF from the request: ---&gt;
	&lt;cfdocument format="pdf" name="pdfData"&gt;
	&lt;cfoutput&gt;
	&lt;h2&gt;Order Request&lt;/h2&gt;
	&lt;p&gt;
	Order made by #form.name# (#form.email#) on #dateFormat(now(), "mm/dd/yy")# at #timeFormat(now(), "h:mm tt")#.
	&lt;/p&gt;
	&lt;p&gt;
	The request was for:
	&lt;/p&gt;
	&lt;p&gt;
	#form.orderrequest#
	&lt;/p&gt;
	&lt;/cfoutput&gt;
	&lt;/cfdocument&gt;

	&lt;cfmail to="raymondcamden@gmail.com" from="#form.email#" subject="Order Request"&gt;
		&lt;cfmailparam disposition="attachment" file="request.pdf" type="application/pdf" content="#pdfData#" &gt;
		An order request has been filed. See the attached PDF for details.
	&lt;/cfmail&gt;
	&lt;cfset showForm = false&gt;
&lt;/cfif&gt;	

</cfif>

<html>

<head> <title>Order Request / Kuat Drive Yards</title> <style> #orderForm { width: 500px; margin-left: auto; margin-right: auto; margin-top: 10px; background-color: white; padding: 10px; }

input[type='text'] { 
	width: 250px;
	float: right;
}
textarea {
	width: 100%;
	height: 200px;
}

body {
	background-color: #c0c0c0;;	
}

</style> </head>

<body>

<div id="orderForm">

&lt;cfif showForm&gt;
	&lt;form method="post"&gt;
	
		&lt;h2&gt;Order Request / Kuat Drive Yards&lt;/h2&gt;
		
		&lt;p&gt;
			Thank you for your interest in ordering form Kuat Drive Yards. Please fill out the
			form below and be as complete as possible with your needs.
		&lt;/p&gt;
		
		&lt;cfoutput&gt;
		&lt;cfif structKeyExists(variables, "errors")&gt;
			&lt;p&gt;
				&lt;b&gt;Please correct these errors:&lt;br&gt;
				#errors#
				&lt;/b&gt;
			&lt;/p&gt;
		&lt;/cfif&gt;
		
		&lt;p&gt;
		&lt;label for="name"&gt;Your Name:&lt;/label&gt;
		&lt;input type="text" name="name" id="name" value="#form.name#"&gt;&lt;br/&gt;
		&lt;/p&gt;
		
		&lt;p&gt;
		&lt;label for="email"&gt;Your Email:&lt;/label&gt;
		&lt;input type="text" name="email" id="email" value="#form.email#"&gt;&lt;br/&gt;
		&lt;/p&gt;
		
		&lt;p&gt;
		&lt;label for="orderrequest"&gt;Your Request:&lt;/label&gt;&lt;br/&gt;
		&lt;textarea name="orderrequest" id="orderrequest"&gt;#form.orderrequest#&lt;/textarea&gt;
		&lt;/p&gt;
		
		&lt;/cfoutput&gt;
		&lt;p&gt;
		&lt;input type="submit" name="submit" value="Send Request"&gt;
		&lt;/p&gt;
	&lt;/form&gt;
	
&lt;cfelse&gt;
	&lt;h2&gt;Thank you&lt;/h2&gt;
	&lt;p&gt;
	Your order request has been received. Thank you.
	&lt;/p&gt;
&lt;/cfif&gt;

</div>

</body> </html>

Download attached file.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Donny posted on 8/12/2011 at 11:48 PM

Thanks . . its been a while since I had to do this . .great refresher!

Comment 2 by Michael G. Workman posted on 8/13/2011 at 12:57 AM

Thats a good example of using a self posting form to generate a pdf document and mail it. One thing I also do is use the name attribute on the submit button, but I differ in that I reference it as FORM.Submit, then you can process the form submit with an if statement like <cfif FORM.Submit eq "Create PDF">, I use this often, and also you can have more than one submit button to process different methods of processing the form.

Comment 3 by Ian Finch posted on 8/13/2011 at 3:07 AM

Another way to do this would be to grab the entire innerHTML of the ORIGINAL form in Javascript and use an Ajax request to handle the PDF creation / emailing. This could work nicely as a generic and reusable function that uses the on-screen formatting as-is. Saves having to re-do the form at the back end. You could perhaps just top and tail it and grab/validate the email address.

Comment 4 by Chris Bowyer posted on 8/13/2011 at 3:08 AM

@ Ray

As usual, you have thrown my well planned day out of schedule - by tickling my never ending thirst for new knowledge about ColdFusion :)

I have never had need to create and add a PDF document as a mail attachment, but due to a conversation with another developer yesterday, in particular about PDF document creation, I was already well primed for this article.

Just a couple of things:

1/. There is no need to use the cfmailparam 'disposition' attribute, with a value of 'attachment' in your example, because it is the default value and works fine without it. Of course, it does no harm using it, but I have a pet hate of using things unnecessarily :)

2/. I was a bit confused by what initially looked like you 'Request' scoping the cfmailparam 'file' attribute value. I eventually realised that 'request.pdf' was just the file name, it would have been clearer if named order_request.pdf. I also discovered that it could be just named order_request without the .pdf, as that is covered by the 'type' attribute value, and that if I put whitespace in the file name i.e. 'order request', ColdFusion would name it with an incrementing (each send), alpha prefixed file number i.e. ATT12345.pdf

Comment 5 by Raymond Camden posted on 8/13/2011 at 5:37 AM

+Chris:
So using filename="x" would create an attachment called x.pdf? You sure? Even if it worked, I just feel better supplying a complete filename. :)

Comment 6 by Raymond Camden posted on 8/13/2011 at 5:39 AM

+Michael G Workman: I did use the name of the form button. What you mean - if I may paraphrase you - is that you use the _value_ of the button to allow for multiple types of actions for the form. That certainly makes sense, but for a form with one action, it's simpler to just check for the existence. :)

Comment 7 by Chris Bowyer posted on 8/13/2011 at 9:15 AM

@ Ray

Re: So using filename="x" would create an attachment called x.pdf? You sure?

Absolutely. Double checked this. Mind you, it seems this is just a case of ColdFusion being lenient because there is binary data for content.

The Adobe documentation is basically correct, but easily misinterpreted...

Attaches a file in a message. Mutually exclusive with name attribute. The file is MIME encoded before sending.

Comment 8 by Raymond Camden posted on 8/13/2011 at 4:55 PM

To be clear, I'm aware it is mutually exclusive with name, and the point of it in general, which is why I used it, but I was not aware that leaving the extension off the file name would force CF to add it. I still say you should just go ahead and supply it to be safe .

Comment 9 by Craig Inman posted on 8/13/2011 at 7:56 PM

Ray, I have a required real estate form that looks like it was created by un-trained monkeys. I am definitely going to create a form to populate the PDF. My plan is to populate the PDF and post the information to a database for use later and in other forms. Can that be done with one web form?

Comment 10 by Raymond Camden posted on 8/13/2011 at 8:29 PM

As far as I know, yes. I've not done form population in a PDF with CF, but it can be done with cfpdfform. You can take the same data and save it to the db too.

Comment 11 by Wilma Warchol posted on 8/13/2011 at 10:28 PM

What happens with the pdf if the email doesn't get sent right away? We use external mail servers (exchange or another hosted option) and if an email can't be sent and gets saved in the undelivr folder we move it back to the spool folder to try again. Will the email refer to a temporary object properly?

On my sites attachments are always saved to the server so that when it gets re-sent the file location is clear. If need be the email then can also have a link to the attachment on the server if need be.

Comment 12 by Raymond Camden posted on 8/13/2011 at 11:35 PM

That's what I'm seeing here on my laptop. The mail was left in the Undeliver folder and the tmp file still exists. I assume not forever though.

Comment 13 by Indy Griffiths posted on 8/20/2011 at 9:32 AM

I've spent hours trying to get this to work, but every time it tries to generate the PDF, the page will timeout. Even a very basic cfdocument will throw this error: 503 Request timed out waiting to execute.

Could this have something to do with me running the Developer Edition of CF?

Comment 14 by Chris Bowyer posted on 8/20/2011 at 9:58 AM

@Indy

The developer edition works fine with this. Other than that, I don't know what is causing your problem

Comment 15 by Raymond Camden posted on 8/20/2011 at 3:58 PM

Try turning off the cfdocument and see if it helps. You will get an error trying to email it obviously, but see if it gets there.

Comment 16 by Indy Griffiths posted on 8/21/2011 at 10:06 AM

A simple cfmail will work perfectly, it's only the cfdocument that fails.

Something as simple as this will cause a timeout
<cfdocument format="pdf" name="tester">
<cfoutput>
Hello #name#
</cfoutput>
</cfdocument>

Comment 17 by Chris Bowyer posted on 8/21/2011 at 10:28 AM

@Indy

Have you tried running cfdocument on its own?

<cfset name = "Indy">

<cfdocument format="pdf" name="tester">
<cfoutput>
Hello #name#
</cfoutput>
</cfdocument>

<cfdump var="#tester#">