Ask Ben: Getting Line Breaks To Work In A ColdFusion And POI Generated Excel Document

Posted February 1, 2007 at 4:45 PM by Ben Nadel

Tags: ColdFusion, Ask Ben

I am trying to use your export to excel code, and am having a small issue that I can't figure out. One of my columns needs to contain data having multiple lines in it, but I can't seem to get the output to break a newline where I need it to. I have tried "\n", <br>, #chr(10)#, and none give the desired output in the cell. Is it possible to get this type of output?

I did not know how to do this off hand and had to do a little bit of research, so sorry if this is not the most complete / best answer. In short, line breaks only work when "Text Wrapping" is turned on for a given cell. Without the text wrapping, the line break shows up (for me) as a weird ASCII box character.

So, how do you turn on the text wrapping? Through POI's mad-easy API (how awesome is POI?!? I mean really!). Assuming that the variable "LOCAL.Style" is a POI CellStyle object, the following line sends in either a boolean true or false to the API method SetWrapText():

  • // Set the cell to wrap text. This will allow new
  • // lines to show up properly in the text.
  • LOCAL.Style.SetWrapText(
  • JavaCast( "boolean", true )
  • );

Now, this does come with some caveats. The biggest one that you will notice immediately is that when you turn this option on, Excel freaks and doesn't seem to be able auto-width any columns. Each column will be quite narrow and single words will break across multiple lines. To combat this, you can set the default column width on the sheet (or individual column widths) so that it doesn't make a habit of breaking individual words:

  • // Create the sheet in the workbook.
  • LOCAL.Sheet = ARGUMENTS.WorkBook.CreateSheet(
  • JavaCast(
  • "string",
  • "Movies"
  • )
  • );
  •  
  • // Set the sheet's default column width. This will
  • // apply to all columns generated for this column
  • // unless otherwise stated.
  • LOCAL.Sheet.SetDefaultColumnWidth(
  • JavaCast( "int", 23 )
  • );

Here, we are setting the default column width to "23". This makes it so that all columns generated for that sheet will have (unless otherwise overridden) a width of 23. Now, what is 23? Not sure. Points? Certainly not pixels. Certainly not inches. I do know that the 23 here corresponds to the 23 in Excel when you right-click on the column and set the column width.

The problem with this, though, is that each column will have that width, even if that width is far too wide for the column. That's a new problem. You can try to calculate the column based on fractions of a font width (but I am not about to do that yet).

