libGDX FreeType Font Generate Fallback Fonts

22nd December 2021, 22:00:00

Intro

Gdx-Freetype is an awesome library for libgdx which loads in runtime a freetype font file and creates a Bitmap font on the fly.

There are however some situations where you need to specify multiple fonts, for example for asian characters. By default this isn’t really supported but we can hack around that!

General setup

You probably have something similar to this in your game:

1
2
3
4
5
6
7
8
9
var parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = 16;
parameter.incremental = true;
parameter.packer = new PixmapPacker(2048, 2048, Pixmap.Format.RGBA8888, 2, false);


var baseFontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("font.ttf"));

var font = baseFontGenerator.generateFont(parameter);

Example

Let’s say we want to use 3 other fonts as fallback fonts:

1
var fallbackFontNames = new String[] { "KR.otf", "SC.otf", "TC.otf" };

First we need to create FreeTypeFontGenerators for every fallback font, I just threw it into a HashMap for easier access. You could also specify different parameters for every font, but I was lazy and didn’t really need to:

1
2
3
4
5
6
7
8
var fallbackFonts = new HashMap<String, BitmapFont>();

for (String fallbackFontName : fallbackFontNames) {
var generator = new FreeTypeFontGenerator(Gdx.files.internal(fallbackFontName));
var font = generator.generateFont(parameter);

fallbackFonts.put(fallbackFontName, font);
}

Now for the fun part. We can create our own FreeTypeBitmapFontData and overwrite the getGlyph method. That method gets called whenever the generator requests a new glyph (character).

Therefore we simply need to check if the main font contains the glyph and return that in case it does.

If not we loop through the fallback fonts and return the first matching one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var fallbackData = new FreeTypeFontGenerator.FreeTypeBitmapFontData() {
@Override
public BitmapFont.Glyph getGlyph(char ch) {
var glyph = super.getGlyph(ch);
if (glyph != null)
return glyph;

for (var font : fallbackFonts.values()) {
glyph = font.getData().getGlyph(ch);
if (glyph != null) {
return glyph;
}
}

return null;
}
};

And instead of generating the font via generateFont(parameter) we pass the fallbackData to it as well:

1
var font = baseFontGenerator.generateFont(parameter, fallbackData);

Full Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = 16;
parameter.incremental = true;
parameter.packer = new PixmapPacker(2048, 2048, Pixmap.Format.RGBA8888, 2, false);

var fallbackFontNames = new String[] { "Fonts/NotoSansKR-Regular.otf", "Fonts/NotoSansSC-Regular.otf", "Fonts/NotoSansTC-Regular.otf" };
var fallbackFonts = new HashMap<String, BitmapFont>();

for (String fallbackFontName : fallbackFontNames) {
var generator = new FreeTypeFontGenerator(Gdx.files.internal(fallbackFontName));
var font = generator.generateFont(parameter);

fallbackFonts.put(fallbackFontName, font);
}

var baseFontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("Fonts/Rubik-Regular.ttf"));

var fallbackData = new FreeTypeFontGenerator.FreeTypeBitmapFontData() {
@Override
public BitmapFont.Glyph getGlyph(char ch) {
var glyph = super.getGlyph(ch);
if (glyph != null)
return glyph;

for (var font : fallbackFonts.values()) {
glyph = font.getData().getGlyph(ch);
if (glyph != null) {
return glyph;
}
}

return null;
}
};

var font = baseFontGenerator.generateFont(parameter, fallbackData);

That’s all you need to do, now enjoy glyphs from different font files!

Bonus tip

It’s very easy to use a that generated font in a scene2d.ui skin:

1
2
var style = skin.get(Label.LabelStyle.class); 
style.font = generatedFont;

You need to do that with every Style you’re using.