blog.kronis.dev


You might not need font subsetting

Date:

As you've probably noticed, this blog uses a few custom fonts, because I rather liked how the PT Fonts look. They support my own language, a bunch of extended latin characters, offer both sans serif, serif and monospaced variations and have an open license, meaning that I can use them wherever I want, from my websites to any desktop apps or even games I might make.

However, the obvious issue with this approach is that anyone who comes to the site needs to make some additional web requests and, even with how efficient WOFF2 is, that is still a bit of bandwidth:

00-custom-fonts

I will acknowledge that nowadays additional 200 KB of downloaded data probably isn't the end of the world, but admittedly it isn't ideal either. The good news is that the fonts only need to be downloaded once and can be cached for a while, depending on the web server configuration:

00-font-caching

But there's also another approach that you can use, which could help quite a bit.

Font subsetting

You have the option of taking a font which contains all of the glyphs and splitting it up into multiple files, so that the browser only downloads the font files that are actually used (the glyphs are present in the page).

For example, the PT Fonts have support for cyrillic characters but I don't really think many (any?) of my blog posts need those, since I write in English. Therefore, I could create a separate font file and people wouldn't need to download unneeded characters in most pages. Google Fonts does that by default across various sets of languages, and there are even web based tools for doing that:

01-font-subsetter

Now, for my own font site, I might want to use a local solution, that I can run from the confines of my OS, commit some new font files to the Git repository and have it be live in a little bit. The same probably goes for my blog (where I could use those prepared font files). Luckily, there exist a few packages to do that, for example, Font Ranger:

01-font-ranger

It's not a super active project (the last commit being years ago), but thankfully it still seems to work and it's not like the workload itself has changed much over the years. The usage is pretty simple and we do get the support for a variety of font sets out of the box:

02-script-example

Here's the full list:

Admittedly, I haven't even heard of all the languages, but I'm sure that they're separated from the larger sets due to having interesting and unique characters, that would still increase the file sizes quite a bit. Just for the fun of it, I decided to retrieve every individual font set into a separate file. For comparison's sake, here's how the regular WOFF2 fonts look like.

You can see that the PTSans-Regular.woff2 font is 111 KB in size. It was generated with a different tool, but since the compression is more or less the same, any other output should be comparable:

03-regular-font-contents

After running the script, we get a folder with a whole bunch of PTSans-Regular.* subsets, for example, the latin variety is only 44 KB in size, whereas the latin-ext variety is an additional 26 KB, meaning that if we only need the characters contained within those two, then we could save on not downloading a bunch of cyrillic characters.

This also accidentally ends up being a great tool for exploring what character sets are truly represented by a font, because we can see that aside from the latin and cyrillic characters, there aren't many more sets that have a large number of characters in them:

04-subset-font-contents

However, things are sadly not that simple.

There are drawbacks

The most immediate drawback is that the total sum of all the font sizes in that folder with the subsets is now 167 KB instead of the self contained font, which was just 111 KB. In other words, you only benefit from those savings when the initial font has support for a lot of languages and symbols which you won't need, so this will depend on what exactly you're dealing with, whether the added overhead won't become a problem.

Secondly, there's the CSS. Originally, I had a pretty simple font file, which just defined all of the font families that I want to support: PT Sans, PT Serif, PT Mono and their various versions, with the styles and weights. That was pretty simple and the total CSS file was about 3 KB in size, which might get some additional space savings from further compression, because it's just a text file:

05-regular-font

However, with the subset font, now we have a 5 KB file just for the regular variety of PT Sans Regular, which also doesn't have the same configuration as my own CSS file did previously. Either way, the trend here is clear: if I had 15 different font files then with 5 KB per file, we'd end up with the total size of 75 KB and a lot of contents:

06-subset-font

Furthermore, those CSS files would always need to be loaded, to tell the browser that we even have our custom font subsets available. Now, it wouldn't be super bad, because there would usually still be compression for the CSS, but each of the files would get compressed separately and realistically we'd still need to deal with about 20 KB or so of additional downloads, as well as the fact that this would mean more HTTP requests and round trips to the server.

Summary

I didn't actually go onwards to test the numbers, to automate generating every variety, which seems like it would probably be around 345 font files (even is most of those wouldn't have many/any characters in them), because at this point it was quite clear that for my particular use case, I don't need font subsets.

It definitely makes sense for the scales that Google Fonts operates at and while I might consider separating the cyrillic glyphs from the rest of them, for now the simplicity of having a manageable amount of files and CSS that I can fit within a single file without issues beats the operational complexity font subsetting would introduce, for marginal at best bandwidth savings.

I do appreciate that font subsetting exists, but I'd probably need to look into optimizing the images first and foremost, there's about 512 KB of them in this page alone. For now, I just convert everything to JPG because it's widely supported and is a lossy format that lets me slightly decrease the quality while having better filesizes than PNG, but WebP or even AVIF might be worth a look at some point.


Other posts: Previous »