Monday, February 18, 2008

Cool text effects with SVG and Batik

I was reading an older, but very cool entry about Batik and SVG, on Barney Boisvert's blog. He showed how you could can use Batik and ColdFusion to convert SVG content into different formats like png's, jpg's, etcetera. Having never used SVG myself, I was really impressed at how easy it is to convert what is basically text into some impressive graphics.

In my travels I stumbled across a cool text effect that can be achieved with SVG, on the Luxor site. So I decided to post an example of how you could modify it to work with dynamic text. For simplicity I stored the text in a file. Though if you prefer, you could simply build the SVG string using <cfsavecontent>.

The code below produces this image. But you can modify it to use whatever text, color, font-size and dimensions you want. Pretty cool, huh? The beauty of it is that it will work with MX7 too. In case you do not have access to ColdFusion 8's cool new image functions.

I made a few minor modifications but all credit and glory for the code, and the great idea, go to Barney Boisvert and the Luxor site for the example. (I do not know who wrote it). Note, the built in Batik classes in ColdFusion were not sufficient to run the example, so it uses the JavaLoader.cfc to load Batik 1.7. (See Installing Batik 1.7 for instructions).

ColdFusion Code

<!--- read in the file containing the svg text --->
<cffile action="read" file="#ExpandPath('coolText.svg')#" variable="svgXML">

<!--- fill in the dynamic values --->
<cfset svgXML = replaceNoCase(svgXML, "{x}", 20, "all")>
<cfset svgXML = replaceNoCase(svgXML, "{y}", 80, "all")>
<cfset svgXML = replaceNoCase(svgXML, "{height}", 125, "all")>
<cfset svgXML = replaceNoCase(svgXML, "{width}", 680, "all")>
<cfset svgXML = replaceNoCase(svgXML, "{fontSize}", 70, "all")>
<cfset svgXML = replaceNoCase(svgXML, "{backgroundColor}", "gold")>
<cfset svgXML = replaceNoCase(svgXML, "{displayText}", "ColdFusion Rocks!", "all")>

<!--- create an image from the svg text. save it to a file and display it --->
<cfset transcoder = javaLoader.create("org.apache.batik.transcoder.image.PNGTranscoder").init()>
<cfset inputStream = createObject("java", "").init(svgXML)>
<cfset input = javaLoader.create("org.apache.batik.transcoder.TranscoderInput").init(inputStream)>
<cfset outputStream = createObject("java", "").init(ExpandPath("savedImage.png"))>
<cfset output = javaLoader.create("org.apache.batik.transcoder.TranscoderOutput").init(outputStream)>
<cfset transcoder.transcode(input, output)>
<cfset outputStream.close()>
<img src="savedImage.png">

SVG Code

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "">
<svg width="{width}" height="{height}">
<filter id="dropShadow" filterUnits="objectBoundingBox" width="1.4" height="1.4">
<feGaussianBlur in="SourceAlpha" stdDeviation="4" />
<feOffset dx="4" dy="4" />
<feComponentTransfer result="shadow">
<feFuncA type="linear" slope=".5" intercept="0" />

<filter id="emboss" >
<feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/>
<feSpecularLighting in="blur" surfaceScale="-3" style="lighting-color:white"
specularConstant="1" specularExponent="16" result="spec" kernelUnitLength="1" >
<feDistantLight azimuth="45" elevation="45" />
<feComposite in="spec" in2="SourceGraphic" operator="in" result="specOut"/>

<rect x="0" y="0" width="100%" height="100%" style="fill:{backgroundColor}" />
<g style="font-size:{fontSize}; font-weight:bold;">
<text x="{x}" y="{y}" style="filter:url(#dropShadow)">{displayText}</text>
<text x="{x}" y="{y}" style="fill: black;">{displayText}</text>
<text x="{x}" y="{y}" style="filter:url(#emboss)">{displayText}</text>


stelt February 19, 2008 at 7:07 AM  

Looks nice, but i prefer the actual SVG above the PNG.
See loads more at the SVG link resource

cfSearching February 19, 2008 at 8:05 AM  

Yes. I lean towards using PNG's because we cannot always control the environment, and PNG has a broader range of browser support than SVG. Though that may change .. :)

Exist-Dissolve August 25, 2013 at 5:12 AM  

Have you worked with this in CF9 or CF10? When I follow your instructions, I get a very generic Apache error: "client denied by server configuration"

When I copy the Batik files to the CF class path, however, it works fine.

Any ideas? Thanks!

cfSearching August 27, 2013 at 7:27 AM  


No, I have not used it with CF9+. But .. I am not surprised it does not work with only the built in Batik jars. CF8 required adding additional jars to the class path as well (or using the JavaLoader.cfc like I did)

  © Blogger templates The Professional Template by 2008

Header image adapted from atomicjeep