Automating PETI Font Changes

Last Updated: 2024-02-06 05:00:00 -0600

Just last time, I talked about how I’ve been working on making some changes to how fonts are handled in PETI; specifically, switching to a more standard font “source” format that would allow more choice on the developer end, insofar as what tools would be usable for editing the fotns. We chiefly did this by creating a FontX2-format exporter for the older fontedit editor, since fontx2 is a relatively simple bitmap font format used by lots of other embedded devlopers, and usable for quite a few different font editors. The simplicity of the format gave me a pretty clear idea for how to get the fonts included directly into PETI’s source code, which itself gave me some ideas for working toward one of the loftier end-goals of the project.

Reducing Operator Complexity

I won’t deny that developing PETI is a little complex, possibly more complex is strictly necessary, and not just because the code-base clearly has “used to module architectures” influences in its structure. On currently-released firmware versions, this is the process needed if you wanted to, say, replace the Zazenkuchi character’s art.

  1. Identify the font glyphs being used by the character.
  2. Make the changes using fontedit against the matching fontedit source files
  3. Export the changes using the fontedit exporter for the font size you’re modifying.
  4. Open the exported file and copy out the actual array lines from the file.
  5. Past this change over lib/display/font.h
  6. make clean && make target-flash

And that’s gross.

Broadly speaking, I want to reduce developer complexity to the greatest extent possible - at one step early in the planning for this project I even considered building up a custom IDE. This step is not that, but there’s a really basic change I can make that eliminates steps 3, 4, and 5 from the above process, by making two little changes:

  1. Replace the fontedit source files with a more standard format, fontx2.
  2. Make converting fontx2 to c header files an automated part of the build process.

Automating Font-to-Header Conversions

Last night on a twitch stream, I wrote the converter for this step of the process in less than an hour, using python. For a while I debated whether or not to use python, but I reasoned that since python was already required for fontedit (which we’re admittedly deprecating from the PETI toolbox), it wasn’t exactly adding a new build dependency that wasn’t already there.

It turns out that this is actually a very simple process. By the very nature of the format, the layout of the file is extremely predictable. My script simply creates an output .h file with the font data expressed as an array of arrays, exactly the same way that the current implementations in fontedit exporters used to do; it reads the two bytes that describe the font size from within the file itself, bases the array names on the name of the input file, and takes some CLI arguments to know what file it’s converting and where it needs to put out the converted files.

Unfortunately, this really only solves step 3 of the above convoluted problem.

font.h, Conditional Includes, And A Prototype for Locales

Some folks might have been watching the project long enough to know that I had special plans for the locale system, and that was part of why all the actual, human-readable text strings in the pet are being stored in enCA_strings.c. That lofty goal is fairly simple: eventually, I want PETI to basically accept a preprocessor directive (in the form of a flag passed through make, or perhaps set in the configuration file) to determine which locale strings to actually include. I have no idea how to do that, so it’s not been done yet.

The next step of solving the font management problem is also a prototype for looking at doing that.

Currently, all fonts that are actually used (on the release branches, anyway), are set up in lib/display/font.h, by taking their converted forms and pasting them in directly. I want to find a way to not have to do that manually. If only there was some way to tell the C compiler that you want it to read the contents of another file inline to the file it’s currently reading.

Patch, buddy, there is - it’s the #include directive.

We can’t just use simple includes though, for the very important reason that a font might not exist. See, even though we now have an automated process taking all the fonts in lib/display/fontx2 source fonts/ and outputing them to lib/display/fonts as header files, we have no way to guarantee that every possible font exists as a file. Remember, all three supported font sizes also support up to 16 fonts in that size to be generated and stored. That’s a lot of memory overhead (even stored in the persistant FRAM), so we don’t want to do that if we’re not going to use it.

Enter the __has_include() conditional. Some versions of C include this conditional for preprocessor #if statements, letting you take a certain action if a certain file exists. Functionally, this lets us include a file only if it exists:

#if __has_include("fonts/font16x16_1.h")
#include "fonts/font16x16_1.h"

And then, because we have the good grace to have these header files set a #define when they’re included, we can check later to see if that definition was made, and if not, set up a placeholder symbol to try and prevent other parts of the system from breaking:

#ifndef FONT16x16_1_H_
#define FONT16x16_1_H_
#pragma PERSISTENT(font16x16_1)
const char *font16x16_1[] = {0};

Simply repeat this process a lot (remember, we technically support 48 individual fonts), and you’re done. font.h becomes a sort of multiselector that automates the process of including the other fonts into itself, and because they’re technically included through font.h, other parts of the system that include it get all the font data, at least assuming you build everything, which is how the makefile-driven build system for PETI currently works.

At the time of this writing, this work isn’t totally finished, but it’s being done on the f/evolution branch, which will eventually be merged into the 0.4.0 firmware release. While the work isn’t directly related to evolution, the janky handling of font edits within the system is a thorn in the side of getting all the new forms that are coming in the evolution update packed into the codebase, and it’s relatively low-hanging fruit to fix this. I anticipate finishing this this week, so that I can move on more or less immediately to developing the rest of the evolution branch, which, other than some documentation updates and bug fixes, more or less completes the update.

If you wanted to show your support financially for Arcana Labs projects like PETI, but don’t need a virtual pet development kit, your best avenue is through the pathways detailed on our support page.


Using your Fediverse account, you can respond to this article's Mastodon Post. Embracing the spirit of decentralization inherent to the Fediverse, you can use your account on any compatible platform to post. Clicking the "load comments" button below will make your browser request all of the non-private comments and display them below.

This was built based on this reference implementation.