How to Create a Font Subset
When building my OpenStreetMap pathfinding visualiser, I initially used flag icons I made in Figma to indicate start and destination points on the map. But the icons themselves were SVGs and a little blurry when rasterised on the canvas. I also had to position them with quite a large offset to make them look like they were sitting on top of the selected graph nodes. This was noticeable when I later made the icons draggable as they would jump a large distance to snap to the mouse's position when dragging started, which didn't look great.
I decided I wanted to use the location marker icon from Material Icons to solve the offset problem, as the marker is visually more central to a selected point so should reduce the dragging jump. I then set about figuring out how I might get that icon to render using deck.gl, which is the library I used to render and animate the pathing. I came across this comment in the deck.gl repository:
Don't use SVG if you want to use Material Icons. Follow the TextLayer example linked above. There is no advantage of using the SVG Material Icons versus using the web font.
I wasn't aware there was a Material Icons font so this gave me a new avenue to explore. But when I looked at the download size of the font it was 348KB! A 348KB download to use one icon is far too much. I couldn't justify that, but I still wanted to use the icon. The obvious question was: could I create a font that only contained that icon? The answer was yes, and the technique I employed to do it has a name: font subsetting.
Now I had two issues to solve:
- What is the character I need from the Material Icons font?
- How could I create a font subset using just that character?
To identify the character, I used Wakamai Fondue ("What can my font do?"). Once you select a font, Wakamai Fondue will analyse it, showing some statistics and the full character set below. Find the characters you're interested in and make a note of the hexadecimal number underneath the character(s) – this number is the Unicode code point of the character. A Unicode code point is a number assigned to a character in the Unicode standard. In the context of a font, this number is used to look up the character's visual representation in the font file. In my case, the location marker has a Unicode code point of E0C8.
Now that I knew what Unicode code point I needed, I had to create a font subset. To do that, I used a python library called fontTools. fontTools has a utility for creating subsets called fontTools.subset.
To install it, run:
py -m pip install fonttools
It also has a dependency on Brotli – Google's compression algorithm – so you'll need to install that too:
py -m pip install brotli
Finally, I ran the following to create a web open font:
py -m fontTools.subset .\MaterialIcons-Regular.ttf
--output-file="Test.woff2"
--flavor=woff2
--layout-features=*
--unicodes=e0c8
The --layout-features=* switch preserves all layout features embedded in the font. Fonts have various typographic features that enable them to render as intended. --unicodes allows you to specify the range of Unicode code points you want to include in the subset. In my case, that's just the location marker at e0c8.
After running the command, I tested the subsetted font in Wakamai Fondue to make sure the location marker was present. All good. The best part? The subsetted font is just 528 bytes – only about 0.15% of the original – and renders perfectly in deck.gl! Mission accomplished.