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
class we develop in this tutorial is available as a
paste.
Let's start off by understanding the format.
Understanding the FntPack format
FontPacker packs the whole font into an XML file with an extension of
Since these are XML files, let's see an example font here.
1 2 3 4 5 6 7 8
| <?xml version="1.0" encoding="UTF-8"?> <FontPacker> <!-- --> <Font name="Name of font here"> <!-- --> <Glyph char="A" <!-- --> data="Base64 encoded PNG image data here" <!-- --> xadvance="33" /> <!-- --> </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.
Parsing the FntPack file
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.
1
| SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); |
Next we create a new handler which parses the data with the parser.
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
| DefaultHandler handler = new DefaultHandler() { @Override public void startElement(String uri, String localName, String qName, Attributes attr) { if (qName.equalsIgnoreCase("Font")) { name = attr.getValue("name"); } if (qName.equalsIgnoreCase("Glyph")) { char ch = attr.getValue("char").charAt(0); int xadvance = Integer.parseInt(attr.getValue("xadvance")); String data = attr.getValue("data"); byte[] png = Base64Coder.decode(data); Texture tex = new Texture(new Pixmap(png, 0, png.length)); 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.
1
| 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
refers to the
class part of LibGdx.
1
| parser.parse(handle.read(), handler); |
Next I've made methods
,
,
and
which contain the following code.
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 37 38 39 40 41 42 43 44 45 46 47
| public Texture getGlyph(char id) { if (glyphs.containsKey(id)) { return glyphs.get(id); } else { return glyphs.get(' '); } }
public int getAdvanceWidth(char id) { if (advances.containsKey(id)) { return advances.get(id); } else { return advances.get(' '); } }
public int getLineHeight() { return getGlyph(' ').getHeight(); }
public String getName() { return name; }
|
In every font, the line-height is the height of the 'space' character. Next, we make the
method with the following code.
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 37 38 39 40 41 42 43 44 45 46 47
| public void renderText(String text, float x, float y, SpriteBatch batch) { float xpos = x; float ypos = y;
for (char ch: text.toCharArray()) { if (ch == '\r') { continue; }
if (ch == '\n') { ypos -= getLineHeight(); xpos = x; continue; }
Texture tex = getGlyph(ch);
batch.begin(); { batch.draw(tex, xpos, ypos); } batch.end();
xpos += getAdvanceWidth(ch); } }
|
It's that simple. Now it's time for testing a sample font. I've tested the font "Mistral".
Rendering the text using the SpriteBatch
Before rendering, I've loaded the font in the
method like this.
1 2 3 4
| public void create() { font = new PackedFont(Gdx.files.internal("data/Mistral.fntpack")); } |
And in the render method,
1 2 3 4 5 6 7 8
| @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.
