Ubuntu – Subsetting a font from the command line and turning it into a webfont

command linefonts

I have a few web projects that link into various icon fonts (like FontAwesome and Glyphicons). These are great fonts but they include a lot of glyphs that I never use. ~65KB is a relatively big download for something you only use 4% of.

It is possible to subset fonts, that is only include the characters you use. You can do this through Font Squirrel (and probably others) while converting an .OTF into your webfonts (a pack of EOT, WOFF2, WOFF, TTF and SVG for different browsers). This works great.

I usually do that right at the end of a project… Once… But if your range of characters changes, you have to go through the whole process again. And it's completely manual. And tedious.

I can generate a list of characters I need so all I need is something that can subset the font (and then for bonus points, generates the webfonts pack). I suspect the whole thing is possible through Fontforge (it has scripting APIs) but I've no experience with that.

Best Answer

This is bigger than I originally thought. To do the whole thing, we need some extra tools and some aren't well packaged for Ubuntu. I'm doing the custom installs in ~/src/ —which you'll probably need to create— rather than installing to the system, if only because that's Good Enough™ to get the job done.

sudo apt-get install fontforge python-scour woff-tools build-essential

git clone http://github.com/behdad/fonttools ~/src/fonttools
ln -s ~/src/fonttools/Tools/pyftsubset ~/bin/subset  # vanity symlink

git clone https://github.com/metaflop/ttf2eot.git ~/src/ttf2eot
cd ~/src/ttf2eot
make
cd -

git clone --recursive https://github.com/google/woff2.git ~/src/woff2
cd ~/src/woff2
make clean all
cd -

The next step is working out what unicode characters it is we need. We're going to build a list of characters in the hex format 0x####. Identifying these is just a case of picking through my .less file for the function I use for Font Awesome, but you could do something similar looking for raw content: "..." groups:

perl -n -e '/\.font-awesome..(\w+)/ && print "0xf$1\n"' less/*.less | tail -n+2 | sort -u

Now we have the list, we can tell FontForge to subset FontAwesome.otf:

~/src/fonttools/Tools/pyftsubset fonts/FontAwesome.otf \
--unicodes-file=<(perl -n -e '/\.font-awesome..(\w+)/ && print "0xf$1\n"' less/sbnew-*.less\
| tail -n+2 | sort -u) --output-file=fonts/fa-subset.otf --no-recommended-glyphs --no-hinting

This creates a new .otf font. We can then recondition that into a set of webfonts:

# generate TTF and SVG
fontforge -lang=pe -script <(echo -e 'Open($1)\nGenerate($1:r + ".ttf")\nGenerate($1:r + "big.svg")') fonts/fa-subset.otf

# Clean up SVG
scour -i fonts/fa-subset.big.svg -o fonts/fa-subset.svg --enable-id-stripping --enable-comment-stripping --shorten-ids

# Create WOFF
sfnt2woff fonts/fa-subset.otf

# Create WOFF2 for most modern browsers
~/src/woff2/woff2_compress fonts/fa-subset.ttf

# Create EOT (eotfast might be better)
~/src/ttf2eot/ttf2eot fonts/fa-subset.ttf > fonts/fa-subset.eot

The result is a much smaller set of fonts. Here's the side-by-side comparison:

-rw-r--r-- 1 oli oli  62K Dec 11  2013 FontAwesome.otf
-rw-rw-r-- 1 oli oli 2.0K Aug 27 15:16 fa-subset.otf

-rwxr-xr-x 1 oli oli  38K Dec 11  2013 fontawesome-webfont.eot
-rw-rw-r-- 1 oli oli 3.1K Aug 27 15:31 fa-subset.eot

-rwxr-xr-x 1 oli oli 198K Dec 11  2013 fontawesome-webfont.svg
-rw-rw-r-- 1 oli oli 4.4K Aug 27 15:37 fa-subset.svg

-rwxr-xr-x 1 oli oli  79K Dec 11  2013 fontawesome-webfont.ttf
-rw-rw-r-- 1 oli oli 2.9K Aug 27 15:22 fa-subset.ttf

-rwxr-xr-x 1 oli oli  44K Dec 11  2013 fontawesome-webfont.woff
-rw-rw-r-- 1 oli oli 1.9K Aug 27 15:25 fa-subset.woff
-rw-rw-r-- 1 oli oli 1.4K Aug 27 16:01 fa-subset.woff2