Doc:Svg2synfig

From Synfig Studio :: Documentation
Revision as of 20:27, 26 January 2008 by Dmd (Talk | contribs) (initial version)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Languages Language: 

English • español • français • italiano • română • русский


Objective

Turn an SVG image into a Synfig file for import.

Prerequisites

  1. Make sure a Java runtime environment is installed.
  2. Get a recent version of the SAXON XSLT processor for Java from http://saxon.sourceforge.net/. Recommended version: Saxon-SA 9.0 (saxonsa9-0-0-2j.zip).
  3. Extract the SAXON package to a folder of your choice. As an example we're going to use d:\saxon in the following. The folder will contain several JAR files.
  4. Create the file d:\saxon\svg2synfig.xsl with the content provided at the bottom of this document (#svg2synfig.xsl).

Optional Prerequites for Windows

If you don't want to use the command line, create a batch file d:\saxon\svg2synfig.bat with this content:

@java -jar %0\..\saxon9.jar -xsl:%0\..\svg2synfig.xsl %1 > %0\..\synfig.sif
@pause

Transforming an SVG into a Synfig File

Windows Batch File

  1. Just drop the SVG file onto svg2synfig.bat.

Command line

  1. Change directory to d:\saxon.
  2. Enter the following command (replace your_input.svg by the path of the SVG file):
java -jar saxon9.jar -xsl:svg2synfig.xsl your_input.svg > synfig.sif

Result

If the conversion has been successful, the result will be written to the file d:\saxon\synfig.sif. You can open this file in Synfig.

Limitations

  • Compressed SVG (svgz) must be uncompressed first.
  • Only SVG path objects are supported. Try converting all objects to paths.
  • Only a subset of path elements is supported. Try to modify all path nodes to have split tangents, and all path segments to be curves.
  • Sophisticated coloring (e. g. gradients) is not supported.
  • Only basic transformations are supported.
  • Fill and outline on the same object is not supported.

svg2synfig.xsl

<xsl:stylesheet version="2.0" exclude-result-prefixes="#all"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
		xmlns:svg="http://www.w3.org/2000/svg"
		xmlns:xs="http://www.w3.org/2001/XMLSchema"
		xmlns:math="http://exslt.org/math">
	<xsl:output method="xml" indent="yes" encoding="UTF-8"/>

	<xsl:template match="/">
		<xsl:apply-templates/>
	</xsl:template>

	<xsl:template match="svg:svg">
		<xsl:variable name="width" select="math:units_to_px(@width)"/>
		<xsl:variable name="height" select="math:units_to_px(@height)"/>
		<xsl:variable name="has_view_box" select="matches(@viewBox, '(\d+\s){3}\d+')"/>
		<canvas version="0.2" id="{@id}"
				width="{if ($has_view_box) then replace(@viewBox, '(\d+)\s(\d+)\s(\d+)\s(\d+)', '$3') else $width}"
				height="{if ($has_view_box) then replace(@viewBox, '(\d+)\s(\d+)\s(\d+)\s(\d+)', '$4') else $height}"
				view-box="{if ($has_view_box) then @viewBox else concat('0 0 ', $width, ' ', $height)}">
			<xsl:apply-templates select="svg:g|svg:svg|svg:path"/>
		</canvas>
	</xsl:template>

	<xsl:template match="svg:g">
		<layer type="PasteCanvas" active="true" version="0.1" desc="{@id}">
			<param name="canvas">
				<canvas>
					<xsl:apply-templates/>
					<xsl:apply-templates select="@transform"/>
				</canvas>
			</param>
		</layer>
	</xsl:template>

	<xsl:template match="svg:path">
		<xsl:variable name="style">
			<xsl:for-each select="ancestor-or-self::*">
				<xsl:sort select="position()" data-type="number" order="descending"/>
				<xsl:value-of select="concat(@style, ';fill:', @fill, ';stroke:', @stroke, ';stroke-width:', @stroke-width, ';')"/>
			</xsl:for-each>
		</xsl:variable>
		<xsl:variable name="self" select="."/>
		<xsl:variable name="is_fill" select="not(matches(replace($style, 'fill:[^n;][^o].*', ''), 'fill:none'))"/>
		<xsl:analyze-string select="@d" regex="m[^z]+(z|$)" flags="i">
			<xsl:matching-substring>
				<layer type="{if ($is_fill) then 'region' else 'outline'}" version="0.1" desc="{$self/@id}">
					<xsl:call-template name="style-to-color">
						<xsl:with-param name="style" select="replace(replace($style, ':none.*', ''), if ($is_fill) then '.*fill:([^;]+).*' else '.*stroke:([^;]+).*', '$1')"/>
					</xsl:call-template>
					<xsl:if test="not ($is_fill)">
						<xsl:call-template name="style-to-width">
							<xsl:with-param name="style" select="replace($style, '.*stroke-width:([^;]+).*', '$1')"/>
						</xsl:call-template>
					</xsl:if>
					<param name="bline">
						<bline type="bline_point" loop="{matches(., 'z', 'i')}">
							<xsl:call-template name="path-to-bline">
								<xsl:with-param name="path" select="."/>
								<xsl:with-param name="node" select="$self" tunnel="yes"/>
							</xsl:call-template>
						</bline>
					</param>
				</layer>
			</xsl:matching-substring>
		</xsl:analyze-string>
	</xsl:template>

	<xsl:template match="@transform">
		<xsl:analyze-string select="." regex="translate[(]([-\d.]+).([-\d.]*)">
			<xsl:matching-substring>
				<layer type="translate" active="true" version="0.1">
					<param name="origin">
						<vector>
							<x><xsl:value-of select="xs:float(regex-group(1))"/></x>
							<y><xsl:value-of select="xs:float(regex-group(2))"/></y>
						</vector>
					</param>
				</layer>
			</xsl:matching-substring>
		</xsl:analyze-string>
	</xsl:template>

	<xsl:template name="path-to-bline">
		<xsl:param name="path"/>
		<xsl:variable name="stripped" select="replace(replace(translate($path, ',', ' '), '(\d)-', '$1 -'), '\s*([a-z]+)\s*', '$1', 'i')"/>
		<xsl:variable name="closed" select="if (matches($stripped, 'z', 'i')) then $stripped else replace($stripped, 'm([-\d.]+\s[-\d.]+).*$', '$0l$1z', 'i')"/>
		<xsl:variable name="tmp" select="replace($closed, '([-\d.]+\s[-\d.]+)l([-\d.]+\s[-\d.]+)', '$1c$1 $2 $2', 'i')"/>
		<xsl:variable name="curve" select="replace($tmp, '([-\d.]+\s[-\d.]+)l([-\d.]+\s[-\d.]+)', '$1c$1 $2 $2', 'i')"/>
		<xsl:analyze-string select="$curve" regex="\s([-\d.]+\s[-\d.]+)\s[-\d.]+\s[-\d.]+z" flags="i">
			<xsl:matching-substring>
				<xsl:analyze-string select="concat(regex-group(1), $curve)" regex="([-\d.]+)\s([-\d.]+)[m\s]([-\d.]+)\s([-\d.]+)c([-\d.]+)\s([-\d.]+)" flags="i">
					<xsl:matching-substring>
						<xsl:call-template name="node-to-bline-point">
							<xsl:with-param name="c1_x" select="regex-group(1)"/>
							<xsl:with-param name="c1_y" select="regex-group(2)"/>
							<xsl:with-param name="x" select="regex-group(3)"/>
							<xsl:with-param name="y" select="regex-group(4)"/>
							<xsl:with-param name="c2_x" select="regex-group(5)"/>
							<xsl:with-param name="c2_y" select="regex-group(6)"/>
						</xsl:call-template>
					</xsl:matching-substring>
				</xsl:analyze-string>
			</xsl:matching-substring>
		</xsl:analyze-string>
	</xsl:template>

	<xsl:template name="node-to-bline-point">
		<xsl:param name="x"/>
		<xsl:param name="y"/>
		<xsl:param name="c1_x"/>
		<xsl:param name="c1_y"/>
		<xsl:param name="c2_x"/>
		<xsl:param name="c2_y"/>
		<xsl:param name="node" tunnel="yes"/>
		<xsl:variable name="transform">
			<xsl:for-each select="$node/ancestor-or-self::*/@transform">
				<xsl:value-of select="."/>
			</xsl:for-each>
		</xsl:variable>
		<xsl:variable name="t_a" select="if (matches($transform, 'matrix')) then xs:float(replace($transform, '.*matrix\(([^,]+,){0}([^,]+)[,)].*', '$2')) else 1"/>
		<xsl:variable name="t_d" select="if (matches($transform, 'matrix')) then xs:float(replace($transform, '.*matrix\(([^,]+,){3}([^,]+)[,)].*', '$2')) else 1"/>
		<xsl:variable name="t_e" select="if (matches($transform, 'matrix')) then xs:float(replace($transform, '.*matrix\(([^,]+,){4}([^,]+)[,)].*', '$2')) else 0"/>
		<xsl:variable name="t_f" select="if (matches($transform, 'matrix')) then xs:float(replace($transform, '.*matrix\(([^,]+,){5}([^,]+)[,)].*', '$2')) else 0"/>
		<xsl:variable name="transformed_x" select="$t_e + $t_a * xs:float($x)"/>
		<xsl:variable name="transformed_y" select="$t_f + $t_d * xs:float($y)"/>
		<xsl:variable name="transformed_c1_x" select="$t_e + $t_a * xs:float($c1_x)"/>
		<xsl:variable name="transformed_c1_y" select="$t_f + $t_d * xs:float($c1_y)"/>
		<xsl:variable name="transformed_c2_x" select="$t_e + $t_a * xs:float($c2_x)"/>
		<xsl:variable name="transformed_c2_y" select="$t_f + $t_d * xs:float($c2_y)"/>
		<entry>
			<composite type="bline_point">
				<point>
					<vector>
						<x><xsl:value-of select="$transformed_x"/></x>
						<y><xsl:value-of select="$transformed_y"/></y>
					</vector>
				</point>
				<width>
					<real value="1"/>
				</width>
				<origin>
					<real value="0.5"/>
				</origin>
				<split>
					<bool value="true"/>
				</split>
				<t1>
					<xsl:call-template name="vector-pair-to-radial">
						<xsl:with-param name="origin-x" select="$transformed_c1_x"/>
						<xsl:with-param name="origin-y" select="$transformed_c1_y"/>
						<xsl:with-param name="x" select="$transformed_x"/>
						<xsl:with-param name="y" select="$transformed_y"/>
					</xsl:call-template>
				</t1>
				<t2>
					<xsl:call-template name="vector-pair-to-radial">
						<xsl:with-param name="origin-x" select="$transformed_x"/>
						<xsl:with-param name="origin-y" select="$transformed_y"/>
						<xsl:with-param name="x" select="$transformed_c2_x"/>
						<xsl:with-param name="y" select="$transformed_c2_y"/>
					</xsl:call-template>
				</t2>
			</composite>
		</entry>
	</xsl:template>

	<xsl:template name="vector-pair-to-radial">
		<xsl:param name="x"/>
		<xsl:param name="y"/>
		<xsl:param name="origin-x"/>
		<xsl:param name="origin-y"/>
		<xsl:variable name="dx" select="xs:float($x) - xs:float($origin-x)"/>
		<xsl:variable name="dy" select="xs:float($y) - xs:float($origin-y)"/>
		<xsl:variable name="d" select="math:sqrt($dx * $dx + $dy * $dy)"/>
		<xsl:variable name="angle" select="math:atan2($dy, $dx)"/>
		<radial_composite type="vector">
			<radius>
				<real value="{$d * 3}"/>
			</radius>
			<theta>
				<angle value="{$angle * 57.295779513082320876798154814105}"/>
			</theta>
		</radial_composite>
	</xsl:template>

	<xsl:template name="style-to-width">
		<xsl:param name="style"/>
		<xsl:if test="matches($style, '^\d')">
			<param name="width">
				<real value="{math:units_to_px($style)}"/>
			 </param>
		</xsl:if>
	</xsl:template>

	<xsl:template name="style-to-color">
		<xsl:param name="style"/>
		<xsl:if test="matches($style, '#')">
			<xsl:analyze-string select="concat($style, ';')" regex="#([\da-f]{{2}})([\da-f]{{2}})([\da-f]{{2}});">
				<xsl:matching-substring>
					<param name="color">
						<color>
							<r><xsl:value-of select="math:hex_to_color(regex-group(1))"/></r>
							<g><xsl:value-of select="math:hex_to_color(regex-group(2))"/></g>
							<b><xsl:value-of select="math:hex_to_color(regex-group(3))"/></b>
							<a><xsl:value-of select="if (matches($style, 'fill-opacity:')) then math:power(xs:float(replace($style, '.*fill-opacity:([-\d.]+).*', '$1')), 1 div 2.2) else 1"/></a>
						</color>
					</param>
				</xsl:matching-substring>
			</xsl:analyze-string>
		</xsl:if>
		<xsl:if test="matches($style, 'rgb')">
			<xsl:analyze-string select="concat($style, ';')" regex="rgb[(\s]+([-\d.]+)[,\s]+([-\d.]+)[,\s]+([-\d.]+)[\s)]+;">
				<xsl:matching-substring>
					<param name="color">
						<color>
							<r><xsl:value-of select="math:power(xs:float(regex-group(1)) div 255, 2.2)"/></r>
							<g><xsl:value-of select="math:power(xs:float(regex-group(2)) div 255, 2.2)"/></g>
							<b><xsl:value-of select="math:power(xs:float(regex-group(3)) div 255, 2.2)"/></b>
							<a>1</a>
						</color>
					</param>
				</xsl:matching-substring>
			</xsl:analyze-string>
		</xsl:if>
		<xsl:if test="matches($style, 'url')">
			<param name="color">
				<color><r>0.5</r><g>0.5</g><b>0.5</b><a>0.5</a>	</color>
			</param>
		</xsl:if>
	</xsl:template>

	<xsl:function name="math:hex_to_color" as="xs:float">
		<xsl:param name="hex"/>
		<xsl:value-of select="math:power(xs:float(string-length(substring-before('0123456789abcdef', substring($hex,1,1))) * 16 + string-length(substring-before('0123456789abcdef', substring($hex,2,1)))) div 255, 2.2)"/>
	</xsl:function>

	<xsl:function name="math:units_to_px" as="xs:float">
		<xsl:param name="size"/>
		<xsl:analyze-string select="$size" regex="^([-\d.]+)([a-z%]*)$">
			<xsl:matching-substring>
				<xsl:variable name="factor">
					<xsl:choose>
						<xsl:when test="regex-group(2) = 'pt'">1.25</xsl:when>
						<xsl:when test="regex-group(2) = 'em'">16</xsl:when>
						<xsl:when test="regex-group(2) = 'mm'">3.54</xsl:when>
						<xsl:when test="regex-group(2) = 'pc'">15</xsl:when>
						<xsl:when test="regex-group(2) = 'cm'">35.43</xsl:when>
						<xsl:when test="regex-group(2) = 'in'">90</xsl:when>
						<xsl:otherwise>1</xsl:otherwise>
					</xsl:choose>
				</xsl:variable>
				<xsl:value-of select="xs:float($factor) * xs:float(regex-group(1))"/>
			</xsl:matching-substring>
		</xsl:analyze-string>
	</xsl:function>
</xsl:stylesheet>


Languages Language: 

English • español • français • italiano • română • русский