User:Jiangxin/Improved SVG Extension

Note: this improved svg extension is based on User:Coffman/SVG Extension.

What's improvement

edit
  • Improvement:
    • Add svg url support, you can use the extension like this:
      <svgextension content=url height=100>http://sitename/svgfile</svgextension>
    • Can be installed on Windows + cygwin platform. passed on my own laptop.
    • Add more parameters to batik. like:
      <svgextension width=200>Media:LogoPVB.svg</svgextension>
  • Example

    <svgextension>[[Media:LogoPVB.svg]]</svgextension>
    <svgextension width=200>[[Media:LogoPVB.svg]]</svgextension>
    <svgextension content=url height=100>http://sitename/svgfile</svgextension>

Code

edit

<?php

# CoffMan (http://www.wickle.com) code adapted from timeline extension.
/*
 * Improved version:
 * *add svg url support;
 * *passed on my Windows + cygwin platform;
 * *add more parameters to batik;

 * Example
 * *<svgextension>[[Media:LogoPVB.svg]]</svgextension>
 * *<svgextension width=200>[[Media:LogoPVB.svg]]</svgextension>
 * *<svgextension content=url height=100>http://sitename/svgfile</svgextension>
 */

class SVGSettings {
	var $javaCommand , $batikCommand;
};
$wgSVGSettings = new SVGSettings;
$wgSVGSettings->javaCommand = "C:\\Program Files\\Java\\jdk1.5.0_04\\bin\\java.exe";
$wgSVGSettings->batikCommand = "C:\\Apps\\batik\\batik-rasterizer.jar";

$wgExtensionFunctions[] = "wfSVGExtension";

function wfSVGExtension() {
	global $wgParser;
	$wgParser->setHook( "svgextension", "renderSVG" );
}

/* 
 * params = array
 * (
 *     [content] => url|data, // content between <svgextension> and </svgextension>, is data? or url? 
 *                            // take is easy, I can guess if not provided.
 *     [width] => width,      // param for SVG Rasterizer: Output width. This is a floating point value. 
 *     [height] => height,    // Output height. This is a floating point value. 
 *     [maxw] => maxw,        // param for SVG Rasterizer: Maximum output width. This is a floating point value. 
 *     [maxh] => maxh,        // param for SVG Rasterizer: Maximum output height. This is a floating point value. 
 * )
 */
