Skip to main content
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Brian Kotek
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Brian Kotek

Ask Ben: Printing Shipping Labels With ColdFusion

By
Published in , Comments (24)

I can't seem the find the question in my Inbox at the moment, but a few days ago, someone asked me about using ColdFusion to print shipping labels. Now, I know that Microsoft gets a bad reputation, but I happen to think that a lot Microsoft applications are quite useful and make my work a whole lot easier. This is one of those occasions. Using ColdFusion in conjunction with Microsoft Word's Web Archive File format (MHT files) makes creating and printing shipping label templates a piece of tasty cake (and we all know that cake is good).

When it comes to printing shipping lables, a lot of the time, you need to create a printable format that matches up with some sort of sticky label paper that you have in your printer. Most likely, there is a Microsoft Word template that accomodates what ever kind of paper you are dealing with. The first thing you want to do is find this Word template, download it, and open it up in Word. It will probably look something like this:

Microsoft Word Shipping Label Template

Now, it's time to leverage the web archive file (MHT) that Microsoft offers. Go to the File menu and save the current Word document as a single web page:

Microsoft Word Save Document As MHT File (Single Webpage)

When doing this, it might tell you that it has to convert certain Microsoft Word features into web-compatible features. Most of the time this should be fine, so just accept whatever they tell you needs to get done.

Now, you should have an MHT file that can be opened in any text editor (such as Macromedia Homesite). In reality, this shipping label MHT file is just an amped up HTML file and we can use ColdFusion to generate it. Once you open the MHT file, you will see that the hardest part is just trying to clean up the Microsoft Word HTML enough to be able to work with it. Once you tab out the HTML into a readable format, you can delete the majority of data and replace it with some sort of CFLoop to output your shipping labels.

Since I don't have a query full of address information, my demo MTH / HTML just uses an indexed CFLoop and outputs the same address 30 times. I am hoping that when you replace this with a ColdFusion query loop, you will see that the transition is quite easy.

Here is my generate_labels.cfm ColdFusion template. This does not serve up the document, it only creates the data:

MIME-Version: 1.0
Content-Location: file:///C:/9E42A1F3/shipping_labels.htm
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset="us-ascii"

<html
	xmlns:o=3D"urn:schemas-microsoft-com:office:office"
	xmlns:w=3D"urn:schemas-microsoft-com:office:word"
	xmlns:dt=3D"uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
	xmlns=3D"http://www.w3.org/TR/REC-html40">

