[swfmill] Inkscape SVG

Gerrit Karius g99k at hotmail.com
Wed Sep 20 08:24:07 EDT 2006


hi everyone. in july, i submitted a changed "simple-svg.xslt" stylesheet, 
where i omitted the swf wrapper transforms for svg:g nodes. it turned out 
that this was a mistake. deepest apologies. :(

my question was: should outer transforms of svg nodes apply to referencing 
svg:use nodes as well? the svg spec defines svg:use nodes as deep-cloning 
the original (implicitly including the transform). i thought that inkscape 
ignored the transforms, since the position of clones does not change when 
the transform of the original is altered. however, inkscape actually 
silently adjusts the matrices of all clones to the new transform instead.

i wanted to omit the wrappers for a direct node hierarchy with better 
actionscript accessability. my compromise for the xslt is now that it checks 
whether an original node actually has a transform or is simply placed at the 
origin. if it's placed at the origin, the wrapper can safely be omitted. 
that way, everyone should be happy. :)



here's a list of changes:

* the svg:g template now checks for a transform and applies a wrapper, which 
gets the id of the g node. for nonexisting, empty or identity transforms, 
the wrapper is omitted, and the id is applied to the g node itself.

* a named transform template catches empty transform atributes 
(transform=""). inkscape regularly produces them, and they caused swfmill to 
crash.

* svg:flowRoot elements are now included again in the stylesheet. the 
definition template simply extracts the text content of the flowRegion and 
flowPara child nodes via the "svg-text" mode. the styling is not parsed, but 
at least the text is now visible in the swf movie.

* the svg:text template has been changed. before, the text was invisible, 
because it was all white color. it's now visible, without styling, just like 
flowRoot.



for testing swfmill with non-inkscape files, i also included the following 
lines at the start of parse_css_simple in swft_css.cpp. it checks for a 
missing style attribute and applies a black stroke.

if( strlen(style_str) == 0 ) {
	style->no_fill = true;
	style->no_stroke = false;
	style->width = 20;
	style->stroke.a = 0xff;
	return;
}

inkscape uses the style attribute exclusively for colors and styles, and 
swfmill checks only this attribute. this means that svgs using regular 
atributes like "fill" and "stroke" become all white color and fully 
transparent, turning them invisible. now they at least get a black outline, 
and can thus prove their existence. :)

i used this to run an swfmill test over the w3c svg testsuite, but the 
results looked rather discouraging, even with outlines. however, since 
almost all of the testcases represent features that inkscape doesn't use, i 
wouldn't bother to implement them.

it might also be more efficient to have inkscape directly export basic 
swfml, rather than parsing the svg output in swfmill. the stuff gets very 
complicated in xslt, even with external functions. i suppose that in 
inkscape, you'd have the data as binary variables instead of xml strings, 
and would have global access to the entire tree.

dan, what does your inkscape experience suggest?

anyway, here's the "simple-svg.xslt" again:




<?xml version="1.0"?>
<xsl:stylesheet	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
				xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
				xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
				xmlns:svg="http://www.w3.org/2000/svg"
				xmlns:swft="http://subsignal.org/swfml/swft"
				xmlns:str="http://exslt.org/strings"
				xmlns:xlink="http://www.w3.org/1999/xlink"
				extension-element-prefixes="swft"
				version='1.0'>

<!-- named template for redundant transforms -->
<xsl:template name="transform">
	<transform>
		<xsl:choose>
			<!-- catch empty transform attributes. -->
			<xsl:when test="not(@transform) or @transform=''">
				<Transform transX="0" transY="0"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:copy-of select="swft:transform(@transform)"/>
			</xsl:otherwise>
		</xsl:choose>
	</transform>
</xsl:template>