Here is a little demo of building an Excel using ColdFusion and POI so I can show you what it looks like with this compromise:

  • <!--- Create a query from scratch for our list of movies. --->
  • <cfset qMovie = QueryNew(
  • "id, name, date_watched, rating, has_fighting, has_boobies",
  • "CF_SQL_INTEGER, CF_SQL_VARCHAR, CF_SQL_VARCHAR, CF_SQL_DECIMAL, CF_SQL_INTEGER, CF_SQL_INTEGER"
  • ) />
  •  
  • <!--- Add rows to query. --->
  • <cfset QueryAddRow( qMovie, 5 ) />
  •  
  • <!--- Set row data. --->
  • <cfset qMovie[ "id" ][ 1 ] = JavaCast( "int", 1 ) />
  • <cfset qMovie[ "name" ][ 1 ] = JavaCast( "string", "Terminator 2:#Chr( 10 )#Judgment Day" ) />
  • <cfset qMovie[ "date_watched" ][ 1 ] = JavaCast( "string", "2006/05/25" ) />
  • <cfset qMovie[ "rating" ][ 1 ] = JavaCast( "float", 10.0 ) />
  • <cfset qMovie[ "has_fighting" ][ 1 ] = JavaCast( "boolean", true ) />
  • <cfset qMovie[ "has_boobies" ][ 1 ] = JavaCast( "boolean", false ) />
  •  
  • <cfset qMovie[ "id" ][ 2 ] = JavaCast( "int", 2 ) />
  • <cfset qMovie[ "name" ][ 2 ] = JavaCast( "string", "American Pie" ) />
  • <cfset qMovie[ "date_watched" ][ 2 ] = JavaCast( "string", "2005/08/02" ) />
  • <cfset qMovie[ "rating" ][ 2 ] = JavaCast( "float", 9.0 ) />
  • <cfset qMovie[ "has_fighting" ][ 2 ] = JavaCast( "boolean", false ) />
  • <cfset qMovie[ "has_boobies" ][ 2 ] = JavaCast( "boolean", true ) />
  •  
  • <cfset qMovie[ "id" ][ 3 ] = JavaCast( "int", 3 ) />
  • <cfset qMovie[ "name" ][ 3 ] = JavaCast( "string", "Friends With Money" ) />
  • <cfset qMovie[ "date_watched" ][ 3 ] = JavaCast( "string", "2006/06/21" ) />
  • <cfset qMovie[ "rating" ][ 3 ] = JavaCast( "float", 8.0 ) />
  • <cfset qMovie[ "has_fighting" ][ 3 ] = JavaCast( "boolean", false ) />
  • <cfset qMovie[ "has_boobies" ][ 3 ] = JavaCast( "boolean", false ) />
  •  
  • <cfset qMovie[ "id" ][ 4 ] = JavaCast( "int", 4 ) />
  • <cfset qMovie[ "name" ][ 4 ] = JavaCast( "string", "Better Than Chocolate" ) />
  • <cfset qMovie[ "date_watched" ][ 4 ] = JavaCast( "string", "2006/10/07" ) />
  • <cfset qMovie[ "rating" ][ 4 ] = JavaCast( "float", 8.5 ) />
  • <cfset qMovie[ "has_fighting" ][ 4 ] = JavaCast( "boolean", true ) />
  • <cfset qMovie[ "has_boobies" ][ 4 ] = JavaCast( "boolean", true ) />
  •  
  • <cfset qMovie[ "id" ][ 5 ] = JavaCast( "int", 5 ) />
  • <cfset qMovie[ "name" ][ 5 ] = JavaCast( "string", "Real Genius" ) />
  • <cfset qMovie[ "date_watched" ][ 5 ] = JavaCast( "string", "2006/12/12" ) />
  • <cfset qMovie[ "rating" ][ 5 ] = JavaCast( "float", 9.0 ) />
  • <cfset qMovie[ "has_fighting" ][ 5 ] = JavaCast( "boolean", false ) />
  • <cfset qMovie[ "has_boobies" ][ 5 ] = JavaCast( "boolean", false ) />
  •  
  •  
  • <!--- Create a new instance of the POI utility. --->
  • <cfset objPOIUtility = CreateObject(
  • "component",
  • "POIUtility"
  • ).Init()
  • />
  •  
  • <!--- Get the path to our Excel document. --->
  • <cfset strFilePath = ExpandPath( "./multi_line.xls" ) />
  •  
  •  
  • <!--- Create default sheet object. --->
  • <cfset objSheet = objPOIUtility.GetNewSheetStruct() />
  •  
  • <!--- Set sheet query data. --->
  • <cfset objSheet.Query = qMovie />
  •  
  • <!---
  • This is the name that will show up in the first
  • Excel sheet tab.
  • --->
  • <cfset objSheet.SheetName = "Movies" />
  •  
  • <!---
  • This is the list of columns that we want to use. This
  • specifies the columns AND the order in which they
  • should appear.
  • --->
  • <cfset objSheet.ColumnList = "id,name,rating,date_watched,rating,has_fighting,has_boobies" />
  •  
  • <!--- This defines the header row values. --->
  • <cfset objSheet.ColumnNames = "ID,Name,Rating,Date Watched,Rating,Has Fighting,Has Boobies" />
  •  
  •  
  • <!---
  • Write the excel from the query. We are passing in the
  • CSS values for the header, row, and altrow.
  • --->
  • <cfset objSheet = objPOIUtility.WriteExcel(
  • FilePath = strFilePath,
  • Sheets = objSheet,
  • HeaderCSS = "font: italic 16pt verdana ; background: lime ; color: white ; border-bottom: 3px solid green ;",
  • RowCSS = "border-bottom: 1px solid gold ; font-size: 12pt ;",
  • AltRowCSS = "background-color: lemon_chiffon ;"
  • ) />

When we open the ColdFusion and POI generated "multi_line.xls" Excel document it looks like this:


 
 
 

 
ColdFusion POI Excel With Text Wrapping For New Lines  
 
 
 

As you can see, the movie title "Terminator 2: Judgment Day" did indeed wrap to the next line based on the Chr( 10 ) line break, but you can also see that all the columns, even the narrow ones have the same width. Of course, you can then go into the Excel document and re-width the columns to make them pretty:


 
 
 

 
ColdFusion POI Excel Document With Text Wrapping  
 
 
 

