I can't count the number of times I've tried installing a command line tool on my machine, only to find myself going down a black hole of node modules & dependencies, and a seemingly non-ending list of error messages in the terminal. This would go on for a while before I finally give up and call it quits, only to revert back to find myself googling the name of an online app and using that to do what I need instead.
Yesterday, the same thing almost happened again. I got into another black hole in iTerm as I tried to install a couple of Web font conversion and optimization tools. I almost gave up before I finally managed to kind of get everything working the way I wanted it to. A lot of googling and “stack-overflowing” was involved in this process. I’m sharing my struggles in this post in the hopes that it might help someone out should they get stuck where I did. I also want to have a reference to come back in the future should I ever find myself installing the same tools again.
Yesterday was just another typical work day. I was setting up the foundation for a new client project. The Web site we’re building uses the Inter font family for the typography. So, I head over to Google Fonts, found the Inter family page, and clicked that “Download family” button.
As you probably already know, Google Fonts doesn’t supply the fonts in WOFF or WOFF2 formats. When you download a font, you only get TTF font files. Why that is, I don’t know. Jeremy has wondered the same before. And if you’re a front-end developer you should also know that TTF is not the best choice for serving Web fonts, and that WOFF and WOFF2 are far leaner and more performant.
So, as usual, I set out to convert the TTF files to WOFF and WOFF2 to serve them up in my project.
My previous font conversion workflow
Normally, I’d head over to one of the online tools available — whatever comes up first in Google search, because I’ve never bookmarked one tool to use at all times, even though I tend to use Font Squirrel generator quite often. But that has always felt like too much of a hassle. Ideally, I’d be able make this conversion within the folder in which they are located.
So the workflow would look like this:
- download font,
- unzip & open folder,
- convert to WOFF & WOFF2,
- copy to project folder and embed on page.
…instead of: download font, unzip & open folder, find an online tool, upload font files, wait for conversion, download fonts, unzip & open folder, and then move to project folder and embed on the page. This has always felt like an clunky workflow. It always bothered me that I had to click and switch contexts as much as I did.
So, yesterday I saught the help of the amazing Twitter #lazyWeb and asked my friends there what they use to convert their font files on macOS. Many responded with links to online tools, which is what I’m wanting to avoid. And some responded with recommendations for a few command line tools.
I imagined I’d want a nice drag and drop tool for this. But command line tools are also pretty handy, and usually very flexible, too. Once you’ve got one set up, it could help streamline your font conversion process. And since I’m much more comfortable in the command line today than I was a few years ago, I thought it was about time I switched to one of those tools.
Not only does
glyphhanger convert font files, but it also subsets them (i.e. it removes unnecessary characters from a font file), which is another optimization step you want to make to serve more performant fonts. (That, and the fact that I love the work of the folks at Filament Group, who have always been big proponents of performance.)
The Github repository’s README file includes installation instructions. And that’s where I started yesterday…
I’m going to start with the process I went through and the issues and errors I got. If you’re not interested in that, you can skip straight to the solution.
Down the rabbit hole
glyphhanger is available on npm. According to the reposiroty’s README, installation is as simple as running:
npm install -g glyphhanger
I ran that command.
glyphhanger was installed. Yay. So far so good.
Following that line in the README is a note that says that
pyftsubset is a prerequisite. I wasn’t familiar with what
pyftsubset is, so I kept on reading. (Honestly, does everyone know what everything is?)
There was a link to another Github repository: fontTools. I chose
glyphhanger for its subsetting feature, and the subsetting functionality requires you to install fonttools. So, I had a dependency to install. Reading the README of the fontTools repo only complicated things for me. So, I kept on reading the
(At this point: I couldn’t make the direct connection between
pyftsubset and fontTools at first. Later on, I googled “pyftsubset fonttools” and learned that
pyftsubset is an OpenType font subsetter and optimizer, based on fontTools. And that’s why you need to install fontTools.)
The next instruction is:
pip install fonttools
I had no idea what
pip is. I decided to run the command before googling anything. Unsurprisingly, that’s when I got my first error.
zsh: command not found: pip
Oups. OK so apparently I need to have Pip installed on my machine. Not knowing what it is, I started googling. I learned that Pip is a package manager for Python. And that to install Pip, I needed to have Python installed.
Many articles mentioned that macOS already comes with a version of Python installed. So I looked for what I needed to install Pip using the Python that’s already installed. For some reason, none of the commands I found worked. So I thought: “maybe Python isn’t installed on my machine?“. So I opened a new browser tab and started googling for how to install Python.
While I was at it, I was following the rest of the instructions in the README. I didn’t know what depended on what, so I thought I’d try continuing async.
The following instructions are about cloning the Brotli and Zopfli git repositories and installing them because they are required to get WOFF and WOFF2 support:
# Additional installation for --flavor=woff2 git clone https://github.com/google/brotli cd brotli python setup.py install # Additional installation for --flavor=woff --with-zopfli git clone https://github.com/anthrotype/py-zopfli cd py-zopfli git submodule update --init --recursive python setup.py install
As you might have noticed already, these steps require Python to work, and since I didn’t have Python working for me at this point yet, I got quite a bunch of errors in the terminal. Trying to install Brotli and Zopfli like that confirmed to me that Python maybe is, indeed, not installed by default.
fontTools is also a Python project, which, naturally, requires Python to work. (Duh.)
So it was very obvious at this point that what I needed to focus on was getting Python and Pip set up if I wanted any of this to work.
After googling quite a bit and jumping between articles and Stack Overflow Q&As, I learned that there are multiple versions of Python (obviously), the latest (I think) is Python 3. Now this is where the weirdness started. After installing Python 3, if I wanted to run a Python command, I needed to use
python3 followed by the command. Not
python. According to a nice article I found later, “in order to send commands to Python 3, you will need to enter
python3 in the terminal. If you enter
python, the command will be sent to Python 2.”
So anyway, I installed Python using
brew install python3
Yep. That worked. I don’t remember if I had any issues here. But then again, I had so many issues that I don’t remember when and where I got each.
Here’s a quick tip: If you, like me, want to avoid
brew updating every time you use the
brew command, you can use
I have a snippet setup so that every time I type
brew and press TAB, it autocompletes to
HOMEBREW_NO_AUTO_UPDATE=1 brew, so that I can run whatever brew command I want without allowing it to update. I learned this a few months ago when I got sick of brew updating every time I wanted to run a quick command. I googled, and found this tip on Stack Overflow and have been using it since.
Next, I needed to install Pip. Once again, I found several articles, each mentioning a different way to do it. To be quite honest, I don’t remember which one of those I ended up following. I did learn though that with Python 3 comes Pip 3.
I installed Pip 3.
At this point, I realized I should have everything I needed to continue going through the README instructions. So, again, I tried installing
pip install fonttools. After getting yet more errors, I learned that, just like
python3 I need to use
pip3 instead of
pip3 install fonttools
That worked. So I got Python, Pip, and fontTools out of the way.
To generate optimized WOFF and WOFF2 font files, I needed Brotli and Zopfli. I have no idea why I still got some errors by following the installation instructions in the README (cloning the git repos for each). So, more googling ensued. Turns out, installing Brotli and Zopfli was much easier now that I had Pip installed. I didn’t even need to clone the git repositories like the README instructed. All I needed is these two commands:
pip3 install brotli pip3 install zopfli
And Boom! all dependencies were installed. Now when I used
glyphhanger to convert my font files, it finally generated the optimized TTF, WOFF, and WOFF2 files.
So, to sum up:
tl;dr: installing all dependencies and running glyphhanger
npm install -g glyphhanger
Install Python 3:
brew install python3
Install pip. Python 3 comes with pip 3. Follow the instructions in this article to install pip.
Install fontTools using pip:
pip3 install fonttools
Install Brotli and Zopfli using pip:
pip3 install brotli pip3 install zopfli
And then use
glyphhangerto subset and create optimized formats of your TTF font files:
Zach Leatherman has a post that introduces
glyphhanger with examples of how to use it. You’ll want to read his article and the repository’s README for further customizations and usage options because there are quite a few.
Optimizing Variable Fonts
glyphhanger also generates WOFF2 files for Variable fonts. The file size of the subsetted Variable Inter WOFF2 file generated by
glyphhanger is slightly smaller than the file size generated by Google’s woff2 library (302KB compared to 308KB). It does not break the Variable axes of the font like other online tools might, so it is safe to use it in your projects.
- How to use variable fonts in the real world
- How To Convert Variable TTF Font Files to WOFF2 — this post explains how to set up the woff2 library and use it to generate WOFF2 from a Variable TTF font file. I’ve found it to be more helpful than the library’s README file. Make sure to follow the steps and commands to the letter.
While I finally managed to get
glyphhanger set up and can now use it generate optimized Web font files, it still bugs me that I am still getting an error when using it. My terminal looks like this every time I run the command:
I googled a lot trying to find what that error means and where it’s coming from. I would love to get a clean output every time I run
glyphhanger. I mean, it’s such a great tool. I want the output to be just as great. But after spending quite some time on getting it to work, I wasn’t left with enough patience to figure the solution out. If you have an idea what might be causing it, please do let me know — I’d appreciate it a lot.
which glyphhangerin the command line. This should give you the path to the folder inside which
glyphhangerresides. (My result was
/usr/local/bin/glyphhanger; it’s a hidden folder so I had to
cmd+shift+.to view it.)
- Find the
package.jsonfile in the folder and change the version of
npm install(inside the folder)
Now the errors are finally gone. 🥳 If I run
glyphhanger --subset=Inter-Light.ttf for example, the result I get in the command line looks like this:
(Look at those WOFF2 savings!)
✨ UPDATE #2 (March 11, 2021): ✨ Zach announced the release of glyphhanger v4, which includes dependency updates that fix the console warning issue that I had and that required the manually changing the
shelljs version in the package.json file. So if you follow the installation steps above, you should have a clean console output without the extra hassle. 🎉
If you’ve read all the way here, thank you. I hope someone finds this article helpful.