<head>
	<meta http-equiv=3DContent-Type content=3D"text/html; charset=3Dus-ascii">
	<meta name=3DProgId content=3DWord.Document>
	<meta name=3DGenerator content=3D"Microsoft Word 11">
	<meta name=3DOriginator content=3D"Microsoft Word 11">
	<link rel=3DFile-List href=3D"shipping_labels_files/filelist.xml">

	<style>
	@font-face
		{font-family:Tahoma;
		panose-1:2 11 6 4 3 5 4 4 2 4;
		mso-font-charset:0;
		mso-generic-font-family:swiss;
		mso-font-pitch:variable;
		mso-font-signature:1627421319 -2147483648 8 0 66047 0;}
	@font-face
		{font-family:"Monotype Corsiva";
		panose-1:3 1 1 1 1 2 1 1 1 1;
		mso-font-charset:0;
		mso-generic-font-family:script;
		mso-font-pitch:variable;
		mso-font-signature:647 0 0 0 159 0;}
	@font-face
		{font-family:Garamond;
		panose-1:2 2 4 4 3 3 1 1 8 3;
		mso-font-charset:0;
		mso-generic-font-family:roman;
		mso-font-pitch:variable;
		mso-font-signature:647 0 0 0 159 0;}

	p.MsoNormal, li.MsoNormal, div.MsoNormal
		{mso-style-parent:"";
		margin:0in;
		margin-bottom:.0001pt;
		mso-pagination:widow-orphan;
		font-size:10.0pt;
		font-family:"Times New Roman";
		mso-fareast-font-family:"Times New Roman";}
	p.AveryWizard, li.AveryWizard, div.AveryWizard
		{mso-style-name:"Avery Wizard";
		margin:0in;
		margin-bottom:.0001pt;
		line-height:133%;
		mso-pagination:widow-orphan;
		font-size:16.0pt;
		mso-bidi-font-size:10.0pt;
		font-family:"Times New Roman";
		mso-fareast-font-family:"Times New Roman";}
	@page Section1
		{size:8.5in 11.0in;
		margin:.75in 0in 0in 29.25pt;
		mso-header-margin:.5in;
		mso-footer-margin:.5in;
		mso-paper-source:0;}
	div.Section1
		{page:Section1;}

	table.MsoNormalTable
		{mso-style-name:"Table Normal";
		mso-tstyle-rowband-size:0;
		mso-tstyle-colband-size:0;
		mso-style-noshow:yes;
		mso-style-parent:"";
		mso-padding-alt:0in 5.4pt 0in 5.4pt;
		mso-para-margin:0in;
		mso-para-margin-bottom:.0001pt;
		mso-pagination:widow-orphan;
		font-size:10.0pt;
		font-family:"Times New Roman";
		mso-ansi-language:#0400;
		mso-fareast-language:#0400;
		mso-bidi-language:#0400;}
	</style>
</head>
<body lang=3DEN-US style=3D'tab-interval:.5in'>

	<div class=3DSection1>

		<table
			class=3DMsoNormalTable
			border=3D0
			cellspacing=3D0
			cellpadding=3D0
			style=3D'margin-left:.4pt;border-collapse:collapse;mso-padding-alt:0in 0in 0in 0in'>

			<tr style=3D'page-break-inside:avoid;height:93.0pt'>

				<cfoutput>

					<!---
						Instead of looping over a query as we probably
						would want to do, we are going to loop over an
						index array and just repeat the same address
						over and over again; we are just demonstrating
						label printing with ColdFusion and MS Word -
						we don't need real addresses.
					--->
					<cfloop
						index="intI"
						from="1"
						to="30"
						step="1">

						<td
							width=3D336
							valign=3Dtop
							style=3D'width:3.5in;padding:0in 0in 0in 0in; height:93.0pt'>

							<p
								class=3DAveryWizard
								align=3Dcenter
								style=3D'text-align:center;line-height:normal'>

								<span style=3D'font-size:30.0pt;mso-bidi-font-size:10.0pt;font-family:"Monotype Corsiva"'>
									COMPANY NAME
								</span>
								<span style=3D'font-size:18.0pt;mso-bidi-font-size:10.0pt;font-family:"Monotype Corsiva";layout-grid-mode:line'><o:p></o:p></span>
							</p>

							<p
								class=3DAveryWizard
								align=3Dcenter
								style=3D'text-align:center;line-height:normal'>

								<span style=3D'font-size:20.0pt;mso-bidi-font-size:10.0pt;font-family:Garamond;layout-grid-mode:line'>
									STREET 1 ADDRESS
									<o:p></o:p>
								</span>
							</p>

							<p
								class=3DAveryWizard
								align=3Dcenter
								style=3D'text-align:center;line-height:20.0pt'>

								<span style=3D'font-size:20.0pt;mso-bidi-font-size:10.0pt;font-family:Garamond;layout-grid-mode:line'>
									CITY HERE, STATE
									<span style=3D'mso-spacerun:yes'>&nbsp;</span>
									ZIP CODE
								</span>
								<span style=3D'font-size:18.0pt;mso-bidi-font-size:10.0pt;font-family:Tahoma;mso-bidi-font-family:"Times New Roman";layout-grid-mode:line'><o:p></o:p></span>
							</p>

							<p
								class=3DAveryWizard
								align=3Dcenter
								style=3D'text-align:center;line-height:20.0pt'>

								<span style=3D'font-size:18.0pt;mso-bidi-font-size:10.0pt;font-family:Tahoma;mso-bidi-font-family:"Times New Roman";layout-grid-mode:line'><o:p>&nbsp;</o:p></span>
							</p>

						</td>


						<!---
							The Left/Right columns are sepparated by
							a spacer column. Check to see if we need
							to put that in.
						--->
						<cfif ((intI MOD 2) EQ 1)>

							<td
								width=3D66
								style=3D'width:49.55pt;padding:0in 0in 0in 0in;height:93.0pt'>

								<p
									class=3DAveryWizard
									align=3Dcenter
									style=3D'text-align:center;line-height:normal'>

									<span style=3D'font-size:12.0pt;mso-bidi-font-size:10.0pt;layout-grid-mode:line'><o:p>&nbsp;</o:p></span>

								</p>

							</td>

						</cfif>


						<!---
							Check to see if we need to end this row
							and put in the next row (when checking the
							index modulus, be sure to check to see
							if another row is even required).
						--->
						<cfif (
							(NOT (intI MOD 2)) AND
							(intI LT 30)
							)>

							</tr>

							<!--- In between each row is a spacer row. --->
							<tr style=3D'page-break-inside:avoid;height:.25in'>

								<td width=3D336 style=3D'width:3.5in;padding:0in 0in 0in 0in;height:.25in'>
									<p class=3DAveryWizard align=3Dcenter style=3D'text-align:center;line-height:normal'>
										<span style=3D'font-size:12.0pt;mso-bidi-font-size:10.0pt;layout-grid-mode:line'><o:p>&nbsp;</o:p></span>
									</p>
								</td>
								<td width=3D66 style=3D'width:49.55pt;padding:0in 0in 0in 0in;height:.25in'>
									<p class=3DAveryWizard align=3Dcenter style=3D'text-align:center;line-height:normal'>
										<span style=3D'font-size:12.0pt;mso-bidi-font-size:10.0pt;layout-grid-mode:line'><o:p>&nbsp;</o:p></span>
									</p>
								</td>
								<td width=3D336 style=3D'width:3.5in;padding:0in 0in 0in 0in;height:.25in'>
									<p class=3DAveryWizard align=3Dcenter style=3D'text-align:center;line-height:normal'>
										<span style=3D'font-size:12.0pt;mso-bidi-font-size:10.0pt;layout-grid-mode:line'><o:p>&nbsp;</o:p></span>
									</p>
								</td>
							</tr>

							<tr style=3D'page-break-inside:avoid;height:93.0pt'>

						</cfif>

					</cfloop>
					<!--- End address loop. --->

				</cfoutput>

			</tr>
		</table>

		<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center'>
			<span style=3D'font-size:12.0pt;mso-bidi-font-size:10.0pt;layout-grid-mode:line'><o:p>&nbsp;</o:p></span>
		</p>

	</div>

</body>
</html>

Now that we have the data generating ColdFusion template for our shipping labels, all we need is a simple ColdFusion file that grabs that content and serves it up as a Microsoft Word document:

<!--- Create the label data. --->
<cfsavecontent variable="strMHTData">

	<!---
		Include the MHT data. We are doing this as
		a CFSaveContent so that we can have better
		trim and streaming control.
	--->
	<cfinclude template="./generate_labels.cfm" />

</cfsavecontent>


<!---
	Trim the MHT content. Microsoft seems to be
	very picky about leading whitespace.
--->
<cfset strMHTData = Trim( strMHTData ) />


<!--- Set the header information. --->
<cfheader
	name="content-disposition"
	value="attachment; filename=shipping-labels.mht"
	/>

<!--- Set the content to be a Microsoft Word file. --->
<cfcontent
	type="application/msword"
	variable="#ToBinary( ToBase64( strMHTData ) )#"
	/>

Running the above code, we get prompted to open up a Microsoft Word document that looks like this:

Generated Shipping Labels Using ColdFusion And Microsoft Word

Print that and you should be good to go. Now, I hope I don't get flamed for this technique, but if you embrace the feature of Microsoft Word, rather than shunning all the stuff that makes them suck, you will find that they do have a lot of things that just make your life easier.

Want to use code from this post? Check out the license.

Reader Comments

7 Comments

Interesting technique, but I'd definitely recommend using cfreport for this. Custom or standard labels work great with your choice of output type and no reliance on Word.

T

15,841 Comments

@Terry,

Sadly, I have never used CFReport. How does it handle things like having to match up with custom paper layouts? Or is it basically hit and miss configuration (but then once you got it, you got it)?

4 Comments

This is a good tutorial on using the CF Report Builder to create Avery labels:

http://www.adobe.com/devnet/coldfusion/articles/averylabels.html

The tutorial includes pre-created files for a number of common label numbers, and all you have to do is

1) install the CF Report Builder (free, quick install)
2) copy a label file your web directory
3) modify the file to include your query and fields

