GetTextDimensions() For Finding ColdFusion Image Text Dimensions

Posted February 11, 2008 at 7:13 AM by Ben Nadel

Tags: ColdFusion

I have added a user defined function to the imageUtils.cfc ColdFusion image manipulation component. This function, GetTextDimensions(), takes a string and a font-properties struct and determines the height and width dimensions of the rendered text in a ColdFusion image. This can be used to more precisely layout text elements of a generated ColdFusion image. In fact, this functionality is what is powering my ImageDrawTextarea() and the imageUtils.cfc DrawTextarea() functions.

  • <cffunction
  • name="GetTextDimensions"
  • access="public"
  • returntype="struct"
  • output="false"
  • hint="Give the string and the font properties, the width and height of the text is calculated. If the font properties struct is missing any values, ColdFusion's default values will be used.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Text"
  • type="string"
  • required="true"
  • hint="The text whose dimensions we are going to calculate."
  • />
  •  
  • <cfargument
  • name="FontProperties"
  • type="struct"
  • required="true"
  • hint="The font properties used to calculate the text dimensions."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!---
  • Create an canvas to work with. We won't actually need
  • to draw on this, we just need it to get access to some
  • of the default values that ColdFusion uses plus we can
  • get access to some of the utility methods.
  • --->
  • <cfset LOCAL.Image = ImageNew( "", 1, 1, "rgb" ) />
  •  
  • <!---
  • From our temporary ColdFusion image, get the underlying
  • Java AWT object. This will allow us access to properties
  • that we will need to space and style the font.
  • --->
  • <cfset LOCAL.Graphics = ImageGetBufferedImage( LOCAL.Image )
  • .GetGraphics()
  • />
  •  
  • <!---
  • Get the font that is currently set in the image. From
  • this, we will be able to default the properties of our
  • text attributes (any that were not set explicitly).
  • --->
  • <cfset LOCAL.CurrentFont = LOCAL.Graphics.GetFont() />
  •  
  •  
  • <!---
  • Now, we are going to check to see if the passed in
  • font properties has all the properties that we need to
  • properly render the new font. If it does not, then we
  • are going to use the ColdFusion default values that are
  • supplied with our temporary canvas.
  • --->
  •  
  • <!--- Check for a defined size.--->
  • <cfif NOT StructKeyExists( ARGUMENTS.FontProperties, "Size" )>
  •  
  • <!--- Get size from current font. --->
  • <cfset ARGUMENTS.FontProperties.Size = LOCAL.CurrentFont.GetSize() />
  •  
  • </cfif>
  •  
  •  
  • <!--- Check for a defined font. --->
  • <cfif NOT StructKeyExists( ARGUMENTS.FontProperties, "Font" )>
  •  
  • <!--- Get font name from current font. --->
  • <cfset ARGUMENTS.FontProperties.Font = LOCAL.CurrentFont.GetFontName() />
  •  
  • </cfif>
  •  
  •  
  • <!--- Check for a defined style. --->
  • <cfif NOT StructKeyExists( ARGUMENTS.FontProperties, "Style" )>
  •  
  • <!---
  • When it comes to defaulting the style, we need to
  • build not only the font attributes, but also the
  • font style argument for creating our new font (for
  • the Font Metrics). Because of that, we will be
  • building a bit-mask for the style.
  •  
  • Because the Styles are just constants, we can pull
  • them out of our Current Font object.
  • --->
  • <cfif (
  • LOCAL.CurrentFont.IsBold() AND
  • LOCAL.CurrentFont.IsItalic()
  • )>
  •  
  • <!--- Set the style. --->
  • <cfset ARGUMENTS.FontProperties.Style = "bolditalic" />
  •  
  • <!--- Set the bit mask. --->
  • <cfset LOCAL.FontStyleMask = BitOR(
  • LOCAL.CurrentFont.BOLD,
  • LOCAL.CurrentFont.ITALIC
  • ) />
  •  
  • <cfelseif LOCAL.CurrentFont.IsBold()>
  •  
  • <!--- Set the style. --->
  • <cfset ARGUMENTS.FontProperties.Style = "bold" />
  •  
  • <!--- Set the bit mask. --->
  • <cfset LOCAL.FontStyleMask = LOCAL.CurrentFont.BOLD />
  •  
  • <cfelseif LOCAL.CurrentFont.IsItalic()>
  •  
  • <!--- Set the style. --->
  • <cfset ARGUMENTS.FontProperties.Style = "italic" />
  •  
  • <!--- Set the bit mask. --->
  • <cfset LOCAL.FontStyleMask = LOCAL.CurrentFont.ITALIC />
  •  
  • <cfelse>
  •  
  • <!--- Set the style. --->
  • <cfset ARGUMENTS.FontProperties.Style = "plain" />
  •  
  • <!--- Set the bit mask. --->
  • <cfset LOCAL.FontStyleMask = LOCAL.CurrentFont.PLAIN />
  •  
  • </cfif>
  •  
  • <cfelse>
  •  
  • <!--- Set the plain font mask. --->
  • <cfset LOCAL.FontStyleMask = LOCAL.CurrentFont.PLAIN />
  •  
  • </cfif>
  •  
  •  
  • <!---
  • ASSERT: At this point, we have fully set up our font
  • properties struct (filling in any gaps that the user
  • missed), as well as create a style bit-mask based on
  • those properties. We now have enough information to
  • create a new Java Font object.
  • --->
  •  
  •  
  • <!---
  • Now that we have our Font attributes all paramed, we
  • need to create a new Font object (that will be used to
  • get the Font Metrics for the passed-in text).
  • --->
  • <cfset LOCAL.NewFont = CreateObject(
  • "java",
  • "java.awt.Font"
  • ).Init(
  • JavaCast( "string", ARGUMENTS.FontProperties.Font ),
  • JavaCast( "int", LOCAL.FontStyleMask ),
  • JavaCast( "int", ARGUMENTS.FontProperties.Size )
  • )
  • />
  •  
  • <!---
  • Now that we have our new Font set up, get the
  • Font Metrics for our graphic in the context of
  • the new Font.
  • --->
  • <cfset LOCAL.FontMetrics = LOCAL.Graphics.GetFontMetrics(
  • LOCAL.NewFont
  • ) />
  •  
  • <!---
  • Using the Font Metrics, get the bounds of the
  • current text line with the addition of the next
  • word.
  • --->
  • <cfset LOCAL.TextBounds = LOCAL.FontMetrics.GetStringBounds(
  • JavaCast( "string", ARGUMENTS.Text ),
  • LOCAL.Graphics
  • ) />
  •  
  •  
  • <!--- Now that we have the font metrics, let's create the dimensions return value. --->
  • <cfset LOCAL.Return = {
  • Width = Ceiling( LOCAL.TextBounds.GetWidth() ),
  • Height = Ceiling( LOCAL.TextBounds.GetHeight() )
  • } />
  •  
  • <!--- Return text dimensions. --->
  • <cfreturn LOCAL.Return />
  • </cffunction>

