//public domain assumed from cyotek.com using System; using System.Reflection; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Xml; namespace Cyotek.Drawing.BitmapFont { // Parsing class for bitmap fonts generated by AngelCode BMFont // http://www.angelcode.com/products/bmfont/ public static class BitmapFontLoader { #region  Public Class Methods ///// ///// Loads a bitmap font from a file, attempting to auto detect the file type ///// ///// Name of the file to load. ///// //public static BitmapFont LoadFontFromFile(string fileName) //{ // BitmapFont result; // if (string.IsNullOrEmpty(fileName)) // throw new ArgumentNullException("fileName", "File name not specified"); // else if (!File.Exists(fileName)) // throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); // using (FileStream file = File.OpenRead(fileName)) // { // using (TextReader reader = new StreamReader(file)) // { // string line; // line = reader.ReadLine(); // if (line.StartsWith("info ")) // result = BitmapFontLoader.LoadFontFromTextFile(fileName); // else if (line.StartsWith(" /// Loads a bitmap font from a text file. /// /// Name of the file to load. /// public static BitmapFont LoadFontFromTextFile(string fileName) { BitmapFont font; IDictionary pageData; IDictionary kerningDictionary; IDictionary charDictionary; string resourcePath; string[] lines; if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName", "File name not specified"); else if (!File.Exists(fileName)) throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); pageData = new SortedDictionary(); kerningDictionary = new Dictionary(); charDictionary = new Dictionary(); font = new BitmapFont(); resourcePath = Path.GetDirectoryName(fileName); lines = File.ReadAllLines(fileName); foreach (string line in lines) { string[] parts; parts = BitmapFontLoader.Split(line, ' '); if (parts.Length != 0) { switch (parts[0]) { case "info": font.FamilyName = BitmapFontLoader.GetNamedString(parts, "face"); font.FontSize = BitmapFontLoader.GetNamedInt(parts, "size"); font.Bold = BitmapFontLoader.GetNamedBool(parts, "bold"); font.Italic = BitmapFontLoader.GetNamedBool(parts, "italic"); font.Charset = BitmapFontLoader.GetNamedString(parts, "charset"); font.Unicode = BitmapFontLoader.GetNamedBool(parts, "unicode"); font.StretchedHeight = BitmapFontLoader.GetNamedInt(parts, "stretchH"); font.Smoothed = BitmapFontLoader.GetNamedBool(parts, "smooth"); font.SuperSampling = BitmapFontLoader.GetNamedInt(parts, "aa"); font.Padding = BitmapFontLoader.ParsePadding(BitmapFontLoader.GetNamedString(parts, "padding")); font.Spacing = BitmapFontLoader.ParsePoint(BitmapFontLoader.GetNamedString(parts, "spacing")); font.OutlineSize = BitmapFontLoader.GetNamedInt(parts, "outline"); break; case "common": font.LineHeight = BitmapFontLoader.GetNamedInt(parts, "lineHeight"); font.BaseHeight = BitmapFontLoader.GetNamedInt(parts, "base"); font.TextureSize = new Size ( BitmapFontLoader.GetNamedInt(parts, "scaleW"), BitmapFontLoader.GetNamedInt(parts, "scaleH") ); font.Packed = BitmapFontLoader.GetNamedBool(parts, "packed"); font.AlphaChannel = BitmapFontLoader.GetNamedInt(parts, "alphaChnl"); font.RedChannel = BitmapFontLoader.GetNamedInt(parts, "redChnl"); font.GreenChannel = BitmapFontLoader.GetNamedInt(parts, "greenChnl"); font.BlueChannel = BitmapFontLoader.GetNamedInt(parts, "blueChnl"); break; case "page": int id; string name; string textureId; id = BitmapFontLoader.GetNamedInt(parts, "id"); name = BitmapFontLoader.GetNamedString(parts, "file"); textureId = Path.GetFileNameWithoutExtension(name); pageData.Add(id, new Page(id, Path.Combine(resourcePath, name))); break; case "char": Character charData; charData = new Character { Char = (char)BitmapFontLoader.GetNamedInt(parts, "id"), Bounds = new Rectangle ( BitmapFontLoader.GetNamedInt(parts, "x"), BitmapFontLoader.GetNamedInt(parts, "y"), BitmapFontLoader.GetNamedInt(parts, "width"), BitmapFontLoader.GetNamedInt(parts, "height") ), Offset = new Point ( BitmapFontLoader.GetNamedInt(parts, "xoffset"), BitmapFontLoader.GetNamedInt(parts, "yoffset") ), XAdvance = BitmapFontLoader.GetNamedInt(parts, "xadvance"), TexturePage = BitmapFontLoader.GetNamedInt(parts, "page"), Channel = BitmapFontLoader.GetNamedInt(parts, "chnl") }; charDictionary.Add(charData.Char, charData); break; case "kerning": Kerning key; key = new Kerning((char)BitmapFontLoader.GetNamedInt(parts, "first"), (char)BitmapFontLoader.GetNamedInt(parts, "second"), GetNamedInt(parts, "amount")); if (!kerningDictionary.ContainsKey(key)) kerningDictionary.Add(key, key.Amount); break; } } } font.Pages = BitmapFontLoader.ToArray(pageData.Values); font.Characters = charDictionary; font.Kernings = kerningDictionary; return font; } /// /// Loads a bitmap font from an XML file. /// /// Name of the file to load. /// public static BitmapFont LoadFontFromXmlFile(Stream stream) { XmlDocument document; BitmapFont font; IDictionary pageData; IDictionary kerningDictionary; IDictionary charDictionary; XmlNode root; XmlNode properties; document = new XmlDocument(); pageData = new SortedDictionary(); kerningDictionary = new Dictionary(); charDictionary = new Dictionary(); font = new BitmapFont(); document.Load(stream); root = document.DocumentElement; // load the basic attributes properties = root.SelectSingleNode("info"); font.FamilyName = properties.Attributes["face"].Value; font.FontSize = Convert.ToInt32(properties.Attributes["size"].Value); font.Bold = Convert.ToInt32(properties.Attributes["bold"].Value) != 0; font.Italic = Convert.ToInt32(properties.Attributes["italic"].Value) != 0; font.Unicode = Convert.ToInt32(properties.Attributes["unicode"].Value) != 0; font.StretchedHeight = Convert.ToInt32(properties.Attributes["stretchH"].Value); font.Charset = properties.Attributes["charset"].Value; font.Smoothed = Convert.ToInt32(properties.Attributes["smooth"].Value) != 0; font.SuperSampling = Convert.ToInt32(properties.Attributes["aa"].Value); font.Padding = BitmapFontLoader.ParsePadding(properties.Attributes["padding"].Value); font.Spacing = BitmapFontLoader.ParsePoint(properties.Attributes["spacing"].Value); font.OutlineSize = Convert.ToInt32(properties.Attributes["outline"].Value); // common attributes properties = root.SelectSingleNode("common"); font.BaseHeight = Convert.ToInt32(properties.Attributes["lineHeight"].Value); font.LineHeight = Convert.ToInt32(properties.Attributes["base"].Value); font.TextureSize = new Size ( Convert.ToInt32(properties.Attributes["scaleW"].Value), Convert.ToInt32(properties.Attributes["scaleH"].Value) ); font.Packed = Convert.ToInt32(properties.Attributes["packed"].Value) != 0; font.AlphaChannel = Convert.ToInt32(properties.Attributes["alphaChnl"].Value); font.RedChannel = Convert.ToInt32(properties.Attributes["redChnl"].Value); font.GreenChannel = Convert.ToInt32(properties.Attributes["greenChnl"].Value); font.BlueChannel = Convert.ToInt32(properties.Attributes["blueChnl"].Value); // load texture information foreach (XmlNode node in root.SelectNodes("pages/page")) { Page page; page = new Page(); page.Id = Convert.ToInt32(node.Attributes["id"].Value); page.FileName = node.Attributes["file"].Value; pageData.Add(page.Id, page); } font.Pages = BitmapFontLoader.ToArray(pageData.Values); // load character information foreach (XmlNode node in root.SelectNodes("chars/char")) { Character character; character = new Character(); character.Char = (char)Convert.ToInt32(node.Attributes["id"].Value); character.Bounds = new Rectangle ( Convert.ToInt32(node.Attributes["x"].Value), Convert.ToInt32(node.Attributes["y"].Value), Convert.ToInt32(node.Attributes["width"].Value), Convert.ToInt32(node.Attributes["height"].Value) ); character.Offset = new Point ( Convert.ToInt32(node.Attributes["xoffset"].Value), Convert.ToInt32(node.Attributes["yoffset"].Value) ); character.XAdvance = Convert.ToInt32(node.Attributes["xadvance"].Value); character.TexturePage = Convert.ToInt32(node.Attributes["page"].Value); character.Channel = Convert.ToInt32(node.Attributes["chnl"].Value); charDictionary.Add(character.Char, character); } font.Characters = charDictionary; // loading kerning information foreach (XmlNode node in root.SelectNodes("kernings/kerning")) { Kerning key; key = new Kerning((char)Convert.ToInt32(node.Attributes["first"].Value), (char)Convert.ToInt32(node.Attributes["second"].Value), Convert.ToInt32(node.Attributes["amount"].Value)); if (!kerningDictionary.ContainsKey(key)) kerningDictionary.Add(key, key.Amount); } font.Kernings = kerningDictionary; return font; } #endregion  Public Class Methods #region  Private Class Methods /// /// Returns a boolean from an array of name/value pairs. /// /// The array of parts. /// The name of the value to return. /// private static bool GetNamedBool(string[] parts, string name) { return BitmapFontLoader.GetNamedInt(parts, name) != 0; } /// /// Returns an integer from an array of name/value pairs. /// /// The array of parts. /// The name of the value to return. /// private static int GetNamedInt(string[] parts, string name) { return Convert.ToInt32(BitmapFontLoader.GetNamedString(parts, name)); } /// /// Returns a string from an array of name/value pairs. /// /// The array of parts. /// The name of the value to return. /// private static string GetNamedString(string[] parts, string name) { string result; result = string.Empty; name = name.ToLowerInvariant(); foreach (string part in parts) { int nameEndIndex; nameEndIndex = part.IndexOf("="); if (nameEndIndex != -1) { string namePart; string valuePart; namePart = part.Substring(0, nameEndIndex).ToLowerInvariant(); valuePart = part.Substring(nameEndIndex + 1); if (namePart == name) { if (valuePart.StartsWith("\"") && valuePart.EndsWith("\"")) valuePart = valuePart.Substring(1, valuePart.Length - 2); result = valuePart; break; } } } return result; } /// /// Creates a Padding object from a string representation /// /// The string. /// private static Padding ParsePadding(string s) { string[] parts; parts = s.Split(','); return new Padding() { Left = Convert.ToInt32(parts[3].Trim()), Top = Convert.ToInt32(parts[0].Trim()), Right = Convert.ToInt32(parts[1].Trim()), Bottom = Convert.ToInt32(parts[2].Trim()) }; } /// /// Creates a Point object from a string representation /// /// The string. /// private static Point ParsePoint(string s) { string[] parts; parts = s.Split(','); return new Point() { X = Convert.ToInt32(parts[0].Trim()), Y = Convert.ToInt32(parts[1].Trim()) }; } /// /// Splits the specified string using a given delimiter, ignoring any instances of the delimiter as part of a quoted string. /// /// The string to split. /// The delimiter. /// private static string[] Split(string s, char delimiter) { string[] results; if (s.Contains("\"")) { List parts; int partStart; partStart = -1; parts = new List(); do { int partEnd; int quoteStart; int quoteEnd; bool hasQuotes; quoteStart = s.IndexOf("\"", partStart + 1); quoteEnd = s.IndexOf("\"", quoteStart + 1); partEnd = s.IndexOf(delimiter, partStart + 1); if (partEnd == -1) partEnd = s.Length; hasQuotes = quoteStart != -1 && partEnd > quoteStart && partEnd < quoteEnd; if (hasQuotes) partEnd = s.IndexOf(delimiter, quoteEnd + 1); parts.Add(s.Substring(partStart + 1, partEnd - partStart - 1)); if (hasQuotes) partStart = partEnd - 1; partStart = s.IndexOf(delimiter, partStart + 1); } while (partStart != -1); results = parts.ToArray(); } else results = s.Split(new char[] { delimiter }, StringSplitOptions.RemoveEmptyEntries); return results; } /// /// Converts the given collection into an array /// /// Type of the items in the array /// The values. /// private static T[] ToArray(ICollection values) { T[] result; // avoid a forced .NET 3 dependency just for one call to Linq result = new T[values.Count]; values.CopyTo(result, 0); return result; } #endregion  Private Class Methods } }