ColdFusion CFImage Bug - Font Size Data Type Casting
Normally, I am hesitant to call anything in ColdFusion a bug, but since ColdFusion is a dynamically typed language that can cast all simple values to strings, I have to come right out and say that this is a bug. Take a look at the following code:
<!--- Loop over font sizes. --->
<cfloop
index="intFontSize"
from="10"
to="50"
step="10">
<!--- Create ColdFusion image canvas. --->
<cfset objImage = ImageNew( "", 500, 150, "rgb" ) />
<!--- Create font properties. --->
<cfset objFontProperties = {
Font = "Courier New",
Size = intFontSize,
Style = "normal"
} />
<!--- Draw text. --->
<cfset ImageDrawText(
objImage,
"Test Font (#intFontSize#pt)",
10,
(intFontSize + 20),
objFontProperties
) />
<!--- Draw image. --->
<cfimage
action="writetobrowser"
source="#objImage#"
/>
</cfloop>
Notice here that we are just looping over different font sizes so that we can see how they look on the canvas. Pretty simple, right. Well, when you run this code, you get the following ColdFusion error:
java.lang.Double cannot be cast to java.lang.String. Error casting an object of type java.lang.Double cannot be cast to java.lang.String to an incompatible type. This usually indicates a programming error in Java, although it could also mean you have tried to use a foreign object in a different way than it was designed.
This error, while explicit, is not obvious. If you look at the code above, all the string values are text and all the numeric values are, well, numeric. So, how did I fix this error? When setting up the font properties struct, I had to cast the font size to a string:
<!--- Create font properties. --->
<cfset objFontProperties = {
Font = "Courier New",
Size = ToString( intFontSize ),
Style = "normal"
} />
Notice that the Size property value is now wrapped in a ToString() method. This is because the CFLoop tag is creating an index value of type Double and for some reason ColdFusion cannot cast this Double value to a String value which is clearly what the ImageDrawText() function needs. I have to put my foot down and say that this is definitely a bug. Part of the reason that I am so adamant about this being a bug is that I discovered it while writing a ColdFusion user defined function for Image creation. In the UDF, I had an argument for FontSize and the Type attribute of the CFArgument tag was set to "String". The value I was passing in clearly validated against the Type checking of the CFArgument tag but then threw a type casting error when used within the CFFunction to draw image text.
In general, I am going to say that any time ColdFusion cannot cast a simple value to a string, it is a bug.
Want to use code from this post? Check out the license.
Reader Comments
Ben,
I would not be so fast to call it a bug. I may be deadly wrong but for me ColdFusion is doing things this way:
1. You call your UDF and pass a clearly numeric type (as defined directly by the CFLOOP);
2. Your UDF tries to validate it - so, CF converts numeric type to string successfully and goes on.
BUT WAIT - CF didn't convert the VARIABLE - only the value! And for validation purposes. The variable still IS a numeric type, as defined by the loop that created it;
3. You pass the argument received to a CF function, which is JAVA and therefore doesn't convert anything, so you get the error.
Now, I can see how you thought - and I'm not certain if you're wrong - this is a bug. If my UDF needs a STRING, CF should not only test it but rather convert it directly, right?
Well, that's a question beyond my developer skills ;) but as I was trapped in errors like this one so many times, I would like to share the process I went through to find a solution and see if any CF-related developer went down here to enlighten us :)
Best wishes and continues with your really great blogging! I don't miss one of your posts.
Fernando
@Fernando,
I think the whole fact that CFArgument validates a ColdFusion and then doesn't have it work properly in later calls is enough to say it is a bug. I could understand if we were passing this value directly to a Java method and therefore needed to do some sort of JavaCast() call, but the fact is, we are passing the value to a ColdFusion function which should never require a JavaCast() since CF should automatically be able to handle simple data type conversions.
You are the best... I owe you at least 1000 hours of my life!
@Anthony,
No worries - it's my pleasure.