package org.andengine.opengl.font; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.andengine.entity.text.Text; import org.andengine.opengl.font.exception.FontException; import org.andengine.opengl.font.exception.LetterNotFoundException; import org.andengine.opengl.texture.ITexture; import org.andengine.opengl.texture.TextureManager; import org.andengine.opengl.texture.TextureOptions; import org.andengine.opengl.texture.bitmap.BitmapTexture; import org.andengine.opengl.texture.bitmap.BitmapTextureFormat; import org.andengine.util.StreamUtils; import org.andengine.util.TextUtils; import org.andengine.util.adt.io.in.AssetInputStreamOpener; import android.content.res.AssetManager; import android.util.SparseArray; /** * The {@link BitmapFont} class is capable of parsing and loading <code>*.fnt</code> fonts. * A {@link BitmapFont}, just like a {@link Font} can be used to create {@link Text} objects. * * Use any of these editors to generate <code>*.fnt</code> fonts: * * <ul> * <li><a href="http://glyphdesigner.71squared.com/">http://glyphdesigner.71squared.com/</a> (Commercial, Mac OS X)</li> * <li><a href="http://www.n4te.com/hiero/hiero.jnlp">http://www.n4te.com/hiero/hiero.jnlp</a> (Free, Java)</li> * <li><a href="http://slick.cokeandcode.com/demos/hiero.jnlp">http://slick.cokeandcode.com/demos/hiero.jnlp</a> (Free, Java)</li> * <li><a href="http://www.angelcode.com/products/bmfont/">http://www.angelcode.com/products/bmfont/</a> (Free, Windows only)</li> * </ul> * * (c) Zynga 2011 * * @author Nicolas Gramlich <ngramlich@zynga.com> * @since 17:54:59 - 01.11.2011 */ public class BitmapFont implements IFont { // =========================================================== // Constants // =========================================================== private static final String TAG_INFO = "info"; private static final int TAG_INFO_ATTRIBUTECOUNT = 11; private static final String TAG_INFO_ATTRIBUTE_FACE = "face"; private static final String TAG_INFO_ATTRIBUTE_SIZE = "size"; private static final String TAG_INFO_ATTRIBUTE_BOLD = "bold"; private static final String TAG_INFO_ATTRIBUTE_ITALIC = "italic"; private static final String TAG_INFO_ATTRIBUTE_CHARSET = "charset"; private static final String TAG_INFO_ATTRIBUTE_UNICODE = "unicode"; private static final String TAG_INFO_ATTRIBUTE_STRETCHHEIGHT = "stretchH"; private static final String TAG_INFO_ATTRIBUTE_SMOOTH = "smooth"; private static final String TAG_INFO_ATTRIBUTE_ANTIALIASED = "aa"; private static final String TAG_INFO_ATTRIBUTE_PADDING = "padding"; private static final String TAG_INFO_ATTRIBUTE_SPACING = "spacing"; private static final int TAG_INFO_ATTRIBUTE_FACE_INDEX = 1; private static final int TAG_INFO_ATTRIBUTE_SIZE_INDEX = BitmapFont.TAG_INFO_ATTRIBUTE_FACE_INDEX + 1; private static final int TAG_INFO_ATTRIBUTE_BOLD_INDEX = BitmapFont.TAG_INFO_ATTRIBUTE_SIZE_INDEX + 1; private static final int TAG_INFO_ATTRIBUTE_ITALIC_INDEX = BitmapFont.TAG_INFO_ATTRIBUTE_BOLD_INDEX + 1; private static final int TAG_INFO_ATTRIBUTE_CHARSET_INDEX = BitmapFont.TAG_INFO_ATTRIBUTE_ITALIC_INDEX + 1; private static final int TAG_INFO_ATTRIBUTE_UNICODE_INDEX = BitmapFont.TAG_INFO_ATTRIBUTE_CHARSET_INDEX + 1; private static final int TAG_INFO_ATTRIBUTE_STRETCHHEIGHT_INDEX = BitmapFont.TAG_INFO_ATTRIBUTE_UNICODE_INDEX + 1; private static final int TAG_INFO_ATTRIBUTE_SMOOTH_INDEX = BitmapFont.TAG_INFO_ATTRIBUTE_STRETCHHEIGHT_INDEX + 1; private static final int TAG_INFO_ATTRIBUTE_ANTIALIASED_INDEX = BitmapFont.TAG_INFO_ATTRIBUTE_SMOOTH_INDEX + 1; private static final int TAG_INFO_ATTRIBUTE_PADDING_INDEX = BitmapFont.TAG_INFO_ATTRIBUTE_ANTIALIASED_INDEX + 1; private static final int TAG_INFO_ATTRIBUTE_SPACING_INDEX = BitmapFont.TAG_INFO_ATTRIBUTE_PADDING_INDEX + 1; private static final String TAG_COMMON = "common"; private static final int TAG_COMMON_ATTRIBUTECOUNT = 6; private static final String TAG_COMMON_ATTRIBUTE_LINEHEIGHT = "lineHeight"; private static final String TAG_COMMON_ATTRIBUTE_BASE = "base"; private static final String TAG_COMMON_ATTRIBUTE_SCALEWIDTH = "scaleW"; private static final String TAG_COMMON_ATTRIBUTE_SCALEHEIGHT = "scaleH"; private static final String TAG_COMMON_ATTRIBUTE_PAGES = "pages"; private static final String TAG_COMMON_ATTRIBUTE_PACKED = "packed"; private static final int TAG_COMMON_ATTRIBUTE_LINEHEIGHT_INDEX = 1; private static final int TAG_COMMON_ATTRIBUTE_BASE_INDEX = BitmapFont.TAG_COMMON_ATTRIBUTE_LINEHEIGHT_INDEX + 1; private static final int TAG_COMMON_ATTRIBUTE_SCALEWIDTH_INDEX = BitmapFont.TAG_COMMON_ATTRIBUTE_BASE_INDEX + 1; private static final int TAG_COMMON_ATTRIBUTE_SCALEHEIGHT_INDEX = BitmapFont.TAG_COMMON_ATTRIBUTE_SCALEWIDTH_INDEX + 1; private static final int TAG_COMMON_ATTRIBUTE_PAGES_INDEX = BitmapFont.TAG_COMMON_ATTRIBUTE_SCALEHEIGHT_INDEX + 1; private static final int TAG_COMMON_ATTRIBUTE_PACKED_INDEX = BitmapFont.TAG_COMMON_ATTRIBUTE_PAGES_INDEX + 1; private static final String TAG_PAGE = "page"; private static final int TAG_PAGE_ATTRIBUTECOUNT = 2; private static final String TAG_PAGE_ATTRIBUTE_ID = "id"; private static final String TAG_PAGE_ATTRIBUTE_FILE = "file"; private static final int TAG_PAGE_ATTRIBUTE_ID_INDEX = 1; private static final int TAG_PAGE_ATTRIBUTE_FILE_INDEX = BitmapFont.TAG_PAGE_ATTRIBUTE_ID_INDEX + 1; private static final String TAG_CHARS = "chars"; private static final int TAG_CHARS_ATTRIBUTECOUNT = 1; private static final String TAG_CHARS_ATTRIBUTE_COUNT = "count"; private static final int TAG_CHARS_ATTRIBUTE_COUNT_INDEX = 1; private static final String TAG_CHAR = "char"; private static final int TAG_CHAR_ATTRIBUTECOUNT = 10; private static final String TAG_CHAR_ATTRIBUTE_ID = "id"; private static final String TAG_CHAR_ATTRIBUTE_X = "x"; private static final String TAG_CHAR_ATTRIBUTE_Y = "y"; private static final String TAG_CHAR_ATTRIBUTE_WIDTH = "width"; private static final String TAG_CHAR_ATTRIBUTE_HEIGHT = "height"; private static final String TAG_CHAR_ATTRIBUTE_XOFFSET = "xoffset"; private static final String TAG_CHAR_ATTRIBUTE_YOFFSET = "yoffset"; private static final String TAG_CHAR_ATTRIBUTE_XADVANCE = "xadvance"; private static final String TAG_CHAR_ATTRIBUTE_PAGE = "page"; // private static final String TAG_CHAR_ATTRIBUTE_CHANNEL = "chnl"; private static final int TAG_CHAR_ATTRIBUTE_ID_INDEX = 1; private static final int TAG_CHAR_ATTRIBUTE_X_INDEX = BitmapFont.TAG_CHAR_ATTRIBUTE_ID_INDEX + 1; private static final int TAG_CHAR_ATTRIBUTE_Y_INDEX = BitmapFont.TAG_CHAR_ATTRIBUTE_X_INDEX + 1; private static final int TAG_CHAR_ATTRIBUTE_WIDTH_INDEX = BitmapFont.TAG_CHAR_ATTRIBUTE_Y_INDEX + 1; private static final int TAG_CHAR_ATTRIBUTE_HEIGHT_INDEX = BitmapFont.TAG_CHAR_ATTRIBUTE_WIDTH_INDEX + 1; private static final int TAG_CHAR_ATTRIBUTE_XOFFSET_INDEX = BitmapFont.TAG_CHAR_ATTRIBUTE_HEIGHT_INDEX + 1; private static final int TAG_CHAR_ATTRIBUTE_YOFFSET_INDEX = BitmapFont.TAG_CHAR_ATTRIBUTE_XOFFSET_INDEX + 1; private static final int TAG_CHAR_ATTRIBUTE_XADVANCE_INDEX = BitmapFont.TAG_CHAR_ATTRIBUTE_YOFFSET_INDEX + 1; private static final int TAG_CHAR_ATTRIBUTE_PAGE_INDEX = BitmapFont.TAG_CHAR_ATTRIBUTE_XADVANCE_INDEX + 1; // private static final int TAG_CHAR_ATTRIBUTE_CHANNEL_INDEX = BitmapFont.TAG_CHAR_ATTRIBUTE_PAGE_INDEX + 1; private static final String TAG_KERNINGS = "kernings"; private static final int TAG_KERNINGS_ATTRIBUTECOUNT = 1; private static final String TAG_KERNINGS_ATTRIBUTE_COUNT = "count"; private static final int TAG_KERNINGS_ATTRIBUTE_COUNT_INDEX = 1; private static final String TAG_KERNING = "kerning"; private static final int TAG_KERNING_ATTRIBUTECOUNT = 3; private static final String TAG_KERNING_ATTRIBUTE_FIRST = "first"; private static final String TAG_KERNING_ATTRIBUTE_SECOND = "second"; private static final String TAG_KERNING_ATTRIBUTE_AMOUNT = "amount"; private static final int TAG_KERNING_ATTRIBUTE_FIRST_INDEX = 1; private static final int TAG_KERNING_ATTRIBUTE_SECOND_INDEX = BitmapFont.TAG_KERNING_ATTRIBUTE_FIRST_INDEX + 1; private static final int TAG_KERNING_ATTRIBUTE_AMOUNT_INDEX = BitmapFont.TAG_KERNING_ATTRIBUTE_SECOND_INDEX + 1; // =========================================================== // Fields // =========================================================== private final TextureManager mTextureManager; private final BitmapTextureFormat mBitmapTextureFormat; private final TextureOptions mTextureOptions; private final SparseArray<Letter> mCharacterToLetterMap = new SparseArray<Letter>(); private final BitmapFontInfo mBitmapFontInfo; private final BitmapFontPage[] mBitmapFontPages; private final int mLineHeight; private final int mBase; private final int mScaleWidth; private final int mScaleHeight; private final int mBitmapFontPageCount; private final boolean mPacked; private final BitmapFontOptions mBitmapFontOptions; // =========================================================== // Constructors // =========================================================== public BitmapFont(final TextureManager pTextureManager, final AssetManager pAssetManager, final String pAssetPath) { this(pTextureManager, pAssetManager, pAssetPath, BitmapTextureFormat.RGBA_8888, TextureOptions.DEFAULT, BitmapFontOptions.DEFAULT); } public BitmapFont(final TextureManager pTextureManager, final AssetManager pAssetManager, final String pAssetPath, final BitmapTextureFormat pBitmapTextureFormat) { this(pTextureManager, pAssetManager, pAssetPath, pBitmapTextureFormat, TextureOptions.DEFAULT, BitmapFontOptions.DEFAULT); } public BitmapFont(final TextureManager pTextureManager, final AssetManager pAssetManager, final String pAssetPath, final TextureOptions pTextureOptions) { this(pTextureManager, pAssetManager, pAssetPath, BitmapTextureFormat.RGBA_8888, pTextureOptions, BitmapFontOptions.DEFAULT); } public BitmapFont(final TextureManager pTextureManager, final AssetManager pAssetManager, final String pAssetPath, final BitmapTextureFormat pBitmapTextureFormat, final TextureOptions pTextureOptions) { this(pTextureManager, pAssetManager, pAssetPath, pBitmapTextureFormat, pTextureOptions, BitmapFontOptions.DEFAULT); } public BitmapFont(final TextureManager pTextureManager, final AssetManager pAssetManager, final String pAssetPath, final BitmapTextureFormat pBitmapTextureFormat, final TextureOptions pTextureOptions, final BitmapFontOptions pBitmapFontOptions) { this.mTextureManager = pTextureManager; this.mBitmapTextureFormat = pBitmapTextureFormat; this.mTextureOptions = pTextureOptions; this.mBitmapFontOptions = pBitmapFontOptions; InputStream in = null; try { in = pAssetManager.open(pAssetPath); final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in), StreamUtils.IO_BUFFER_SIZE); final String assetBasePath; if(pAssetPath.indexOf('/') == -1) { assetBasePath = ""; } else { assetBasePath = pAssetPath.substring(0, pAssetPath.lastIndexOf('/') + 1); } /* Info. */ { this.mBitmapFontInfo = new BitmapFontInfo(bufferedReader.readLine()); } /* Common. */ { final String common = bufferedReader.readLine(); if((common != null) && common.startsWith(BitmapFont.TAG_COMMON)) { final String[] commonAttributes = TextUtils.SPLITPATTERN_SPACE.split(common, BitmapFont.TAG_COMMON_ATTRIBUTECOUNT + 1); if((commonAttributes.length - 1) != BitmapFont.TAG_COMMON_ATTRIBUTECOUNT) { throw new FontException("Expected: '" + BitmapFont.TAG_COMMON_ATTRIBUTECOUNT + "' " + BitmapFont.TAG_COMMON + " attributes, found: '" + (commonAttributes.length - 1) + "'."); } if(!commonAttributes[0].equals(BitmapFont.TAG_COMMON)) { throw new FontException("Expected: '" + BitmapFont.TAG_COMMON + "' attributes."); } this.mLineHeight = BitmapFont.getIntAttribute(commonAttributes, BitmapFont.TAG_COMMON_ATTRIBUTE_LINEHEIGHT_INDEX, BitmapFont.TAG_COMMON_ATTRIBUTE_LINEHEIGHT); this.mBase = BitmapFont.getIntAttribute(commonAttributes, BitmapFont.TAG_COMMON_ATTRIBUTE_BASE_INDEX, BitmapFont.TAG_COMMON_ATTRIBUTE_BASE); this.mScaleWidth = BitmapFont.getIntAttribute(commonAttributes, BitmapFont.TAG_COMMON_ATTRIBUTE_SCALEWIDTH_INDEX, BitmapFont.TAG_COMMON_ATTRIBUTE_SCALEWIDTH); this.mScaleHeight = BitmapFont.getIntAttribute(commonAttributes, BitmapFont.TAG_COMMON_ATTRIBUTE_SCALEHEIGHT_INDEX, BitmapFont.TAG_COMMON_ATTRIBUTE_SCALEHEIGHT); this.mBitmapFontPageCount = BitmapFont.getIntAttribute(commonAttributes, BitmapFont.TAG_COMMON_ATTRIBUTE_PAGES_INDEX, BitmapFont.TAG_COMMON_ATTRIBUTE_PAGES); this.mPacked = BitmapFont.getBooleanAttribute(commonAttributes, BitmapFont.TAG_COMMON_ATTRIBUTE_PACKED_INDEX, BitmapFont.TAG_COMMON_ATTRIBUTE_PACKED); if(this.mBitmapFontPageCount != 1) { throw new FontException("Only a single page is supported."); } this.mBitmapFontPages = new BitmapFontPage[this.mBitmapFontPageCount]; if(this.mPacked) { throw new FontException("Packed is not supported."); } } else { throw new FontException("Expected: '" + BitmapFont.TAG_COMMON + "' attributes."); } } /* Pages. */ for(int i = 0; i < this.mBitmapFontPageCount; i++) { this.mBitmapFontPages[i] = new BitmapFontPage(pAssetManager, assetBasePath, bufferedReader.readLine()); } /* Chars. */ { final String chars = bufferedReader.readLine(); if((chars != null) && chars.startsWith(BitmapFont.TAG_CHARS)) { final String[] charsAttributes = TextUtils.SPLITPATTERN_SPACE.split(chars, BitmapFont.TAG_CHARS_ATTRIBUTECOUNT + 1); if((charsAttributes.length - 1) != BitmapFont.TAG_CHARS_ATTRIBUTECOUNT) { throw new FontException("Expected: '" + BitmapFont.TAG_CHARS_ATTRIBUTECOUNT + "' " + BitmapFont.TAG_CHARS + " attributes, found: '" + (charsAttributes.length - 1) + "'."); } if(!charsAttributes[0].equals(BitmapFont.TAG_CHARS)) { throw new FontException("Expected: '" + BitmapFont.TAG_CHARS + "' attributes."); } final int characterCount = BitmapFont.getIntAttribute(charsAttributes, BitmapFont.TAG_CHARS_ATTRIBUTE_COUNT_INDEX, BitmapFont.TAG_CHARS_ATTRIBUTE_COUNT); this.parseCharacters(characterCount, bufferedReader); } else { throw new FontException("Expected: '" + BitmapFont.TAG_CHARS + "' attributes."); } } /* Kernings. */ { final String kernings = bufferedReader.readLine(); if((kernings != null) && kernings.startsWith(BitmapFont.TAG_KERNINGS)) { final String[] kerningsAttributes = TextUtils.SPLITPATTERN_SPACE.split(kernings, BitmapFont.TAG_KERNINGS_ATTRIBUTECOUNT + 1); if((kerningsAttributes.length - 1) != BitmapFont.TAG_KERNINGS_ATTRIBUTECOUNT) { throw new FontException("Expected: '" + BitmapFont.TAG_KERNINGS_ATTRIBUTECOUNT + "' " + BitmapFont.TAG_KERNINGS + " attributes, found: '" + (kerningsAttributes.length - 1) + "'."); } if(!kerningsAttributes[0].equals(BitmapFont.TAG_KERNINGS)) { throw new FontException("Expected: '" + BitmapFont.TAG_KERNINGS + "' attributes."); } final int kerningsCount = BitmapFont.getIntAttribute(kerningsAttributes, BitmapFont.TAG_KERNINGS_ATTRIBUTE_COUNT_INDEX, BitmapFont.TAG_KERNINGS_ATTRIBUTE_COUNT); this.parseKernings(kerningsCount, bufferedReader); } } } catch (final IOException e) { throw new FontException("Failed loading BitmapFont. AssetPath: " + pAssetPath, e); } finally { StreamUtils.close(in); } } // =========================================================== // Getter & Setter // =========================================================== public BitmapFontInfo getBitmapFontInfo() { return this.mBitmapFontInfo; } public int getBase() { return this.mBase; } public int getScaleWidth() { return this.mScaleWidth; } public int getScaleHeight() { return this.mScaleHeight; } public int getBitmapFontPageCount() { return this.mBitmapFontPageCount; } public BitmapFontPage[] getBitmapFontPages() { return this.mBitmapFontPages; } public BitmapFontPage getBitmapFontPage(final int pIndex) { return this.mBitmapFontPages[pIndex]; } public boolean isPacked() { return this.mPacked; } // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @Override public ITexture getTexture() { return this.mBitmapFontPages[0].getTexture(); } @Override public void load() { this.loadTextures(); } @Override public void unload() { this.unloadTextures(); } @Override public float getLineHeight() { return this.mLineHeight; } @Override public Letter getLetter(final char pChar) throws LetterNotFoundException { final Letter letter = this.mCharacterToLetterMap.get(pChar); if(letter == null) { throw new LetterNotFoundException("Letter '" + pChar + "' not found."); } return letter; } // =========================================================== // Methods // =========================================================== public void loadTextures() { final BitmapFontPage[] bitmapFontPages = this.mBitmapFontPages; final int bitmapFontPageCount = bitmapFontPages.length; for(int i = 0; i < bitmapFontPageCount; i++) { bitmapFontPages[i].getTexture().load(); } } public void unloadTextures() { final BitmapFontPage[] bitmapFontPages = this.mBitmapFontPages; final int bitmapFontPageCount = bitmapFontPages.length; for(int i = 0; i < bitmapFontPageCount; i++) { bitmapFontPages[i].getTexture().unload(); } } private void parseCharacters(final int pCharacterCount, final BufferedReader pBufferedReader) throws IOException { for(int i = pCharacterCount - 1; i >= 0; i--) { final String character = pBufferedReader.readLine(); final String[] charAttributes = TextUtils.SPLITPATTERN_SPACES.split(character, BitmapFont.TAG_CHAR_ATTRIBUTECOUNT + 1); if((charAttributes.length - 1) != BitmapFont.TAG_CHAR_ATTRIBUTECOUNT) { throw new FontException("Expected: '" + BitmapFont.TAG_CHAR_ATTRIBUTECOUNT + "' " + BitmapFont.TAG_CHAR + " attributes, found: '" + (charAttributes.length - 1) + "'."); } if(!charAttributes[0].equals(BitmapFont.TAG_CHAR)) { throw new FontException("Expected: '" + BitmapFont.TAG_CHAR + "' attributes."); } final char id = BitmapFont.getCharAttribute(charAttributes, BitmapFont.TAG_CHAR_ATTRIBUTE_ID_INDEX, BitmapFont.TAG_CHAR_ATTRIBUTE_ID); final int x = this.mBitmapFontOptions.mTextureOffsetX + BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_CHAR_ATTRIBUTE_X_INDEX, BitmapFont.TAG_CHAR_ATTRIBUTE_X); final int y = this.mBitmapFontOptions.mTextureOffsetY + BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_CHAR_ATTRIBUTE_Y_INDEX, BitmapFont.TAG_CHAR_ATTRIBUTE_Y); final int width = BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_CHAR_ATTRIBUTE_WIDTH_INDEX, BitmapFont.TAG_CHAR_ATTRIBUTE_WIDTH); final int height = BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_CHAR_ATTRIBUTE_HEIGHT_INDEX, BitmapFont.TAG_CHAR_ATTRIBUTE_HEIGHT); final int xOffset = BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_CHAR_ATTRIBUTE_XOFFSET_INDEX, BitmapFont.TAG_CHAR_ATTRIBUTE_XOFFSET); final int yOffset = BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_CHAR_ATTRIBUTE_YOFFSET_INDEX, BitmapFont.TAG_CHAR_ATTRIBUTE_YOFFSET); final int xAdvance = BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_CHAR_ATTRIBUTE_XADVANCE_INDEX, BitmapFont.TAG_CHAR_ATTRIBUTE_XADVANCE); final int page = BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_CHAR_ATTRIBUTE_PAGE_INDEX, BitmapFont.TAG_CHAR_ATTRIBUTE_PAGE); // final int channel = BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_CHAR_ATTRIBUTE_CHANNEL_INDEX, BitmapFont.TAG_CHAR_ATTRIBUTE_CHANNEL); final ITexture bitmapFontPageTexture = this.mBitmapFontPages[page].getTexture(); final float textureWidth = bitmapFontPageTexture.getWidth(); final float textureHeight = bitmapFontPageTexture.getHeight(); final float u = x / textureWidth; final float v = y / textureHeight; final float u2 = (x + width) / textureWidth; final float v2 = (y + height) / textureHeight; this.mCharacterToLetterMap.put(id, new Letter(id, x, y, width, height, xOffset, yOffset, xAdvance, u, v, u2, v2)); } } private void parseKernings(final int pKerningsCount, final BufferedReader pBufferedReader) throws IOException { for(int i = pKerningsCount - 1; i >= 0; i--) { final String kerning = pBufferedReader.readLine(); final String[] charAttributes = TextUtils.SPLITPATTERN_SPACES.split(kerning, BitmapFont.TAG_KERNING_ATTRIBUTECOUNT + 1); if((charAttributes.length - 1) != BitmapFont.TAG_KERNING_ATTRIBUTECOUNT) { throw new FontException("Expected: '" + BitmapFont.TAG_KERNING_ATTRIBUTECOUNT + "' " + BitmapFont.TAG_KERNING + " attributes, found: '" + (charAttributes.length - 1) + "'."); } if(!charAttributes[0].equals(BitmapFont.TAG_KERNING)) { throw new FontException("Expected: '" + BitmapFont.TAG_KERNING + "' attributes."); } final int first = BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_KERNING_ATTRIBUTE_FIRST_INDEX, BitmapFont.TAG_KERNING_ATTRIBUTE_FIRST); final int second = BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_KERNING_ATTRIBUTE_SECOND_INDEX, BitmapFont.TAG_KERNING_ATTRIBUTE_SECOND); final int amount = BitmapFont.getIntAttribute(charAttributes, BitmapFont.TAG_KERNING_ATTRIBUTE_AMOUNT_INDEX, BitmapFont.TAG_KERNING_ATTRIBUTE_AMOUNT); this.mCharacterToLetterMap.get(first).addKerning(second, amount); } } private static boolean getBooleanAttribute(final String[] pData, final int pPosition, final String pAttribute) { final String data = pData[pPosition]; final int attributeLength = pAttribute.length(); if(!data.startsWith(pAttribute) || (data.charAt(attributeLength) != '=')) { throw new FontException("Expected '" + pAttribute + "' at position '" + pPosition + "', but found: '" + data + "'."); } return Integer.parseInt(data.substring(attributeLength + 1)) != 0; } private static char getCharAttribute(final String[] pData, final int pPosition, final String pAttribute) { return (char) BitmapFont.getIntAttribute(pData, pPosition, pAttribute); } private static int getIntAttribute(final String[] pData, final int pPosition, final String pAttribute) { final String data = pData[pPosition]; final int attributeLength = pAttribute.length(); if(!data.startsWith(pAttribute) || (data.charAt(attributeLength) != '=')) { throw new FontException("Expected '" + pAttribute + "' at position '" + pPosition + "', but found: '" + data + "'."); } return Integer.parseInt(data.substring(attributeLength + 1)); } private static String getStringAttribute(final String[] pData, final int pPosition, final String pAttribute) { final String data = pData[pPosition]; final int attributeLength = pAttribute.length(); if(!data.startsWith(pAttribute) || (data.charAt(attributeLength) != '=')) { throw new FontException("Expected '" + pAttribute + "' at position '" + pPosition + "', but found: '" + data + "'."); } return data.substring(attributeLength + 2, data.length() - 1); } private static String getAttribute(final String[] pData, final int pPosition, final String pAttribute) { final String data = pData[pPosition]; final int attributeLength = pAttribute.length(); if(!data.startsWith(pAttribute)) { throw new FontException("Expected '" + pAttribute + "' at position '" + pPosition + "', but found: '" + data + "'."); } return data.substring(attributeLength + 1); } // =========================================================== // Inner and Anonymous Classes // =========================================================== public static class BitmapFontOptions { // =========================================================== // Constants // =========================================================== public static final BitmapFontOptions DEFAULT = new BitmapFontOptions(0, 0); // =========================================================== // Fields // =========================================================== private final int mTextureOffsetX; private final int mTextureOffsetY; // =========================================================== // Constructors // =========================================================== public BitmapFontOptions(final int pTextureOffsetX, final int pTextureOffsetY) { this.mTextureOffsetX = pTextureOffsetX; this.mTextureOffsetY = pTextureOffsetY; } // =========================================================== // Getter & Setter // =========================================================== public int getTextureOffsetX() { return this.mTextureOffsetX; } public int getTextureOffsetY() { return this.mTextureOffsetY; } // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== } public class BitmapFontInfo { // =========================================================== // Constants // =========================================================== private static final int PADDING_LEFT_INDEX = 0; private static final int PADDING_TOP_INDEX = BitmapFontInfo.PADDING_LEFT_INDEX + 1; private static final int PADDING_RIGHT_INDEX = BitmapFontInfo.PADDING_TOP_INDEX + 1; private static final int PADDING_BOTTOM_INDEX = BitmapFontInfo.PADDING_RIGHT_INDEX + 1; private static final int SPACING_X_INDEX = 0; private static final int SPACING_Y_INDEX = BitmapFontInfo.SPACING_X_INDEX + 1; // =========================================================== // Fields // =========================================================== private final String mFace; private final int mSize; private final boolean mBold; private final boolean mItalic; private final String mCharset; private final int mUnicode; private final int mStretchHeight; private final boolean mSmooth; private final boolean mAntiAliased; private final int mPaddingLeft; private final int mPaddingTop; private final int mPaddingRight; private final int mPaddingBottom; private final int mSpacingX; private final int mSpacingY; // =========================================================== // Constructors // =========================================================== public BitmapFontInfo(final String pData) throws FontException { if(pData == null) { throw new FontException("pData must not be null."); } final String[] infoAttributes = TextUtils.SPLITPATTERN_SPACE.split(pData, BitmapFont.TAG_INFO_ATTRIBUTECOUNT + 1); if((infoAttributes.length - 1) != BitmapFont.TAG_INFO_ATTRIBUTECOUNT) { throw new FontException("Expected: '" + BitmapFont.TAG_INFO_ATTRIBUTECOUNT + "' " + BitmapFont.TAG_INFO + " attributes, found: '" + (infoAttributes.length - 1) + "'."); } if(!infoAttributes[0].equals(BitmapFont.TAG_INFO)) { throw new FontException("Expected: '" + BitmapFont.TAG_INFO + "' attributes."); } this.mFace = BitmapFont.getStringAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_FACE_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_FACE); this.mSize = BitmapFont.getIntAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_SIZE_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_SIZE); this.mBold = BitmapFont.getBooleanAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_BOLD_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_BOLD); this.mItalic = BitmapFont.getBooleanAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_ITALIC_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_ITALIC); this.mCharset = BitmapFont.getStringAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_CHARSET_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_CHARSET); this.mUnicode = BitmapFont.getIntAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_UNICODE_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_UNICODE); this.mStretchHeight = BitmapFont.getIntAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_STRETCHHEIGHT_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_STRETCHHEIGHT); this.mSmooth = BitmapFont.getBooleanAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_SMOOTH_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_SMOOTH); this.mAntiAliased = BitmapFont.getBooleanAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_ANTIALIASED_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_ANTIALIASED); final String padding = BitmapFont.getAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_PADDING_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_PADDING); final String[] paddings = TextUtils.SPLITPATTERN_COMMA.split(padding, 4); this.mPaddingLeft = Integer.parseInt(paddings[BitmapFontInfo.PADDING_LEFT_INDEX]); this.mPaddingTop = Integer.parseInt(paddings[BitmapFontInfo.PADDING_TOP_INDEX]); this.mPaddingRight = Integer.parseInt(paddings[BitmapFontInfo.PADDING_RIGHT_INDEX]); this.mPaddingBottom = Integer.parseInt(paddings[BitmapFontInfo.PADDING_BOTTOM_INDEX]); final String spacing = BitmapFont.getAttribute(infoAttributes, BitmapFont.TAG_INFO_ATTRIBUTE_SPACING_INDEX, BitmapFont.TAG_INFO_ATTRIBUTE_SPACING); final String[] spacings = TextUtils.SPLITPATTERN_COMMA.split(spacing, 2); this.mSpacingX = Integer.parseInt(spacings[BitmapFontInfo.SPACING_X_INDEX]); this.mSpacingY = Integer.parseInt(spacings[BitmapFontInfo.SPACING_Y_INDEX]); } // =========================================================== // Getter & Setter // =========================================================== public String getFace() { return this.mFace; } public int getSize() { return this.mSize; } public boolean isBold() { return this.mBold; } public boolean isItalic() { return this.mItalic; } public String getCharset() { return this.mCharset; } public int getUnicode() { return this.mUnicode; } public int getStretchHeight() { return this.mStretchHeight; } public boolean isSmooth() { return this.mSmooth; } public boolean isAntiAliased() { return this.mAntiAliased; } public int getPaddingLeft() { return this.mPaddingLeft; } public int getPaddingTop() { return this.mPaddingTop; } public int getPaddingRight() { return this.mPaddingRight; } public int getPaddingBottom() { return this.mPaddingBottom; } public int getSpacingX() { return this.mSpacingX; } public int getSpacingY() { return this.mSpacingY; } // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== } public class BitmapFontPage { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== private int mID; private final ITexture mTexture; // =========================================================== // Constructors // =========================================================== public BitmapFontPage(final AssetManager pAssetManager, final String pAssetBasePath, final String pData) throws IOException { final String[] pageAttributes = TextUtils.SPLITPATTERN_SPACE.split(pData, BitmapFont.TAG_PAGE_ATTRIBUTECOUNT + 1); if((pageAttributes.length - 1) != BitmapFont.TAG_PAGE_ATTRIBUTECOUNT) { throw new FontException("Expected: '" + BitmapFont.TAG_PAGE_ATTRIBUTECOUNT + "' " + BitmapFont.TAG_PAGE + " attributes, found: '" + (pageAttributes.length - 1) + "'."); } if(!pageAttributes[0].equals(BitmapFont.TAG_PAGE)) { throw new FontException("Expected: '" + BitmapFont.TAG_PAGE + "' attributes."); } this.mID = BitmapFont.getIntAttribute(pageAttributes, BitmapFont.TAG_PAGE_ATTRIBUTE_ID_INDEX, BitmapFont.TAG_PAGE_ATTRIBUTE_ID); final String file = BitmapFont.getStringAttribute(pageAttributes, BitmapFont.TAG_PAGE_ATTRIBUTE_FILE_INDEX, BitmapFont.TAG_PAGE_ATTRIBUTE_FILE); final String assetPath = pAssetBasePath + file; this.mTexture = new BitmapTexture(BitmapFont.this.mTextureManager, new AssetInputStreamOpener(pAssetManager, assetPath), BitmapFont.this.mBitmapTextureFormat, BitmapFont.this.mTextureOptions); } // =========================================================== // Getter & Setter // =========================================================== public int getID() { return this.mID; } public ITexture getTexture() { return this.mTexture; } // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== } }