TMP_FontAssetUtilities.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.TextCore;
  6. using UnityEngine.TextCore.LowLevel;
  7. namespace TMPro
  8. {
  9. public class TMP_FontAssetUtilities
  10. {
  11. private static readonly TMP_FontAssetUtilities s_Instance = new TMP_FontAssetUtilities();
  12. /// <summary>
  13. /// Default constructor
  14. /// </summary>
  15. static TMP_FontAssetUtilities() { }
  16. /// <summary>
  17. /// Get a singleton instance of the Font Asset Utilities class.
  18. /// </summary>
  19. public static TMP_FontAssetUtilities instance
  20. {
  21. get { return s_Instance; }
  22. }
  23. /// <summary>
  24. /// List containing instance ID of font assets already searched.
  25. /// </summary>
  26. private static List<int> k_SearchedFontAssets;
  27. /// <summary>
  28. /// Returns the text element (character) for the given unicode value taking into consideration the requested font style and weight.
  29. /// Function searches the source font asset, its list of font assets assigned as alternative typefaces and potentially its fallbacks.
  30. /// The font asset out parameter contains a reference to the font asset containing the character.
  31. /// The typeface type indicates whether the returned font asset is the source font asset, an alternative typeface or fallback font asset.
  32. /// </summary>
  33. /// <param name="unicode">The unicode value of the requested character</param>
  34. /// <param name="sourceFontAsset">The font asset to be searched</param>
  35. /// <param name="includeFallbacks">Include the fallback font assets in the search</param>
  36. /// <param name="fontStyle">The font style</param>
  37. /// <param name="fontWeight">The font weight</param>
  38. /// <param name="type">Indicates if the OUT font asset is an alternative typeface or fallback font asset</param>
  39. /// <param name="fontAsset">The font asset that contains the requested character</param>
  40. /// <returns></returns>
  41. public static TMP_Character GetCharacterFromFontAsset(uint unicode, TMP_FontAsset sourceFontAsset, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface, out TMP_FontAsset fontAsset)
  42. {
  43. if (includeFallbacks)
  44. {
  45. if (k_SearchedFontAssets == null)
  46. k_SearchedFontAssets = new List<int>();
  47. else
  48. k_SearchedFontAssets.Clear();
  49. }
  50. return GetCharacterFromFontAsset_Internal(unicode, sourceFontAsset, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface, out fontAsset);
  51. }
  52. /// <summary>
  53. /// Internal function returning the text element character for the given unicode value taking into consideration the font style and weight.
  54. /// Function searches the source font asset, list of font assets assigned as alternative typefaces and list of fallback font assets.
  55. /// </summary>
  56. private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TMP_FontAsset sourceFontAsset, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface, out TMP_FontAsset fontAsset)
  57. {
  58. fontAsset = null;
  59. isAlternativeTypeface = false;
  60. TMP_Character characterData = null;
  61. #region FONT WEIGHT AND FONT STYLE HANDLING
  62. // Determine if a font weight or style is used. If so check if an alternative typeface is assigned for the given weight and / or style.
  63. bool isItalic = (fontStyle & FontStyles.Italic) == FontStyles.Italic;
  64. if (isItalic || fontWeight != FontWeight.Regular)
  65. {
  66. // Get reference to the font weight pairs of the given font asset.
  67. TMP_FontWeightPair[] fontWeights = sourceFontAsset.fontWeightTable;
  68. int fontWeightIndex = 4;
  69. switch (fontWeight)
  70. {
  71. case FontWeight.Thin:
  72. fontWeightIndex = 1;
  73. break;
  74. case FontWeight.ExtraLight:
  75. fontWeightIndex = 2;
  76. break;
  77. case FontWeight.Light:
  78. fontWeightIndex = 3;
  79. break;
  80. case FontWeight.Regular:
  81. fontWeightIndex = 4;
  82. break;
  83. case FontWeight.Medium:
  84. fontWeightIndex = 5;
  85. break;
  86. case FontWeight.SemiBold:
  87. fontWeightIndex = 6;
  88. break;
  89. case FontWeight.Bold:
  90. fontWeightIndex = 7;
  91. break;
  92. case FontWeight.Heavy:
  93. fontWeightIndex = 8;
  94. break;
  95. case FontWeight.Black:
  96. fontWeightIndex = 9;
  97. break;
  98. }
  99. fontAsset = isItalic ? fontWeights[fontWeightIndex].italicTypeface : fontWeights[fontWeightIndex].regularTypeface;
  100. if (fontAsset != null)
  101. {
  102. if (fontAsset.characterLookupTable.TryGetValue(unicode, out characterData))
  103. {
  104. isAlternativeTypeface = true;
  105. return characterData;
  106. }
  107. else if (fontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic)
  108. {
  109. if (fontAsset.TryAddCharacterInternal(unicode, out characterData))
  110. {
  111. isAlternativeTypeface = true;
  112. return characterData;
  113. }
  114. // Check if the source font file contains the requested character.
  115. //if (TryGetCharacterFromFontFile(unicode, fontAsset, out characterData))
  116. //{
  117. // isAlternativeTypeface = true;
  118. // return characterData;
  119. //}
  120. // If we find the requested character, we add it to the font asset character table
  121. // and return its character data.
  122. // We also add this character to the list of characters we will need to add to the font atlas.
  123. // We assume the font atlas has room otherwise this font asset should not be marked as dynamic.
  124. // Alternatively, we could also add multiple pages of font atlas textures (feature consideration).
  125. }
  126. // At this point, we were not able to find the requested character in the alternative typeface
  127. // so we check the source font asset and its potential fallbacks.
  128. }
  129. }
  130. #endregion
  131. // Search the source font asset for the requested character.
  132. if (sourceFontAsset.characterLookupTable.TryGetValue(unicode, out characterData))
  133. {
  134. // We were able to locate the requested character in the given font asset.
  135. fontAsset = sourceFontAsset;
  136. return characterData;
  137. }
  138. else if (sourceFontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic)
  139. {
  140. if (sourceFontAsset.TryAddCharacterInternal(unicode, out characterData))
  141. {
  142. fontAsset = sourceFontAsset;
  143. return characterData;
  144. }
  145. //// Check if the source font file contains the requested character.
  146. //if (TryGetCharacterFromFontFile(unicode, sourceFontAsset, out characterData))
  147. //{
  148. // fontAsset = sourceFontAsset;
  149. // //fontAsset.AddCharacterToRasterList(unicode);
  150. // return characterData;
  151. //}
  152. // If we find the requested character, we add it to the font asset character table
  153. // and return its character data.
  154. // We also add this character to the list of characters we will need to add to the font atlas.
  155. // We assume the font atlas has room otherwise this font asset should not be marked as dynamic.
  156. // Alternatively, we could also add multiple pages of font atlas textures (feature consideration)
  157. }
  158. // Search fallback font assets if we still don't have a valid character and include fallback is set to true.
  159. if (characterData == null && includeFallbacks && sourceFontAsset.fallbackFontAssetTable != null)
  160. {
  161. // Get reference to the list of fallback font assets.
  162. List<TMP_FontAsset> fallbackFontAssets = sourceFontAsset.fallbackFontAssetTable;
  163. int fallbackCount = fallbackFontAssets.Count;
  164. if (fallbackFontAssets != null && fallbackCount > 0)
  165. {
  166. for (int i = 0; i < fallbackCount && characterData == null; i++)
  167. {
  168. TMP_FontAsset temp = fallbackFontAssets[i];
  169. if (temp == null) continue;
  170. int id = temp.GetInstanceID();
  171. // Skip over the fallback font asset in the event it is null or if already searched.
  172. if (k_SearchedFontAssets.Contains(id))
  173. continue;
  174. // Add to list of font assets already searched.
  175. k_SearchedFontAssets.Add(id);
  176. characterData = GetCharacterFromFontAsset_Internal(unicode, temp, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface, out fontAsset);
  177. if (characterData != null)
  178. {
  179. return characterData;
  180. }
  181. }
  182. }
  183. }
  184. return null;
  185. }
  186. /// <summary>
  187. /// Returns the text element (character) for the given unicode value taking into consideration the requested font style and weight.
  188. /// Function searches the provided list of font assets, the list of font assets assigned as alternative typefaces to them as well as their fallbacks.
  189. /// The font asset out parameter contains a reference to the font asset containing the character.
  190. /// The typeface type indicates whether the returned font asset is the source font asset, an alternative typeface or fallback font asset.
  191. /// </summary>
  192. /// <param name="unicode">The unicode value of the requested character</param>
  193. /// <param name="fontAssets">The list of font assets to search</param>
  194. /// <param name="includeFallbacks">Determines if the fallback of each font assets on the list will be searched</param>
  195. /// <param name="fontStyle">The font style</param>
  196. /// <param name="fontWeight">The font weight</param>
  197. /// <param name="type">Determines if the OUT font asset is an alternative typeface or fallback font asset</param>
  198. /// <param name="fontAsset">The font asset that contains the requested character</param>
  199. /// <returns></returns>
  200. public static TMP_Character GetCharacterFromFontAssets(uint unicode, List<TMP_FontAsset> fontAssets, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface, out TMP_FontAsset fontAsset)
  201. {
  202. isAlternativeTypeface = false;
  203. // Make sure font asset list is valid
  204. if (fontAssets == null || fontAssets.Count == 0)
  205. {
  206. fontAsset = null;
  207. return null;
  208. }
  209. if (includeFallbacks)
  210. {
  211. if (k_SearchedFontAssets == null)
  212. k_SearchedFontAssets = new List<int>();
  213. else
  214. k_SearchedFontAssets.Clear();
  215. }
  216. int fontAssetCount = fontAssets.Count;
  217. for (int i = 0; i < fontAssetCount; i++)
  218. {
  219. if (fontAssets[i] == null) continue;
  220. TMP_Character characterData = GetCharacterFromFontAsset_Internal(unicode, fontAssets[i], includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface, out fontAsset);
  221. if (characterData != null)
  222. return characterData;
  223. }
  224. fontAsset = null;
  225. return null;
  226. }
  227. // =====================================================================
  228. // FONT ENGINE & FONT FILE MANAGEMENT - Fields, Properties and Functions
  229. // =====================================================================
  230. private static bool k_IsFontEngineInitialized;
  231. private static bool TryGetCharacterFromFontFile(uint unicode, TMP_FontAsset fontAsset, out TMP_Character character)
  232. {
  233. character = null;
  234. // Initialize Font Engine library if not already initialized
  235. if (k_IsFontEngineInitialized == false)
  236. {
  237. FontEngineError error = FontEngine.InitializeFontEngine();
  238. if (error == 0)
  239. k_IsFontEngineInitialized = true;
  240. }
  241. // Load the font face for the given font asset.
  242. // TODO: Add manager to keep track of which font faces are currently loaded.
  243. FontEngine.LoadFontFace(fontAsset.sourceFontFile, fontAsset.faceInfo.pointSize);
  244. Glyph glyph = null;
  245. uint glyphIndex = FontEngine.GetGlyphIndex(unicode);
  246. // Check if glyph is already contained in the font asset as the same glyph might be referenced by multiple character.
  247. if (fontAsset.glyphLookupTable.TryGetValue(glyphIndex, out glyph))
  248. {
  249. character = fontAsset.AddCharacter_Internal(unicode, glyph);
  250. return true;
  251. }
  252. GlyphLoadFlags glyphLoadFlags = ((GlyphRasterModes)fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_HINTED) == GlyphRasterModes.RASTER_MODE_HINTED ? GlyphLoadFlags.LOAD_RENDER : GlyphLoadFlags.LOAD_RENDER | GlyphLoadFlags.LOAD_NO_HINTING;
  253. if (FontEngine.TryGetGlyphWithUnicodeValue(unicode, glyphLoadFlags, out glyph))
  254. {
  255. // Add new character to font asset (if needed)
  256. character = fontAsset.AddCharacter_Internal(unicode, glyph);
  257. return true;
  258. }
  259. return false;
  260. }
  261. public static bool TryGetGlyphFromFontFile(uint glyphIndex, TMP_FontAsset fontAsset, out Glyph glyph)
  262. {
  263. glyph = null;
  264. // Initialize Font Engine library if not already initialized
  265. if (k_IsFontEngineInitialized == false)
  266. {
  267. FontEngineError error = FontEngine.InitializeFontEngine();
  268. if (error == 0)
  269. k_IsFontEngineInitialized = true;
  270. }
  271. // Load the font face for the given font asset.
  272. // TODO: Add manager to keep track of which font faces are currently loaded.
  273. FontEngine.LoadFontFace(fontAsset.sourceFontFile, fontAsset.faceInfo.pointSize);
  274. GlyphLoadFlags glyphLoadFlags = ((GlyphRasterModes)fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_HINTED) == GlyphRasterModes.RASTER_MODE_HINTED ? GlyphLoadFlags.LOAD_RENDER : GlyphLoadFlags.LOAD_RENDER | GlyphLoadFlags.LOAD_NO_HINTING;
  275. if (FontEngine.TryGetGlyphWithIndexValue(glyphIndex, glyphLoadFlags, out glyph))
  276. {
  277. // Add new glyph to font asset (if needed)
  278. //fontAsset.AddGlyph_Internal(glyph);
  279. return true;
  280. }
  281. return false;
  282. }
  283. }
  284. }