Fonts and Icons on Circuit Python

I’m continuing to work on a little smartwatch prototype using a little round LCD and I want to have nice looking text. The default font for CircuitPython is fine, but it’s very tiny. This LCD has a pretty high DPI compared to other hobbyist screens( > 200 ppi), so I need to find a new font. CP has a way of importing new fonts, but there are a few pitfalls and tricks I discovered, so that’s what I’m covering today.

Adafruit has a guide to using custom fonts on CircuitPython. CP only supports bitmap fonts, so you’ll need a font converted to BDF (ascii) or PCF (binary) format. Modern fonts are vectors instead of bitmaps, usually in TrueType (TTF) or related font formats, so we'll need a converter. The command line program otf2bdf is such a converter. If you’ve already downloaded a TrueType font locally you can convert with these instructions. I’m using MacOS but similar should work on Windows and Linux.

First install otf2bdf with brew, apt-get, or the install program of your choice

brew install otf2bdf

Now convert the font to bitmap at a specific point height. I’m using 16pt.

otfbdf SomeFont.ttf -o cpfont.bdf -p 16

Now copy the output bitmap font to your CircuitPython device and use it in Yython code like this:

from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.bitmap_label import Label
font = bitmap_font.load_font("cpfont.bdf")
label = Label(
font=font,
text='Text in a nice Font',
)

And that’s it. Pretty easy.

Icon Fonts

Now let’s suppose you want some nice icons. You could download each icon as PNG or SVG and then convert to CircuitPython’s preferred BMP format, but icon fonts are a thing. Let’s use them. Google created the Material icon font for use across all of Android and the Web. They are completely open source and have icons for pretty much everything. We can’t use it directly, however, because it’s in what’s known as a variable webfont format which can have multiple weights and styles in a single file. Instead we want the static truetype fonts from the GitHub repo. I chose the font called MaterialIcons-Regular.ttf.

When you download a single Material icon font you’ll see that it is huge. 348KB for a single weight! That’s because it contains over 3000 icons. 348KB is too big for our little RP2040 that has less than 200KB, and of course we don’t need all 3000 icons. In most cases we only want five or ten of them. So we need a way to subset the font.

We will use a python program called pyftsubset that knows how to subset fonts. It is part of the font-tools. Install font tools with pip then run pyftsubset to make sure it’s installed correctly.

pip install fonttools
pyftsubset --help

Now let’s create a new icon font with a subset of the Material Symbol Font containing only the glyphs we want. If you select an icon back on the Material Symbol Font webpage a sidebar will appear on the right. At the very bottom it will list the code point of that particular icon. That’s what you need. Write down this code point, and the points of any other icons you want to extract. For my example I want the icons for Settings (e8b8), Battery Full (e1a4), and Timer (e425).

pyftsubset MaterialIcons-Regular.ttf --unicodes="U+e8b8,e1a4,e425" --output-file=icons.ttf

You can add the --no-ignore-missing-unicodes option to print an error if you typed in a code that doesn’t exist in the font. The resulting font is still in TrueType format, so we need to convert to bitmap to use it.

otf2bdf icons.ttf -o icons.bdf -p 16

And now we can use it in our program. To use the icons we have to specify the codes for each icon with unicode escapes:

time_label = Label(
font=icons,
text="\ue8b8\ue1a4\ue425",
)

Here it is on my prototype device.

z

C’est magnifique

Talk to me about it on Twitter

Posted June 25th, 2023

Tagged: embedded circuitpython