Menu

SVG and Typography: Bells and Whistles

June 2, 2004

Fabio Arciniegas A.

In this installment of our discussion of SVG and typography, we make a departure from the sobriety of the typographic strategies we've been discussing so far and go for the other half of the fun: the bells and whistles of effects, distortions, coloring, and other unusual treatments of type.

We will create reusable code (basically a cookbook) of common typographic treatments implemented in SVG. The idea is to recreate some of the most famous effects you would expect from a design book in SVG:

  • Blur
  • Outer Shadow
  • Image within Type
  • Type within Image
  • Stroke
  • Dilate
  • Bevel
  • Gradients
  • Spacing
  • Skew
  • Mixing

This article is intended as a little arsenal of typographic treatments implemented in SVG, not as a tutorial on filters, which is something I will do in future columns. However, for some techniques we take the opportunity to briefly discuss some relevant aspect of SVG style and syntax.

Blur

Figure 1. Blur

<?xml version="1.0"?>

<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150" version="1.1">

  <defs>

    <filter id="blur" x="0" y="0" width="150" height="150">

      <feGaussianBlur in="SourceGraphic" stdDeviation="2" />

    </filter>

  </defs>

<rect x="0" y="0" width="150" height="150" style="fill:#F1B813;" />

<text filter="url(#blur)" 

      x="40" y="85" 

      style="font-family:Arial; font-size: 30pt; fill:red;">blur</text>

</svg>

Listing 1. blur.svg

Blur is a good starting point because it is based on just one primitive filter, predefined in SVG. As you can see from the code above, we define filters inside the defs element. Inside each filter we can define series of operations and their combinations; in our case we are doing only one operation, a gaussian blur identified by the primitive feGaussianBlur.

Then, on the text element itself, we apply the defined filter by providing its location as the value of the filter attribute. Note that because of the way they are defined, in order to use any of the effects in this page all you have to do is copy and paste its definition in your document.

Drop Shadow

Figure 2. Drop Shadow

<?xml version="1.0"?>

<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150" version="1.1">

  <defs>

    <filter id="dropShadow" x="0" y="0" width="150" height="150">

      <feOffset in="SourceGraphic" dx="0" dy="0" result="topCopy" />

      <feGaussianBlur in="SourceAlpha" stdDeviation="2" result="shadow" />

      <feOffset in="shadow" dx="3" dy="3" result="movedShadow" />

      <feMerge>

        <feMergeNode in="topCopy" />

        <feMergeNode in="movedShadow" />

      </feMerge>

    </filter>

  </defs>



<g filter="url(#dropShadow)" >

<text x="10" y="45" 

      style="font-family:Verdana; font-size: 20pt; fill:green;">Shadows</text>

<text x="10" y="65" 

      style="font-family:Verdana; font-size: 20pt; fill:green;">and</text>

<text x="10" y="85" 

      style="font-family:Verdana; font-size: 20pt; fill:green;">Tall Trees</text>

</g>



</svg>

Listing 2. outershadow.svg

For the drop shadow effect we use a slightly more elaborate filter. This time we use three operations: first we create a copy of the source graphic, then we blur the source, and then we move the result of the blur 3 pixels to the right and 3 pixels down.

Note that in order to specify the result of a primitive as the input of another we use the result and in attributes.

At the end of the description of our filter we merge the two layers (copy and shadow) to create the final output. Finally, the filter can be applied not only to individual elements but also to groups.

Type Within Image

For the type-within-image effect we use a mask, not a filter. Masks allow you to define a region through which other elements are visible.

Figure 3. type within Image

<?xml version="1.0"?>

<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" version="1.1">

 <defs>

 <mask id="star">

  <polygon points="155.5,45.6146 181.334,119.935 260,121.538 197.3,169.074 

                   220.085,244.385 155.5,199.444 90.9154,244.385 113.7,169.074 

                   51,121.538 129.666,119.935"

      transform="matrix(1 0 0 1.04643 1.9873e-014 -6.73254) 

                 translate(-52.381 -37.9218)"

      style="fill:rgb(255,255,255);stroke:rgb(0,0,0);stroke-width:1" />

 </mask>

 </defs>



