Java Batik

Trying to use Apache Batik with Lucee on Ubuntu 18. I used batik a long time ago in ColdFusion/Windows but can’t seem to get it going in Lucee. I put the latest batik-all-1.12.jar file in /opt/lucee/tomcat/webapps/ROOT/WEB-INF/lucee/lib/ and restarted Lucee but I don’t see it in the list of jars on Lucee admin page. I can load it manually with Batik = createObject(‘java’,‘org.apache.batik.transcoder.image.PNGTranscoder’,’/opt/lucee/lib/batik-all-1.12.jar’).init(); but I get Error: cannot load class through its string name, because no definition for the class with the specified name [org.apache.batik.transcoder.TranscoderInput].

I am trying to convert .svg file to png and batik was awesome in the past but I’m not a java programmer and I don’t know what I’m doing… ANY help would be appreciated.

Code: (funkywebsite.com not real site)

<cfset Batik = createObject(‘java’,‘org.apache.batik.transcoder.image.PNGTranscoder’,’/opt/lucee/lib/batik-all-1.12.jar’).init();>
<cfset svgURI = createObject(“java”, “java.io.File”).init(svgFile).toURL().toString()>
<cfset input = createObject(“java”, “org.apache.batik.transcoder.TranscoderInput”).init(svgURI)>
<cfset ostream = createObject(“java”, “java.io.FileOutputStream”).init(pngFile)>
<cfset output = createObject(“java”, “org.apache.batik.transcoder.TranscoderOutput”).init(ostream)>
<cfset Batik.transcode(input, output)>
<cfset ostream.flush()>
<cfset ostream.close()>

Thanks!

Which version of Lucee?

Lucee 5.3.3.62

Could you try the latest RC, there have been some fixes to the java handling

Will try it now.

Still same error. :frowning:
Cause: cannot load class through its string name, because no definition for the class with the specified name [org.apache.batik.transcoder.TranscoderInput] could be found caused by (java.lang.ClassNotFoundException:org.apache.batik.transcoder.TranscoderInput not found by lucee.core [46];java.lang.ClassNotFoundException:org.apache.batik.transcoder.TranscoderInput;)

I have batik working using the old java loader approach… https://github.com/markmandel/JavaLoader

there were some strange problems with thread context, can’t remember if that was still with ACF or after I switched to Lucee tho… anyway it works with lucee!

