Update: In the examples I discuss below, the tspan should be inside the textPath, not the other way around. Source files and the formatted versions are updated as of 8/31. Thanks to Holger for pointing this out.
I want to make another simple example of XSLT and SVG today. So I'm going to do another XML document and transformation. The mechanism is similar to my last example, but I'm not using any of the same SVG. Today, instead, I'm going to show an example with a textPath for making some wavy prose.
It also happens that the Mozilla team just landed support for textPath last night for Firefox trunk builds. I developed today's example in Internet Explorer with Adobe SVG Viewer 6 preview, but I'll be testing later with Firefox. If you want to use the latest trunk build of Firefox for testing, get it here.
Here's the SVG with some text borrowed from Les Misérables (thank you Project Gutenberg):
The source files that I needed to make this were just an XML file and an XSLT file. Here are the files that are so you can easily follow along:
The formatted example XML (unformatted here), the formatted example XSLT (unformatted here) and the example SVG.
The syntax for the XML is simple, I invented a textline entity that has some text content and a colour for the text it's made of. I called a collection of textline entities a stanza. The stanza has a style attribute. I was going to use the style attribute to allow selection from a number of different textPath choices. The example would be a bit more complicated that way, so I decided to leave that out for now.
The XSLT has two templates again, one to match the stanza and one for the textlines. For the stanza, I have some basic SVG boilerplate and I create a text element. Inside the text element is the call to apply templates to each textline node.
The template will create a path for each textline. The id of the path is "wave" followed by a number for the position of the node. This is handled by id = "wave{position()}" in the template. The value of id is an attribute value template because it uses the curly braces { } inside the quotes. I've got a couple more places they're used in the definition of the path (the d attribute) and the places that the path is referenced by it's id.
In the path data, I've used an absolute moveto (M), a relative cubic Bézier curveto (c) and many relative smooth curveto (s) commands.
M {2 * position() + 20},{15 * position() + 30}
The moveto sets the starting point for the path based on the position of the textline in the XML source. The path uses the same coordinate system as the text entity that the textPath lives in. This confused me for a while, maybe just because it's different from the gradient fills I've been looking at, which get their own coordinate system mapped to the place that they're used. I could have achieved the same results as the above moveto by using M 0,0 and applying a transform to the text at some point.
c 30,-20 30,-20 50,0
The cubic curveto has two control points and an end point. The control points are relative to the last point where we drew. So if we drew to the point 5,5, both the control points and the end point of the curveto are relative to the point 5,5 - as opposed to the control points being relative to each other or previous control points.
s 30,20 50,0
The rest of the path data is a repeated smooth curveto command. This one uses takes the last control point of the last curve, reflects it and uses that for it's own first control point. This avoids any discontinuity in the line being drawn.
Inside the template for the textline, I apply templates to "node()". This effectively gets the contents of the textline in the XML source and copies them into the SVG source. I've only tested this with plain text content, but it should allow arbitrary XML to be copied (with any templates applied of course).
Hi Rob
we had a discussion yesterday, and we came to the conclusion, that textPath inside tspan is invalid, and should not render, your example works in ASV3/6 but this is a bug in ASV. see the comments in bugzilla.
cheers
Holger
Thanks for the correction Holger, I looked through the spec and I can see what you mean now. I'll move the tspan inside the textPath and that should fix it. Unfortunately I don't think I can correct my examples until this afternoon.