<g mask="url(#star)" style="font-family:Verdana; font-size: 10pt; fill:red;">

<text x="80" y="80" 

      style="font-size:24pt; fill:pink;">the</text>

<text x="0" y="130" 

      style="font-size: 60pt; fill:pink;">Girlie</text>

<text x="20" y="190" 

      style="font-size: 60pt; fill:pink;">Show</text>



<g>

<text x="30" y="20" >Bye bye baby bye bye</text>

<!-- other text -->

</g>



</svg>

Listing 3. typeinimage.svg

As you can see, in the defs element we define a mask, in this case a polygon (the star). We give it a name which we use later on in the g element to refer to the mask.

Image Within Type

The inverse of the previous effect is also a common typographic treatment: image within type, or, in SVG terms, using type as the mask.


<?xml version="1.0"?>

<svg xmlns="http://www.w3.org/2000/svg" 

xmlns:xlink="http://www.w3.org/1999/xlink"

width="400" height="300" version="1.1">

  <defs>

  <mask id="britannia">

      <text x="0" y="100" 

      style="font-size:50pt; 

      font-weight:bold;  

      fill:white;">BRITANNIA</text>

  </mask>

  </defs>

  <g mask="url(#britannia)">

    <image x="0" y="0" 

    width="400px" height="205px"

    xlink:href="brit.jpg" />

  </g>

</svg>

Listing 4. imageintype.svg

Figure 4. Image within type

Stroke

One very common effect applied to text in design programs is stroke. The idea is an effect similar to that of outline (which you can do with the CSS property), but slightly more subtle as it comes not from really outlining the text but from composing a dilated version of it (expanded) with the original.

<?xml version="1.0"?>

<svg xmlns="http://www.w3.org/2000/svg" 

     width="400" height="300" version="1.1">



<defs>

 <filter id="Stroke" 

            filterUnits="objectBoundingBox" 

            x="-10%" y="-10%"

	 width="150%" height="150%">

   <feMorphology in="SourceGraphic" radius="2" 

