FntParse.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. using UnityEngine;
  2. using System.Xml;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Text.RegularExpressions;
  6. namespace litefeel
  7. {
  8. public struct Kerning
  9. {
  10. public int first;
  11. public int second;
  12. public int amount;
  13. }
  14. public class FntParse
  15. {
  16. public int textureWidth;
  17. public int textureHeight;
  18. public string[] textureNames;
  19. public string fontName;
  20. public int fontSize;
  21. public int lineHeight;
  22. public int lineBaseHeight;
  23. public CharacterInfo[] charInfos { get; private set; }
  24. public Kerning[] kernings { get; private set; }
  25. public static FntParse GetFntParse(ref string text)
  26. {
  27. FntParse parse = null;
  28. if (text.StartsWith("info"))
  29. {
  30. parse = new FntParse();
  31. parse.DoTextParse(ref text);
  32. }
  33. else if (text.StartsWith("<"))
  34. {
  35. parse = new FntParse();
  36. parse.DoXMLPase(ref text);
  37. }
  38. return parse;
  39. }
  40. #region xml
  41. public void DoXMLPase(ref string content)
  42. {
  43. XmlDocument xml = new XmlDocument();
  44. xml.LoadXml(content);
  45. XmlNode info = xml.GetElementsByTagName("info")[0];
  46. XmlNode common = xml.GetElementsByTagName("common")[0];
  47. XmlNodeList pages = xml.GetElementsByTagName("pages")[0].ChildNodes;
  48. XmlNodeList chars = xml.GetElementsByTagName("chars")[0].ChildNodes;
  49. fontName = info.Attributes.GetNamedItem("face").InnerText;
  50. fontSize = ToInt(info, "size");
  51. lineHeight = ToInt(common, "lineHeight");
  52. lineBaseHeight = ToInt(common, "base");
  53. textureWidth = ToInt(common, "scaleW");
  54. textureHeight = ToInt(common, "scaleH");
  55. int pageNum = ToInt(common, "pages");
  56. textureNames = new string[pageNum];
  57. for (int i = 0; i < pageNum; i++)
  58. {
  59. XmlNode page = pages[i];
  60. int pageId = ToInt(page, "id");
  61. textureNames[pageId] = page.Attributes.GetNamedItem("file").InnerText;
  62. }
  63. charInfos = new CharacterInfo[chars.Count];
  64. for (int i = 0; i < chars.Count; i++)
  65. {
  66. XmlNode charNode = chars[i];
  67. charInfos[i] = CreateCharInfo(
  68. ToInt(charNode, "id"),
  69. ToInt(charNode, "x"),
  70. ToInt(charNode, "y"),
  71. ToInt(charNode, "width"),
  72. ToInt(charNode, "height"),
  73. ToInt(charNode, "xoffset"),
  74. ToInt(charNode, "yoffset"),
  75. ToInt(charNode, "xadvance"),
  76. ToInt(charNode, "page"));
  77. }
  78. // kernings
  79. XmlNode kerningsNode = xml.GetElementsByTagName("kernings")[0];
  80. if (kerningsNode != null && kerningsNode.HasChildNodes)
  81. {
  82. XmlNodeList kerns = kerningsNode.ChildNodes;
  83. kernings = new Kerning[kerns.Count];
  84. for (int i = 0; i < kerns.Count; i++)
  85. {
  86. XmlNode kerningNode = kerns[i];
  87. kernings[i] = new Kerning();
  88. kernings[i].first = ToInt(kerningNode, "first");
  89. kernings[i].second = ToInt(kerningNode, "second");
  90. kernings[i].amount = ToInt(kerningNode, "amount");
  91. }
  92. }
  93. }
  94. private static int ToInt(XmlNode node, string name)
  95. {
  96. return int.Parse(node.Attributes.GetNamedItem(name).InnerText);
  97. }
  98. #endregion
  99. #region text
  100. private Regex pattern;
  101. public void DoTextParse(ref string content)
  102. {
  103. // letter=" " // \S+=".+?"
  104. // letter="x" // \S+=".+?"
  105. // letter=""" // \S+=".+?"
  106. // letter="" // \S+
  107. // char // \S+
  108. pattern = new Regex(@"\S+="".+?""|\S+");
  109. string[] lines = content.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
  110. ReadTextInfo(ref lines[0]);
  111. ReadTextCommon(ref lines[1]);
  112. for (int j = 0; j < textureNames.Length; j++)
  113. {
  114. ReadTextPage(ref lines[j + 2]);
  115. }
  116. // don't use count of chars, count is incorrect if has space
  117. //ReadTextCharCount(ref lines[3]);
  118. List<CharacterInfo> list = new List<CharacterInfo>();
  119. int i = 2 + textureNames.Length;
  120. int l = lines.Length;
  121. for (; i < l; i++)
  122. {
  123. if (!ReadTextChar(i - 4, ref lines[i], ref list))
  124. break;
  125. }
  126. charInfos = list.ToArray();
  127. // skip empty line
  128. for (; i <l; i++)
  129. {
  130. if (lines[i].Length > 0)
  131. break;
  132. }
  133. // kernings
  134. if (i < l)
  135. {
  136. int count = 0;
  137. if (ReadTextCount(ref lines[i++], out count))
  138. {
  139. int start = i;
  140. kernings = new Kerning[count];
  141. for (; i < l; i++)
  142. {
  143. if (!ReadTextKerning(i - start, ref lines[i], ref list))
  144. break;
  145. }
  146. };
  147. }
  148. }
  149. private void ReadTextInfo(ref string line)
  150. {
  151. string[] keys;
  152. string[] values;
  153. SplitParts(line, out keys, out values);
  154. for (int i = keys.Length - 1; i >= 0; i--)
  155. {
  156. switch (keys[i])
  157. {
  158. case "face": fontName = values[i]; break;
  159. case "size": fontSize = int.Parse(values[i]); break;
  160. }
  161. }
  162. }
  163. private void ReadTextCommon(ref string line)
  164. {
  165. string[] keys;
  166. string[] values;
  167. SplitParts(line, out keys, out values);
  168. for (int i = keys.Length - 1; i >= 0; i--)
  169. {
  170. switch (keys[i])
  171. {
  172. case "lineHeight": lineHeight = int.Parse(values[i]); break;
  173. case "base": lineBaseHeight = int.Parse(values[i]); break;
  174. case "scaleW": textureWidth = int.Parse(values[i]); break;
  175. case "scaleH": textureHeight = int.Parse(values[i]); break;
  176. case "pages": textureNames = new string[int.Parse(values[i])];break;
  177. }
  178. }
  179. }
  180. private void ReadTextPage(ref string line)
  181. {
  182. string[] keys;
  183. string[] values;
  184. SplitParts(line, out keys, out values);
  185. string textureName = null;
  186. int pageId = -1;
  187. for (int i = keys.Length - 1; i >= 0; i--)
  188. {
  189. switch (keys[i])
  190. {
  191. case "file": textureName = values[i]; break;
  192. case "id": pageId = int.Parse(values[i]); break;
  193. }
  194. }
  195. textureNames[pageId] = textureName;
  196. }
  197. private bool ReadTextCount(ref string line, out int count)
  198. {
  199. string[] keys;
  200. string[] values;
  201. SplitParts(line, out keys, out values);
  202. count = 0;
  203. for (int i = keys.Length - 1; i >= 0; i--)
  204. {
  205. switch (keys[i])
  206. {
  207. case "count":
  208. count = int.Parse(values[i]);
  209. return true;
  210. }
  211. }
  212. return false;
  213. }
  214. private bool ReadTextChar(int idx, ref string line, ref List<CharacterInfo> list)
  215. {
  216. if (!line.StartsWith("char")) return false;
  217. string[] keys;
  218. string[] values;
  219. SplitParts(line, out keys, out values);
  220. int id = 0, x = 0, y = 0, w = 0, h = 0, xo = 0, yo = 0, xadvance = 0;
  221. for (int i = keys.Length - 1; i >= 0; i--)
  222. {
  223. switch (keys[i])
  224. {
  225. case "id": id = int.Parse(values[i]); break;
  226. case "x": x = int.Parse(values[i]); break;
  227. case "y": y = int.Parse(values[i]); break;
  228. case "width": w = int.Parse(values[i]); break;
  229. case "height": h = int.Parse(values[i]); break;
  230. case "xoffset": xo = int.Parse(values[i]); break;
  231. case "yoffset": yo = int.Parse(values[i]); break;
  232. case "xadvance": xadvance = int.Parse(values[i]); break;
  233. }
  234. }
  235. list.Add(CreateCharInfo(id, x, y, w, h, xo, yo, xadvance));
  236. return true;
  237. }
  238. private bool ReadTextKerning(int idx, ref string line, ref List<CharacterInfo> list)
  239. {
  240. if (!line.StartsWith("kerning")) return false;
  241. string[] keys;
  242. string[] values;
  243. SplitParts(line, out keys, out values);
  244. Kerning kerning = new Kerning();
  245. for (int i = keys.Length - 1; i >= 0; i--)
  246. {
  247. switch (keys[i])
  248. {
  249. case "first" : kerning.first = int.Parse(values[i]); break;
  250. case "second": kerning.second = int.Parse(values[i]); break;
  251. case "amount": kerning.amount = int.Parse(values[i]); break;
  252. }
  253. }
  254. kernings[idx] = kerning;
  255. return true;
  256. }
  257. private bool SplitParts(string line, out string[] keys, out string[] values)
  258. {
  259. MatchCollection parts = pattern.Matches(line);
  260. int count = parts.Count;
  261. keys = new string[count - 1];
  262. values = new string[count - 1];
  263. for (int i = count - 2; i >= 0; i--)
  264. {
  265. string part = parts[i + 1].Value;
  266. int pos = part.IndexOf('=');
  267. keys[i] = part.Substring(0, pos);
  268. values[i] = part.Substring(pos + 1).Trim('"');
  269. }
  270. return true;
  271. }
  272. #endregion
  273. private CharacterInfo CreateCharInfo(int id, int x, int y, int w, int h, int xo, int yo, int xadvance, int page = 0)
  274. {
  275. Rect uv = new Rect();
  276. uv.x = (float)x / textureWidth + page;
  277. uv.y = (float)y / textureHeight;
  278. uv.width = (float)w / textureWidth;
  279. uv.height = (float)h / textureHeight;
  280. uv.y = 1f - uv.y - uv.height;
  281. Rect vert = new Rect();
  282. vert.x = xo;
  283. #if UNITY_5_0 || UNITY_5_1 || UNITY_5_2
  284. // unity 5.0 can not support baseline for
  285. vert.y = yo;
  286. #else
  287. vert.y = yo - lineBaseHeight;
  288. #endif
  289. vert.width = w;
  290. vert.height = h;
  291. vert.y = -vert.y;
  292. vert.height = -vert.height;
  293. CharacterInfo charInfo = new CharacterInfo();
  294. charInfo.index = id;
  295. #if UNITY_5_3_OR_NEWER || UNITY_5_3 || UNITY_5_2
  296. charInfo.uvBottomLeft = new Vector2(uv.xMin, uv.yMin);
  297. charInfo.uvBottomRight = new Vector2(uv.xMax, uv.yMin);
  298. charInfo.uvTopLeft = new Vector2(uv.xMin, uv.yMax);
  299. charInfo.uvTopRight = new Vector2(uv.xMax, uv.yMax);
  300. charInfo.minX = (int)vert.xMin;
  301. charInfo.maxX = (int)vert.xMax;
  302. charInfo.minY = (int)vert.yMax;
  303. charInfo.maxY = (int)vert.yMin;
  304. charInfo.bearing = (int)vert.x;
  305. charInfo.advance = xadvance;
  306. #else
  307. #pragma warning disable 618
  308. charInfo.uv = uv;
  309. charInfo.vert = vert;
  310. charInfo.width = xadvance;
  311. #pragma warning restore 618
  312. #endif
  313. return charInfo;
  314. }
  315. }
  316. }