function renderSVG( $SvgData, $params = array() )
{
	global $wgUploadDirectory, $wgUploadPath, $IP, $wgSVGSettings, $wgArticlePath, $wgTmpDirectory;
	global $wgServer, $wgScriptPath, $wgTitle, $wgUrlProtocols, $wgUser;

	if (isset($params['mime']))
	{
		$batikOpt = "-m " . $params['mime'] ." ";
		if ( strcasecmp($params['mime'], "image/jpeg") == 0 || strcasecmp($params['mime'], "image/jpg") == 0 )
		{
			$file_ext = ".jpeg";
		}
		else if ( strcasecmp($params['mime'], "image/tiff") == 0 )
		{	
			$file_ext = ".tiff";
		}
		else
		{
			$batikOpt = "-m image/png ";
			$file_ext = ".png";
		}
	}
	else
	{
		$batikOpt = "-m image/png ";
		$file_ext = ".png";
	}
	
	$SvgData = trim($SvgData);
	$hash = md5( $SvgData . var_export($params, true) );
	$svg_dir = $wgUploadDirectory."/svg/";
	$dest_png_file = $svg_dir . $hash . $file_ext;
	
	if ( ! is_dir( $svg_dir ) ) { mkdir( $svg_dir, 0777 ); }
	if ( ! is_dir( $wgTmpDirectory ) ) { mkdir( $wgTmpDirectory, 0777 ); }

	// if $dest_png_file already exist, DO NOT convert svg again.
	if ( ! file_exists( $dest_png_file ) || filesize($dest_png_file) === 0 )
	{
		if (!isset($params['content']))
		{
			// test content type, data or url?
			if ( preg_match( '/<svg\s+.*<\/svg>/is', $SvgData ) ) 
			{
				$params['content'] = "data";
			}
			else
			{
				$params['content'] = "url";
			}
		}

		if ( isset($params['width']) )
		{
			$batikOpt .= "-w " . $params['width'] ." ";
		}
		if ( isset($params['height']) )
		{
			$batikOpt .= "-h " . $params['height'] ." ";
		}
		if ( isset($params['maxw']) )
		{
			$batikOpt .= "-maxw " . $params['maxw'] ." ";
		}
		if ( isset($params['maxh']) )
		{
			$batikOpt .= "-maxh " . $params['maxh'] ." ";
		}

		// Parse url from $SvgData
		if(strcasecmp($params['content'], "url") == 0)
		{
			$localParser = new Parser();
			$parserOptions = ParserOptions::newFromUser( $wgUser );

			$html = $localParser->parse($SvgData,$wgTitle,$parserOptions);
			$url = preg_replace('/^.*<a[\s]+href=*"(.*?)".*$/is', '\1' , $html->mText);

			if ( $url === $html->mText )
			{
				return renderSvgError("Wrong url: $url.");
			}
			else if ( !preg_match( '/^(?:' . $wgUrlProtocols . ')/', $url ) )
			{
				if ( strpos($url, '/') === 0 )
				{
					$url = "$wgServer".$url;
				}
				else 
				{
					return renderSvgError("Wrong url: $url.");
				}
			}

			// as my experience, it works in Windows + Cygwin, just like texvc does.
			if ( wfIsWindows() ) {
				$dict_dir_from = array("\\");
				$dict_dir_to   = array("/");

				$cmdline = escapeshellarg( str_replace( $dict_dir_from, $dict_dir_to, $wgSVGSettings->javaCommand)) .
					" -Djava.awt.headless=true -jar " .
					escapeshellarg( str_replace( $dict_dir_from, $dict_dir_to, $wgSVGSettings->batikCommand) ) .
					" $batikOpt " . " -d ".
					escapeshellarg( str_replace( $dict_dir_from, $dict_dir_to, $dest_png_file) ) . " " .
					escapeshellarg( $url );

				$cmdline = 'sh -c ' . wfEscapeShellArg( $cmdline );
			}
			else
			{
				$cmdline = wfEscapeShellArg( $wgSVGSettings->javaCommand) .
					" -Djava.awt.headless=true -jar " .
					wfEscapeShellArg( $wgSVGSettings->batikCommand) . " " .
					" $batikOpt " . " -d ".
					wfEscapeShellArg( $dest_png_file ) . " " .
					wfEscapeShellArg( $url ) ;
			}
		}
		// If $SvgData is real svg data, save it to a tmp file, then convert using batik
		else
		{
			$tmp_svg_file = $svg_dir . $hash;

			$handle = fopen($tmp_svg_file, "w");
			fwrite($handle, $SvgData);
			fclose($handle);

			if ( wfIsWindows() )
			{
				$dict_dir_from = array("\\");
				$dict_dir_to   = array("/");

				$cmdline = escapeshellarg( str_replace( $dict_dir_from, $dict_dir_to, $wgSVGSettings->javaCommand)) .
					" -Djava.awt.headless=true -jar " .
					escapeshellarg( str_replace( $dict_dir_from, $dict_dir_to, $wgSVGSettings->batikCommand) ) . " " .
					" $batikOpt " . " -d ".
					escapeshellarg( str_replace( $dict_dir_from, $dict_dir_to, $dest_png_file) ) . " " .
					escapeshellarg( str_replace( $dict_dir_from, $dict_dir_to, $tmp_svg_file ) );

				$cmdline = 'sh -c ' . wfEscapeShellArg( $cmdline );
			}
			else
			{
				$cmdline = wfEscapeShellArg( $wgSVGSettings->javaCommand) .
					" -Djava.awt.headless=true -jar " .
					wfEscapeShellArg( $wgSVGSettings->batikCommand) . " " .
					" $batikOpt " . " -d ".
					wfEscapeShellArg( $dest_png_file ) . " " .
					wfEscapeShellArg( $tmp_svg_file ) ;
			}
		}

		$ret = `{$cmdline}`;
		$ret = "command line was: {$cmdline},<br>\n<pre>" . $ret . "</pre>\n";
	}

	if (isset($tmp_svg_file))
	{
		unlink($tmp_svg_file);
	}

	if ( ! file_exists( $dest_png_file ) || filesize($dest_png_file) === 0)
	{
		return renderSvgError($ret);
	}

	$txt  = "<img src=\"{$wgUploadPath}/svg/{$hash}{$file_ext}\">";
	return $txt;
}


function renderSvgError( $err="" )
{
	return "<div style='border: solid red 1px'><p>SVG convert failed! </p>\n{$err}</div>\n";
}

?>

--Jiangxin 02:13, 15 November 2005 (UTC)