<cfcomponent displayName="SVG Converter" output="false">
	<cffunction name="Init" access="public" returntype="any" output="false">
		<!--- Return This reference. --->
		<cfscript>
			variables.Javaloader=getJavaLoader();
        </cfscript>
       	<cfreturn this />
	</cffunction>

	<cffunction name="getJavaLoader" returntype="any" output="false" access="public">
		<cflock name="server.JavaLoader" throwontimeout="true" timeout="60">
			<cfscript>
				if (not StructKeyExists(server,"svgJavaLoader1_11"))
					server.svgJavaLoader1_11 = loadJavaLoader();
	       		return server.svgJavaLoader1_11;
	    	</cfscript>
		</cflock>
	</cffunction>

	<cffunction name="loadJavaLoader" returntype="any" output="false" access="private">
		<cfscript>
			var q_JarFiles="";
			var q_FilePaths="";
			var local={};
			local.paths = [];
		</cfscript>
		<cfdirectory action="list" directory="#GetDirectoryFromPath(GetCurrentTemplatePath())#\batik-1.11"
			name="q_JarFiles" recurse="true" filter="*.jar">
		<cfquery name="q_FilePaths" dbtype="query">
        	SELECT	Directory +'\'+ Name AS FilePath
        	FROM	q_JarFiles
    	</cfquery>
    	<cfloop query="q_FilePaths">
			<cfscript>
				ArrayAppend(local.paths,q_filepaths.filepath);
            </cfscript>
		</cfloop>

		<cfscript>
       		local.poiJavaLoader=createObject("component", "lib.javaloader.JavaLoader").init(loadPaths=local.paths,
       			loadColdFusionClassPath=true);
        </cfscript>
		<cfreturn local.poiJavaLoader>
    </cffunction>

	<CFfunction name="svgToImage" returntype="any" access="public" output="false">
		<cfargument name="svg" type="any" required="true">
		<cfargument name="height" type="numeric" required="true" default="0">
		<cfargument name="width" type="numeric" required="true" default="0">
		<cfscript>
			var local={};
			local.temp_file=GetTempFile(GetTempDirectory(),"svg");
			local.src_svg_file=local.temp_file & ".svg";
			local.output_png=local.temp_file & ".png";
			local.start=getTickCount();
			FileWrite(local.src_svg_file,arguments.svg);
		</cfscript>

		<cfscript>
			local._Thread = createObject("java", "java.lang.Thread");
		    local.currentClassloader = local._Thread.currentThread().getContextClassLoader();
		</cfscript>
		<cftry>
			<!---set the current thread's context class loader as Javaloader's classloader--->
			<cfscript>
				local.file=variables.javaloader.create("java.io.File").init(local.src_svg_file).toURL().toString();
				local.t_input = variables.javaloader.create("org.apache.batik.transcoder.TranscoderInput").init(local.file);

				local.ostream = variables.javaloader.create("java.io.FileOutputStream").init(local.output_png);
				local.t_output = variables.javaloader.create("org.apache.batik.transcoder.TranscoderOutput").init(local.ostream);

				local._Thread.currentThread().setContextClassLoader(variables.javaloader.getURLClassLoader());

				local.PNGTranscoder=variables.javaloader.create("org.apache.batik.transcoder.image.PNGTranscoder").init();

				if(arguments.height gt 0)
					local.PNGTranscoder.addTranscodingHint(local.PNGTranscoder.KEY_HEIGHT, JavaCast("float", arguments.height));
				if(arguments.width gt 0)
					local.PNGTranscoder.addTranscodingHint(local.PNGTranscoder.KEY_WIDTH, JavaCast("float", arguments.width));

				local.PNGTranscoder.addTranscodingHint(local.PNGTranscoder.KEY_MAX_HEIGHT, JavaCast("float", 1200));
				local.PNGTranscoder.addTranscodingHint(local.PNGTranscoder.KEY_MAX_WIDTH, JavaCast("float", 2024));

				local.PNGTranscoder.transcode(local.t_input, local.t_output);
				// set the classloader back to the original one
				local._Thread.currentThread().setContextClassLoader(local.currentClassloader);
			</cfscript>
			<cfcatch type="Any" >
				<!---// set the classloader back to the original one--->
				<cfscript>
					local._Thread.currentThread().setContextClassLoader(local.currentClassloader);
					cleanUpFiles('#local.temp_file#|#local.src_svg_file#|#local.output_png#');
					local.ostream.flush();
					local.ostream.close();
        		</cfscript>
				<cfrethrow>
	       </cfcatch>
		</cftry>
		<cfscript>
			local.ostream.flush();
			local.ostream.close();

			local.time=(getTickCount()-local.start)/1000;

			local.png=ImageRead(local.output_png);
		</cfscript>
		<cflog file="#application.applicationname#" type="information"
			text="SVG file produced in #local.time#s, #decimalFormat(FileInfo(local.output_png).size/1024)#Kb from SVG size #decimalformat(len(arguments.svg)/1024)#Kb">

		<cfif not IsImage(local.png)>
			<cfthrow message="No SVG file Produced">
		</cfif>
		<cfscript>
			cleanUpFiles('#local.temp_file#|#local.src_svg_file#|#local.output_png#');
        </cfscript>

		<cfreturn local.png>
	</CFfunction>

	<CFfunction name="cleanUpFiles" returntype="void" access="public" output="false">
		<cfargument name="files" type="string" required="true">
		<cfscript>
			var local={};
        </cfscript>
		<cfloop list="#arguments.files#" index="local.f" delimiters="|">
			<cfif FileExists(local.f)>
				<cffile action="delete" file="#local.f#">
			</cfif>
		</cfloop>
	</CFfunction>
