Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with:

Ask Ben: Printing Shipping Labels With ColdFusion

By Ben Nadel on

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.



Reader Comments

1.) Download CF8 and enable developer mode.
2.) Go buy a printer.
3.) Write up tutorial and post it.
4.) ????
5.) Profit!!1!!

Reply to this Comment

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

Reply to this Comment

@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)?

Reply to this Comment

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.

Reply to this Comment

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!

Reply to this Comment

@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.

Reply to this Comment

If you are just outputting formatted html, how about using cfdocument to generate a PDF? This is available on >= CF7 and a lot easier.

Reply to this Comment

@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.

Reply to this Comment

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>

Reply to this Comment

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!

Reply to this Comment

I tried this example but apparently coldfusion doesn't have a "variable" attribute for the "content" tag.

Reply to this Comment

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.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.