Developing a Custom Font for the Embedded Systems
by Northstrix in Circuits > Microcontrollers
723 Views, 4 Favorites, 0 Comments
Developing a Custom Font for the Embedded Systems
Disclaimer: I don’t claim that the process of font development described in this article is the one and only proper way of developing a font. This article is nothing more than me documenting how I developed a custom font for the embedded systems without ever doing it before and learning about the way it could or should be done. I acknowledge that there may be more efficient ways to approach this task, and by the way, I have a gut feeling that I did the extra work and reinvented the wheel during the development process.
Anyway, let’s get to it!
This article is also available on Medium.
Supplies
Couple of sheets of paper;
Pen;
Camera or scanner;
Photo editor that can work with layers;
Image Convertor (from https://github.com/Northstrix/Miscellaneous);
Lots of time and patience;
320x240 tft LCD;
Microcontroller that can handle the LCD mentioned above;
Writing Down the Characters
I figured that the first step in developing a font is to literally write down the characters that the font will include.
I wrote down several versions of each character to have a larger subset to choose from.
The photo in this step contains roughly 15% of what I wrote down. As you might’ve already guessed, I adjusted the levels to hide the grid.
Getting the Characters to Your Workstation
After I wrote down the characters, I wanted to take a photo of both sheets with the characters, but then I decided to take the hard route and scanned them instead. Unfortunately, I can’t attach the scanned file since I deleted it before I decided to write an article about developing the font. But you get the point.
Compiling a Set of Characters
After carefully examining the scanned bitmap (with the adjusted levels), I lined up the best characters on the separate bitmap.
Adjusting the Size
I appreciate the benefits of high-resolution fonts, but it’s very inconvenient to display a 400-px high letter on the 320x240 display. Therefore, I resized the set to be 54 pixels high.
The picture in this step contains the “Alef” and “Bet” letters after the resizing.
Redrawing Each Character
Now it’s time to draw the actual characters that will be shown on the display connected to the embedded system.
To do so, I created the second layer above the bitmap with the resized set and started redrawing each character pixel-by-pixel. It took me roughly four hours to do the job. I abandoned redrawing digits after they turned out to look “worse than expected” in the binary view. I also drew a new character for the “Tsadi” letter.
The picture in this step contains the “Alef” and “Bet” letters after the redrawing on top of the scanned versions of them.
Aligning and Adjusting the Characters
height differed from the desired height by a couple of pixels in both directions. So, I aligned them along the bottom line of the “Alef” and adjusted them to be as high as “Alef.”
The picture in this step contains characters after alignment and adjustment.
Saving Each Character Into a Separate Bitmap
Instead of converting the entire set at once and then separating the characters, I’ve decided to save each character into a separate bitmap. And then convert each bitmap into an array that can be utilized by the embedded system. I also replaced the transparent background with a white one.
Adding a Sacrificial Pixel (line) at the Bottom of Each Bitmap
Apparently, that’s the extra work. The reason why I did it, is that I decided to use the image converter I wrote earlier to convert the bitmaps to the arrays. And that program spoils the pixel (the line to be exact) on the bottom of the bitmap
The picture in this step contains the “Alef” letter with the sacrificial pixel (line) at the bottom.
Converting Each Character Into an Array
Finally, it’s time to convert each character into a format that can be “understood” by the embedded system.
At first, I converted the character to the array using the image converter, then I opened the resulting array, replaced the sequence “65535” with “1,” removed the column representing the spoiled pixel, and renamed the array while also changing its type to “bool.”
Adding the Missing Letter
While converting the characters, I noticed that the letter “Shin” was missing. So, I took a photo of the sheet that contained the missing letter, resized it, redrew the letter, saved it into a separate bitmap, and finally converted it to the bool array.
Adding the Comma, Dot, Exclamation Mark, and Question Mark
I just figured that the font is missing the following characters. Without further ado, I added them.
Assembling the Testing Platform
Now, when the tedious work is done, it’s time to assemble the testing platform and start coding.
I took the 320x240 ILI9341 display and connected it to Teensy 4.1.
The picture in this step contains the circuit diagram of the testing platform.
Coding
Actually, the coding part isn’t as hard as it seems.
The pixels on the 320x240 display are numbered 0–319 and 0–239.
To display the string using the custom font, I created a function that takes four arguments:
print_custom_hebrew_font(“Text to display”, shift_down_by_n_pixels, shift_left_by_n_pixels, text color);
Since Hebrew is written from right to left, the function should first set the pointer value one pixel after the last. Then, the function will traverse the entire input string. When it encounters a letter, it subtracts its width from the pointer value and begins drawing the character from the new pointer value. The character is also shifted downwards by the needed amount. Afterward, the pointer value is decremented by “letter_spacing_pxls,” and the function moves on to the next character.
Now let me explain that using more practical terms:
1) Function is called;
2) The first thing it does is set the pointer value to 320;
3) Function traverses the string;
4) Let’s suppose that it encounters the character representing the “Alef” letter. The width of that letter is 20 pixels;
5) Function subtracts 20 from 320, therefore, setting the pointer value to 300;
6) Function draws the letter from the pixel N300 (actually, it 301st);
7) After drawing the letter, the function shifts and subtracts the value from the pointer defined under the “letter_spacing_pxls” and moves on to the next character.
The first picture in this step contains the part of a function responsible for drawing the string using the custom font.
The second picture in this step contains the value of the pointer (first two columns); 20 pixels taken by the “Alef” letter (third column). Note that this picture depicts the horizontal pixels.
Displaying All Characters
I called the function three times and provided it with all the characters associated with the font’s characters. While using Latin letters to represent the Hebrew ones might be a bit inconvenient, it’s the best solution that I found (for now).
Fixing the Improper Spacing for the “Lamed” Letter
If the “Lamed” letter isn’t the first in the line, the gap between it and the letter before it is too big. To fix this problem, I wrote two lines of code that shift the “Lamed” letter twelve pixels to the right if it’s not the first one in the line.
Writing a Function to Display Multi-colored Text
Aside from writing the function that displays the string using the custom font, I decided to write the function that also displays a multi-colored string using that font.
To do so, I modified the original function adding two arguments to it;
uint16_t font_colors[] — Array with colors;
int how_many_colors — Number of colors to take from that array.
Testing the End Result
Well, I believe that the best way to demonstrate the result of this work to you is to show you a photo of the display showing the quote that I took from one of the episodes of the “Losing Alice” TV show.
By the way, I made this font a work of the public domain and uploaded it to GitHub. So, if you want to use this font, don’t hesitate to do it.
Updated* You can also play with this font on Wokwi.