You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

635 lines
22 KiB

  1. using System;
  2. using UnityEngine;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. namespace TMPro
  7. {
  8. /// <summary>
  9. /// Contains the font asset for the specified font weight styles.
  10. /// </summary>
  11. [Serializable]
  12. public struct TMP_FontWeights
  13. {
  14. public TMP_FontAsset regularTypeface;
  15. public TMP_FontAsset italicTypeface;
  16. }
  17. [Serializable]
  18. public class TMP_FontAsset : TMP_Asset
  19. {
  20. /// <summary>
  21. /// Default Font Asset used as last resort when glyphs are missing.
  22. /// </summary>
  23. public static TMP_FontAsset defaultFontAsset
  24. {
  25. get
  26. {
  27. if (s_defaultFontAsset == null)
  28. {
  29. s_defaultFontAsset = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF");
  30. }
  31. return s_defaultFontAsset;
  32. }
  33. }
  34. private static TMP_FontAsset s_defaultFontAsset;
  35. public enum FontAssetTypes { None = 0, SDF = 1, Bitmap = 2 };
  36. public FontAssetTypes fontAssetType;
  37. /// <summary>
  38. /// The general information about the font.
  39. /// </summary>
  40. public FaceInfo fontInfo
  41. { get { return m_fontInfo; } }
  42. [SerializeField]
  43. private FaceInfo m_fontInfo;
  44. [SerializeField]
  45. public Texture2D atlas; // Should add a property to make this read-only.
  46. // Glyph Info
  47. [SerializeField]
  48. private List<TMP_Glyph> m_glyphInfoList;
  49. public Dictionary<int, TMP_Glyph> characterDictionary
  50. {
  51. get
  52. {
  53. if (m_characterDictionary == null)
  54. ReadFontDefinition();
  55. return m_characterDictionary;
  56. }
  57. }
  58. private Dictionary<int, TMP_Glyph> m_characterDictionary;
  59. /// <summary>
  60. /// Dictionary containing the kerning data
  61. /// </summary>
  62. public Dictionary<int, KerningPair> kerningDictionary
  63. {
  64. get { return m_kerningDictionary; }
  65. }
  66. private Dictionary<int, KerningPair> m_kerningDictionary;
  67. /// <summary>
  68. ///
  69. /// </summary>
  70. public KerningTable kerningInfo
  71. {
  72. get { return m_kerningInfo; }
  73. }
  74. [SerializeField]
  75. private KerningTable m_kerningInfo;
  76. [SerializeField]
  77. #pragma warning disable 0169 // Property is used to create an empty Kerning Pair in the editor.
  78. private KerningPair m_kerningPair; // Used for creating a new kerning pair in Editor Panel.
  79. /// <summary>
  80. /// List containing the Fallback font assets for this font.
  81. /// </summary>
  82. [SerializeField]
  83. public List<TMP_FontAsset> fallbackFontAssets;
  84. /// <summary>
  85. /// The settings used in the Font Asset Creator when this font asset was created or edited.
  86. /// </summary>
  87. public FontAssetCreationSettings creationSettings
  88. {
  89. get { return m_CreationSettings; }
  90. set { m_CreationSettings = value; }
  91. }
  92. [SerializeField]
  93. public FontAssetCreationSettings m_CreationSettings;
  94. // FONT WEIGHTS
  95. [SerializeField]
  96. public TMP_FontWeights[] fontWeights = new TMP_FontWeights[10];
  97. private int[] m_characterSet; // Array containing all the characters in this font asset.
  98. public float normalStyle = 0;
  99. public float normalSpacingOffset = 0;
  100. public float boldStyle = 0.75f;
  101. public float boldSpacing = 7f;
  102. public byte italicStyle = 35;
  103. public byte tabSize = 10;
  104. private byte m_oldTabSize;
  105. void OnEnable()
  106. {
  107. //Debug.Log("OnEnable has been called on " + this.name);
  108. }
  109. void OnDisable()
  110. {
  111. //Debug.Log("TextMeshPro Font Asset [" + this.name + "] has been disabled!");
  112. }
  113. #if UNITY_EDITOR
  114. /// <summary>
  115. ///
  116. /// </summary>
  117. void OnValidate()
  118. {
  119. if (m_oldTabSize != tabSize)
  120. {
  121. m_oldTabSize = tabSize;
  122. ReadFontDefinition();
  123. }
  124. }
  125. #endif
  126. /// <summary>
  127. ///
  128. /// </summary>
  129. /// <param name="faceInfo"></param>
  130. public void AddFaceInfo(FaceInfo faceInfo)
  131. {
  132. m_fontInfo = faceInfo;
  133. }
  134. /// <summary>
  135. ///
  136. /// </summary>
  137. /// <param name="glyphInfo"></param>
  138. public void AddGlyphInfo(TMP_Glyph[] glyphInfo)
  139. {
  140. m_glyphInfoList = new List<TMP_Glyph>();
  141. int characterCount = glyphInfo.Length;
  142. m_fontInfo.CharacterCount = characterCount;
  143. m_characterSet = new int[characterCount];
  144. for (int i = 0; i < characterCount; i++)
  145. {
  146. TMP_Glyph g = new TMP_Glyph();
  147. g.id = glyphInfo[i].id;
  148. g.x = glyphInfo[i].x;
  149. g.y = glyphInfo[i].y;
  150. g.width = glyphInfo[i].width;
  151. g.height = glyphInfo[i].height;
  152. g.xOffset = glyphInfo[i].xOffset;
  153. g.yOffset = (glyphInfo[i].yOffset);
  154. g.xAdvance = glyphInfo[i].xAdvance;
  155. g.scale = 1;
  156. m_glyphInfoList.Add(g);
  157. // While iterating through list of glyphs, find the Descender & Ascender for this GlyphSet.
  158. //m_fontInfo.Ascender = Mathf.Max(m_fontInfo.Ascender, glyphInfo[i].yOffset);
  159. //m_fontInfo.Descender = Mathf.Min(m_fontInfo.Descender, glyphInfo[i].yOffset - glyphInfo[i].height);
  160. //Debug.Log(m_fontInfo.Ascender + " " + m_fontInfo.Descender);
  161. m_characterSet[i] = g.id; // Add Character ID to Array to make it easier to get the kerning pairs.
  162. }
  163. // Sort List by ID.
  164. m_glyphInfoList = m_glyphInfoList.OrderBy(s => s.id).ToList();
  165. }
  166. /// <summary>
  167. ///
  168. /// </summary>
  169. /// <param name="kerningTable"></param>
  170. public void AddKerningInfo(KerningTable kerningTable)
  171. {
  172. m_kerningInfo = kerningTable;
  173. }
  174. /// <summary>
  175. ///
  176. /// </summary>
  177. public void ReadFontDefinition()
  178. {
  179. //Debug.Log("Reading Font Definition for " + this.name + ".");
  180. // Make sure that we have a Font Asset file assigned.
  181. if (m_fontInfo == null)
  182. {
  183. return;
  184. }
  185. // Check Font Asset type
  186. //Debug.Log(name + " " + fontAssetType);
  187. // Create new instance of GlyphInfo Dictionary for fast access to glyph info.
  188. m_characterDictionary = new Dictionary<int, TMP_Glyph>();
  189. for (int i = 0; i < m_glyphInfoList.Count; i++)
  190. {
  191. TMP_Glyph glyph = m_glyphInfoList[i];
  192. if (!m_characterDictionary.ContainsKey(glyph.id))
  193. m_characterDictionary.Add(glyph.id, glyph);
  194. // Compatibility
  195. if (glyph.scale == 0) glyph.scale = 1;
  196. }
  197. //Debug.Log("PRE: BaseLine:" + m_fontInfo.Baseline + " Ascender:" + m_fontInfo.Ascender + " Descender:" + m_fontInfo.Descender); // + " Centerline:" + m_fontInfo.CenterLine);
  198. TMP_Glyph temp_charInfo = new TMP_Glyph();
  199. // Add Character (10) LineFeed, (13) Carriage Return & Space (32) to Dictionary if they don't exists.
  200. if (m_characterDictionary.ContainsKey(32))
  201. {
  202. m_characterDictionary[32].width = m_characterDictionary[32].xAdvance; // m_fontInfo.Ascender / 5;
  203. m_characterDictionary[32].height = m_fontInfo.Ascender - m_fontInfo.Descender;
  204. m_characterDictionary[32].yOffset= m_fontInfo.Ascender;
  205. m_characterDictionary[32].scale = 1;
  206. }
  207. else
  208. {
  209. //Debug.Log("Adding Character 32 (Space) to Dictionary for Font (" + m_fontInfo.Name + ").");
  210. temp_charInfo = new TMP_Glyph();
  211. temp_charInfo.id = 32;
  212. temp_charInfo.x = 0;
  213. temp_charInfo.y = 0;
  214. temp_charInfo.width = m_fontInfo.Ascender / 5;
  215. temp_charInfo.height = m_fontInfo.Ascender - m_fontInfo.Descender;
  216. temp_charInfo.xOffset = 0;
  217. temp_charInfo.yOffset = m_fontInfo.Ascender;
  218. temp_charInfo.xAdvance = m_fontInfo.PointSize / 4;
  219. temp_charInfo.scale = 1;
  220. m_characterDictionary.Add(32, temp_charInfo);
  221. }
  222. // Add Non-Breaking Space (160)
  223. if (!m_characterDictionary.ContainsKey(160))
  224. {
  225. temp_charInfo = TMP_Glyph.Clone(m_characterDictionary[32]);
  226. m_characterDictionary.Add(160, temp_charInfo);
  227. }
  228. // Add Zero Width Space (8203)
  229. if (!m_characterDictionary.ContainsKey(8203))
  230. {
  231. temp_charInfo = TMP_Glyph.Clone(m_characterDictionary[32]);
  232. temp_charInfo.width = 0;
  233. temp_charInfo.xAdvance = 0;
  234. m_characterDictionary.Add(8203, temp_charInfo);
  235. }
  236. //Add Zero Width no-break space (8288)
  237. if (!m_characterDictionary.ContainsKey(8288))
  238. {
  239. temp_charInfo = TMP_Glyph.Clone(m_characterDictionary[32]);
  240. temp_charInfo.width = 0;
  241. temp_charInfo.xAdvance = 0;
  242. m_characterDictionary.Add(8288, temp_charInfo);
  243. }
  244. // Add Linefeed (10)
  245. if (m_characterDictionary.ContainsKey(10) == false)
  246. {
  247. //Debug.Log("Adding Character 10 (Linefeed) to Dictionary for Font (" + m_fontInfo.Name + ").");
  248. temp_charInfo = new TMP_Glyph();
  249. temp_charInfo.id = 10;
  250. temp_charInfo.x = 0; // m_characterDictionary[32].x;
  251. temp_charInfo.y = 0; // m_characterDictionary[32].y;
  252. temp_charInfo.width = 10; // m_characterDictionary[32].width;
  253. temp_charInfo.height = m_characterDictionary[32].height;
  254. temp_charInfo.xOffset = 0; // m_characterDictionary[32].xOffset;
  255. temp_charInfo.yOffset = m_characterDictionary[32].yOffset;
  256. temp_charInfo.xAdvance = 0;
  257. temp_charInfo.scale = 1;
  258. m_characterDictionary.Add(10, temp_charInfo);
  259. if (!m_characterDictionary.ContainsKey(13))
  260. m_characterDictionary.Add(13, temp_charInfo);
  261. }
  262. // Add Tab Character to Dictionary. Tab is Tab Size * Space Character Width.
  263. if (m_characterDictionary.ContainsKey(9) == false)
  264. {
  265. //Debug.Log("Adding Character 9 (Tab) to Dictionary for Font (" + m_fontInfo.Name + ").");
  266. temp_charInfo = new TMP_Glyph();
  267. temp_charInfo.id = 9;
  268. temp_charInfo.x = m_characterDictionary[32].x;
  269. temp_charInfo.y = m_characterDictionary[32].y;
  270. temp_charInfo.width = m_characterDictionary[32].width * tabSize + (m_characterDictionary[32].xAdvance - m_characterDictionary[32].width) * (tabSize - 1);
  271. temp_charInfo.height = m_characterDictionary[32].height;
  272. temp_charInfo.xOffset = m_characterDictionary[32].xOffset;
  273. temp_charInfo.yOffset = m_characterDictionary[32].yOffset;
  274. temp_charInfo.xAdvance = m_characterDictionary[32].xAdvance * tabSize;
  275. temp_charInfo.scale = 1;
  276. m_characterDictionary.Add(9, temp_charInfo);
  277. }
  278. // Centerline is located at the center of character like { or in the middle of the lowercase o.
  279. //m_fontInfo.CenterLine = m_characterDictionary[111].yOffset - m_characterDictionary[111].height * 0.5f;
  280. // Tab Width is using the same xAdvance as space (32).
  281. m_fontInfo.TabWidth = m_characterDictionary[9].xAdvance;
  282. // Set Cap Height
  283. if (m_fontInfo.CapHeight == 0 && m_characterDictionary.ContainsKey(72))
  284. m_fontInfo.CapHeight = m_characterDictionary[72].yOffset;
  285. // Adjust Font Scale for compatibility reasons
  286. if (m_fontInfo.Scale == 0)
  287. m_fontInfo.Scale = 1.0f;
  288. // Set Strikethrough Offset (if needed)
  289. if (m_fontInfo.strikethrough == 0)
  290. m_fontInfo.strikethrough = m_fontInfo.CapHeight / 2.5f;
  291. // Set Padding value for legacy font assets.
  292. if (m_fontInfo.Padding == 0)
  293. {
  294. if (material.HasProperty(ShaderUtilities.ID_GradientScale))
  295. m_fontInfo.Padding = material.GetFloat(ShaderUtilities.ID_GradientScale) - 1;
  296. }
  297. // Populate Dictionary with Kerning Information
  298. m_kerningDictionary = new Dictionary<int, KerningPair>();
  299. List<KerningPair> pairs = m_kerningInfo.kerningPairs;
  300. //Debug.Log(m_fontInfo.Name + " has " + pairs.Count + " Kerning Pairs.");
  301. for (int i = 0; i < pairs.Count; i++)
  302. {
  303. KerningPair pair = pairs[i];
  304. // Convert legacy kerning data
  305. if (pair.xOffset != 0)
  306. pairs[i].ConvertLegacyKerningData();
  307. KerningPairKey uniqueKey = new KerningPairKey(pair.firstGlyph, pair.secondGlyph);
  308. if (m_kerningDictionary.ContainsKey((int)uniqueKey.key) == false)
  309. {
  310. m_kerningDictionary.Add((int)uniqueKey.key, pair);
  311. }
  312. else
  313. {
  314. if (!TMP_Settings.warningsDisabled)
  315. Debug.LogWarning("Kerning Key for [" + uniqueKey.ascii_Left + "] and [" + uniqueKey.ascii_Right + "] already exists.");
  316. }
  317. }
  318. // Compute Hashcode for the font asset name
  319. hashCode = TMP_TextUtilities.GetSimpleHashCode(this.name);
  320. // Compute Hashcode for the material name
  321. materialHashCode = TMP_TextUtilities.GetSimpleHashCode(material.name);
  322. // Unload font atlas texture
  323. //ShaderUtilities.GetShaderPropertyIDs();
  324. //Resources.UnloadAsset(material.GetTexture(ShaderUtilities.ID_MainTex));
  325. // Initialize Font Weights if needed
  326. //InitializeFontWeights();
  327. }
  328. /// <summary>
  329. /// Function to sort the list of glyphs.
  330. /// </summary>
  331. public void SortGlyphs()
  332. {
  333. if (m_glyphInfoList == null || m_glyphInfoList.Count == 0) return;
  334. m_glyphInfoList = m_glyphInfoList.OrderBy(item => item.id).ToList();
  335. }
  336. /// <summary>
  337. /// Function to check if a certain character exists in the font asset.
  338. /// </summary>
  339. /// <param name="character"></param>
  340. /// <returns></returns>
  341. public bool HasCharacter(int character)
  342. {
  343. if (m_characterDictionary == null)
  344. return false;
  345. if (m_characterDictionary.ContainsKey(character))
  346. return true;
  347. return false;
  348. }
  349. /// <summary>
  350. /// Function to check if a certain character exists in the font asset.
  351. /// </summary>
  352. /// <param name="character"></param>
  353. /// <returns></returns>
  354. public bool HasCharacter(char character)
  355. {
  356. if (m_characterDictionary == null)
  357. return false;
  358. if (m_characterDictionary.ContainsKey(character))
  359. return true;
  360. return false;
  361. }
  362. /// <summary>
  363. /// Function to check if a character is contained in the font asset with the option to also check through fallback font assets.
  364. /// </summary>
  365. /// <param name="character"></param>
  366. /// <param name="searchFallbacks"></param>
  367. /// <returns></returns>
  368. public bool HasCharacter(char character, bool searchFallbacks)
  369. {
  370. // Read font asset definition if it hasn't already been done.
  371. if (m_characterDictionary == null)
  372. {
  373. ReadFontDefinition();
  374. if (m_characterDictionary == null)
  375. return false;
  376. }
  377. // Check font asset
  378. if (m_characterDictionary.ContainsKey(character))
  379. return true;
  380. if (searchFallbacks)
  381. {
  382. // Check font asset fallbacks
  383. if (fallbackFontAssets != null && fallbackFontAssets.Count > 0)
  384. {
  385. for (int i = 0; i < fallbackFontAssets.Count && fallbackFontAssets[i] != null; i++)
  386. {
  387. if (fallbackFontAssets[i].HasCharacter_Internal(character, searchFallbacks))
  388. return true;
  389. }
  390. }
  391. // Check general fallback font assets.
  392. if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0)
  393. {
  394. for (int i = 0; i < TMP_Settings.fallbackFontAssets.Count && TMP_Settings.fallbackFontAssets[i] != null; i++)
  395. {
  396. if (TMP_Settings.fallbackFontAssets[i].characterDictionary == null)
  397. TMP_Settings.fallbackFontAssets[i].ReadFontDefinition();
  398. if (TMP_Settings.fallbackFontAssets[i].characterDictionary != null && TMP_Settings.fallbackFontAssets[i].HasCharacter_Internal(character, searchFallbacks))
  399. return true;
  400. }
  401. }
  402. // Check TMP Settings Default Font Asset
  403. if (TMP_Settings.defaultFontAsset != null)
  404. {
  405. if (TMP_Settings.defaultFontAsset.characterDictionary == null)
  406. TMP_Settings.defaultFontAsset.ReadFontDefinition();
  407. if (TMP_Settings.defaultFontAsset.characterDictionary != null && TMP_Settings.defaultFontAsset.HasCharacter_Internal(character, searchFallbacks))
  408. return true;
  409. }
  410. }
  411. return false;
  412. }
  413. /// <summary>
  414. /// Function to check if a character is contained in a font asset with the option to also check through fallback font assets.
  415. /// This private implementation does not search the fallback font asset in the TMP Settings file.
  416. /// </summary>
  417. /// <param name="character"></param>
  418. /// <param name="searchFallbacks"></param>
  419. /// <returns></returns>
  420. bool HasCharacter_Internal(char character, bool searchFallbacks)
  421. {
  422. // Read font asset definition if it hasn't already been done.
  423. if (m_characterDictionary == null)
  424. {
  425. ReadFontDefinition();
  426. if (m_characterDictionary == null)
  427. return false;
  428. }
  429. // Check font asset
  430. if (m_characterDictionary.ContainsKey(character))
  431. return true;
  432. if (searchFallbacks)
  433. {
  434. // Check Font Asset Fallback fonts.
  435. if (fallbackFontAssets != null && fallbackFontAssets.Count > 0)
  436. {
  437. for (int i = 0; i < fallbackFontAssets.Count && fallbackFontAssets[i] != null; i++)
  438. {
  439. if (fallbackFontAssets[i].HasCharacter_Internal(character, searchFallbacks))
  440. return true;
  441. }
  442. }
  443. }
  444. return false;
  445. }
  446. /// <summary>
  447. /// Function to check if certain characters exists in the font asset. Function returns a list of missing characters.
  448. /// </summary>
  449. /// <param name="character"></param>
  450. /// <returns></returns>
  451. public bool HasCharacters(string text, out List<char> missingCharacters)
  452. {
  453. if (m_characterDictionary == null)
  454. {
  455. missingCharacters = null;
  456. return false;
  457. }
  458. missingCharacters = new List<char>();
  459. for (int i = 0; i < text.Length; i++)
  460. {
  461. if (!m_characterDictionary.ContainsKey(text[i]))
  462. missingCharacters.Add(text[i]);
  463. }
  464. if (missingCharacters.Count == 0)
  465. return true;
  466. return false;
  467. }
  468. /// <summary>
  469. /// Function to check if certain characters exists in the font asset. Function returns false if any characters are missing.
  470. /// </summary>
  471. /// <param name="text">String containing the characters to check</param>
  472. /// <returns></returns>
  473. public bool HasCharacters(string text)
  474. {
  475. if (m_characterDictionary == null)
  476. return false;
  477. for (int i = 0; i < text.Length; i++)
  478. {
  479. if (!m_characterDictionary.ContainsKey(text[i]))
  480. return false;
  481. }
  482. return true;
  483. }
  484. /// <summary>
  485. /// Function to extract all the characters from a font asset.
  486. /// </summary>
  487. /// <param name="fontAsset"></param>
  488. /// <returns></returns>
  489. public static string GetCharacters(TMP_FontAsset fontAsset)
  490. {
  491. string characters = string.Empty;
  492. for (int i = 0; i < fontAsset.m_glyphInfoList.Count; i++)
  493. {
  494. characters += (char)fontAsset.m_glyphInfoList[i].id;
  495. }
  496. return characters;
  497. }
  498. /// <summary>
  499. /// Function which returns an array that contains all the characters from a font asset.
  500. /// </summary>
  501. /// <param name="fontAsset"></param>
  502. /// <returns></returns>
  503. public static int[] GetCharactersArray(TMP_FontAsset fontAsset)
  504. {
  505. int[] characters = new int[fontAsset.m_glyphInfoList.Count];
  506. for (int i = 0; i < fontAsset.m_glyphInfoList.Count; i++)
  507. {
  508. characters[i] = fontAsset.m_glyphInfoList[i].id;
  509. }
  510. return characters;
  511. }
  512. }
  513. }