</CFcomponent>

Wow thank you. I’ll dig in.

1 Like

Question, inside \batik-1.11 what jar file do you use? In my original download from Apache there are a ton but I only put “batik-all-1.12.jar”. Should I put all jar files in /WEB-INF/lucee/lib/ ? Should I create a folder for them?

Thanks again.

I just have the whole distribution extracted in a folder under my own code base, I try and avoid putting anything in the lucee/lib folders… with 1.11 it was 62 files

it makes installing a code base on a server much easier, just checkout the repo… no messing around with other deployment steps required

Makes perfect sense. Thanks!

I usually use JavaLoader too when working with java libraries as it’s more flexible, but you should be able to use CreateObject() directly with the complete batik distro in a folder within your code base as Zac suggests. Just add the path to the distro folder as the third argument:

Batik = CreateObject( "java", "org.apache.batik.transcoder.image.PNGTranscoder", "batik/" ).init();

This assumes a file structure of:

- root/
 - batik/
 - script.cfm

oh, wow, you can pass folders in?

Yes indeed! From the docs:

context: [where object type is “java”] classpath used to load the defined class. This can be a single absolute path, or an array of absolute paths, where paths are jar files or directories containing class files.

So when you say the complete distro I don’t think I understand. Do I unzip the entire download from Apache “batik-bin-1.12.zip” to my directory or just the jar files? Sorry, I really don’t know much about Java.

Thanks for the response!

I would unzip everything to your folder. Lucee will ignore anything it doesn’t need. Just make sure your third argument has the correct folder path to the root of the distro (i.e. the folder containing batik-rasterizer-1.12.jar etc)

Works like a charm. Thank you!

My solution.
I unzipped to batik-bin-1.12.zip to /var/www/my_website/html/java/batik-1.2/

In cfm (pure cfm nothing fancy)

<cfset svgFile = '/var/www/my_website/html/img/temp/coolText.svg'>
<cfset pngFile = '/var/www/my_website/html/img/temp/coolText.png'>

<!--- get svg source from drive --->
<cfset input = createObject("java", "org.apache.batik.transcoder.TranscoderInput", "/var/www/my_website/html/java/batik-1.12/").init(svgFile)>
<!--- set png file and path we want to save --->
<cfset ostream 	= createObject("java", "java.io.FileOutputStream").init(pngFile)>
<cfset output = createObject("java", "org.apache.batik.transcoder.TranscoderOutput", "/var/www/my_website/html/java/batik-1.12/").init(ostream)>

<!--- do the conversion from svg to png --->
<cfset transcode = CreateObject( "java", "org.apache.batik.transcoder.image.PNGTranscoder", "/var/www/my_website/html/java/batik-1.12/" ).transcode(input, output)>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 342.34 72.44">
  <title>coolText</title>
  <text transform="translate(0 55.78)" style="font-size: 66.64205169677734px;fill: #15B14A;font-family: MyriadPro-Regular, Myriad Pro"><tspan style="letter-spacing: -0.005993423856716505em">c</tspan><tspan x="29.46" y="0" style="letter-spacing: -0.000007326924030215777em">ool</tspan><tspan x="118.36" y="0" style="letter-spacing: -0.07099789385279087em">T</tspan><tspan x="146.74" y="0" style="letter-spacing: -0.0029967119283582527em">e</tspan><tspan x="179.93" y="0" style="letter-spacing: 0.012990636305572572em">x</tspan><tspan x="211.65" y="0" style="letter-spacing: -0.002989385004328037em">t</tspan><tspan x="233.51" y="0">.s</tspan><tspan x="273.7" y="0" style="letter-spacing: -0.00999392437721432em">v</tspan><tspan x="305.08" y="0">g</tspan></text>
</svg>
1 Like