<!-- named template for redundant placing -->
<xsl:template name="placeObject">
	<!-- place the element, or the referenced element (if it's a reference). 
-->
	<xsl:variable name="id">
		<xsl:choose>
			<xsl:when test="name()='use'">
				<xsl:value-of select="swft:map-id(substring(@xlink:href,2))"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:value-of select="swft:map-id(@id)"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:variable>
	<!-- use inkscape label as an instance name instead of id, allowing 
multiple instances with the same name. -->
	<xsl:variable name="name">
		<xsl:choose>
			<xsl:when test="@inkscape:label">
				<xsl:choose>
					<xsl:when test="substring(@inkscape:label,1,1)='#'">
						<xsl:value-of select="substring(@inkscape:label,2)"/>
					</xsl:when>
					<xsl:otherwise>
						<xsl:value-of select="@inkscape:label"/>
					</xsl:otherwise>
				</xsl:choose>
			</xsl:when>
			<xsl:otherwise>
				<xsl:value-of select="@id"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:variable>
	<!-- place the object. -->
	<PlaceObject2 replace="0" depth="{swft:next-depth()}" name="{$name}" 
objectID="{$id}">
		<!-- svg:use elements add an instance transform, all others have it 
included in their definition. -->
		<xsl:choose>
			<xsl:when test="name()='use'">
				<xsl:call-template name="transform" />
			</xsl:when>
			<xsl:otherwise>
				<transform>
					<Transform transX="0" transY="0"/>
				</transform>
			</xsl:otherwise>
		</xsl:choose>
	</PlaceObject2>
</xsl:template>

<!-- named template for redundant wrappers -->
<xsl:template name="wrapElement">
	<xsl:param name="innerid" />
	<xsl:variable name="id" select="swft:map-id(@id)" />
	<DefineSprite objectID="{$id}" frames="1">
		<tags>
			<PlaceObject2 replace="0" depth="{swft:next-depth()}" 
objectID="{$innerid}">
				<xsl:call-template name="transform" />
			</PlaceObject2>
			<ShowFrame/>
			<End/>
		</tags>
	</DefineSprite>
</xsl:template>

<!-- named template for redundant exports -->
<xsl:template name="exportElement">
	<xsl:variable name="id" select="swft:map-id(@id)" />
	<xsl:variable name="name" select="@id" />
	<!-- export the element. -->
	<xsl:if test="@id">
		<Export>
			<symbols>
				<Symbol objectID="{$id}" name="{$name}"/>
			</symbols>
		</Export>
	</xsl:if>
	<!-- define a class, if applicable. -->
	<xsl:variable name="class" select="@class"/>
	<xsl:if test="string-length($class) > 0">
		<xsl:call-template name="register-class">
			<xsl:with-param name="class" select="$class"/>
			<xsl:with-param name="linkage-id" select="$name"/>
		</xsl:call-template>
	</xsl:if>

</xsl:template>


<!-- entry point: starts 2 passes, one for queuing up the definitions, one 
for placing the elements. -->
<xsl:template match="svg:svg" mode="svg">
	<xsl:param name="id"/>
	<!-- initiate the definition pass. -->
	<xsl:apply-templates mode="queue" />
	<!-- define svg root as sprite. -->
	<DefineSprite objectID="{$id}" frames="1">
		<tags>
			<!-- initiate the placement pass. -->
			<xsl:apply-templates mode="placement" />
			<ShowFrame/>
			<End/>
		</tags>
	</DefineSprite>
</xsl:template>

<xsl:template match="svg:g|svg:path|svg:rect|svg:use|svg:text|svg:flowRoot" 
mode="queue">
	<xsl:variable name="id"><xsl:value-of 
select="swft:map-id(@id)"/></xsl:variable>
	<xsl:variable name="name" select="@id"/>

	<!-- first define the subparts, so that we get the innermost ones queued 
first. -->
	<xsl:apply-templates mode="queue" />
	<!-- now define this element, which is based on the subparts. -->
	<xsl:apply-templates select="." mode="definition">
		<xsl:with-param name="id" select="$id"/>
		<xsl:with-param name="name" select="$name"/>
	</xsl:apply-templates>

</xsl:template>

<xsl:template match="svg:g|svg:path|svg:rect|svg:use|svg:text|svg:flowRoot" 
mode="placement">
	<!-- no definition. just place this element. -->
	<xsl:call-template name="placeObject" />
</xsl:template>

<xsl:template match="svg:g" mode="definition" priority="-1">
	<xsl:param name="id"/>
	<xsl:param name="name"/>

	<!-- test if a wrapper is needed for a group transform -->
	<xsl:choose>
		<xsl:when test="not(@transform) or @transform='' or 
transform='translate(0,0)'">
			<!-- no transform, define the group and place the subparts -->
			<DefineSprite objectID="{$id}" frames="1">
				<tags>
					<xsl:apply-templates mode="placement" />
					<ShowFrame/>
					<End/>
				</tags>
			</DefineSprite>
		</xsl:when>
		<xsl:otherwise>
			<!-- define an inner group and wrap it with the group transform -->
			<xsl:variable name="innerid"><xsl:value-of 
select="swft:next-id()"/></xsl:variable>
			<DefineSprite objectID="{$innerid}" frames="1">
				<tags>
					<xsl:apply-templates mode="placement" />
					<ShowFrame/>
					<End/>
				</tags>
			</DefineSprite>
			<xsl:call-template name="wrapElement">
				<xsl:with-param name="innerid" select="$innerid" />
			</xsl:call-template>
		</xsl:otherwise>
	</xsl:choose>
	<!-- export -->
	<xsl:call-template name="exportElement" />

</xsl:template>

<xsl:template match="svg:path" mode="definition">
	<xsl:param name="id"/>
	<xsl:variable name="shapeid"><xsl:value-of 
select="swft:next-id()"/></xsl:variable>

	<!-- define the path -->
	<xsl:copy-of select="swft:path( @d, $shapeid, @style )"/>
	<!-- wrap in sprite -->
	<xsl:call-template name="wrapElement">
		<xsl:with-param name="innerid" select="$shapeid" />
	</xsl:call-template>
	<!-- export -->
	<xsl:call-template name="exportElement" />
</xsl:template>

<xsl:template match="svg:rect" mode="definition">
	<xsl:param name="id"/>
	<xsl:param name="name"/>
	<xsl:variable name="shapeid"><xsl:value-of 
select="swft:next-id()"/></xsl:variable>

	<!-- define the element -->
	<DefineShape3 objectID="{$shapeid}">
		<bounds>
			<Rectangle left="{@x}" right="{(@x+ at width)*20}" top="{@y}" 
bottom="{(@y+ at height)*20}"/>
		</bounds>
		<styles>
			<StyleList>
				<xsl:copy-of select="swft:css(@style)/tmp/*"/>
			</StyleList>
		</styles>
		<shapes>
			<Shape>
				<edges>
					<ShapeSetup x="{(@x+ at width)*20}" y="{(@y+ at height)*20}" fillStyle0="1" 
lineStyle="1"/>
					<LineTo x="-{(@width)*20}" y="0"/>
					<LineTo x="0" y="-{(@height)*20}"/>
					<LineTo x="{(@width)*20}" y="0"/>
					<LineTo x="0" y="{(@height)*20}"/>
					<ShapeSetup/>
				</edges>
			</Shape>
		</shapes>
	</DefineShape3>
	<!-- wrap in sprite -->
	<xsl:call-template name="wrapElement">
		<xsl:with-param name="innerid" select="$shapeid" />
	</xsl:call-template>
	<!-- export -->
	<xsl:call-template name="exportElement" />
</xsl:template>

<xsl:template match="svg:flowRoot" mode="definition">
	<xsl:param name="id"/>
	<xsl:param name="name"/>
	<xsl:variable name="shapeid"><xsl:value-of 
select="swft:next-id()"/></xsl:variable>

	<!-- define the element -->
	<DefineEditText objectID="{$shapeid}" wordWrap="1" multiLine="1" 
password="0" readOnly="0" autoSize="0" hasLayout="1" notSelectable="0" 
hasBorder="0" isHTML="0" useOutlines="0" fontRef="{swft:map-id('vera')}" 
fontHeight="240" align="0" leftMargin="0" rightMargin="0" indent="0" 
leading="40" variableName="{@name}">
		<xsl:attribute name="initialText">
			<xsl:apply-templates mode="svg-text"/>
		</xsl:attribute>
		<size>
			<Rectangle left="{svg:flowRegion/svg:rect/@x * 20}" 
right="{(svg:flowRegion/svg:rect/@x + svg:flowRegion/svg:rect/@width)* 20}" 
top="{svg:flowRegion/svg:rect/@y * 20}" bottom="{(svg:flowRegion/svg:rect/@y 
+ svg:flowRegion/svg:rect/@height)* 20}"/>
		</size>
		<color>
			<Color red="0" green="0" blue="0" alpha="255"/>
		</color>
	</DefineEditText>
	<!-- wrap in sprite -->
	<xsl:call-template name="wrapElement">
		<xsl:with-param name="innerid" select="$shapeid" />
	</xsl:call-template>
	<!-- export -->
	<xsl:call-template name="exportElement" />
