SVG & colors in OpenType fonts

Sample of a colorfont


Until recently having more than one color in a glyph of a vector font was technically not possible. Getting a polychrome letter required multiplying the content for every color. Like it happened with many other techniques before, it took some time for digital type to overcome the constraints of the old technique. When printing with wood or lead type the limitation to one color per glyph is inherent (if you don’t count random gradients). More than one color per letter required separate fonts for the differently colored parts and a new print run for every color. This has been done beautifully and pictures of some magnificent examples are available online. Using overprinting the impression of three colors can be achieved with just two colors.

Overprinting colors
Simulation of two overprinting colors resulting in a third.

Digital font formats kept the limitation to one ‘surface’ per glyph. There can be several outlines in a glyph but when the font is used to set type the assigned color applies to all outlines. Analog to letterpress the content needs to be doubled and superimposed to have more than one color per glyph. Multiplying does not sound like an elegant solution and it is a constant source of errors.

It took some emojis until the demand for multi-colored fonts was big enough to develop additional tables to store this information within OpenType fonts. As of this writing there are several different ways to implement this. Adam Twardoch compares all proposed solutions in great detail on the FontLab blog.

To me the Adobe/Mozilla way looks the most intriguing.

Upon its proposal it was discussed by a W3C community group and published as a stable document. The basic idea is to store the colored glyphs as svgs in the OpenType font. Of course this depends on the complexity of your typeface but svgs should usually result in a smaller file size than pngs. With the development of high resolution screens vectors also seem to be a better solution than pixels. The possibility to animate the svgs is an interesting addition and will surely be used in interesting (and very annoying) ways. BLING BLING.


I am not a font technician or a web developer just very curious about this new developments. There might be other ways but this is how I managed to build colorful OpenType fonts.

In order to make your own you will need a font editor. There are several options like RoboFont and Glyphs (both Mac only), FontLab and the free FontForge. RoboFont is the editor of my choice, since it is highly customizable and you can build your own extensions with python. In a new font I added as many new layers as the amount of colors I wanted to have in the final font. Either draw in the separate layers right away or just copy the outlines into the respective layer after you’ve drawn them in the foreground layer. With the very handy Layer Preview extension you can preview all Layers overlapping. You can also just increase the size of the thumbnails in the font window. At some point they will show all layers. Adjust the colors to your liking in the Inspector since they are used for the preview.

RoboFont Inspector
Define the colors you want to see in the Layer Preview
A separated letter
Layer preview
The outlines of the separate layers and their combination

When you are done drawing your outlines you will need to safe a ufo for every layer / color. I used a little python script to safe them in the same place as the main file:

f = CurrentFont()
path = f.path

for layer in f.layerOrder:
newFont = RFont()

for g in f:
    orig = g.getLayer(layer)
    newFont[].width = orig.width
    newFont[].update() = = layer = path[:-4] +"_%s" % layer +".ufo")

print "Done Splitting"

Once I had all my separate ufos I loaded them into TransType from FontLab. Just drop your ufos in the main window and select the ones you want to combine. In the Effect menu click ‘Overlay Fonts …’. You get a preview window where you can assign a rgba value for each ufo and then hit OK. Select the newly added font in the collection and export it as OpenType (ttf). You will get a folder with all colorfont versions.

The preview of your colorfont in TransType.


In case you don’t want to use TransType you might have a look at the very powerful RoboFont extension by Jens Kutílek called RoboChrome. You will need a separate version of your base-glyph for every color, which can also be done with a scipt if you have all of your outlines in layers.

f = CurrentFont()
selection = f.selection

for l, layer in enumerate(f.layerOrder):
for g in selection:
    char = f[g]
    name = g + ".layer%d" % l
    f[name].width = f[g].width
    l_glyph = f[g].getLayer(layer)
    f[name].mark = (.2, .2, .2, .2)

print "Done with the Devision"
For RoboChrome you will need to split your glyph into several.


You can also modify the svg table of a compiled font or insert your own if it does not have any yet. To do so I used the very helpful fonttools by Just van Rossum. Just generate a otf or ttf with the font editor of your choice. Open the Terminal and type ttx if you are on Mac OS and have fonttools installed. Drop the font file in the Terminal window and hit return. Fonttools will convert your font into an xml (YourFontName.ttx) in the same folder. This file can then be opened, modified and recompiled into a otf or ttf.

