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-007F | Basic Latin |
U+00A0-00FF | Latin-1 Supplement |
U+0100-017F | Latin Extended-A |
U+2000-206F | General Punctuation |
U+2070-209F | Superscripts and Subscripts |
U+20A0-20CF | Currency Symbols |
U+2100-214F | Letterlike Symbols |
U+2200-22FF | Mathematical Operators |
U+FB00-FB4F | Alphabetic 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.