SVGBasics

Text - Plain or Not-So-Plain

Fancy Uses for Text

Okay, so SVG isn't a great format for your word-processor to save files in (with the whole "no word wrap" thing). It is great for rendering shiny little baubles that have text in them or revolve around some highly stylized text.

Making text follow a curve or shape

The textPath element can be used in conjunction with the path element to make things interesting very quickly.

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg viewBox = "0 0 500 300" version = "1.1">
    <desc>
        Text example
    </desc>
    <defs>
        <path id = "s3" d = "M 60 0 L 120 0 L 180 60 L 180 120 L 120 180 L 60 180 L 0 120 L 0 60" fill = "green" stroke = "black" stroke-width = "3"/>
    </defs>
    <g fill = "navy">
        <text font-size = "20">
            <textPath xlink:href = "#s3">
                Most wordprocessors don't allow octagonal lines ;)
            </textPath>
        </text>
    </g>
</svg>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg viewBox = "0 0 500 300" version = "1.1">
    <defs>
        <!-- Start at (10,90) end at (200,70) use control point (100,15) -->
        <!-- Continue from (200,70) end at (400,30) use control point (340,140) -->
        <path id = "s3" d = "M 10,90 Q 100,15 200,70 Q 340,140 400,30"/>
    </defs>
    <g fill = "navy">
        <text font-size = "20">
            <textPath xlink:href = "#s3">
                Wavy text is the gimmick for many years to come
            </textPath>
        </text>
        <use x = "0" y = "0" xlink:href = "#s3" stroke = "black" fill = "none"/>
    </g>
</svg>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg viewBox = "0 0 500 300" version = "1.1">
    <defs>
        <!-- Start at (10,90) end at (200,70) use control point (100,15) -->
        <!-- Continue from (200,70) end at (400,30) use control point (340,140) -->
        <path id = "s3" d = "M 50,50 A 20,20 0 0 1 90,50 A 40,40 0 0 1 10,50 A 60,60 0 0 1 130,50"/>
    </defs>
    <g fill = "navy">
        <text font-size = "14" font-family = "fantasy">
            <textPath xlink:href = "#s3">
                Text written around a spiral path is too hard to read!
            </textPath>
        </text>
        <!-- use x="0" y="0" xlink:href="#s3" stroke="black" fill="none"/ -->
    </g>
</svg>

Text fill and stroking

Text can be stroked and filled in with gradients, solid colours or patterns. In this way, rendered text is treated like other graphical elements.

Here's a gradient used to fill a rectangle then some text (courtesy of Robert Service).

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg viewBox = "0 0 600 200" version = "1.1">
    <defs>
        <rect id = "r1" width = "250" height = "50" stroke = "black" stroke-width = "1"/>
        <linearGradient id = "g1" x = "0%" y = "100%">
            <stop stop-color = "olivedrab" offset = "0%"/>
            <stop stop-color = "peru" offset = "20%"/>
            <stop stop-color = "goldenrod" offset = "40%"/>
            <stop stop-color = "firebrick" offset = "60%"/>
            <stop stop-color = "thistle" offset = "80%"/>
            <stop stop-color = "sandybrown" offset = "100%"/>
        </linearGradient>
    </defs>
    <g font-size = "40">
        <use x = "0" y = "0" xlink:href = "#r1" fill = "url(#g1)"/>
        <text font-size = "20" fill = "url(#g1)" stroke = "none">
            <tspan x = "10" y = "80">
                Have you wandered in the wilderness, the sagebrush desolation,
            </tspan>
            <tspan x = "10" y = "140">
                The bunch-grass levels where the cattle graze?
            </tspan>
        </text>
    </g>
</svg>

Here's that same gradient used to stroke text, this time there is no fill.

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg viewBox = "0 0 600 200" version = "1.1">
    <defs>
        <rect id = "r1" width = "250" height = "50" stroke = "black" stroke-width = "1"/>
        <linearGradient id = "g1" x = "0%" y = "100%">
            <stop stop-color = "olivedrab" offset = "0%"/>
            <stop stop-color = "peru" offset = "20%"/>
            <stop stop-color = "goldenrod" offset = "40%"/>
            <stop stop-color = "firebrick" offset = "60%"/>
            <stop stop-color = "thistle" offset = "80%"/>
            <stop stop-color = "sandybrown" offset = "100%"/>
        </linearGradient>
    </defs>
    <g font-size = "40">
        <use x = "0" y = "0" xlink:href = "#r1" fill = "url(#g1)"/>
        <text font-size = "20" stroke = "url(#g1)" fill = "none">
            <tspan x = "10" y = "80">
                Have you whistled bits of rag-time at the end of all creation,
            </tspan>
            <tspan x = "10" y = "140">
                And learned to know the desert's little ways?
            </tspan>
        </text>
    </g>
</svg>

The 'stroke-width' attribute adjusts the width of the line used to trace text. You can also control the shape of corners and ends of those lines with 'stroke-linejoin' and 'stroke-linecap' which are shown next.

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg viewBox = "0 0 600 200" version = "1.1">
    <desc>
        Gradient example
    </desc>
    <g>
        <text font-size = "200" fill = "none" stroke = "red" stroke-width = "10">
            <tspan x = "10" y = "120" stroke-linejoin = "bevel">
                 Aa
            </tspan>
            <tspan x = "340" y = "120" stroke-linejoin = "round">
                 Aa
            </tspan>
        </text>
        <text font-size = "10" fill = "black" stroke = "none">
            <tspan x = "50" y = "140">
                bevel join
            </tspan>
            <tspan x = "380" y = "140">
                round join
            </tspan>
        </text>
    </g>
</svg>

Caveats

SVG doesn't do automatic word wrapping. Tools that implement SVG could handle the wrapping for the author, then make multiple one-line text elements out of it. This approach precludes the text being changed at the viewing end (for example, changing the font or replacing text with translated text). This is something that it appears may change with SVG version 1.2.
Another approach (mentioned in the spec) is using the foreignObject element to render text using XHTML or some other XML derivative that handles wrapped text. I'm pretty sure that means today that your SVG plugin has to either render that foreignObject (which means understanding XHTML) or run some other plugin in the space the foreignObject occupies.

Here's an example of the foreignObject method based on the one provided by the w3c.

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg viewBox = "0 0 200 200" version = "1.1">
    <desc>
        This example uses the 'switch' element to provide a fallback graphical representation of an paragraph, if XMHTML is not supported.
    </desc>
    <switch>
        <!-- Process the embedded XHTML if the requiredExtensions attribute evaluates to true (i.e., the user agent supports XHTML embedded within SVG). -->
        <foreignObject width = "100" height = "50" requiredExtensions = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
            <!-- XHTML content goes here -->
            <body>
                <p>
                    This text is written inside a foreignObject tag in order to take advantage of XHTML automatic word-wrapping.
                </p>
            </body>
        </foreignObject>
        <!-- Else, process the following alternate SVG. Note that there are no testing attributes on the 'text' element. If no testing attributes are provided, it is as if there were testing attributes and they evaluated to true. -->
        <text font-size = "10" font-family = "Verdana">
            <tspan x = "10" y = "10">
                The foreignObject is not displayed.
            </tspan>
        </text>
    </switch>
</svg>

One more workable approach exists, it relies on using the tspan element to mark a relative position for successive lines of text. This one is about the same as the multiple text element method, but one might be easier to adjust with a script than the other.

The Adobe viewer seems to fail the Text spacing test from w3c.

©2004 Late Night PC Service | About Us | Site Map | Contact Us