Brett Klamer

Subset and Self-Host Web Fonts

Unfortunately, not every device has the same set of system fonts. In order to use specific fonts on webpages, you will need to serve a font file from the server. It's become popular to off-load this process using Google Fonts or similar services, but it's not hard to make a robust solution yourself.

Choose a font

Font Squirrel has a nice database of fonts that is also searchable. When you find one you like, download the .woff or .otf font file for each style and weight you need. Make sure the license allows embedding within CSS.

Subset the font

The size of font files can be a problem, especially when you need several styles. To combat this, you can strip away any unnecessary glyphs. This process is known as subsetting the font file. Manual subsetting can be done with FontForge. FontForge is an open source tool that makes subsetting relatively easy. The online book Design With FontForge has a nice section on installing and is a good reference for usage.

# Install for ubuntu
sudo add-apt-repository ppa:fontforge/fontforge
sudo apt-get update
sudo apt-get install fontforge
If you have issues with the Ubuntu install, try installing libtiff4 from here and the FontForge files from here.

Automatic subsetting can be done with Font Squirrel's Webfont Generator and, to a lesser extent, Fontie.

Before subsetting the font file, consider which glyphs you need. It's likely you only need the basic Latin Unicode glyphs and a few extras. See this Unicode table to get a sense of the possibilities.

Automatic subsetting with Font Squirrel

  1. Go to Font Squirrel's Webfont Generator
  2. Upload your font file(s)
  3. Select Expert
    • Font Formats: Select WOFF
    • Truetype Hinting: TTFAutohint
      • This makes small font sizes look better on Windows. Ideally you want the Direct Write version of TTFAutohint.
    • Rendering: Select Fix Vertical Metrics and Fix GASP Table
    • Subsetting: Select Custom Subsetting
      • Character Type: Select Currency and Typographics
      • Language: Select English and/or your locale. French and German are useful to have for those pesky letters with diacritics.
      • Unicode Tables: Select Basic Latin
    • Advanced Options: Inspect the Em Square Value of the original font file with FontForge. It's likely 1000, 1024, or 2048.

Automatic subsetting with Fontie

  1. Go to the Fontie webpage
  2. Upload your font file
  3. Subsetting: Select Latin
  4. Hinting: Select Direct Write
  5. Output: Select WOFF
Hinting is only useful for low/normal resolution Windows devices. Linux and Mac automatically apply their own font corrections. This problem should go away as pixel density is finally increasing.

Manual subsetting with FontForge

  1. Open the font file with FontForge
  2. Select Encoding > Compact to easily view the defined glyphs.
  3. Select (highlight) the glyph you don't need.
    • Use shift to select more than one glyph.
    • You can invert your selection with Edit > Select > Invert Selection.
  4. Remove the highlighted glyphs using Encoding > Detach & Remove Glyphs.
  5. Save the subsetted font by File > Generate Fonts. Choose the .woff file format.

FontForge

Include the font in CSS

Add something like this to your CSS file

@font-face {
    font-family: 'FontName';
    src: url('fonts/LightFont.woff') format('woff');
    font-weight:200;
    font-style:normal;
}
@font-face {
    font-family: 'FontName';
    src: url('fonts/ItalicFont.woff') format('woff');
    font-weight:normal;
    font-style:italic;
}
@font-face {
    font-family: 'FontName';
    src: url('fonts/RegularFont.woff') format('woff');
    font-weight:normal;
    font-style:normal;
}
@font-face {
    font-family: 'FontName';
    src: url('fonts/BoldFont.woff') format('woff');
    font-weight:700;
    font-style:normal;
}
@font-face {
    font-family: 'MonospaceFontName';
    src: url('fonts/MonospaceFont.woff') format('woff');
    font-weight:normal;
    font-style:normal;
}

To use the font, simply add the font name to the front of the list in the CSS file.

body {
    font-family: 'FontName', sans-serif;
}
pre, code {
    font-family: 'MonospaceFontName', monospace;
}

Additional Speed improvements

Font Squirrel's Webfont generator includes the option for Data URI Base64 encoding to use in the CSS file. The idea is similar to before, but instead of requesting separate woff files from the CSS, the woff file information is included directly in the CSS. You will end up with a much longer version of the following

@font-face {
    font-family: 'FontName';
    src: url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAA...MAAA==) format('woff');
    font-weight: 200;
    font-style: normal;
}
@font-face {
    font-family: 'FontName';
    src: url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAA...EBobwAA) format('woff');
    font-weight: normal;
    font-style: italic;
}
/* etc... */

In the graph below, you can see that Data URIs were not used and it took three separate requests to build the page. By including the woff information in the css, you will be able to skip the font file requests and reduce page load time. This method also helps with the flash of unstyled text as seen in older browsers or Internet Explorer.

timeline

You should also make sure your pages are being cached and Gzipped. If you have an apache style server, you can add this to your .htaccess file (based on html5 boilerplate's .htaccess).

# ----------------------------------------------------------------------
# Gzip compression
# ----------------------------------------------------------------------
<IfModule mod_deflate.c>
    # Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
    <IfModule mod_setenvif.c>
        <IfModule mod_headers.c>
            SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
            RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
        </IfModule>
    </IfModule>

    <IfModule mod_mime.c>
        AddEncoding gzip svgz
    </IfModule>

  # Compress all output labeled with one of the following MIME-types
    <IfModule mod_filter.c>
        AddOutputFilterByType DEFLATE "application/atom+xml" \
                                      "application/javascript" \
                                      "application/manifest+json" \
                                      "application/rss+xml" \
                                      "application/xml" \
                                      "image/svg+xml" \
                                      "image/x-icon" \
                                      "text/cache-manifest" \
                                      "text/css" \
                                      "text/html" \
                                      "text/javascript" \
                                      "text/plain" \
                                      "text/x-component" \
                                      "text/xml"
    </IfModule>
</IfModule>
# ----------------------------------------------------------------------
# Cache assets
# ----------------------------------------------------------------------
# Remove `ETags` as resources are sent with far-future expires headers.
# `FileETag None` doesn't work in all cases.
<IfModule mod_headers.c>
    Header unset ETag
</IfModule>
FileETag None
# Serve resources with far-future expires headers.
<IfModule mod_expires.c>
    ExpiresActive on
    ExpiresDefault "access plus 1 week"
        # CSS
        ExpiresByType text/css "access plus 1 day"
        # Favicon (cannot be renamed!) and cursor images
        ExpiresByType image/x-icon "access plus 1 week"
        # HTML components (HTCs)
        ExpiresByType text/x-component "access plus 1 week"
        # HTML
        ExpiresByType text/html "access plus 1200 seconds"
        # JavaScript
        ExpiresByType application/javascript "access plus 1 day"
        ExpiresByType text/javascript "access plus 1 day"
        # Manifest files
        ExpiresByType text/cache-manifest "access plus 0 seconds"
        # Media files
        ExpiresByType image/gif "access plus 1 month"
        ExpiresByType image/jpeg "access plus 1 month"
        ExpiresByType image/png "access plus 1 month"
        # Web feeds
        ExpiresByType application/atom+xml "access plus 1 hour"
        ExpiresByType application/rss+xml "access plus 1 hour"
        # Web fonts
        ExpiresByType application/font-woff "access plus 1 week"
</IfModule>

Enjoy your fonts

Now you will want to inspect your pages to make sure your fonts are being displayed properly. Create a page with a set of relevant glyphs in each style to check for errors.

Last updated 2014-10-27 - Feel free to email me with any questions or comments.