Very simple.

I haven't yet tried the label builder mentioned by Mark above because there were sample files for the labels I needed, but I'm interested to see if it works for less common labels that don't already have a file built.

33 Comments

I'll second the suggestion others have made, labels are pretty easy to do using the report builder, there are a bunch of pre-built ones, and if they don't fit your needs it's easy to create some with the specifications you want.

Having said that, your solution is still pretty cool and probably accomodates people who for some reason are still running an old version of CF. Good thinking there!

7 Comments

@Ben
Sorry for the delay, but as Thomas stated, it's fairly straight forward to create custom labels. Just get the measurements set up correctly and it works great.
I think that the report builder is a pretty powerful tool that is overlooked by many CF developers. Even though I don't use it a lot I find the tool priceless for labels and so on. The only problem is that the CF7 app was pretty lame. It was about the clunkiest, buggiest app I've seen in a long time. Once you figured out the idiosyncrasies, the final printed output is worth the effort though.
I have not used the CF8 report builder yet, but supposedly they put work into improving it.

3 Comments

@Josh

you did make a very good point. i just finished an overhaul of an in-house label-generating page using cfdocument to generate a pdf. i found the detailed specs for my label of interest on http://www.onlinelabels.com/ and used these to create the css necessary to layout the labels quite precisely.

