teaser

Converting fonts to C source using bmfont2c.py script

This is an unedited republication. It first appeared on the Lars' Electric Endeavors blog on larsee.dk

Lars, February 2018


I’d like to share a Python script I wrote for converting bitmap fonts generated by BMFont to C source code byte arrays and definitions suitable for rendering on a monochrome matrix LCD. The script outputs a C header and C source file ready to plug into some source code tree. Thanks to BMFont, this process will convert any TrueType font or font acceptable by Windows to C source code. Please be aware of licensing issues when doing this.

The problem I was facing, was how to generate bitmap font definitions for writing text on a graphical matrix LCD. I already had routines for rendering text on the display, but needed more and better fonts. Searching and trying various solutions, I ended up using the nice BMFont tool by AngelCode, which does a good job of rasterizing TrueType fonts. All that was needed was a way to convert the output into C source, so, allmighty Python to the rescue, and I wrote bmfont2c.py:

Lets walk through the process. I installed BMFont version 1.13 under Linux using the PlayOnLinux Wine wrapper, which worked without any problems. For this example, choose the public domain font Dejavu Sans in BMFont, set size 18 pixels high, disable font smoothing, and set XML as file format and png as texture format under Export options, select all the characters and save the bitmap font as “dejavu18.fnt”. What you get then is two files:

dejavu18.fnt containing definitions of the font and characters:

<?xml version="1.0"?>
<font>
<info face="DejaVu Sans" size="18" bold="0" italic="0" charset="" unicode="1" stretchH="100" smooth="0" aa="1" padding="0,0,0,0" spacing="1,1" outline="0"/>
<common lineHeight="18" base="14" scaleW="256" scaleH="256" pages="1" packed="0" alphaChnl="1" redChnl="0" greenChnl="0" blueChnl="0"/>
<pages>
<page id="0" file="dejavu18_0.png" />
</pages>
<chars count="190">
<char id="32" x="255" y="0" width="0" height="1" xoffset="0" yoffset="17" xadvance="5" page="0" chnl="15" />
<char id="33" x="134" y="52" width="1" height="11" xoffset="2" yoffset="3" xadvance="6" page="0" chnl="15" />
<char id="34" x="175" y="61" width="4" height="4" xoffset="1" yoffset="3" xadvance="6" page="0" chnl="15" />
<char id="35" x="197" y="28" width="10" height="11" xoffset="1" yoffset="3" xadvance="13" page="0" chnl="15" />
<char id="36" x="211" y="0" width="7" height="14" xoffset="2" yoffset="2" xadvance="10" page="0" chnl="15" />
<char id="37" x="53" y="29" width="13" height="11" xoffset="1" yoffset="3" xadvance="14" page="0" chnl="15" />
<char id="38" x="0" y="44" width="9" height="11" xoffset="1" yoffset="3" xadvance="11" page="0" chnl="15" />
<char ... (snip)

and the texture bitmap dejavu18_0.png:

texture bitmap

What bmfont2c.py does is to read the XML description and pick out each character from the texture bitmap. The character data is processed and output as C source.

A configuration file is needed to specify the details of the process. It is possible to configure conversion of multiple fonts that will end up in the same C source and header file, by adding sections named Font1, Font2, Font3 etc. in the configuration file.

For this example, a configuration file named bmfont2c.cfg is used, with the following contents:

[General]
OutputHeader = fontlibrary.h
OutputSource = fontlibrary.c

[Font1]
InputFile = dejavu18.fnt
CFontName = Dejavu18
FirstAscii = 32
LastAscii = 122
BytesWidth = 2
BytesHeight = 14
CropX = 0
CropY = 3

Now, running bmfont2c.py, in the same folder as the configuration file and the BMFont output, will produce the following C source code output:

fontlibrary.h

//
// Bitmap font C header file generated by bmfont2c.py 
// 

#ifndef FONTLIBRARY_H_
#define FONTLIBRARY_H_

#include <stdint.h>

typedef struct
{
    uint8_t GlyphCount;
    uint8_t FirstAsciiCode;
    uint8_t GlyphBytesWidth;
    uint8_t GlyphHeight;
    uint8_t FixedWidth;
    uint8_t const *GlyphWidth;
    uint8_t const *GlyphBitmaps;
} fontStyle_t;


extern fontStyle_t FontStyle_Dejavu18;

#endif /* FONTLIBRARY_H_ */

fontlibrary.c

//
// Bitmap font C source generated by bmfont2c.py 
// 


#include <stdint.h>
#include <avr/pgmspace.h>
#include "fontlibrary.h"

static uint8_t const PROGMEM Dejavu18_Bitmaps[2548] = 
{
    // ASCII: 32, char width: 5
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........
    0x00, 0x00,  // -----...........

    // ASCII: 33, char width: 6
    0x20, 0x00,  // --O---..........
    0x20, 0x00,  // --O---..........
    0x20, 0x00,  // --O---..........
    0x20, 0x00,  // --O---..........
    0x20, 0x00,  // --O---..........
    0x20, 0x00,  // --O---..........
    0x20, 0x00,  // --O---..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........
    0x20, 0x00,  // --O---..........
    0x20, 0x00,  // --O---..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........

    // ASCII: 34, char width: 6
    0x48, 0x00,  // -O--O-..........
    0x48, 0x00,  // -O--O-..........
    0x48, 0x00,  // -O--O-..........
    0x48, 0x00,  // -O--O-..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........
    0x00, 0x00,  // ------..........

    // ASCII: 35, char width: 13
    0x04, 0x80,  // -----O--O----...
    0x04, 0x80,  // -----O--O----...
    0x04, 0x80,  // -----O--O----...
    0x3f, 0xe0,  // --OOOOOOOOO--...
    0x09, 0x00,  // ----O--O-----...
    0x09, 0x00,  // ----O--O-----...
    0x09, 0x00,  // ----O--O-----...
    0x7f, 0xc0,  // -OOOOOOOOO---...
    0x13, 0x00,  // ---O--OO-----...
    0x12, 0x00,  // ---O--O------...
    0x12, 0x00,  // ---O--O------...
    0x00, 0x00,  // -------------...
    0x00, 0x00,  // -------------...
    0x00, 0x00,  // -------------...

    // ASCII: 36, char width: 10
    0x04, 0x00,  // -----O----......
    0x1f, 0x00,  // ---OOOOO--......
    0x34, 0x80,  // --OO-O--O-......
    0x24, 0x00,  // --O--O----......
    0x24, 0x00,  // --O--O----......
    0x1c, 0x00,  // ---OOO----......
    0x07, 0x00,  // -----OOO--......
    0x04, 0x80,  // -----O--O-......
    0x04, 0x80,  // -----O--O-......
    0x25, 0x80,  // --O--O-OO-......
    0x1f, 0x00,  // ---OOOOO--......
    0x04, 0x00,  // -----O----......
    0x04, 0x00,  // -----O----......
    0x00, 0x00,  // ----------......

    // ASCII: 37, char width: 14
    0x38, 0x20,  // --OOO-----O---..
    0x44, 0x20,  // -O---O----O---..
    0x44, 0x40,  // -O---O---O----..
    0x44, 0x80,  // -O---O--O-----..
    0x44, 0x80,  // -O---O--O-----..
    0x39, 0x38,  // --OOO--O--OOO-..
    0x02, 0x44,  // ------O--O---O..
    0x02, 0x44,  // ------O--O---O..
    0x04, 0x44,  // -----O---O---O..
    0x08, 0x44,  // ----O----O---O..
    0x08, 0x38,  // ----O-----OOO-..
    0x00, 0x00,  // --------------..
    0x00, 0x00,  // --------------..
    0x00, 0x00,  // --------------..

(snip)

    // ASCII: 120, char width: 10
    0x00, 0x00,  // ----------......
    0x00, 0x00,  // ----------......
    0x00, 0x00,  // ----------......
    0x61, 0x80,  // -OO----OO-......
    0x21, 0x00,  // --O----O--......
    0x12, 0x00,  // ---O--O---......
    0x0c, 0x00,  // ----OO----......
    0x0c, 0x00,  // ----OO----......
    0x12, 0x00,  // ---O--O---......
    0x21, 0x00,  // --O----O--......
    0x61, 0x80,  // -OO----OO-......
    0x00, 0x00,  // ----------......
    0x00, 0x00,  // ----------......
    0x00, 0x00,  // ----------......

    // ASCII: 121, char width: 8
    0x00, 0x00,  // --------........
    0x00, 0x00,  // --------........
    0x00, 0x00,  // --------........
    0x81, 0x00,  // O------O........
    0x41, 0x00,  // -O-----O........
    0x42, 0x00,  // -O----O-........
    0x22, 0x00,  // --O---O-........
    0x24, 0x00,  // --O--O--........
    0x14, 0x00,  // ---O-O--........
    0x18, 0x00,  // ---OO---........
    0x08, 0x00,  // ----O---........
    0x08, 0x00,  // ----O---........
    0x10, 0x00,  // ---O----........
    0x60, 0x00,  // -OO-----........

    // ASCII: 122, char width: 8
    0x00, 0x00,  // --------........
    0x00, 0x00,  // --------........
    0x00, 0x00,  // --------........
    0x7e, 0x00,  // -OOOOOO-........
    0x02, 0x00,  // ------O-........
    0x04, 0x00,  // -----O--........
    0x08, 0x00,  // ----O---........
    0x10, 0x00,  // ---O----........
    0x20, 0x00,  // --O-----........
    0x40, 0x00,  // -O------........
    0x7e, 0x00,  // -OOOOOO-........
    0x00, 0x00,  // --------........
    0x00, 0x00,  // --------........
    0x00, 0x00,  // --------........
};

static uint8_t const PROGMEM Dejavu18_Widths[91] = 
{
     5,  6,  6, 13, 10, 14, 11,  3, 
     6,  6,  8, 13,  5,  5,  5,  5, 
    10, 10, 10, 10, 10, 10, 10, 10, 
    10, 10,  5,  5,  5, 13, 13,  8, 
    15, 10, 10, 10, 11,  9,  9, 12, 
    11,  3,  3, 10,  8, 13, 11, 12, 
     9, 12, 10, 10,  9, 11, 10, 15, 
     9,  9, 11,  6,  5,  6, 13,  8, 
     8,  9,  9,  8,  9,  9,  5,  9, 
     9,  3,  3,  8,  3, 15,  9,  9, 
     9,  9,  6,  8,  6,  9,  8, 13, 
    10,  8,  8, 
};

fontStyle_t FontStyle_Dejavu18 = 
{
    91, // Glyph count
    32, // First ascii code
    2, // Glyph width (bytes)
    14, // Glyph height (bytes)
    0, // Fixed width or 0 if variable
    Dejavu18_Widths,
    Dejavu18_Bitmaps
};

The C output can easily be customized by modifying the Python script.


I hope you enjoyed this content!

ko-fi donate

Comments

Comments powered by Talkyard