`                operator="dilate" result="dilated" />

   <feColorMatrix type="hueRotate" in="SourceGraphic" 

                  values="90" result="black" />

   <feMerge>

     <feMergeNode in="dilated" />

     <feMergeNode in="black" />

   </feMerge>

 </filter>

</defs>



<g filter="url(#Stroke)">

  <text x="0" y="80" style="font-size: 40pt; 

                           fill:#C62D13;">

         Stroke</text>

</g>



</svg>

Listing 5. stroke.svg

Figure 5. Stroke

Note that the code for stroke above uses the original color as the color of the stroke and provides a simple formula for calculating a complementary color in the inside. If this is not desirable, you can use dilate separately and simply superimpose normal text over dilated text as shown next.

Dilate

Dilate is commonly used on its own, as shown in the first line of text of Figure 7; or by superimposing another line of text on top of it. That creates the same effect as Stroke, without the restriction of a formula for colors as prescribed in Listing 5.

Figure 6. Dilate

<?xml version="1.0"?>

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"

     width="400" height="300" version="1.1">



<defs>

 <filter id="Dilate" filterUnits="objectBoundingBox" x="-10%" y="-10%"

	 width="150%" height="150%">

   <feMorphology in="SourceGraphic" radius="2" 

                operator="dilate" result="dilated" />

 </filter>

</defs>



  <text filter="url(#Dilate)" x="0" y="80" style="font-size: 40pt; 

                              fill:#00000; ">white</text>



  <text filter="url(#Dilate)" x="0" y="110" style="font-size: 40pt; 

                              fill:#FF0000; ">stripes</text>



  <text x="0" y="110" style="font-size: 40pt; 

                              fill:#FFFFFF; ">stripes</text>





</svg>

Listing 6. dilate.svg

Bevel

A true classic:

Figure 7. Bevel

<?xml version="1.0"?>

<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150" version="1.1">

  <defs>

   <filter id="Bevel" filterUnits="objectBoundingBox" x="-10%" y="-10%"

  	   width="150%" height="150%">

	<feGaussianBlur in="SourceAlpha" stdDeviation="3" result="AlphaBlur" />

	<feSpecularLighting in="AlphaBlur" surfaceScale="4" 

                            specularConstant="0.5"

  	                    specularExponent="10" result="AlphaBlurSpecular"

		            style="lighting-color:rgb(255,255,255)">

    	    <fePointLight x="-10000" y="-10000" z="20000" />

	</feSpecularLighting>

	<feComposite in="AlphaBlurSpecular"  in2="SourceAlpha" 

                        operator="in" result="output" />

	<feComposite in="SourceGraphic" in2="output" operator="arithmetic" k1="0"

			k2="1" k3="1" k4="0" result="litPaint" />

   </filter>

  </defs>



<rect x="0" y="0" width="150" height="150" style="fill:gray; stroke:black" />

<rect x="0" y="60" width="150" height="30" style="fill:white;" />

<rect x="40" y="0" width="40" height="150" style="fill:white;" />

<text filter="url(#Bevel)" 

      x="20" y="85" 

      style="font-family:Arial; font-weight:bold; font-size: 30pt; fill:Black;

	     letter-spacing:-3;">S.K.G.</text>



</svg>

Listing 7. Bevel.svg

Gradient

Unlike filters, gradients don't need to be defined inside the defs element. The syntax for gradients is pretty self-explanatory, as you can see for the case of linear gradients in Listing 8. However, if you would like the technical specification of each parameter, please refer to the the SVG Specification.

Figure 8. Gradient

<?xml version="1.0"?>

<svg xmlns="http://www.w3.org/2000/svg" 

      width="350" height="350" version="1.1">



    <linearGradient id="orangeGrad" 

        gradientUnits="userSpaceOnUse"

         x1="0" y1="0" x2="350" y2="350">

      <stop offset="0"   stop-color="#ff7b00" />

      <stop offset=".33" stop-color="#ffDF01" />

      <stop offset="1"   stop-color="#ff7b00" />

    </linearGradient>





<g style="font-family:Arial; font-weight:bold;

      font-size: 30pt; fill:url(#orangeGrad);">

<text x="20" y="85"> If you're fond of</text>

<text x="20" y="115"> sand dunes and</text>

<text x="20" y="145"> salty air</text>

</g>



</svg>

Listing 8. Gradient.svg

A little word of advice about gradients: less is more. Subtle gradients between colors close to each other in the wheel or between hues of a color usually work a lot better than rainbowesque gradients of complementary (or many) colors

Spacing

In the first part of this article we saw the CSS property letter-spacing, which when taken as as design devise yields interesting results. Use this tool with care as it's (over)use as a sign of elegance may be getting old.

Figure 9. Spacing


<?xml version="1.0"?>

<svg xmlns="http://www.w3.org/2000/svg" width="400" height="100" version="1.1">



<text x="20" y="85" style="letter-spacing:30">huey lewis</text>

</g>



</svg>

Listing 9 Spacing.svg

Skew

The primitive transformation of skew allows you to obtain this classic effect. Although most applications of the effect you see in designs are skewed horizontally (on the x-axis), SVG provides also, like most other design tools, skewing on the y-axis.

Figure 10. Skew

<?xml version="1.0" standalone="yes"?>

<svg width="200px" height="300px" version="1.1" >

 <g transform="skewX(-25)">

   <g fill="black" stroke="gray" stroke-width="1" >

     <text x="20" y="40" font-size="19" font-family="Arial">

      Fast Moving Company

     </text>

   </g>

 </g>

 <g transform="skewY(40)">

   <g fill="blue">

     <text x="20" y="60" font-size="19" font-family="Arial">

      Down the Drain

    </text>

  </g>

 </g>

</svg>

Listing 10. Skew.svg

Paths

Another common static typographic treatment is to put text on a particular path. For this we need only to define the path within our defs and include the textPath subelement inside the text we want to adhere to it.

Figure 11. Path

<?xml version="1.0" standalone="yes"?>

<svg width="200px" height="300px" version="1.1" 

     xmlns:xlink="http://www.w3.org/1999/xlink"	>

 <defs>

    <path id="rev"

      d="M18.6047 104.651 C53.1235 72.4336 83.6448 87.6563 

	 123.256 69.7674 C141.966 61.3178 155.274 43.3081

	 174.419 36.0465 C209.014 22.9243 200.4 49.8433

	 211.628 -3.48837" />

 </defs>

   <g fill="green" stroke="gray" stroke-width="1" >

     <text x="80" y="40" style="font-size:19; font-family:Arial; fill=green; 

                  stroke:black; stroke-width=1; 

                  text-anchor:right">

       <textPath xlink:href="#rev">

	Our Year Revenue

       </textPath>

     </text>

   </g>

</svg>

Listing 11. Path.svg

Note that, as with any other effect we've discussed, we can still use the orientation, alignment, and other CSS properties we discussed in the two previous sections of this article.

Blend Modes

We finish our cookbook of typographic treatments with a subtle set of tools that are essential for modern design work: blend modes. As you probably know from Photoshop experience, blending layers of text into other images can make the difference between a cohesive and tasteful interplay of content versus the awkward look of text "pasted" on top of images.

Figure 12. Blending Modes

<?xml version="1.0"?>

<svg xmlns="http://www.w3.org/2000/svg" 

     xmlns:xlink="http://www.w3.org/1999/xlink"

     width="150" height="300" version="1.1">

  <defs>

    <filter id="Multiply">

      <feBlend mode="multiply" in="SourceGraphic" 

	       in2="BackgroundImage" />

    </filter>

  </defs>



   <!-- without multiply -->

  <g font-family="arial" font-size="10">

    <image x="0" y="0" width="150px" height="112px" 

                       xlink:href="withchuck.jpg" /> 

    <text x="75" y="10" style="fill:white; 

    text-anchor:middle">

               © 2003 Fabio Arciniegas</text>

  </g>



  <!-- with multiply -->

  <g>

    <image x="0" y="112" width="150px" 

       height="112px" xlink:href="withchuck.jpg" /> 

    <g font-family="Verdana" font-size="10" fill="white"

       fill-opacity=".6" >

       <text x="10" y="122" filter="url(#Multiply)">

	© 2003 Fabio Arciniegas</text>

    </g>

  </g>

</svg>

Listing 12. blend.svg

As shown in Listing 12, the syntax for defining and using blending modes is simple: define a filter using the primitive feBlend and specify in the mode attribute the desired value. The real question, then, is which modes are available. The answer is five: normal, multiply, screen, darken, and lighten (normal being the default). You can find their mathematical description on the SVG Specification, but most likely you will want to experiment with them directly so Listing 13 below provides definitions for each that you can use in your documents.




<defs>

    <filter id="Normal">

      <feBlend mode="normal" in2="BackgroundImage" in="SourceGraphic" />

    </filter>

    <filter id="Multiply">

      <feBlend mode="multiply" in2="BackgroundImage" in="SourceGraphic" />

    </filter>

    <filter id="Screen">

      <feBlend mode="screen" in2="BackgroundImage" in="SourceGraphic" />

    </filter>

    <filter id="Darken">

      <feBlend mode="darken" in2="BackgroundImage" in="SourceGraphic" />

    </filter>

    <filter id="Lighten">

      <feBlend mode="lighten" in2="BackgroundImage" in="SourceGraphic" />

    </filter>

  </defs>



Figure 13. Filter Definitions for blending modes

This concludes our cookbook of static typographic treatments, and the third part of our exploration of SVG and type. Please stay tuned for the fourth and concluding installment, where we explore animation of type in the context of SVG.