This can be quite helpful to streamline the svg compiled by a program and therefore reduce the file size. I rewrote the svg of a 1.6mb font to get it down to 980kb. Using it as a webfont that makes quite a difference. If you want to add your own svg table and font that does not have any yet you might read a bit about the required header information. The endGlyphID and startGlyphID for the glyph you want to supply with svg data can be found in the <GlyphOrder> Table.

    <!-- here goes your svg -->
<svgDoc endGlyphID="19" startGlyphID="19">...</svgDoc>
<svgDoc endGlyphID="20" startGlyphID="20">...</svgDoc>
One thing to keep in mind is the two different coordinate systems.  Contrary to a digital font svg has a y-down axis. So you either have to draw in the negative space or you draw reversed and then mirror everything with: </p>
<pre lang="css">transform="scale(1,-1)"</pre>
    <a href=""><img src="" alt="Y-axis comparison"></a><figcaption>While typefaces usually have a y-up axis SVG uses y-down.</figcaption></figure>
Now if you really want to pimp your fonts you should add some unnecessary animation to annoy everybody.  Just insert it between the opening and closing tags of whatever you want to modify. Here is an example of a circle changing its fill-opacity from zero to 100% over a duration of 500ms in a loop.  </p>
<pre lang="xml"><circle>
<animate    attributeName="fill-opacity"
<img src="" alt=""><br />
<p>Technically these fonts should work in any application that works with otfs or ttfs. But as of this writing only Firefox shows the svg. If the rendering is not supported the application will just use the regular glyph outlines as a fallback.  So if you have your font(s) ready it’s time to write some css and html to test and display them on a website.</p>
<p>The @font-face</p>
<pre lang="css">@font-face {
font-family: "Colors-Yes"; /* reference name */
src: url('./fonts/Name_of_your_font.ttf');
font-weight: 400; /* or whatever applies */
font-style: normal; /* or whatever applies */
text-rendering: optimizeLegibility; /* maybe */
<p>The basic css</p>
<pre lang="css">.color_font { font-family: "Colors-Yes"; }</pre>
<p>The HTML</p>
<pre lang="xml"><p class="color_font">Shiny polychromatic text</p></pre>
<p>As of this writing (October 2014) the format is supported by Firefox (26+) only. Since this was initiated by Adobe and Mozilla there might be a broader support in the future. </p>
While using svg has the advantage of reasonably small files and the content does not have to be multiplied it brings one major drawback.  Since the colors are ‘hard-coded’ into the font there is no possibility to access them with css.  Hopefully this might change with the implementation of a &lt;COLR/CPAL&gt; table. </p>
<p>There is a <a href="" target="_blank">bug</a> that keeps animations from being played in Firefox 32.  While animations are rendered in the current version (33) this might change for obvious reasons. </p>
<p>Depending how you establish your svg table it might blow up and result in fairly big files. Be aware of that in case you use them to render the most crucial content of your websites.</p>
<li><a href="" target="_blank">Carving typeface</a></li>
<li><a href="" target="_blank">International maritime signal flags</a> The flag alphabet of the ICS</li>
<li><a href="" target="_blank">Sparkle Motion</a> Be warned! A hefty 980kb font with lots of animation. </li>
<li><a href="" target="_blank">Bubbles gone wrong</a> do not confuse $1 and $2 in your grep. </li>
<h2>Links, Credits &#038; Thanks</h2>
<li>The <a href="" target="_blank"> Specifications of svg glyphs in OpenType</a> on <a href="" target="_blank"></a></li>
<li>The <a href="" target="_blank">ufo</a> format by Tal Leming, Just van Rossum and Erik van Blokland</li>
<li><a href="" target="_blank">Robofont</a> by Frederik Berlaen</li>
<li><a href="" target="_blank">Layer preview</a> for Robofont by Frederik Berlaen</li>
<li><a href="" target="_blank">RoboChrome</a> for Robofont by Jens Kutílek</li>
<li><a href="" target="_blank">TransType</a> by <a href="" target="_blank">FontLab</a></li>
<li><a href="" target="_blank">fonttools</a> by Just van Rossum and <a href="" target="_blank">a fork on github</a></li>
<li><a href="" target="_blank">Polychromatic type specimen</a> on</li>
<li><a href="" target="_blank">An animated sample from mozilla</a></li>
<li>Syntax highlighting done with <a href="" target="_blank">prism</a>.</li>
<li><a href="" target="_blank">Symbolset</a> has a similar <a href="" target="_blank">Tutorial</a></li>
<p>Thanks Erik, Frederik, Just and Tal for making great tools! </p>
    <section class="about">
                                <h2 class="about__header">About
                          <a class="url" href="" rel="external me">
                Johannes Lang              </a>
                      <p>I am a graphic and type designer working in Vienna (Austria). I am also part-time teaching typography at the university of applied arts Vienna. I like paper objects and riding my bicycle.</p>
                    <ul class="author-meta fa-ul"><li><i class="fa-li fa fa-globe"></i><a href="" class="website" rel="me"></a></li><li><i class="fa-li fa fa-twitter"></i><a href="" class="twitter" rel="me">@langustefonts</a></li></ul>            <p><a class="url" href="">More articles by Johannes Lang&hellip;</a></p>
                  <h2 class="about__header">About
                          <a class="url" href="" rel="external me">
                Robert Nyman [Editor emeritus]              </a>
                      <p>Technical Evangelist &amp; Editor of Mozilla Hacks. Gives talks &amp; blogs about HTML5, JavaScript &amp; the Open Web. Robert is a strong believer in HTML5 and the Open Web and  has been working since 1999 with Front End development for the web - in Sweden and in New York City.

He regularly also blogs at <a href=""></a> and loves to travel and meet people.</p>
                    <ul class="author-meta fa-ul"><li><i class="fa-li fa fa-globe"></i><a href="" class="website" rel="me"></a></li><li><i class="fa-li fa fa-twitter"></i><a href="" class="twitter" rel="me">@robertnyman</a></li><li><i class="fa-li fa fa-google-plus"></i><a href="" class="gplus" rel="me">Google+</a></li></ul>            <p><a class="url" href="">More articles by Robert Nyman [Editor emeritus]&hellip;</a></p>
  <section class="promo">
    <form id="newsletterForm" name="newsletter-form" class="newsletter block block--1 block--polite" action="" method="post">
  <h2 class="heading">Discover great resources for web development</h2>
  <p class="newsletter__description">Sign up for the Mozilla Developer Newsletter:</p>
  <input id="fmt" name="fmt" value="H" type="hidden">
  <input id="newsletterNewslettersInput" name="newsletters" value="app-dev" type="hidden">

  <div id="newsletterErrors" class="newsletter__errors"></div>

  <div id="newsletterEmail" class="form__row">
    <label for="newsletterEmailInput" class="offscreen">E-mail</label>
    <input id="newsletterEmailInput" name="email" class="newsletter__input" required="" placeholder="" size="30" type="email">

  <div id="newsletterPrivacy" class="form__row form__fineprint">
    <input id="newsletterPrivacyInput" name="privacy" required="" type="checkbox">
    <label for="newsletterPrivacyInput">
      I'm okay with Mozilla handling my info as explained in this <a href="">Privacy Policy</a>.
  <button id="newsletter-submit" type="submit" class="button positive">Sign up now</button>
<div id="newsletterThanks" class="newsletter newsletter--thanks block block--1 block--polite hidden">
  <h2 class="heading">Thanks! Please check your inbox to confirm your subscription.</h2>
  <p>If you haven’t previously confirmed a subscription to a Mozilla-related newsletter you may have to do so. Please check your inbox or your spam filter for an email from us.

</main><!-- /#content-main -->


    <footer class="footer section section--fullwidth">
      <div class="row">
        <p class="block block--1">
          Except where otherwise noted, content on this site is licensed
          under the
          <a href="" rel="license external">Creative Commons Attribution Share-Alike License v3.0</a>
          or any later version.
        <img class="footer__logo" alt="the Mozilla dino logo" src="">
  <link rel='stylesheet' id='hljstheme-css'  href='' type='text/css' media='all' />
<script type='text/javascript' src=''></script>
<script type='text/javascript' src=''></script>
<script type='text/javascript' src=''></script>
    <style>pre.hljs {padding: 5px;}
pre.hljs code {}</style>
    <script type="text/javascript">
    (function($, window) {
        var init_fn_flag = false;
        var init_fn = (function() {
            if (init_fn_flag)
            init_fn_flag = true;
             hljs.configure({"tabReplace":"    "});
            $('pre code').each(function(i, block) {
        $(window).on("load", init_fn);
    })(jQuery, window);
    // External links should open in a new tab.
    (function () {
      var postLinks = document.querySelectorAll('#content-main a');

      var origin = location.origin;

      for (var i = 0; i < postLinks.length; i++) {
        var link = postLinks[i];
        if (link.origin !== origin && !link.getAttribute('target')) {
          link.setAttribute('target', '_blank');

    window.addEventListener('load', function () {
      if (document.querySelector('#newsletterForm')) {
        var script = document.createElement('script');
        var path = document.head.getAttribute('data-template-path');
        script.setAttribute('src', path + '/js/newsletter.js');