Here's what I came up with but the text appears jagged with
smaller fonts. I think it's because the arc falls in between two
coordinates sometimes, making it impossible to land on the correct
pixel each time. Or maybe there's another reason? Thanks to
cfsearching who came up with the font measurement technique. I
might end up going with some third party tool.
Here it is:
<cfset img = ImageNew("", 400, 400)>
<cfset Text2Write = "FRED SMITH FOR PRESIDENT">
<!--- 1 for upper curve, 0 for lower curve --->
<cfset isUpper = 1>
<cfset textFont = "arial">
<cfset textStyle = "bold">
<cfset textSize = "20">
<cfset ImageSetDrawingColor(img,"WHITE")>
<cfset ImageSetAntialiasing(img,"on")>
<cfset attr = { font="#textFont#", style="#textStyle#",
size="#textSize#" }>
<!--- get the width of the string --->
<cfscript>
Font = CreateObject("java", "java.awt.Font");
text = {string = "#Text2Write#" };
text.prop = { font="#textFont#", style="#textStyle#",
javaStyle= BitOr(Font.BOLD, Font.ITALIC), size="#textSize#" };
arguments.style = "#textStyle#";
switch (arguments.style) {
case "bold":
text.prop.style = font.BOLD;
break;
case "italic":
text.prop.style = font.ITALIC;
break;
case "bolditalic":
text.prop.style = BitOr( Font.BOLD, Font.ITALIC);
break;
case "plain":
text.prop.style = Font.PLAIN;
break;
default:
text.prop.style = Font.PLAIN;
break;
}
graphics = ImageGetBufferedImage( img ).getGraphics();
currentFont = Font.init( javacast("string", text.prop.font),
javacast("int", text.prop.javaStyle), javacast("int",
text.prop.size));
fontMetrics = graphics.getFontMetrics( currentFont );
fontBounds = fontMetrics.getStringBounds( javacast("string",
text.string), graphics );
textString.width = fontBounds.getWidth();
graphics.dispose();
</cfscript>
<cfset radius = 110>
<cfset centerX = 200>
<cfset centerY = 200>
<!--- for some reason 270 is the top of the circle --->
<cfif isUpper is 1>
<cfset angle = 270>
<cfelse>
<cfset angle = 90>
</cfif>
<!--- character spacing --->
<cfset angleIncrement = .54>
<cfif isUpper is 1>
<cfset angle = angle - ((textString.width / 2) *
angleIncrement)>
<cfelse>
<cfset angle = angle + ((textString.width / 2) *
angleIncrement)>
</cfif>
<cfloop from="1" to="#len(Text2Write)#" index="idx">
<cfset singleChar = mid(Text2Write, idx, 1)>
<!--- get the width of the char --->
<cfscript>
Font = CreateObject("java", "java.awt.Font");
text = { x = 50, y = 100, string = "#singleChar#" };
text.prop = { font="#textFont#", style="#textStyle#",
javaStyle= BitOr(Font.BOLD, Font.ITALIC), size="#textSize#" };
arguments.style = "#textStyle#";
switch (arguments.style) {
case "bold":
text.prop.style = font.BOLD;
break;
case "italic":
text.prop.style = font.ITALIC;
break;
case "bolditalic":
text.prop.style = BitOr( Font.BOLD, Font.ITALIC);
break;
case "plain":
text.prop.style = Font.PLAIN;
break;
default:
text.prop.style = Font.PLAIN;
break;
}
graphics = ImageGetBufferedImage( img ).getGraphics();
currentFont = Font.init( javacast("string", text.prop.font),
javacast("int", text.prop.javaStyle), javacast("int",
text.prop.size));
fontMetrics = graphics.getFontMetrics( currentFont );
fontBounds = fontMetrics.getStringBounds( javacast("string",
text.string), graphics );
charItem.width = fontBounds.getWidth();
graphics.dispose();
</cfscript>
<!--- get coordinates for the char --->
<cfset theta = pi() * (angle / 180.0)>
<cfset circleX = round(centerX + radius * cos(theta))>
<cfset circleY = round(centerY + radius * sin(theta))>
<cfif isUpper is 1>
<cfset rotateAngle = angle + 90>
<cfelse>
<cfset rotateAngle = angle - 90>
</cfif>
<!--- rotate the char --->
<cfset ImageRotateDrawingAxis(img, rotateAngle, circlex,
circley)>
<cfset ImageDrawText(img, "#singleChar#", circlex,
circley, attr)>
<!--- reset rotation --->
<cfset ImageRotateDrawingAxis(img, -(rotateAngle),
circlex, circley)>
<cfif isUpper is 1>
<cfset angle = angle + (angleIncrement *
charItem.width)>
<cfelse>
<cfset angle = angle - (angleIncrement *
charItem.width)>
</cfif>
</cfloop>
<cfimage action="writeToBrowser"
source="#img#">