Load Glyphs with LibGDX
3 min read

Load Glyphs with LibGDX

Load Glyphs with LibGDX

TL;DR: Remember to give the font loader the list of characters to render from the font!

I'm trying to get a more resolution-independent mechanism to load simple shapes in place. After investigating SVG loading for several days I got nowhere. Then, I found out that libGDX actually supports vector fonts (TTF). Woo-hoo! Internally, it'll convert the loaded font to a BitmapFont, for a specific size (if you want to use the same font with different sizes, you'll have to load and store it accordingly).

Preliminary stuff

It is important (to me) to note there are two ways to load fonts:

  1. Manual
  2. Using the asset manager

Manual loading

According to the wiki, this is really simple:

FreeTypeFontGenerator generator = new FreeTypeFontGenerator(
    Gdx.files.internal("fonts/myfont.ttf"));
FreeTypeFontParameter parameter = new FreeTypeFontParameter();
parameter.size = 12;
BitmapFont font12 = generator.generateFont(parameter); // font size 12 pixels
generator.dispose(); // don't forget to dispose to avoid memory leaks!

This is OK when you have a simple example implementing create() from ApplicationAdapter class. However, if you want to persist it, you'll need to use something like an asset manager.

Using the Asset Manager

The asset manager way is a bit more convoluted and has several components. First, we need to initialise the loader for the manager:

// Declare the asset manager static (singleton)
static AssetManager manager = new AssetManager();

public static void initManager() {
    FileHandleResolver resolver = new InternalFileHandleResolver();
    manager.setLoader(FreeTypeFontGenerator.class,
        new FreeTypeFontGeneratorLoader(resolver));
    manager.setLoader(BitmapFont.class, ".ttf",
        new FreetypeFontLoader(resolver));
}

Then, you can use a simple loader function:

public static BitmapFont getFont(String font, int size) {

    String sz = "font-" + size + ".ttf";
    if (!manager.isLoaded(sz)) {
        FreeTypeFontLoaderParameter parameter = new FreeTypeFontLoaderParameter();
        parameter.fontParameters.size = size;
        parameter.fontFileName = "fonts/" + font + ".ttf";
        manager.load(sz, BitmapFont.class, parameter);
        manager.finishLoadingAsset(sz);
    }
    try {
        return manager.get(sz, BitmapFont.class);
    } catch (Exception e) {
        return null;
    }
}

Displaying glyphs

Once I've written the above, I've downloaded font awesome and whipped up some code to load and display. Basically:

private String str = "\uf0f3";
BitmapFont font = Assets.getFont("fontawesome-webfont", Constants.SYMBOL_MAX_SIZE);

// ... and in render:

@Override
public void render(float delta) {

    //Gdx.gl.glClearColor(159 / 255.0f, 220 / 255.0f, 235 / 255.0f, 0xff / 255.0f);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    batch.setProjectionMatrix(camera.combined);
    batch.begin();
    font.draw(batch, str, -letterWidth/2, letterHeight/2);
    batch.end();
}

...and, to my surprise, it didn't show anything. loading something simple like Arial, and using str = "Test" it would work. Sooo... why not with font awesome?

Eureka

The Eureka Moment came from looking at the inner workings of the vector font: It renders internally to a specified resolution. So, you need something to render (a list of characters). Font awesome doesn't have the ASCII ones, but all are 0xF###. To load the right things, I had to gave the manager loader the correct sequence of characters, so the loader method looks something like this:

public static BitmapFont getFont(String font, String characters, int size) {

    String sz = "font-" + size + ".ttf";
    if (!manager.isLoaded(sz)) {
        FreeTypeFontLoaderParameter parameter = new FreeTypeFontLoaderParameter();
        parameter.fontParameters.size = size;
        parameter.fontFileName = "fonts/" + font + ".ttf";
        parameter.fontParameters.borderColor = Color.WHITE;
        parameter.fontParameters.color = Color.RED;
        parameter.fontParameters.characters = characters;
        parameter.fontParameters.borderWidth = 3;
        manager.load(sz, BitmapFont.class, parameter);
        manager.finishLoadingAsset(sz);
    }
    try {
        return manager.get(sz, BitmapFont.class);
    } catch (Exception e) {
        return null;
    }
}

Now, you can load the font:

private BitmapFont font;
private String str = "\uf0f3";

@Override
public void show() {
    super.show();
    font = Assets.getFont("fontawesome-webfont", str, Constants.SYMBOL_MAX_SIZE);
    letterWidth = font.getData().getGlyph(str.charAt(0)).width;
    letterHeight = font.getData().getGlyph(str.charAt(0)).height;
}


@Override
public void render(float delta) {

    //Gdx.gl.glClearColor(159 / 255.0f, 220 / 255.0f, 235 / 255.0f, 0xff / 255.0f);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    batch.setProjectionMatrix(camera.combined);
    batch.begin();
    font.draw(batch, str, -letterWidth/2, letterHeight/2);
    batch.end();
}

HTH,