This looks pretty nice to me with MINIMAL work. It becomes a judgement call; how well formatted do you need the Excel document? Is a report for delivery? Is it a report for data mining? Hopefully in future editions of the ColdFusion POI Utility component I will have an easy way to deal with highly formatted scenarios. I mean, technically, you can just go straight into the POI API, but that is way too technical for something you want to be able to accomplish generically.

I have added this "Text Wrapping" function to the latest POI Utility ColdFusion component code:

DOWNLOAD LATEST VERSION HERE (POIUtility.cfc.2007.02.01)!

Hope that helps a bit.




Reader Comments

Feb 5, 2007 at 4:45 AM // reply »
9 Comments

Your POI entries have been extremely helpful. I am currently doing the initial research to implement Excel generation and there's not much CF POI info out there. Thanks for sharing your hard work and I look forward to learning from any other features/problems you conquer.


Feb 5, 2007 at 8:05 AM // reply »
10,640 Comments

Daniel,

No problem. I am learning this stuff as I go and it is totally changing the way I can do things at work. I look forward to make this process even better and sharing. Let me know if you run into any problems that I might have the answers to.


Jan 18, 2010 at 4:34 PM // reply »
4 Comments

I have been using the POI with your help for a few months, but I don't understand java. Where are you doing this:
// Create the sheet in the workbook.
LOCAL.Sheet = ARGUMENTS.WorkBook.CreateSheet(
JavaCast(
"string",
"Movies"
)
);

// Set the sheet's default column width. This will
// apply to all columns generated for this column
// unless otherwise stated.
LOCAL.Sheet.SetDefaultColumnWidth(
JavaCast( "int", 23 )
);

I am using using:
<poi:document name="REQUEST.ExcelData"
file="#ExpandPath( '/POI/CPT_Transaction_Doctor_POS_NoOffice.xls' )#" style="font-family: verdana ; font-size: 10pt ; color: black ; white-space: nowrap ;">

and cfheader/cfcontent to play the file.


Jan 24, 2010 at 10:50 PM // reply »
10,640 Comments

@Greg,

I am not sure what your question is.


Jan 25, 2010 at 12:23 PM // reply »
4 Comments

I just don't understand what to do with your code in a coldfusion script.

I am familiar with the poi tags, but the code you posted for creating the sheets and setting the column widths doesn't look like it goes in a CF tag or poi tags. Can you show a little more of the coe how this works in a .cfm?


Jan 25, 2010 at 9:10 PM // reply »
10,640 Comments

@Greg,

Try downloading the latest POI Utils project. It should come with some samples files. I believe (I don't have it in front of me), that the column widths can be set via CSS on the column tag:

<poi:column style="width: 234px" />

But, I don't have it in front of me.


Jan 26, 2010 at 5:56 PM // reply »
4 Comments

Ben, my goal was really to get linebreaks to work like this post said. I think the column widths was just part of that right?


Jan 26, 2010 at 6:21 PM // reply »
10,640 Comments

@Greg,

Are you adding the ASCII character 10 (chr(10)) to the text that you want to have break?


Jan 26, 2010 at 6:24 PM // reply »
4 Comments

I'll have to dig up the code again, but I'm pretty sure I tried chr(10), chr(13) neither seemed to work.


Jan 26, 2010 at 6:32 PM // reply »
10,640 Comments

@Greg,

If you use the POI custom tags, there should be a "white-space" CSS property that you have access to. It should default to "normal" (which allows line breaking), but can be override to be "pre" or "nowrap". This creates an abstraction to the SetWrapText() and column width adjustment in this post.


Dec 10, 2010 at 6:28 PM // reply »
31 Comments

Chr(10) works for me (in summary):

  • <poi:class name="header" style="font-family: arial ; font-size: 8pt ; text-align: left ; "/>
  •  
  • <poi:row class="header">
  •  
  • <cfset local.value = "Impact scale:" & chr(10) & "-1=negative" & chr(10) & "0=neutral"> etc...
  •  
  • <poi:cell value="#local.value#" />
  •  
  • </poi:row>


Post A Comment

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.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »
Feb 9, 2012 at 10:29 PM
Learning ColdFusion 9: Application-Specific Data Sources
@Ben, No offence, but if people were really wanting advanced features they would be using a platform like ASP.NET MVC. CFML is so structurally compromised as a tag-based scripting language that ... read »
Feb 9, 2012 at 10:03 PM
Subversion - Cleanup Failed To Process The Following Paths
@Leviaguirre, do you still have problems with this? ... read »