It returns a structure that has Width and Height keys. Here, you can see it in action:

  • <!--- Set text string. --->
  • <cfset strText = "Hey there, hot momma!" />
  •  
  • <!--- Set font properties. --->
  • <cfset objProperties = {
  • Font = "Times New Roman",
  • Size = "18"
  • } />
  •  
  • <!--- Get text dimensions. --->
  • <cfset objDimensions = GetTextDimensions(
  • strText,
  • objProperties
  • ) />
  •  
  • <!--- Dump out dimensions. --->
  • <cfdump
  • var="#objDimensions#"
  • label="Dimensions for: #strText#"
  • />

Running the above code, we get the following CFDump output:


 
 
 

 
GetTextDimensions() Return Value - Returns Height And Width Of Rendered Text In A ColdFusion Image  
 
 
 



Reader Comments

Dec 4, 2008 at 10:55 AM // reply »
1 Comments

As always, your posts are immensely helpful. Thank you for sharing your hard work.

There is a bug in the bit where it sets the LOCAL.FontStyleMask value if the user provided a Style. The FontStyleMask is always getting set to PLAIN, regardless of the Style the user passes in. Instead of setting the FontStyleMask inside the block where ' NOT StructKeyExists("Style") ', you can move the bit mask parts out into a separate block following that and base it off of the now corrected ARGUMENTS.FontProperties.Style.


Dec 4, 2008 at 10:57 AM // reply »
10,640 Comments

@Jim,

Thank you for your debugging. I will definitely look into that.


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 »