/* AngelCode bitmap font parsing using C# * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp * * Copyright © 2012-2015 Cyotek Ltd. * * Licensed under the MIT License. See license.txt for the full text. */ using System; using System.Collections.Generic; using System.IO; #if !XENKO using Microsoft.Xna.Framework; #else using Xenko.Core.Mathematics; #endif namespace Cyotek.Drawing.BitmapFont { /// /// Parsing class for bitmap fonts generated by AngelCode BMFont /// internal static class BitmapFontLoader { #region Static Methods /// /// Loads a bitmap font from a file, attempting to auto detect the file type /// /// Thrown when one or more required arguments are null. /// Thrown when the requested file is not present. /// Thrown when an Invalid Data error condition occurs. /// Name of the file to load. /// /// A containing the loaded data. /// public static BitmapFont LoadFontFromFile(string fileName) { BitmapFont result; if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName", "File name not specified"); if (!File.Exists(fileName)) throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); using (var file = File.OpenRead(fileName)) { using (TextReader reader = new StreamReader(file)) { string line; line = reader.ReadLine(); if (line.StartsWith("info ")) result = LoadFontFromTextFile(fileName); else if (line.StartsWith(" /// Loads a bitmap font from a file containing font data in text format. /// /// Thrown when one or more required arguments are null. /// Thrown when the requested file is not present. /// Name of the file to load. /// /// A containing the loaded data. /// public static BitmapFont LoadFontFromTextFile(string fileName) { BitmapFont font; if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName"); if (!File.Exists(fileName)) throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); font = new BitmapFont(); using (Stream stream = File.OpenRead(fileName)) { font.LoadText(stream); } QualifyResourcePaths(font, Path.GetDirectoryName(fileName)); return font; } /// /// Loads a bitmap font from a file containing font data in XML format. /// /// Thrown when one or more required arguments are null. /// Thrown when the requested file is not present. /// Name of the file to load. /// /// A containing the loaded data. /// public static BitmapFont LoadFontFromXmlFile(string fileName) { BitmapFont font; if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName"); if (!File.Exists(fileName)) throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); font = new BitmapFont(); using (Stream stream = File.OpenRead(fileName)) { font.LoadXml(stream); } QualifyResourcePaths(font, Path.GetDirectoryName(fileName)); return font; } /// /// Returns a boolean from an array of name/value pairs. /// /// The array of parts. /// The name of the value to return. /// Default value(if the key doesnt exist or can't be parsed) /// internal static bool GetNamedBool(string[] parts, string name, bool defaultValue = false) { var s = GetNamedString(parts, name); bool result; int v; if (int.TryParse(s, out v)) result = v > 0; else result = defaultValue; return result; } /// /// Returns an integer from an array of name/value pairs. /// /// The array of parts. /// The name of the value to return. /// Default value(if the key doesnt exist or can't be parsed) /// internal static int GetNamedInt(string[] parts, string name, int defaultValue = 0) { var s = GetNamedString(parts, name); int result; if (!int.TryParse(s, out result)) result = defaultValue; return result; } /// /// Returns a string from an array of name/value pairs. /// /// The array of parts. /// The name of the value to return. /// internal static string GetNamedString(string[] parts, string name) { string result; result = string.Empty; foreach (var part in parts) { int nameEndIndex; nameEndIndex = part.IndexOf('='); if (nameEndIndex != -1) { string namePart; string valuePart; namePart = part.Substring(0, nameEndIndex); valuePart = part.Substring(nameEndIndex + 1); if (string.Equals(name, namePart, StringComparison.OrdinalIgnoreCase)) { int length; length = valuePart.Length; if (length > 1 && valuePart[0] == '"' && valuePart[length - 1] == '"') valuePart = valuePart.Substring(1, length - 2); result = valuePart; break; } } } return result; } /// /// Creates a Padding object from a string representation /// /// The string. /// internal 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. /// internal 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()) }; } /// /// Updates data with a fully qualified path /// /// The to update. /// The path where texture resources are located. internal static void QualifyResourcePaths(BitmapFont font, string resourcePath) { Page[] pages; pages = font.Pages; for (var i = 0; i < pages.Length; i++) { Page page; page = pages[i]; page.FileName = Path.Combine(resourcePath, page.FileName); pages[i] = page; } font.Pages = pages; } /// /// 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. /// internal static string[] Split(string s, char delimiter) { string[] results; if (s.IndexOf('"') != -1) { 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[] { delimiter }, StringSplitOptions.RemoveEmptyEntries); } return results; } /// /// Converts the given collection into an array /// /// Type of the items in the array /// The values. /// internal 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 } }