armed with the detailed specs in inches and the knowledge that the pdfs are 100dpi, it was dead simple to create the pdf.

3 Comments

why certainly. i hope all of this renders correctly.

my notes:
Avery 5366 Label Dimensions & Info
downloaded from http://www.onlinelabels.com/
which was found through a google search http://www.google.com/search?hl=en&q=avery+5366+template&btnG=Search

Label Length: 3.4375"
Label Height: 0.6562"
Sheet Top Margin: 0.5"
Sheet Bottom Margin: 0.5"
Sheet Left Margin: 0.5"
Sheet Right Margin: 0.5"
Label Horizontal Spacing (gutter): 0.547"
Label Vertical Spacing (gutter): 0"
Similar in Layout To**: Avery ® 5066 **, 5366 **, 8366 **
Intended Use: File Labels, Folder Labels

the pdf resolution seems to be 100dpi, so for adjustments based on these measurements, just multiply the measurement in inches time 100.

the code: i had to deidentify some of this so forgive me if i've introduced any syntax errors

<cfset barcode_height = "63px">
<cfset barcode_width = "174px">
<cfset label_width = "142px">
<cfset gutter_width = "50px">
<cfset spacer_height = "0px">
<cfset name = "John Doe">
<cfset id = "WHATEVER">

<cfdocument pagetype="letter" format="pdf" mimetype="application/pdf" margintop=".5" marginright="0" marginbottom="0" marginleft=".5" overwrite="yes">
<cfoutput>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Barcode Labels</title>
<style type="text/css" media="all">
<!--
td.label {
height: #barcode_height#;
width: #label_width#;
}

div.label {
font-family: Arial, Helvetica, sans-serif;
font-size: 14pt;
color: Black;
padding-top: 15px;
padding-left: 3px;
}

td.barcode {
font-family: "BC C39 3 to 1 Narrow";
font-size: 34pt;
height: #barcode_height#;
width: #barcode_width#;
padding-left: 9px;
}

td.gutter {
height: #barcode_height#;
width: #gutter_width#;
}

td.spacer {
height: #spacer_height#;
}

body, div, table, tr, td {
background-color: White;
margin: 0;
padding: 0;
border: 0;
}

td {
empty-cells: show;
}

/* for debugging */
/*
td.label { background-color: ##c0ffc0; }
div.label { background-color: ##80ff80; }
td.barcode { background-color: ##c0c0c0; }
td.gutter { background-color: ##c0c0ff; }
td.spacer { background-color: ##ffffc0; }
table.barcode-container { background-color: ##ffc0c0; }
body.barcode-body { background-color: ##a0a0a0; }
*/
-->
</style>
</head>
<body class="barcode-body">

<table cellspacing="0" class="barcode-container">
<!--- this particular sheet is two labels across and fifteen labels down --->
<cfloop from="1" to="15" index="i">
<cfif i gt 1>
<tr><td colspan="5" class="spacer"></td></tr>
</cfif>
<tr>
<td class="barcode">*#id#*</td>
<td class="label" valign="top"><div class="label">#id#<br />#name#</div></td>
<td class="gutter"></td>
<td class="barcode">*#id#*</td>
<td class="label" valign="top"><div class="label">#id#<br />#name#</div></td>
</tr>
</cfloop>
</table>
</body>
</html>
</cfoutput>
</cfdocument>

1 Comments

First, I'd like to say that this is a very good article.

It's true that sometimes is better to use PDF cfreport solution; however, what if you want to pull some data from your database for your users so they can analyze it, write a brief summary about it.

They would like to make their report in word using the data pulled up from the database. So, I used your technique to do just that.

Thanks, great tutorial!

1 Comments

That seems like a difficult way to create labels, but hey, it elegant and it works. They look ml-1000, or avery 5163 shipping labels.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel