Using FontPacker with libGdx

In this article, I’ll show how to render the text from fonts exported by my FontPacker in libGdx. These are the required steps to render those fonts.

  • Understanding the FntPack format
  • Parsing with SAXParser and read Glyphs
  • Rendering Text

The [icode]PackedFont[/icode] class we develop in this tutorial is available as a paste.

Let’s start off by understanding the format.
[h1]Understanding the FntPack format[/h1]
FontPacker packs the whole font into an XML file with an extension of [icode].fntpack[/icode] Since these are XML files, let’s see an example font here.


<?xml version="1.0" encoding="UTF-8"?>
<FontPacker>                                                <!-- /* Root element                            */ -->
    <Font name="Name of font here">                         <!-- /* Font tag with name                      */ -->
        <Glyph char="A"                                     <!-- /* Glyph tags with char attribute          */ -->
               data="Base64 encoded PNG image data here"    <!-- /* Base64 encoded image data in PNG format */ -->
               xadvance="33" />                             <!-- /* The width of the character in pixels    */ -->
    </Font>
</FontPacker>

I’ve only included a sample file with same structure written by hand. The version produced by the tool doesn’t include any comments or have indentation. They are added just to make you understand the format easily.
[h1]Parsing the FntPack file[/h1]
Though LibGdx has a built-in reader, I’ve chosen to use the SAX parser since LibGdx’s XmlReader doesn’t support UTF-8 encoding. So I’m using the SAX parser. First, we need to create a SAXParser instance.


SAXParser parser = SAXParserFactory.newInstance().newSAXParser();

Next we create a new handler which parses the data with the parser.


DefaultHandler handler = new DefaultHandler()
{
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attr)
    {
        // Read the font name
        if (qName.equalsIgnoreCase("Font"))
        {
            name = attr.getValue("name");
        }
        
        // Read every glyph from the font.
        if (qName.equalsIgnoreCase("Glyph"))
        {
            // Get the character value
            char ch = attr.getValue("char").charAt(0);
                        
            // Get the advance width
            int xadvance = Integer.parseInt(attr.getValue("xadvance"));
                            
            // Decode the glyph image
            String data = attr.getValue("data");
            byte[] png = Base64Coder.decode(data);
                        
            Texture tex = new Texture(new Pixmap(png, 0, png.length));
                            
            // Map the Glyph and AdvanceWidth with the character
            glyphs.put(ch, tex);
            advances.put(ch, xadvance);
        }
    }
};

This code creates a GdxException saying that “Texture’s width and height must be power of two”. To get rid of that, we need to add this line before creating the handler.


Texture.setEnforcePotImages(false);

This resizes the textures in memory to make them “power of two”. Next we call the parser’s parse method with content of the XML file as a string. Here [icode]handle[/icode] refers to the [icode]FileHandle[/icode] class part of LibGdx.


parser.parse(handle.read(), handler);

Next I’ve made methods [icode]getGlyph()[/icode], [icode]getAdvanceWidth()[/icode], [icode]getName()[/icode] and [icode]getLineHeight()[/icode] which contain the following code.


/**
 * @param id The character id
 * @return The Texture of the Glyph
 */
public Texture getGlyph(char id)
{
    if (glyphs.containsKey(id))
    {
        return glyphs.get(id);
    }
    else
    {
        return glyphs.get(' ');
    }
}

/**
 * @param id The character id
 * @return The advance width of that character
 */
public int getAdvanceWidth(char id)
{
    if (advances.containsKey(id))
    {
        return advances.get(id);
    }
    else
    {
        return advances.get(' ');
    }
}

/**
 * @return The line height of this font
 */
public int getLineHeight()
{
    return getGlyph(' ').getHeight();
}

/**
 * @return The name of this font.
 */
public String getName()
{
    return name;
}

In every font, the line-height is the height of the ‘space’ character. Next, we make the [icode]renderText()[/icode] method with the following code.


/**
 * Renders a string at a position x, y using SpriteBatch.
 * Carriage returns are ignored and you can use '\n' for
 * new lines.
 *
 * @param text  The text to be rendered
 * @param x     The x-coordinate of the view
 * @param y     The y-coordinate of the view
 * @param batch The SpriteBatch to use to render textures
 */
public void renderText(String text, float x, float y, SpriteBatch batch)
{
    // Local positions
    float xpos = x;
    float ypos = y;

    // Loop over each char and render it.
    for (char ch: text.toCharArray())
    {
        // Ignore carriage returns
        if (ch == '\r')
        {
            continue;
        }

        // '\n' moves to new line
        if (ch == '\n')
        {
            ypos -= getLineHeight();
            xpos = x;
            continue;
        }

        // Get the texture of a Glyph
        Texture tex = getGlyph(ch);

        // Render the glyph
        batch.begin();
        {
            batch.draw(tex, xpos, ypos);
        }
        batch.end();

        // Advance the position
        xpos += getAdvanceWidth(ch);
    }
}

It’s that simple. Now it’s time for testing a sample font. I’ve tested the font “Mistral”.
[h1]Rendering the text using the SpriteBatch[/h1]
Before rendering, I’ve loaded the font in the [icode]create()[/icode] method like this.


public void create()
{
    font = new PackedFont(Gdx.files.internal("data/Mistral.fntpack"));
}

And in the render method,


@Override
public void render()
{
    Gdx.gl.glClearColor(1, 1, 1, 1);
    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

    font.renderText("Hello World\nMade by Sri Harsha Chilakapati\nThis font is " + font.getName(), 40, 500, batch);
}

And voila, it produced the following output.

It’s completed now. Get the source in pastebin here http://pastebin.java-gaming.org/dd3d452676f

Wouldn’t it be just easier to output BMFont text files? That way we can use BitmapFont in LibGDX without having to roll our own font renderer. It also means we can take advantage of BitmapFontCache (optimized glyph rendering), Label/Scene2D UI, etc.

Also, a single texture for each glyph? Ouch – that is going to be a huge impact on performance, especially android. The point of a font packer is to pack the fonts into a single texture atlas. :point:

A sprite batch is intended to be used with texture atlases… See here for more info: