Perfecting Paths for <textPath>
In SVG 1.1 (and all existing browsers), the “path” used by a <textPath>
layout must truly be a <path>
element: you can’t yet make text ride a <circle>
or <rect>
.
This can be somewhat frustrating, as they (along with ellipses) are the most common shapes people want to wrap text around. If you have a basic shape, and want to convert it to a <path>
without having to code or draw it one point at a time, there are various tools you can use:
-
Most visual editors have tools to convert the shape into a path. In Illustrator, use Object → Compound Path → Make; in Inkscape, use Path → Object to Path.
-
In some editors, including Illustrator, moving any point on a regular shape, even slightly, will also convert it into a path.
-
If a counter-clockwise circle or ellipse is all you need, Dan Hansen’s neat little JavaScript tool to grab the equivalent path data.
The text will proceed along the path in the direction the path is drawn in the code. If your paths have a clockwise winding order, the text will be on the outside of the shape. If the path is counter-clockwise, the text will be on the inside. In either case, it is possible to end up with upside-down letters. If your path is in the wrong direction, your graphics editor can reverse it for you:
-
In Adobe Illustrator, there is a Reverse Path Direction command in the application’s Attributes panel (you may have to use Show Options in the top right corner of the panel to see the path direction controls). This assumes that the path is a compound path (Object → Compound Path → Make).
-
In Inkscape, select the path, and use the Reverse option in the Path menu.
-
If you’re manipulating SVG with scripts rather than visual editors, Mike Kamermans has created a JavaScript utility to do the reversal.
Finally, you may want to adjust the starting point of the text to a position other than the starting point from the path code. Unfortunately, startOffset
is not enough here: although it shifts the text around the shape, it doesn’t change the fact that text gets cropped off when it reaches the path start and end points. (SVG 2 proposes to change this, but browsers aren’t yet on board.)
Tools for changing the start point of a path aren’t as common as for reversing it, but in most graphic editors you can force the matter by breaking and then re-joining your path at the chosen point.
Alternatively, you can avoid the cut-off text issue by duplicating the path data string inside the <path>
element, so that text will happily wrap around and continue along a second “lap” of the path. Just remember that now your total path is twice as long, so percentage startOffset
values need to be divided in half.
Tip
For symmetrical shapes, such as a circle, you can instead use a rotate
transformation on the <path>
itself (or on the <text>
containing the <textPath>
) to re-position the start of the text as you wish.
With all those tips in mind, we’re ready to arrange text around a closed path. Example 7-X3 creates a circular crest out of the Three Musketeer’s “All for One & One for All” motto. Figure 7-X3 is the output.
Example 7-X3. Controlling text length for text on a closed path
<svg
xmlns=
"http://www.w3.org/2000/svg"
xml:lang=
"en"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
viewBox=
"0 0 500 500"
>
<title>
Circular Text Path</title>
<style>
text
{
font-size
:
76px
;
font-family
:
Copperplate
,
Copperplate
Gothic
,
serif
;
font-weight
:
bold
;
font-variant
:
small-caps
;
font-variant-ligatures
:
none
;
stroke
:
black
;
stroke-width
:
1.5px
;
fill
:
white
;
}
</style>
<defs>
<path
id=
"textcircle"
d=
"M250,400
a150,150 0 0,1 0,-300a150,150 0 0,1 0,300Z"
transform=
"rotate(12,250,250)"
/>
</defs>
<rect
width=
"100%"
height=
"100%"
fill=
"silver"
/>
<text>
<textPath
xlink:href=
"#textcircle"
aria-label=
"All for One & One for All"
textLength=
"942"
>
 
One for All for One&
</textPath>
</text>
</svg>
The circle is created with a <path>
element containing two half-circle arcs of 150-unit radius. As we mentioned in Chapter 6, you can’t draw a complete circle with a single arc segment, because it doesn’t have enough positioning information.
The textLength
attribute constrains the text to match the circumference of the circle (2π times the radius), and a rotational transform
is used to tweak the alignment so that the ampersand (&) is centered at the base of the circle.
Warning
Or at least, that’s the end result. Creating this layout was more difficult than expected, due to issues with textLength
on nested elements, textLength
in combination with centered text, and inconsistent treatment of whitespace. Code that looked nice in one browser was horribly out of alignment in others. The markup in Example 7-X3 was the result of a lot of tweaking and testing in different browsers, and it still isn’t perfectly aligned everywhere.
As we mentioned in “Beyond Horizontal: Rotated and Vertical Text”, textLength
declares the total length you want the text to meet, and browsers adjust the letter spacing to make it fit. It should work on any text element, including <textPath>
, but browser implementations are buggy in conflicting ways, and some will ignore it. textLength
can only be used reliably for <text>
elements without children.
When adjusting the textLength
to match the path, you’ll need to know the length of the path you’re using. The browser can calculate it for you with the getTotalLength()
method on the <path>
element. The value returned by that method can be directly used in the textLength
attribute of a <textPath>
to make the text exactly fit.
For example, by opening the SVG from Example 7-X3 in a browser, and then opening up the developer’s console, you can find the path length as follows:
document
.
getElementById
(
"textcircle"
).
getTotalLength
()
//returns 942.61083984375
This gives essentially the same result as calculating it from the geometry of the circle:
150
*
2
*
Math
.
PI
//returns 942.4777960769379
It’s not a perfect match, because the browser uses approximations to draw circular paths, and approximations to calculate the length of paths. But it’s close enough for the drawing. And getTotalLength()
is invaluable for more complicated paths, whose lengths can’t be converted into a simple formula.
In SVG 2 and the latest versions of Chromium browsers, getTotalLength()
can be used on any shape element, not only a <path>
. So you can use it in your Chrome console, even if you can’t rely on it in your scripts. But that won’t be useful for text paths until non-path shapes can be referenced by the <textPath>
element.
There are a few other things to note in Example 7-X3:
-
An
aria-label
attribute replaces the text content of the<textPath>
with a more screen-reader friendly version. -
The
&
entity is used for an ampersand (&), while the numeric entity 
is used for a non-breaking space, to prevent the space at the beginning of the text from collapsing. Because this is a standalone SVG file, the equivalent HTML entity
is not available. -
The
font-variant-ligatures
property is used to turn off common ligatures, which can cause a chunky appearance when text is positioned around tight curves.
If the path is very sharply curved, text may appear too “tight” on the inside of a curve, or too “loose” on the outside. You can adjust the vertical relationship between text and its path by modifying the value of a dy
attribute for the parent <text>
element (or a child <tspan>
element). A positive value will pull the text baseline down to be more centered over the path, loosening up those tight inside curves and tightening the outside ones.
One day, it may be possible to use the CSS baseline-related properties to adjust the position of text relative to the path, in a more intuitive manner. For now, dy
has the better browser support.