</xsl:template>

<xsl:template match="svg:text" mode="definition">
	<xsl:param name="id"/>
	<xsl:param name="name"/>
	<xsl:variable name="shapeid"><xsl:value-of 
select="swft:next-id()"/></xsl:variable>

	<!-- define the element -->
	<DefineEditText objectID="{$shapeid}" wordWrap="0" multiLine="1" 
password="0" readOnly="1" autoSize="1" hasLayout="1" notSelectable="1" 
hasBorder="0" isHTML="0" useOutlines="0" fontRef="{swft:map-id('vera')}" 
fontHeight="240" align="0" leftMargin="0" rightMargin="0" indent="0" 
leading="40" variableName="{@name}">
	<xsl:attribute name="initialText">
		<xsl:apply-templates mode="svg-text"/>
	</xsl:attribute>
	<size>
		<Rectangle left="{@x * 20}" right="{@x * 30}" top="{@y * 20}" bottom="{@y 
* 30}"/>
	</size>
	<color>
		<Color red="0" green="0" blue="0" alpha="255"/>
	</color>
	</DefineEditText>
	<!-- wrap in sprite -->
	<xsl:call-template name="wrapElement">
		<xsl:with-param name="innerid" select="$shapeid" />
	</xsl:call-template>
	<!-- export -->
	<xsl:call-template name="exportElement" />
