Creating Microsoft Excel Documents With ColdFusion And XML

<!---
	Store the XML Excel data. We are storing it first so that
	we can clean up the data afterwards and then stream it as
	a binary object to the browser.
--->
<cfsavecontent variable="strXmlData">
<cfoutput>
 
	<!---
		Define this document as both an XML doucment and a
		Microsoft Excel document.
	--->
	<?xml version="1.0"?>
	<?mso-application progid="Excel.Sheet"?>
 
	<!---
		This is the Workbook root element. This element
		stores characteristics and properties of the
		workbook, such as the namespaces used in
		SpreadsheetML.
	--->
	<Workbook
		xmlns="urn:schemas-microsoft-com:office:spreadsheet"
		xmlns:o="urn:schemas-microsoft-com:office:office"
		xmlns:x="urn:schemas-microsoft-com:office:excel"
		xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
		xmlns:html="http://www.w3.org/TR/REC-html40">
 
		<!---
			The first child element of the WorkBook element
			is DocumentProperties. Office documents store
			metadata related to the document—for example,
			the author name, company, creation date, and
			more in the DocumentProperties element.
		--->
		<DocumentProperties
			xmlns="urn:schemas-microsoft-com:office:office">
			<Author>Ben Nadel</Author>
			<Company>Kinky Solutions</Company>
		</DocumentProperties>
 
		<!---
			The Styles node represents information related
			to individual styles that can be used to format
			components of the workbook.
		--->
		<Styles>
 
			<!--- Basic format used by all cells. --->
			<Style ss:ID="Default" ss:Name="Normal">
				<Alignment ss:Vertical="Top"/>
				<Borders/>
				<Font/>
				<Interior/>
				<NumberFormat/>
				<Protection/>
			</Style>
 
			<!---
				This is the movie rating style. We are going to
				format the number so that it goes to one
				decimal place.
			--->
			<Style ss:ID="Rating">
				<NumberFormat ss:Format="0.0" />
			</Style>
 
			<!---
				This is the date of the movie viewing. It is
				going to be a short date in the format of
				d-mmm-yyyy (ex. 15-Mar-2007).
			--->
			<Style ss:ID="ShortDate">
				<NumberFormat ss:Format="[ENG][$-409]d\-mmm\-yyyy;@" />
			</Style>
 
			<!---
				This is the boolean format of the has
				fighting and has boobies columns. We are
				going to display these values in Yes /
				No format.
			--->
			<Style ss:ID="YesNo">
				<NumberFormat ss:Format="Yes/No" />
			</Style>
 
		</Styles>
 
 
		<!---
			This defines the first worksheeet and it's name.
			We are only using one worksheet in this example,
			but you could add more Worksheet nodes after
			this one for multiple tabs. The "Name" attribute
			here is the name that shows up in the tab.
		--->
		<Worksheet ss:Name="Movie Data">
 
			<Table
				<!---
					We need a column for each column of the
					query. This attribute is required to be
					correct. If the value here does NOT
					match the data in Excel file, the
					document will not render properly.
				--->
				ss:ExpandedColumnCount="#ListLen( qMovie.ColumnList )#"
 
				<!---
					We need a row for every query record
					plus one for the header row. Again, if
					this value does not match what is in the
					document, the excel file will not
					render properly.
				--->
				ss:ExpandedRowCount="#(qMovie.RecordCount + 1)#"
 
				x:FullColumns="1"
				x:FullRows="1">
 
				<!---
					Here, we can define general properties
					regarding each column in the data output.
				--->
				<Column ss:Index="1" ss:Width="30" />
				<Column ss:Index="2" ss:Width="100" />
				<Column ss:Index="3" ss:Width="42" />
				<Column ss:Index="4" ss:Width="84" />
				<Column ss:Index="5" ss:Width="66" />
				<Column ss:Index="6" ss:Width="70" />
 
				<!---
					This is our header row. All cells in the
					header row will be of type string.
				--->
				<Row>
					<Cell>
						<Data ss:Type="String">ID</Data>
					</Cell>
					<Cell>
						<Data ss:Type="String">Name</Data>
					</Cell>
					<Cell>
						<Data ss:Type="String">Rating</Data>
					</Cell>
					<Cell>
						<Data ss:Type="String">Date Watched</Data>
					</Cell>
					<Cell>
						<Data ss:Type="String">Has Fighting</Data>
					</Cell>
					<Cell>
						<Data ss:Type="String">Has Boobies</Data>
					</Cell>
				</Row>
 
				<!--- Loop over the query. --->
				<cfloop query="qMovie">
 
					<!---
						When we output the excel XML row / cell
						data, we can put the format the tabbing
						/ returning of the Cell and data cells
						in relation to each other; however, we
						cannot freely move around the values
						within the Data cells as it may change
						the ability to convert the data type.
						For instance, we cannot put any white
						space in front of a numeric value or it
						will not be parsed as a number and will
						error out.
					--->
					<Row>
						<Cell>
							<Data ss:Type="Number">#qMovie.id#</Data>
						</Cell>
						<Cell>
							<Data ss:Type="String">#qMovie.name#</Data>
						</Cell>
						<Cell ss:StyleID="Rating">
							<Data ss:Type="Number">#qMovie.rating#</Data>
						</Cell>
						<Cell ss:StyleID="ShortDate">
							<Data ss:Type="DateTime">#DateFormat( qMovie.date_watched, "yyyy-mm-dd" )#T#TimeFormat( qMovie.date_watched, "HH:mm:ss.l" )#</Data>
						</Cell>
						<Cell ss:StyleID="YesNo">
							<Data ss:Type="Number">#qMovie.has_fighting#</Data>
						</Cell>
						<Cell ss:StyleID="YesNo">
							<Data ss:Type="Number">#qMovie.has_boobies#</Data>
						</Cell>
					</Row>
 
				</cfloop>
 
			</Table>
 
		</Worksheet>
 
	</Workbook>
 
</cfoutput>
</cfsavecontent>
 
 
<!---
	Define the way in which the browser should interpret
	the content that we are about to stream.
--->
<cfheader
	name="content-disposition"
	value="attachment; filename=basic.xml"
	/>
 
<!---
	When streaming the Excel XML data, trim the data and
	replace all the inter-tag white space. No need to stream
	any more content than we have to.
--->
<cfcontent
	type="application/msexcel"
	variable="#ToBinary( ToBase64( strXmlData.Trim().ReplaceAll( '>\s+', '>' ).ReplaceAll( '\s+<', '<' ) ) )#"
	/>

For Cut-and-Paste