It's a long overdue post, but my real life kicked in heavily last summer and I couldn't get back to working on a blog until now. Read on for the writeup on a tool to convert png images into font definition files.
- https://github.com/ayoy/font2bytes – a multiplatform C++ library with a CLI tool and Qt-based GUI app,
- https://github.com/ayoy/FontToBytes – a MacOS app that was an inspiration (I'm linking here my fork with extended functionality).
Following up on my e-paper display library, I found out fairly quickly that the original fonts provided by Waveshare have some shortcomings. First of all, you might want a different size than 8, 12, 16, 20 or 24px high. I can also imagine situations where you'd need a bold or italic font typeface alongside the regular one. Then, the default font contains basic ASCII characters only, which is fine for English but falls short for virtually any other language. Any of the following:
- the extended ASCII set (code range 128-255),
- non-ASCII Latin alphabet characters (
ąčćęěłňńšśźżž, to name a few :) ),
- any non-Latin alphabet,
is just not there. So whenever you're working on an embedded system that displays messages in your local language, you might be interested in providing your own font for the display.
Last but not least, a custom font allows for defining your own characters (icons, symbols, etc.), which can be very useful on various occasions.
The font file structure
The font definition is basically an array of bytes, where each byte represents 8 horizontally aligned pixels. A set bit means a filled pixel, an unset bit is a clear pixel. For example, the 8x8 font needs 8 bytes per character (one byte per line), but 8x9 (9 pixels wide) would need 16 bytes per character (9 horizontal bits fit into 2 bytes). Likewise, the 24x17 font requires 24x3=72 bytes per character. This is an example font definition in C, with comments allowing for easier understanding of what goes where.
Now by shuffling individual bits, you can adjust the font's characters to your liking, or add new ones at the end, remembering that their codes would start from 127 (as the last character in the font file is the
~, which corresponds to ASCII code 126). If you're fine with it then good for you :) but how cool would it be to have a tool that can do this automatically based on an input image?
The original Font To Bytes
When searching for a tool to prepare a custom font for use in embedded systems displays, I stumbled upon Lucky Resistor's Font To Bytes application. It takes a PNG image with the font characters as input, such as this:
Font input image. Source: https://luckyresistor.me/font-to-byte/
The image is converted into a font source code file, for either Arduino or generic C/C++. Originally it only worked with 8x8 fonts, but the code (pretty neat by the way!) was prepared for adding new types of converters. I updated the code to Swift 4 (it was originally written back in 2015 in Swift 2) and added a parametrized converter that works with arbitrary font widths and heights:
Font To Bytes revamped by me.
The multiplatform rewrite
At that time this was enough for my needs – as I happen to be a Mac user, I got what I needed. But Twitter gave me a reality check as soon as I let the world know about my finding – guess what, not everyone is on a Mac. 💥
At first I was like, well, tough luck. But then I realized that it could be a great opportunity to brush up on my C++ skills and get back to programming with Qt (after doing it for the last time with Qt 4 in 2011). Especially having no experience with modern C++, I took a chance to learn some of it by porting Font To Bytes to C++ with Qt (also to see what's new in Qt 5 – thankfully I found my way around it pretty quickly).
This is how my app looks like:
Font To Bytes running on Linux
There are some changes/improvements compared to the original MacOS X version:
- Font width and height is adjustable freely (that is, between 1 and 255px) and independently of each other.
- Python List and Python Bytes output formats are added. The latter produces a Python
bytesobject, which is the optimal way to store font data (and can additionally benefit from freezing it in the firmware, as described in one of my previous blog posts). New types of converters can still be easily added if need be.
- Besides the GUI application, a command line tool is there for those that don't need the UI or don't want to use Qt for some reason:
f2b command line utility
The code is available from GitHub, together with example font input images and templates. The command-line tool depends only on
libpng (and CMake for building), while the GUI app requires Qt 5. I tested it on Mac OS and Kubuntu with no (obvious) issues. The GUI app *should* work fine on Windows, but I had no way of testing it so far.
Thanks for reading through, and in case you happen to use the app, be sure to let me know of any issues you've encountered.