After you've downloaded the sample sketch, open it up in the Arduino IDE and look at the second tab containing SymbolMono18pt7b.h. You might also want to go to Arduino/libraries/Adafruit_GFX/Fonts/FreeMono18pt7b.h and open it in your favorite text editor.

The font that we will create is going to be an 18 point monospace symbol font designed to work with the FreeMono18pt font supplied with the Adafruit GFX library. That free monospace font defines characters from 32-126. We are going to create a font that uses 0-31 as well as 127 and upwards. We will use a custom display function to switch back-and-forth between these two fonts depending on the character value that you send.

The way that glyph information is stored in these GFX fonts is highly optimized and only uses exactly the number of bits of data necessary to define the particular bitmap. Trying to encode these bits by hand would be prohibitively difficult but we're going to cheat little bit. We're going to always use bitmaps that are exactly 16 bits wide even though some of our symbols are not fully that wide. This is going to be a tiny bit inefficient as far as storage goes. However with M0 or M4 boards, which are necessary to use these custom fonts anyway, using a few extra bytes here and there is not going to hurt too much and it's going to make this manual encoding system possible.

Let's look at the SymbolMono18pt7b.h file contents in the Arduino IDE second tab. The first thing we have is an array of bytes the defined the bitmaps. It looks like this.

const uint8_t SymbolMono18pt7bBitmaps[] PROGMEM = {
//A bunch of data goes here

Next is an array of glyph information. For example:

const GFXglyph SymbolMono18pt7bGlyphs[] PROGMEM = {
  //Index,  W, H,xAdv,dX, dY
  {     0, 16,21, 21, 3,-19}, // 00 test square
  {    42, 16,15, 21, 3,-18}, // 01 Upper_Left_Arrow
  {    72, 16,15, 21, 3,-18}, // 02 Upper_Right_Arrow
  {    42, 16,15, 21, 3,-18}};//144

This is an array of structures of type GFXglyph. The first number is the index into the bitmap array. So in this example our first glyph starts at index zero. Look at the top of the file and you can see that this glyph that we call "test square" defines 42 bytes. That means that the next glyph begins at index 42. The second glyph, a upward left pointing arrow, is 30 bytes long so that means the third glyph starts at index 72 and so on. You just keep adding the length of the previous glyph of the one prior to that.

The second and third items are the width and height of the glyph. In our case, we have to have a glyph that is 16 pixels wide even if we don't really need that much space. Otherwise, we cannot manually compute the bitmap patterns. The height can vary as much as we want. Note that the test square was 21 pixels tall however the arrows are only 15. If you look further down the list you will see that glyph 134 "Space Bar" with only three pixels tall. The index will always advance by 2 times the height value. So as we noted the test square is 21 pixels tall. That means it takes 42 bytes to define it. The arrows are 15 pixels tall and they take 30 bytes. So the index advances by that much.

The next value is the xAdvance value. When using these fonts with the print() or println() functions this tells the software how much space to put between each character horizontally. Because this is a fixed space font we are using a constant value of 21. Although our actual glyph is only 16 pixels wide this gives two extra pixels on one side and three on the other. You need that blank space for readability. The way we have implemented things so that it automatically switches back and forth between our symbol font and the standard mono 18 font, we will not be able to use print or println. Instead we will use a custom drawChar function to draw individual characters.

The final two values are called dX and dY which are used to position the glyph within the cell. As you can see the dX value varies between 2-4. The dY value is the distance from the baseline of the character to the top of the glyph. So for example the large test square starts 19 pixels up from the baseline of the character cell. However the "Space Bar" glyph is only 2 pixels above the baseline. You will have to play around with these values to get the glyph centered horizontally and vertically the way that you want it. The sample sketch we provide lets you visualize that position and you can make the appropriate adjustments.

The final part of the font definition is a structure that ties everything together. It includes a pointer to the bitmap array, a pointer to the glyph data and a few other values. In our case we have listed the next three values as "0,48, 35". The first is the ASCII value of your first character. While most fonts run from the range of 32-126 this one goes from 0-48 but we use a custom display function that actually makes the range "0-31 and 126 upwards". We will explain more when we talk about our sample sketch. The value "35" is the standard vertical spacing between rows of text when using print or println however as mentioned earlier we will be specifically placing individual characters at exact locations and not using this value.

const GFXfont SymbolMono18pt7b PROGMEM = {
  (uint8_t  *)SymbolMono18pt7bBitmaps,
  (GFXglyph *)SymbolMono18pt7bGlyphs,
  0,48, 35 //ASCII start, ASCII stop,y Advance

The remaining items in the file are not part of the standard Adafruit GFX font format. There are just some definitions that we need for this custom symbol font. The DELTA_C, DELTA_R and BASE_R values help us display these symbols in a fixed grid. The other definitions are names that we can use in our application program to reference all of the symbols that we've defined. 

#define DELTA_C 21
#define DELTA_R 30
#define BASE_R 22

#define MY_TEST_SQUARE 0

This guide was first published on Jul 16, 2019. It was last updated on Mar 08, 2024.

This page (Understanding the Font Specification) was last updated on Mar 08, 2024.

Text editor powered by tinymce.