</xsl:template>

<xsl:template match="svg:flowRegion" mode="svg-text">
	<xsl:apply-templates mode="svg-text"/>
</xsl:template>

<xsl:template match="svg:flowPara" mode="svg-text">
	<xsl:apply-templates mode="svg-text"/>
</xsl:template>

<xsl:template match="svg:tspan[position()=1]" mode="svg-text">
	<xsl:apply-templates mode="svg-text"/>
</xsl:template>

<xsl:template match="svg:tspan" mode="svg-text" priority="-1">
	<xsl:text>
	</xsl:text>
	<xsl:apply-templates mode="svg-text"/>
</xsl:template>

<xsl:template match="text()" mode="svg-text">
	<xsl:copy-of select="."/>
</xsl:template>

<xsl:template match="*|@*|text()" mode="svg" priority="-1"/>

<xsl:template match="ShapeSetup" mode="shape">
	<ShapeSetup fillStyle0="1" fillStyle1="2" lineStyle="1">
		<xsl:apply-templates select="*|@*" mode="shape"/>
	</ShapeSetup>
</xsl:template>
<xsl:template match="*|@*|text()" mode="shape" priority="-1">
	<xsl:copy-of select="."/>
</xsl:template>

</xsl:stylesheet>





More information about the swfmill mailing list