How to subset a variable font

Using variable fonts can be an excellent way to reduce bandwidth and latency as they can highly efficiently combine multiple styles into one font file. However, as with standard professional fonts, they can contain a lot characters you will never use (in my case this would include Greek and Cyrillic – your mileage will vary). These un-needed characters can be removed from the font to reduce file-size, a process called subsetting.

This post is primarily an aide-memoire explaining how to subset fonts from first principles on a Mac (macOS Monterey 12.3.1 at time of writing). I'll be using fonttools, an open source font manipulation library written in Python, to subset a font and create a WOFF2 font file for use on the Web.

1. Install Python

macOS no longer comes bundled with Python, so firstly download and install the latest version of Python 3 following the instructions in the macOS installer.

2. Install fonttools

Once Python 3 has installed successfully, open Terminal and install fonttools using pip (which is shipped with Python):

python3 -m pip install fonttools

3. Install Brotli

Next install Brotli compression, which is required to output a WOFF2.

python3 -m pip install brotli

4. Move to where the font file is

Change the Terminal directory to the folder containing the font file you want to convert, eg.

cd /Users/rich/

5. Run fonttools

Finally run the fonttools subsetting routine using the desired options. For example:

pyftsubset LiterataTT.ttf 
    --unicodes="U+0020-007F, U+00A0-00FF, U+0100-017F, U+2000-206F, U+2070-209F, U+20A0-20CF, U+2100-214F, U+2200-22FF, U+FB00-FB4F" 
    --layout-features='*' 
    --flavor="woff2" 
    --output-file="LiterataTT.woff2"

In this case I am subsetting LiterataTT.ttf and outputting as LiterataTT.woff2 into the same directory.

The unicodes option specifies which characters to include in the subset, by way of Unicode character ranges. It doesn't matter if the font doesn't have all the Unicode ranges available in the list, or all the characters within a specified range – these just get ignored. In this case I am including the following Unicode ranges:

U+0020-007FBasic Latin
U+00A0-00FFLatin-1 Supplement
U+0100-017FLatin Extended-A
U+2000-206FGeneral Punctuation
U+2070-209FSuperscripts and Subscripts
U+20A0-20CFCurrency Symbols
U+2100-214FLetterlike Symbols
U+2200-22FFMathematical Operators
U+FB00-FB4FAlphabetic Presentation Forms

The layout-features option specifies which OpenType layout features to include, such as kerning, ligatures, numerals and alternate characters. In this example I'm using layout-features='*' to include all the features available. Instead, you can also add selected features to the default set. For example layout-features+=onum,pnum,ss01 will keep the default set of features and add onum, pnum and ss01 (old-style and proportional numerals, and styleset 1). The default features include calt, clig, kern and liga among others. See the fonttools Documentation for more info on layout-features.

Applying that subsetting routine to LiterataTT reduced the original font file from 913Kb to 207Kb. The latter is still a fairly large file, but I was conservative in my subsetting (as in keeping in plenty of European characters and OpenType features) and, as a variable font, this file will cover all situations from light to black weights and low to high contrast optical sizing.