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.

1466 lines
63 KiB

  1. using System;
  2. using UnityEngine;
  3. using UnityEditor;
  4. using System.Collections.Generic;
  5. using System.Globalization;
  6. using System.Threading;
  7. using System.IO;
  8. using Object = UnityEngine.Object;
  9. namespace TMPro.EditorUtilities
  10. {
  11. public class TMPro_FontAssetCreatorWindow : EditorWindow
  12. {
  13. [MenuItem("Window/TextMeshPro/Font Asset Creator", false, 2025)]
  14. public static void ShowFontAtlasCreatorWindow()
  15. {
  16. var window = GetWindow<TMPro_FontAssetCreatorWindow>();
  17. window.titleContent = new GUIContent("Font Asset Creator");
  18. window.Focus();
  19. // Make sure TMP Essential Resources have been imported.
  20. window.CheckEssentialResources();
  21. }
  22. public static void ShowFontAtlasCreatorWindow(Font sourceFontFile)
  23. {
  24. var window = GetWindow<TMPro_FontAssetCreatorWindow>();
  25. window.titleContent = new GUIContent("Font Asset Creator");
  26. window.Focus();
  27. // Override selected font asset
  28. window.ClearGeneratedData();
  29. window.m_SelectedFontAsset = null;
  30. window.m_LegacyFontAsset = null;
  31. window.m_SourceFontFile = sourceFontFile;
  32. // Make sure TMP Essential Resources have been imported.
  33. window.CheckEssentialResources();
  34. }
  35. public static void ShowFontAtlasCreatorWindow(TMP_FontAsset fontAsset)
  36. {
  37. var window = GetWindow<TMPro_FontAssetCreatorWindow>();
  38. window.titleContent = new GUIContent("Font Creator");
  39. window.Focus();
  40. // Clear any previously generated data
  41. window.ClearGeneratedData();
  42. window.m_LegacyFontAsset = null;
  43. // Load font asset creation settings if we have valid settings
  44. if (!string.IsNullOrEmpty(fontAsset.creationSettings.sourceFontFileGUID))
  45. {
  46. window.LoadFontCreationSettings(fontAsset.creationSettings);
  47. window.m_SavedFontAtlas = fontAsset.atlas;
  48. }
  49. else
  50. {
  51. window.m_WarningMessage = "Font Asset [" + fontAsset.name + "] does not contain any previous \"Font Asset Creation Settings\". This usually means [" + fontAsset.name + "] was created before this new functionality was added.";
  52. window.m_SourceFontFile = null;
  53. window.m_LegacyFontAsset = fontAsset;
  54. }
  55. // Even if we don't have any saved generation settings, we still want to pre-select the source font file.
  56. window.m_SelectedFontAsset = fontAsset;
  57. // Make sure TMP Essential Resources have been imported.
  58. window.CheckEssentialResources();
  59. }
  60. [System.Serializable]
  61. class FontAssetCreationSettingsContainer
  62. {
  63. public List<FontAssetCreationSettings> fontAssetCreationSettings;
  64. }
  65. FontAssetCreationSettingsContainer m_FontAssetCreationSettingsContainer;
  66. //static readonly string[] m_FontCreationPresets = new string[] { "Recent 1", "Recent 2", "Recent 3", "Recent 4" };
  67. int m_FontAssetCreationSettingsCurrentIndex = 0;
  68. //private bool m_IsFontAssetOpenForEdit = false;
  69. const string k_FontAssetCreationSettingsContainerKey = "TextMeshPro.FontAssetCreator.RecentFontAssetCreationSettings.Container";
  70. const string k_FontAssetCreationSettingsCurrentIndexKey = "TextMeshPro.FontAssetCreator.RecentFontAssetCreationSettings.CurrentIndex";
  71. const float k_TwoColumnControlsWidth = 335f;
  72. // Diagnostics
  73. System.Diagnostics.Stopwatch m_StopWatch;
  74. string[] m_FontSizingOptions = { "Auto Sizing", "Custom Size" };
  75. int m_PointSizeSamplingMode;
  76. string[] m_FontResolutionLabels = { "16","32", "64", "128", "256", "512", "1024", "2048", "4096", "8192" };
  77. int[] m_FontAtlasResolutions = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
  78. string[] m_FontCharacterSets = { "ASCII", "Extended ASCII", "ASCII Lowercase", "ASCII Uppercase", "Numbers + Symbols", "Custom Range", "Unicode Range (Hex)", "Custom Characters", "Characters from File" };
  79. enum FontPackingModes { Fast = 0, Optimum = 4 };
  80. FontPackingModes m_PackingMode = FontPackingModes.Fast;
  81. int m_CharacterSetSelectionMode;
  82. string m_CharacterSequence = "";
  83. string m_OutputFeedback = "";
  84. string m_WarningMessage;
  85. const string k_OutputNameLabel = "Font: ";
  86. const string k_OutputSizeLabel = "Pt. Size: ";
  87. const string k_OutputCountLabel = "Characters packed: ";
  88. int m_CharacterCount;
  89. Vector2 m_ScrollPosition;
  90. Vector2 m_OutputScrollPosition;
  91. bool m_IsRepaintNeeded;
  92. float m_RenderingProgress;
  93. bool m_IsRenderingDone;
  94. bool m_IsProcessing;
  95. bool m_IsGenerationDisabled;
  96. bool m_IsGenerationCancelled;
  97. Object m_SourceFontFile;
  98. TMP_FontAsset m_SelectedFontAsset;
  99. TMP_FontAsset m_LegacyFontAsset;
  100. TMP_FontAsset m_ReferencedFontAsset;
  101. TextAsset m_CharacterList;
  102. int m_PointSize;
  103. int m_Padding = 5;
  104. FaceStyles m_FontStyle = FaceStyles.Normal;
  105. float m_FontStyleValue = 2;
  106. RenderModes m_RenderMode = RenderModes.DistanceField16;
  107. int m_AtlasWidth = 512;
  108. int m_AtlasHeight = 512;
  109. FT_FaceInfo m_FontFaceInfo;
  110. FT_GlyphInfo[] m_FontGlyphInfo;
  111. byte[] m_TextureBuffer;
  112. Texture2D m_FontAtlas;
  113. Texture2D m_SavedFontAtlas;
  114. bool m_IncludeKerningPairs;
  115. int[] m_KerningSet;
  116. bool m_Locked;
  117. bool m_IsFontAtlasInvalid;
  118. public void OnEnable()
  119. {
  120. minSize = new Vector2(315, minSize.y);
  121. // Used for Diagnostics
  122. m_StopWatch = new System.Diagnostics.Stopwatch();
  123. // Initialize & Get shader property IDs.
  124. ShaderUtilities.GetShaderPropertyIDs();
  125. // Load last selected preset if we are not already in the process of regenerating an existing font asset (via the Context menu)
  126. if (EditorPrefs.HasKey(k_FontAssetCreationSettingsContainerKey))
  127. {
  128. if (m_FontAssetCreationSettingsContainer == null)
  129. m_FontAssetCreationSettingsContainer = JsonUtility.FromJson<FontAssetCreationSettingsContainer>(EditorPrefs.GetString(k_FontAssetCreationSettingsContainerKey));
  130. if (m_FontAssetCreationSettingsContainer.fontAssetCreationSettings != null && m_FontAssetCreationSettingsContainer.fontAssetCreationSettings.Count > 0)
  131. {
  132. // Load Font Asset Creation Settings preset.
  133. if (EditorPrefs.HasKey(k_FontAssetCreationSettingsCurrentIndexKey))
  134. m_FontAssetCreationSettingsCurrentIndex = EditorPrefs.GetInt(k_FontAssetCreationSettingsCurrentIndexKey);
  135. LoadFontCreationSettings(m_FontAssetCreationSettingsContainer.fontAssetCreationSettings[m_FontAssetCreationSettingsCurrentIndex]);
  136. }
  137. }
  138. // Debug Link to received message from Native Code
  139. //TMPro_FontPlugin.LinkDebugLog(); // Link with C++ Plugin to get Debug output
  140. }
  141. public void OnDisable()
  142. {
  143. //Debug.Log("TextMeshPro Editor Window has been disabled.");
  144. // Cancel font asset generation just in case one is in progress.
  145. TMPro_FontPlugin.SendCancellationRequest(CancellationRequestType.WindowClosed);
  146. // Destroy Engine only if it has been initialized already
  147. TMPro_FontPlugin.Destroy_FontEngine();
  148. if (m_FontAtlas != null && EditorUtility.IsPersistent(m_FontAtlas) == false)
  149. {
  150. //Debug.Log("Destroying font_Atlas!");
  151. DestroyImmediate(m_FontAtlas);
  152. }
  153. if (File.Exists("Assets/TextMesh Pro/Glyph Report.txt"))
  154. {
  155. File.Delete("Assets/TextMesh Pro/Glyph Report.txt");
  156. File.Delete("Assets/TextMesh Pro/Glyph Report.txt.meta");
  157. AssetDatabase.Refresh();
  158. }
  159. // Save Font Asset Creation Settings Index
  160. SaveCreationSettingsToEditorPrefs(SaveFontCreationSettings());
  161. EditorPrefs.SetInt(k_FontAssetCreationSettingsCurrentIndexKey, m_FontAssetCreationSettingsCurrentIndex);
  162. // Unregister to event
  163. TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
  164. Resources.UnloadUnusedAssets();
  165. }
  166. // Event received when TMP resources have been loaded.
  167. void ON_RESOURCES_LOADED()
  168. {
  169. TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
  170. m_IsGenerationDisabled = false;
  171. }
  172. // Make sure TMP Essential Resources have been imported.
  173. void CheckEssentialResources()
  174. {
  175. if (TMP_Settings.instance == null)
  176. {
  177. if (m_IsGenerationDisabled == false)
  178. TMPro_EventManager.RESOURCE_LOAD_EVENT.Add(ON_RESOURCES_LOADED);
  179. m_IsGenerationDisabled = true;
  180. }
  181. }
  182. public void OnGUI()
  183. {
  184. GUILayout.BeginHorizontal();
  185. DrawControls();
  186. if (position.width > position.height && position.width > k_TwoColumnControlsWidth)
  187. {
  188. DrawPreview();
  189. }
  190. GUILayout.EndHorizontal();
  191. }
  192. public void Update()
  193. {
  194. if (m_IsRepaintNeeded)
  195. {
  196. //Debug.Log("Repainting...");
  197. m_IsRepaintNeeded = false;
  198. Repaint();
  199. }
  200. // Update Progress bar is we are Rendering a Font.
  201. if (m_IsProcessing)
  202. {
  203. m_RenderingProgress = TMPro_FontPlugin.Check_RenderProgress();
  204. m_IsRepaintNeeded = true;
  205. }
  206. // Update Feedback Window & Create Font Texture once Rendering is done.
  207. if (m_IsRenderingDone)
  208. {
  209. // Stop StopWatch
  210. m_StopWatch.Stop();
  211. Debug.Log("Font Atlas generation completed in: " + m_StopWatch.Elapsed.TotalMilliseconds.ToString("0.000 ms."));
  212. m_StopWatch.Reset();
  213. m_IsProcessing = false;
  214. m_IsRenderingDone = false;
  215. if (m_IsGenerationCancelled == false)
  216. {
  217. UpdateRenderFeedbackWindow();
  218. CreateFontTexture();
  219. }
  220. Repaint();
  221. }
  222. }
  223. /// <summary>
  224. /// Method which returns the character corresponding to a decimal value.
  225. /// </summary>
  226. /// <param name="sequence"></param>
  227. /// <returns></returns>
  228. static int[] ParseNumberSequence(string sequence)
  229. {
  230. List<int> unicodeList = new List<int>();
  231. string[] sequences = sequence.Split(',');
  232. foreach (string seq in sequences)
  233. {
  234. string[] s1 = seq.Split('-');
  235. if (s1.Length == 1)
  236. try
  237. {
  238. unicodeList.Add(int.Parse(s1[0]));
  239. }
  240. catch
  241. {
  242. Debug.Log("No characters selected or invalid format.");
  243. }
  244. else
  245. {
  246. for (int j = int.Parse(s1[0]); j < int.Parse(s1[1]) + 1; j++)
  247. {
  248. unicodeList.Add(j);
  249. }
  250. }
  251. }
  252. return unicodeList.ToArray();
  253. }
  254. /// <summary>
  255. /// Method which returns the character (decimal value) from a hex sequence.
  256. /// </summary>
  257. /// <param name="sequence"></param>
  258. /// <returns></returns>
  259. static int[] ParseHexNumberSequence(string sequence)
  260. {
  261. List<int> unicodeList = new List<int>();
  262. string[] sequences = sequence.Split(',');
  263. foreach (string seq in sequences)
  264. {
  265. string[] s1 = seq.Split('-');
  266. if (s1.Length == 1)
  267. try
  268. {
  269. unicodeList.Add(int.Parse(s1[0], NumberStyles.AllowHexSpecifier));
  270. }
  271. catch
  272. {
  273. Debug.Log("No characters selected or invalid format.");
  274. }
  275. else
  276. {
  277. for (int j = int.Parse(s1[0], NumberStyles.AllowHexSpecifier); j < int.Parse(s1[1], NumberStyles.AllowHexSpecifier) + 1; j++)
  278. {
  279. unicodeList.Add(j);
  280. }
  281. }
  282. }
  283. return unicodeList.ToArray();
  284. }
  285. void DrawControls()
  286. {
  287. GUILayout.Space(5f);
  288. if (position.width > position.height && position.width > k_TwoColumnControlsWidth)
  289. {
  290. m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition, GUILayout.Width(315));
  291. }
  292. else
  293. {
  294. m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition);
  295. }
  296. GUILayout.Space(5f);
  297. GUILayout.Label(m_SelectedFontAsset != null ? string.Format("Creation Settings ({0})", m_SelectedFontAsset.name) : "Font Settings", EditorStyles.boldLabel);
  298. EditorGUIUtility.labelWidth = 125f;
  299. EditorGUIUtility.fieldWidth = 5f;
  300. EditorGUI.BeginDisabledGroup(m_IsProcessing);
  301. {
  302. // FONT TTF SELECTION
  303. EditorGUI.BeginChangeCheck();
  304. m_SourceFontFile = EditorGUILayout.ObjectField("Source Font File", m_SourceFontFile, typeof(Font), false) as Font;
  305. if (EditorGUI.EndChangeCheck())
  306. {
  307. m_SelectedFontAsset = null;
  308. m_IsFontAtlasInvalid = true;
  309. }
  310. // FONT SIZING
  311. EditorGUI.BeginChangeCheck();
  312. if (m_PointSizeSamplingMode == 0)
  313. {
  314. m_PointSizeSamplingMode = EditorGUILayout.Popup("Sampling Point Size", m_PointSizeSamplingMode, m_FontSizingOptions);
  315. }
  316. else
  317. {
  318. GUILayout.BeginHorizontal();
  319. m_PointSizeSamplingMode = EditorGUILayout.Popup("Sampling Point Size", m_PointSizeSamplingMode, m_FontSizingOptions, GUILayout.Width(225));
  320. m_PointSize = EditorGUILayout.IntField(m_PointSize);
  321. GUILayout.EndHorizontal();
  322. }
  323. if (EditorGUI.EndChangeCheck())
  324. {
  325. m_IsFontAtlasInvalid = true;
  326. }
  327. // FONT PADDING
  328. EditorGUI.BeginChangeCheck();
  329. m_Padding = EditorGUILayout.IntField("Padding", m_Padding);
  330. m_Padding = (int)Mathf.Clamp(m_Padding, 0f, 64f);
  331. if (EditorGUI.EndChangeCheck())
  332. {
  333. m_IsFontAtlasInvalid = true;
  334. }
  335. // FONT PACKING METHOD SELECTION
  336. EditorGUI.BeginChangeCheck();
  337. m_PackingMode = (FontPackingModes)EditorGUILayout.EnumPopup("Packing Method", m_PackingMode);
  338. if (EditorGUI.EndChangeCheck())
  339. {
  340. m_IsFontAtlasInvalid = true;
  341. }
  342. // FONT ATLAS RESOLUTION SELECTION
  343. GUILayout.BeginHorizontal();
  344. GUI.changed = false;
  345. EditorGUI.BeginChangeCheck();
  346. EditorGUILayout.PrefixLabel("Atlas Resolution");
  347. m_AtlasWidth = EditorGUILayout.IntPopup(m_AtlasWidth, m_FontResolutionLabels, m_FontAtlasResolutions); //, GUILayout.Width(80));
  348. m_AtlasHeight = EditorGUILayout.IntPopup(m_AtlasHeight, m_FontResolutionLabels, m_FontAtlasResolutions); //, GUILayout.Width(80));
  349. if (EditorGUI.EndChangeCheck())
  350. {
  351. m_IsFontAtlasInvalid = true;
  352. }
  353. GUILayout.EndHorizontal();
  354. // FONT CHARACTER SET SELECTION
  355. EditorGUI.BeginChangeCheck();
  356. bool hasSelectionChanged = false;
  357. m_CharacterSetSelectionMode = EditorGUILayout.Popup("Character Set", m_CharacterSetSelectionMode, m_FontCharacterSets);
  358. if (EditorGUI.EndChangeCheck())
  359. {
  360. m_CharacterSequence = "";
  361. hasSelectionChanged = true;
  362. m_IsFontAtlasInvalid = true;
  363. //Debug.Log("Resetting Sequence!");
  364. }
  365. switch (m_CharacterSetSelectionMode)
  366. {
  367. case 0: // ASCII
  368. //characterSequence = "32 - 126, 130, 132 - 135, 139, 145 - 151, 153, 155, 161, 166 - 167, 169 - 174, 176, 181 - 183, 186 - 187, 191, 8210 - 8226, 8230, 8240, 8242 - 8244, 8249 - 8250, 8252 - 8254, 8260, 8286";
  369. m_CharacterSequence = "32 - 126, 160, 8203, 8230, 9633";
  370. break;
  371. case 1: // EXTENDED ASCII
  372. m_CharacterSequence = "32 - 126, 160 - 255, 8192 - 8303, 8364, 8482, 9633";
  373. // Could add 9632 for missing glyph
  374. break;
  375. case 2: // Lowercase
  376. m_CharacterSequence = "32 - 64, 91 - 126, 160";
  377. break;
  378. case 3: // Uppercase
  379. m_CharacterSequence = "32 - 96, 123 - 126, 160";
  380. break;
  381. case 4: // Numbers & Symbols
  382. m_CharacterSequence = "32 - 64, 91 - 96, 123 - 126, 160";
  383. break;
  384. case 5: // Custom Range
  385. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  386. GUILayout.Label("Enter a sequence of decimal values to define the characters to be included in the font asset or retrieve one from another font asset.", TMP_UIStyleManager.label);
  387. GUILayout.Space(10f);
  388. EditorGUI.BeginChangeCheck();
  389. m_ReferencedFontAsset = EditorGUILayout.ObjectField("Select Font Asset", m_ReferencedFontAsset, typeof(TMP_FontAsset), false) as TMP_FontAsset;
  390. if (EditorGUI.EndChangeCheck() || hasSelectionChanged)
  391. {
  392. if (m_ReferencedFontAsset != null)
  393. m_CharacterSequence = TMP_EditorUtility.GetDecimalCharacterSequence(TMP_FontAsset.GetCharactersArray(m_ReferencedFontAsset));
  394. m_IsFontAtlasInvalid = true;
  395. }
  396. // Filter out unwanted characters.
  397. char chr = Event.current.character;
  398. if ((chr < '0' || chr > '9') && (chr < ',' || chr > '-'))
  399. {
  400. Event.current.character = '\0';
  401. }
  402. GUILayout.Label("Character Sequence (Decimal)", EditorStyles.boldLabel);
  403. EditorGUI.BeginChangeCheck();
  404. m_CharacterSequence = EditorGUILayout.TextArea(m_CharacterSequence, TMP_UIStyleManager.textAreaBoxWindow, GUILayout.Height(120), GUILayout.ExpandWidth(true));
  405. if (EditorGUI.EndChangeCheck())
  406. {
  407. m_IsFontAtlasInvalid = true;
  408. }
  409. EditorGUILayout.EndVertical();
  410. break;
  411. case 6: // Unicode HEX Range
  412. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  413. GUILayout.Label("Enter a sequence of Unicode (hex) values to define the characters to be included in the font asset or retrieve one from another font asset.", TMP_UIStyleManager.label);
  414. GUILayout.Space(10f);
  415. EditorGUI.BeginChangeCheck();
  416. m_ReferencedFontAsset = EditorGUILayout.ObjectField("Select Font Asset", m_ReferencedFontAsset, typeof(TMP_FontAsset), false) as TMP_FontAsset;
  417. if (EditorGUI.EndChangeCheck() || hasSelectionChanged)
  418. {
  419. if (m_ReferencedFontAsset != null)
  420. m_CharacterSequence = TMP_EditorUtility.GetUnicodeCharacterSequence(TMP_FontAsset.GetCharactersArray(m_ReferencedFontAsset));
  421. m_IsFontAtlasInvalid = true;
  422. }
  423. // Filter out unwanted characters.
  424. chr = Event.current.character;
  425. if ((chr < '0' || chr > '9') && (chr < 'a' || chr > 'f') && (chr < 'A' || chr > 'F') && (chr < ',' || chr > '-'))
  426. {
  427. Event.current.character = '\0';
  428. }
  429. GUILayout.Label("Character Sequence (Hex)", EditorStyles.boldLabel);
  430. EditorGUI.BeginChangeCheck();
  431. m_CharacterSequence = EditorGUILayout.TextArea(m_CharacterSequence, TMP_UIStyleManager.textAreaBoxWindow, GUILayout.Height(120), GUILayout.ExpandWidth(true));
  432. if (EditorGUI.EndChangeCheck())
  433. {
  434. m_IsFontAtlasInvalid = true;
  435. }
  436. EditorGUILayout.EndVertical();
  437. break;
  438. case 7: // Characters from Font Asset
  439. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  440. GUILayout.Label("Type the characters to be included in the font asset or retrieve them from another font asset.", TMP_UIStyleManager.label);
  441. GUILayout.Space(10f);
  442. EditorGUI.BeginChangeCheck();
  443. m_ReferencedFontAsset = EditorGUILayout.ObjectField("Select Font Asset", m_ReferencedFontAsset, typeof(TMP_FontAsset), false) as TMP_FontAsset;
  444. if (EditorGUI.EndChangeCheck() || hasSelectionChanged)
  445. {
  446. if (m_ReferencedFontAsset != null)
  447. m_CharacterSequence = TMP_FontAsset.GetCharacters(m_ReferencedFontAsset);
  448. m_IsFontAtlasInvalid = true;
  449. }
  450. EditorGUI.indentLevel = 0;
  451. GUILayout.Label("Custom Character List", EditorStyles.boldLabel);
  452. EditorGUI.BeginChangeCheck();
  453. m_CharacterSequence = EditorGUILayout.TextArea(m_CharacterSequence, TMP_UIStyleManager.textAreaBoxWindow, GUILayout.Height(120), GUILayout.ExpandWidth(true));
  454. if (EditorGUI.EndChangeCheck())
  455. {
  456. m_IsFontAtlasInvalid = true;
  457. }
  458. EditorGUILayout.EndVertical();
  459. break;
  460. case 8: // Character List from File
  461. EditorGUI.BeginChangeCheck();
  462. m_CharacterList = EditorGUILayout.ObjectField("Character File", m_CharacterList, typeof(TextAsset), false) as TextAsset;
  463. if (EditorGUI.EndChangeCheck())
  464. {
  465. m_IsFontAtlasInvalid = true;
  466. }
  467. if (m_CharacterList != null)
  468. {
  469. m_CharacterSequence = m_CharacterList.text;
  470. }
  471. break;
  472. }
  473. // FONT STYLE SELECTION
  474. GUILayout.BeginHorizontal();
  475. EditorGUI.BeginChangeCheck();
  476. m_FontStyle = (FaceStyles)EditorGUILayout.EnumPopup("Font Style", m_FontStyle, GUILayout.Width(225));
  477. m_FontStyleValue = EditorGUILayout.IntField((int)m_FontStyleValue);
  478. if (EditorGUI.EndChangeCheck())
  479. {
  480. m_IsFontAtlasInvalid = true;
  481. }
  482. GUILayout.EndHorizontal();
  483. // Render Mode Selection
  484. EditorGUI.BeginChangeCheck();
  485. m_RenderMode = (RenderModes)EditorGUILayout.EnumPopup("Render Mode", m_RenderMode);
  486. if (EditorGUI.EndChangeCheck())
  487. {
  488. //m_availableShaderNames = UpdateShaderList(font_renderMode, out m_availableShaders);
  489. m_IsFontAtlasInvalid = true;
  490. }
  491. m_IncludeKerningPairs = EditorGUILayout.Toggle("Get Kerning Pairs", m_IncludeKerningPairs);
  492. EditorGUILayout.Space();
  493. }
  494. EditorGUI.EndDisabledGroup();
  495. if (!string.IsNullOrEmpty(m_WarningMessage))
  496. {
  497. EditorGUILayout.HelpBox(m_WarningMessage, MessageType.Warning);
  498. }
  499. GUI.enabled = m_SourceFontFile != null && !m_IsProcessing && !m_IsGenerationDisabled; // Enable Preview if we are not already rendering a font.
  500. if (GUILayout.Button("Generate Font Atlas") && m_CharacterSequence.Length != 0 && GUI.enabled)
  501. {
  502. if (!m_IsProcessing && m_SourceFontFile != null)
  503. {
  504. DestroyImmediate(m_FontAtlas);
  505. m_FontAtlas = null;
  506. m_OutputFeedback = string.Empty;
  507. m_SavedFontAtlas = null;
  508. int errorCode;
  509. errorCode = TMPro_FontPlugin.Initialize_FontEngine(); // Initialize Font Engine
  510. if (errorCode != 0)
  511. {
  512. if (errorCode == 0xF0)
  513. {
  514. //Debug.Log("Font Library was already initialized!");
  515. errorCode = 0;
  516. }
  517. else
  518. Debug.Log("Error Code: " + errorCode + " occurred while Initializing the FreeType Library.");
  519. }
  520. string fontPath = AssetDatabase.GetAssetPath(m_SourceFontFile); // Get file path of TTF Font.
  521. if (errorCode == 0)
  522. {
  523. errorCode = TMPro_FontPlugin.Load_TrueType_Font(fontPath); // Load the selected font.
  524. if (errorCode != 0)
  525. {
  526. if (errorCode == 0xF1)
  527. {
  528. //Debug.Log("Font was already loaded!");
  529. errorCode = 0;
  530. }
  531. else
  532. Debug.Log("Error Code: " + errorCode + " occurred while Loading the [" + m_SourceFontFile.name + "] font file. This typically results from the use of an incompatible or corrupted font file.");
  533. }
  534. }
  535. if (errorCode == 0)
  536. {
  537. if (m_PointSizeSamplingMode == 0) m_PointSize = 72; // If Auto set size to 72 pts.
  538. errorCode = TMPro_FontPlugin.FT_Size_Font(m_PointSize); // Load the selected font and size it accordingly.
  539. if (errorCode != 0)
  540. Debug.Log("Error Code: " + errorCode + " occurred while Sizing the font.");
  541. }
  542. // Define an array containing the characters we will render.
  543. if (errorCode == 0)
  544. {
  545. int[] characterSet;
  546. if (m_CharacterSetSelectionMode == 7 || m_CharacterSetSelectionMode == 8)
  547. {
  548. List<int> charList = new List<int>();
  549. for (int i = 0; i < m_CharacterSequence.Length; i++)
  550. {
  551. // Check to make sure we don't include duplicates
  552. if (charList.FindIndex(item => item == m_CharacterSequence[i]) == -1)
  553. charList.Add(m_CharacterSequence[i]);
  554. else
  555. {
  556. //Debug.Log("Character [" + characterSequence[i] + "] is a duplicate.");
  557. }
  558. }
  559. characterSet = charList.ToArray();
  560. }
  561. else if (m_CharacterSetSelectionMode == 6)
  562. {
  563. characterSet = ParseHexNumberSequence(m_CharacterSequence);
  564. }
  565. else
  566. {
  567. characterSet = ParseNumberSequence(m_CharacterSequence);
  568. }
  569. m_CharacterCount = characterSet.Length;
  570. m_TextureBuffer = new byte[m_AtlasWidth * m_AtlasHeight];
  571. m_FontFaceInfo = new FT_FaceInfo();
  572. m_FontGlyphInfo = new FT_GlyphInfo[m_CharacterCount];
  573. int padding = m_Padding;
  574. bool autoSizing = m_PointSizeSamplingMode == 0;
  575. float strokeSize = m_FontStyleValue;
  576. if (m_RenderMode == RenderModes.DistanceField16) strokeSize = m_FontStyleValue * 16;
  577. if (m_RenderMode == RenderModes.DistanceField32) strokeSize = m_FontStyleValue * 32;
  578. m_IsProcessing = true;
  579. m_IsGenerationCancelled = false;
  580. // Start Stop Watch
  581. m_StopWatch = System.Diagnostics.Stopwatch.StartNew();
  582. ThreadPool.QueueUserWorkItem(someTask =>
  583. {
  584. m_IsRenderingDone = false;
  585. errorCode = TMPro_FontPlugin.Render_Characters(m_TextureBuffer, m_AtlasWidth, m_AtlasHeight, padding, characterSet, m_CharacterCount, m_FontStyle, strokeSize, autoSizing, m_RenderMode, (int)m_PackingMode, ref m_FontFaceInfo, m_FontGlyphInfo);
  586. m_IsRenderingDone = true;
  587. });
  588. }
  589. SaveCreationSettingsToEditorPrefs(SaveFontCreationSettings());
  590. }
  591. }
  592. // FONT RENDERING PROGRESS BAR
  593. GUILayout.Space(1);
  594. Rect progressRect = EditorGUILayout.GetControlRect(false, 20);
  595. bool isEnabled = GUI.enabled;
  596. GUI.enabled = true;
  597. EditorGUI.ProgressBar(progressRect, m_IsProcessing ? m_RenderingProgress : 0, "Generation Progress");
  598. progressRect.x = progressRect.x + progressRect.width - 20;
  599. progressRect.y += 1;
  600. progressRect.width = 20;
  601. progressRect.height = 16;
  602. GUI.enabled = m_IsProcessing;
  603. if (GUI.Button(progressRect, "X"))
  604. {
  605. TMPro_FontPlugin.SendCancellationRequest(CancellationRequestType.CancelInProgess);
  606. m_RenderingProgress = 0;
  607. m_IsProcessing = false;
  608. m_IsGenerationCancelled = true;
  609. }
  610. GUI.enabled = isEnabled;
  611. // FONT STATUS & INFORMATION
  612. GUISkin skin = GUI.skin;
  613. //GUI.skin = TMP_UIStyleManager.TMP_GUISkin;
  614. GUI.enabled = true;
  615. GUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Height(145));
  616. m_OutputScrollPosition = EditorGUILayout.BeginScrollView(m_OutputScrollPosition);
  617. EditorGUILayout.LabelField(m_OutputFeedback, TMP_UIStyleManager.label);
  618. EditorGUILayout.EndScrollView();
  619. GUILayout.EndVertical();
  620. GUI.skin = skin;
  621. // SAVE TEXTURE & CREATE and SAVE FONT XML FILE
  622. GUI.enabled = m_FontAtlas != null && !m_IsProcessing; // Enable Save Button if font_Atlas is not Null.
  623. EditorGUILayout.BeginHorizontal();
  624. if (GUILayout.Button("Save") && GUI.enabled)
  625. {
  626. if (m_SelectedFontAsset == null)
  627. {
  628. if (m_LegacyFontAsset != null)
  629. SaveNewFontAssetWithSameName(m_LegacyFontAsset);
  630. else
  631. SaveNewFontAsset(m_SourceFontFile);
  632. }
  633. else
  634. {
  635. // Save over exiting Font Asset
  636. string filePath = Path.GetFullPath(AssetDatabase.GetAssetPath(m_SelectedFontAsset)).Replace('\\', '/');
  637. if (m_RenderMode < RenderModes.DistanceField16) // ((int)m_RenderMode & 0x10) == 0x10)
  638. Save_Normal_FontAsset(filePath);
  639. else // ((RasterModes)m_RenderMode & RasterModes.Raster_Mode_SDF) == RasterModes.Raster_Mode_SDF || m_RenderMode == RenderModes.DistanceFieldAA)
  640. Save_SDF_FontAsset(filePath);
  641. }
  642. }
  643. if (GUILayout.Button("Save as...") && GUI.enabled)
  644. {
  645. if (m_SelectedFontAsset == null)
  646. {
  647. SaveNewFontAsset(m_SourceFontFile);
  648. }
  649. else
  650. {
  651. SaveNewFontAssetWithSameName(m_SelectedFontAsset);
  652. }
  653. }
  654. EditorGUILayout.EndHorizontal();
  655. EditorGUILayout.Space();
  656. GUI.enabled = true; // Re-enable GUI
  657. GUILayout.Space(5);
  658. if (position.height > position.width || position.width < k_TwoColumnControlsWidth)
  659. {
  660. DrawPreview();
  661. GUILayout.Space(5);
  662. }
  663. EditorGUILayout.EndScrollView();
  664. if (m_IsFontAtlasInvalid)
  665. ClearGeneratedData();
  666. }
  667. /// <summary>
  668. /// Clear the previously generated data.
  669. /// </summary>
  670. void ClearGeneratedData()
  671. {
  672. m_IsFontAtlasInvalid = false;
  673. if (m_FontAtlas != null)
  674. {
  675. DestroyImmediate(m_FontAtlas);
  676. m_FontAtlas = null;
  677. }
  678. m_SavedFontAtlas = null;
  679. m_OutputFeedback = string.Empty;
  680. m_WarningMessage = string.Empty;
  681. }
  682. /// <summary>
  683. /// Function to update the feedback window showing the results of the latest generation.
  684. /// </summary>
  685. void UpdateRenderFeedbackWindow()
  686. {
  687. m_PointSize = m_FontFaceInfo.pointSize;
  688. string colorTag = m_FontFaceInfo.characterCount == m_CharacterCount ? "<color=#C0ffff>" : "<color=#ffff00>";
  689. string colorTag2 = "<color=#C0ffff>";
  690. var missingGlyphReport = k_OutputNameLabel + "<b>" + colorTag2 + m_FontFaceInfo.name + "</color></b>";
  691. if (missingGlyphReport.Length > 60)
  692. missingGlyphReport += "\n" + k_OutputSizeLabel + "<b>" + colorTag2 + m_FontFaceInfo.pointSize + "</color></b>";
  693. else
  694. missingGlyphReport += " " + k_OutputSizeLabel + "<b>" + colorTag2 + m_FontFaceInfo.pointSize + "</color></b>";
  695. missingGlyphReport += "\n" + k_OutputCountLabel + "<b>" + colorTag + m_FontFaceInfo.characterCount + "/" + m_CharacterCount + "</color></b>";
  696. // Report missing requested glyph
  697. missingGlyphReport += "\n\n<color=#ffff00><b>Missing Characters</b></color>";
  698. missingGlyphReport += "\n----------------------------------------";
  699. m_OutputFeedback = missingGlyphReport;
  700. for (int i = 0; i < m_CharacterCount; i++)
  701. {
  702. if (m_FontGlyphInfo[i].x == -1)
  703. {
  704. missingGlyphReport += "\nID: <color=#C0ffff>" + m_FontGlyphInfo[i].id + "\t</color>Hex: <color=#C0ffff>" + m_FontGlyphInfo[i].id.ToString("X") + "\t</color>Char [<color=#C0ffff>" + (char)m_FontGlyphInfo[i].id + "</color>]";
  705. if (missingGlyphReport.Length < 16300)
  706. m_OutputFeedback = missingGlyphReport;
  707. }
  708. }
  709. if (missingGlyphReport.Length > 16300)
  710. m_OutputFeedback += "\n\n<color=#ffff00>Report truncated.</color>\n<color=#c0ffff>See</color> \"TextMesh Pro\\Glyph Report.txt\"";
  711. // Save Missing Glyph Report file
  712. if (Directory.Exists("Assets/TextMesh Pro"))
  713. {
  714. missingGlyphReport = System.Text.RegularExpressions.Regex.Replace(missingGlyphReport, @"<[^>]*>", string.Empty);
  715. File.WriteAllText("Assets/TextMesh Pro/Glyph Report.txt", missingGlyphReport);
  716. AssetDatabase.Refresh();
  717. }
  718. }
  719. void CreateFontTexture()
  720. {
  721. m_FontAtlas = new Texture2D(m_AtlasWidth, m_AtlasHeight, TextureFormat.Alpha8, false, true);
  722. Color32[] colors = new Color32[m_AtlasWidth * m_AtlasHeight];
  723. for (int i = 0; i < (m_AtlasWidth * m_AtlasHeight); i++)
  724. {
  725. byte c = m_TextureBuffer[i];
  726. colors[i] = new Color32(c, c, c, c);
  727. }
  728. // Clear allocation of
  729. m_TextureBuffer = null;
  730. if (m_RenderMode == RenderModes.Raster || m_RenderMode == RenderModes.RasterHinted)
  731. m_FontAtlas.filterMode = FilterMode.Point;
  732. m_FontAtlas.SetPixels32(colors, 0);
  733. m_FontAtlas.Apply(false, true);
  734. }
  735. /// <summary>
  736. /// Open Save Dialog to provide the option save the font asset using the name of the source font file. This also appends SDF to the name if using any of the SDF Font Asset creation modes.
  737. /// </summary>
  738. /// <param name="sourceObject"></param>
  739. void SaveNewFontAsset(Object sourceObject)
  740. {
  741. string filePath;
  742. // Save new Font Asset and open save file requester at Source Font File location.
  743. string saveDirectory = new FileInfo(AssetDatabase.GetAssetPath(sourceObject)).DirectoryName;
  744. if (m_RenderMode < RenderModes.DistanceField16) // ((int)m_RenderMode & 0x10) == 0x10)
  745. {
  746. filePath = EditorUtility.SaveFilePanel("Save TextMesh Pro! Font Asset File", saveDirectory, sourceObject.name, "asset");
  747. if (filePath.Length == 0)
  748. return;
  749. Save_Normal_FontAsset(filePath);
  750. }
  751. else if (m_RenderMode >= RenderModes.DistanceField16) // ((RasterModes)m_RenderMode & RasterModes.Raster_Mode_SDF) == RasterModes.Raster_Mode_SDF || m_RenderMode == RenderModes.DistanceFieldAA)
  752. {
  753. filePath = EditorUtility.SaveFilePanel("Save TextMesh Pro! Font Asset File", saveDirectory, sourceObject.name + " SDF", "asset");
  754. if (filePath.Length == 0)
  755. return;
  756. Save_SDF_FontAsset(filePath);
  757. }
  758. }
  759. /// <summary>
  760. /// Open Save Dialog to provide the option to save the font asset under the same name.
  761. /// </summary>
  762. /// <param name="sourceObject"></param>
  763. void SaveNewFontAssetWithSameName(Object sourceObject)
  764. {
  765. string filePath;
  766. // Save new Font Asset and open save file requester at Source Font File location.
  767. string saveDirectory = new FileInfo(AssetDatabase.GetAssetPath(sourceObject)).DirectoryName;
  768. filePath = EditorUtility.SaveFilePanel("Save TextMesh Pro! Font Asset File", saveDirectory, sourceObject.name, "asset");
  769. if (filePath.Length == 0)
  770. return;
  771. if (m_RenderMode < RenderModes.DistanceField16) // ((int)m_RenderMode & 0x10) == 0x10)
  772. {
  773. Save_Normal_FontAsset(filePath);
  774. }
  775. else if (m_RenderMode >= RenderModes.DistanceField16) // ((RasterModes)m_RenderMode & RasterModes.Raster_Mode_SDF) == RasterModes.Raster_Mode_SDF || m_RenderMode == RenderModes.DistanceFieldAA)
  776. {
  777. Save_SDF_FontAsset(filePath);
  778. }
  779. }
  780. void Save_Normal_FontAsset(string filePath)
  781. {
  782. filePath = filePath.Substring(0, filePath.Length - 6); // Trim file extension from filePath.
  783. string dataPath = Application.dataPath;
  784. if (filePath.IndexOf(dataPath, System.StringComparison.InvariantCultureIgnoreCase) == -1)
  785. {
  786. Debug.LogError("You're saving the font asset in a directory outside of this project folder. This is not supported. Please select a directory under \"" + dataPath + "\"");
  787. return;
  788. }
  789. string relativeAssetPath = filePath.Substring(dataPath.Length - 6);
  790. string tex_DirName = Path.GetDirectoryName(relativeAssetPath);
  791. string tex_FileName = Path.GetFileNameWithoutExtension(relativeAssetPath);
  792. string tex_Path_NoExt = tex_DirName + "/" + tex_FileName;
  793. // Check if TextMeshPro font asset already exists. If not, create a new one. Otherwise update the existing one.
  794. TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath(tex_Path_NoExt + ".asset", typeof(TMP_FontAsset)) as TMP_FontAsset;
  795. if (fontAsset == null)
  796. {
  797. //Debug.Log("Creating TextMeshPro font asset!");
  798. fontAsset = ScriptableObject.CreateInstance<TMP_FontAsset>(); // Create new TextMeshPro Font Asset.
  799. AssetDatabase.CreateAsset(fontAsset, tex_Path_NoExt + ".asset");
  800. //Set Font Asset Type
  801. fontAsset.fontAssetType = TMP_FontAsset.FontAssetTypes.Bitmap;
  802. // Reference to the source font file
  803. //font_asset.sourceFontFile = font_TTF as Font;
  804. // Add FaceInfo to Font Asset
  805. FaceInfo face = GetFaceInfo(m_FontFaceInfo, 1);
  806. fontAsset.AddFaceInfo(face);
  807. // Add GlyphInfo[] to Font Asset
  808. TMP_Glyph[] glyphs = GetGlyphInfo(m_FontGlyphInfo, 1);
  809. fontAsset.AddGlyphInfo(glyphs);
  810. // Get and Add Kerning Pairs to Font Asset
  811. if (m_IncludeKerningPairs)
  812. {
  813. string fontFilePath = AssetDatabase.GetAssetPath(m_SourceFontFile);
  814. KerningTable kerningTable = GetKerningTable(fontFilePath, (int)face.PointSize);
  815. fontAsset.AddKerningInfo(kerningTable);
  816. }
  817. // Add Font Atlas as Sub-Asset
  818. fontAsset.atlas = m_FontAtlas;
  819. m_FontAtlas.name = tex_FileName + " Atlas";
  820. AssetDatabase.AddObjectToAsset(m_FontAtlas, fontAsset);
  821. // Create new Material and Add it as Sub-Asset
  822. Shader default_Shader = Shader.Find("TextMeshPro/Bitmap"); // m_shaderSelection;
  823. Material tmp_material = new Material(default_Shader);
  824. tmp_material.name = tex_FileName + " Material";
  825. tmp_material.SetTexture(ShaderUtilities.ID_MainTex, m_FontAtlas);
  826. fontAsset.material = tmp_material;
  827. AssetDatabase.AddObjectToAsset(tmp_material, fontAsset);
  828. }
  829. else
  830. {
  831. // Find all Materials referencing this font atlas.
  832. Material[] material_references = TMP_EditorUtility.FindMaterialReferences(fontAsset);
  833. // Destroy Assets that will be replaced.
  834. DestroyImmediate(fontAsset.atlas, true);
  835. //Set Font Asset Type
  836. fontAsset.fontAssetType = TMP_FontAsset.FontAssetTypes.Bitmap;
  837. // Add FaceInfo to Font Asset
  838. FaceInfo face = GetFaceInfo(m_FontFaceInfo, 1);
  839. fontAsset.AddFaceInfo(face);
  840. // Add GlyphInfo[] to Font Asset
  841. TMP_Glyph[] glyphs = GetGlyphInfo(m_FontGlyphInfo, 1);
  842. fontAsset.AddGlyphInfo(glyphs);
  843. // Get and Add Kerning Pairs to Font Asset
  844. if (m_IncludeKerningPairs)
  845. {
  846. string fontFilePath = AssetDatabase.GetAssetPath(m_SourceFontFile);
  847. KerningTable kerningTable = GetKerningTable(fontFilePath, (int)face.PointSize);
  848. fontAsset.AddKerningInfo(kerningTable);
  849. }
  850. // Add Font Atlas as Sub-Asset
  851. fontAsset.atlas = m_FontAtlas;
  852. m_FontAtlas.name = tex_FileName + " Atlas";
  853. // Special handling due to a bug in earlier versions of Unity.
  854. m_FontAtlas.hideFlags = HideFlags.None;
  855. fontAsset.material.hideFlags = HideFlags.None;
  856. AssetDatabase.AddObjectToAsset(m_FontAtlas, fontAsset);
  857. // Assign new font atlas texture to the existing material.
  858. fontAsset.material.SetTexture(ShaderUtilities.ID_MainTex, fontAsset.atlas);
  859. // Update the Texture reference on the Material
  860. for (int i = 0; i < material_references.Length; i++)
  861. {
  862. material_references[i].SetTexture(ShaderUtilities.ID_MainTex, m_FontAtlas);
  863. }
  864. }
  865. // Save Font Asset creation settings
  866. m_SelectedFontAsset = fontAsset;
  867. m_LegacyFontAsset = null;
  868. fontAsset.creationSettings = SaveFontCreationSettings();
  869. AssetDatabase.SaveAssets();
  870. AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(fontAsset)); // Re-import font asset to get the new updated version.
  871. //EditorUtility.SetDirty(font_asset);
  872. fontAsset.ReadFontDefinition();
  873. AssetDatabase.Refresh();
  874. m_FontAtlas = null;
  875. // NEED TO GENERATE AN EVENT TO FORCE A REDRAW OF ANY TEXTMESHPRO INSTANCES THAT MIGHT BE USING THIS FONT ASSET
  876. TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset);
  877. }
  878. void Save_SDF_FontAsset(string filePath)
  879. {
  880. filePath = filePath.Substring(0, filePath.Length - 6); // Trim file extension from filePath.
  881. string dataPath = Application.dataPath;
  882. if (filePath.IndexOf(dataPath, System.StringComparison.InvariantCultureIgnoreCase) == -1)
  883. {
  884. Debug.LogError("You're saving the font asset in a directory outside of this project folder. This is not supported. Please select a directory under \"" + dataPath + "\"");
  885. return;
  886. }
  887. string relativeAssetPath = filePath.Substring(dataPath.Length - 6);
  888. string tex_DirName = Path.GetDirectoryName(relativeAssetPath);
  889. string tex_FileName = Path.GetFileNameWithoutExtension(relativeAssetPath);
  890. string tex_Path_NoExt = tex_DirName + "/" + tex_FileName;
  891. // Check if TextMeshPro font asset already exists. If not, create a new one. Otherwise update the existing one.
  892. TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(tex_Path_NoExt + ".asset");
  893. if (fontAsset == null)
  894. {
  895. //Debug.Log("Creating TextMeshPro font asset!");
  896. fontAsset = ScriptableObject.CreateInstance<TMP_FontAsset>(); // Create new TextMeshPro Font Asset.
  897. AssetDatabase.CreateAsset(fontAsset, tex_Path_NoExt + ".asset");
  898. // Reference to the source font file
  899. //font_asset.sourceFontFile = font_TTF as Font;
  900. //Set Font Asset Type
  901. fontAsset.fontAssetType = TMP_FontAsset.FontAssetTypes.SDF;
  902. //if (m_destination_Atlas != null)
  903. // m_font_Atlas = m_destination_Atlas;
  904. // If using the C# SDF creation mode, we need the scale down factor.
  905. int scaleDownFactor = 1; // ((RasterModes)m_RenderMode & RasterModes.Raster_Mode_SDF) == RasterModes.Raster_Mode_SDF || m_RenderMode == RenderModes.DistanceFieldAA ? 1 : font_scaledownFactor;
  906. // Add FaceInfo to Font Asset
  907. FaceInfo face = GetFaceInfo(m_FontFaceInfo, scaleDownFactor);
  908. fontAsset.AddFaceInfo(face);
  909. // Add GlyphInfo[] to Font Asset
  910. TMP_Glyph[] glyphs = GetGlyphInfo(m_FontGlyphInfo, scaleDownFactor);
  911. fontAsset.AddGlyphInfo(glyphs);
  912. // Get and Add Kerning Pairs to Font Asset
  913. if (m_IncludeKerningPairs)
  914. {
  915. string fontFilePath = AssetDatabase.GetAssetPath(m_SourceFontFile);
  916. KerningTable kerningTable = GetKerningTable(fontFilePath, (int)face.PointSize);
  917. fontAsset.AddKerningInfo(kerningTable);
  918. }
  919. // Add Line Breaking Rules
  920. //LineBreakingTable lineBreakingTable = new LineBreakingTable();
  921. //
  922. // Add Font Atlas as Sub-Asset
  923. fontAsset.atlas = m_FontAtlas;
  924. m_FontAtlas.name = tex_FileName + " Atlas";
  925. AssetDatabase.AddObjectToAsset(m_FontAtlas, fontAsset);
  926. // Create new Material and Add it as Sub-Asset
  927. Shader default_Shader = Shader.Find("TextMeshPro/Distance Field"); //m_shaderSelection;
  928. Material tmp_material = new Material(default_Shader);
  929. tmp_material.name = tex_FileName + " Material";
  930. tmp_material.SetTexture(ShaderUtilities.ID_MainTex, m_FontAtlas);
  931. tmp_material.SetFloat(ShaderUtilities.ID_TextureWidth, m_FontAtlas.width);
  932. tmp_material.SetFloat(ShaderUtilities.ID_TextureHeight, m_FontAtlas.height);
  933. int spread = m_Padding + 1;
  934. tmp_material.SetFloat(ShaderUtilities.ID_GradientScale, spread); // Spread = Padding for Brute Force SDF.
  935. tmp_material.SetFloat(ShaderUtilities.ID_WeightNormal, fontAsset.normalStyle);
  936. tmp_material.SetFloat(ShaderUtilities.ID_WeightBold, fontAsset.boldStyle);
  937. fontAsset.material = tmp_material;
  938. AssetDatabase.AddObjectToAsset(tmp_material, fontAsset);
  939. }
  940. else
  941. {
  942. // Find all Materials referencing this font atlas.
  943. Material[] material_references = TMP_EditorUtility.FindMaterialReferences(fontAsset);
  944. // Destroy Assets that will be replaced.
  945. DestroyImmediate(fontAsset.atlas, true);
  946. //Set Font Asset Type
  947. fontAsset.fontAssetType = TMP_FontAsset.FontAssetTypes.SDF;
  948. int scaleDownFactor = 1; // ((RasterModes)m_RenderMode & RasterModes.Raster_Mode_SDF) == RasterModes.Raster_Mode_SDF || m_RenderMode == RenderModes.DistanceFieldAA ? 1 : font_scaledownFactor;
  949. // Add FaceInfo to Font Asset
  950. FaceInfo face = GetFaceInfo(m_FontFaceInfo, scaleDownFactor);
  951. fontAsset.AddFaceInfo(face);
  952. // Add GlyphInfo[] to Font Asset
  953. TMP_Glyph[] glyphs = GetGlyphInfo(m_FontGlyphInfo, scaleDownFactor);
  954. fontAsset.AddGlyphInfo(glyphs);
  955. // Get and Add Kerning Pairs to Font Asset
  956. if (m_IncludeKerningPairs)
  957. {
  958. string fontFilePath = AssetDatabase.GetAssetPath(m_SourceFontFile);
  959. KerningTable kerningTable = GetKerningTable(fontFilePath, (int)face.PointSize);
  960. fontAsset.AddKerningInfo(kerningTable);
  961. }
  962. // Add Font Atlas as Sub-Asset
  963. fontAsset.atlas = m_FontAtlas;
  964. m_FontAtlas.name = tex_FileName + " Atlas";
  965. // Special handling due to a bug in earlier versions of Unity.
  966. m_FontAtlas.hideFlags = HideFlags.None;
  967. fontAsset.material.hideFlags = HideFlags.None;
  968. AssetDatabase.AddObjectToAsset(m_FontAtlas, fontAsset);
  969. // Assign new font atlas texture to the existing material.
  970. fontAsset.material.SetTexture(ShaderUtilities.ID_MainTex, fontAsset.atlas);
  971. // Update the Texture reference on the Material
  972. for (int i = 0; i < material_references.Length; i++)
  973. {
  974. material_references[i].SetTexture(ShaderUtilities.ID_MainTex, m_FontAtlas);
  975. material_references[i].SetFloat(ShaderUtilities.ID_TextureWidth, m_FontAtlas.width);
  976. material_references[i].SetFloat(ShaderUtilities.ID_TextureHeight, m_FontAtlas.height);
  977. int spread = m_Padding + 1;
  978. material_references[i].SetFloat(ShaderUtilities.ID_GradientScale, spread); // Spread = Padding for Brute Force SDF.
  979. material_references[i].SetFloat(ShaderUtilities.ID_WeightNormal, fontAsset.normalStyle);
  980. material_references[i].SetFloat(ShaderUtilities.ID_WeightBold, fontAsset.boldStyle);
  981. }
  982. }
  983. // Saving File for Debug
  984. //var pngData = destination_Atlas.EncodeToPNG();
  985. //File.WriteAllBytes("Assets/Textures/Debug Distance Field.png", pngData);
  986. // Save Font Asset creation settings
  987. m_SelectedFontAsset = fontAsset;
  988. m_LegacyFontAsset = null;
  989. fontAsset.creationSettings = SaveFontCreationSettings();
  990. AssetDatabase.SaveAssets();
  991. AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(fontAsset)); // Re-import font asset to get the new updated version.
  992. fontAsset.ReadFontDefinition();
  993. AssetDatabase.Refresh();
  994. m_FontAtlas = null;
  995. // NEED TO GENERATE AN EVENT TO FORCE A REDRAW OF ANY TEXTMESHPRO INSTANCES THAT MIGHT BE USING THIS FONT ASSET
  996. TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset);
  997. }
  998. /// <summary>
  999. /// Internal method to save the Font Asset Creation Settings
  1000. /// </summary>
  1001. /// <returns></returns>
  1002. FontAssetCreationSettings SaveFontCreationSettings()
  1003. {
  1004. FontAssetCreationSettings settings = new FontAssetCreationSettings();
  1005. //settings.sourceFontFileName = m_SourceFontFile.name;
  1006. settings.sourceFontFileGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_SourceFontFile));
  1007. settings.pointSizeSamplingMode = m_PointSizeSamplingMode;
  1008. settings.pointSize = m_PointSize;
  1009. settings.padding = m_Padding;
  1010. settings.packingMode = (int)m_PackingMode;
  1011. settings.atlasWidth = m_AtlasWidth;
  1012. settings.atlasHeight = m_AtlasHeight;
  1013. settings.characterSetSelectionMode = m_CharacterSetSelectionMode;
  1014. settings.characterSequence = m_CharacterSequence;
  1015. settings.referencedFontAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_ReferencedFontAsset));
  1016. settings.referencedTextAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_CharacterList));
  1017. settings.fontStyle = (int)m_FontStyle;
  1018. settings.fontStyleModifier = m_FontStyleValue;
  1019. settings.renderMode = (int)m_RenderMode;
  1020. settings.includeFontFeatures = m_IncludeKerningPairs;
  1021. return settings;
  1022. }
  1023. /// <summary>
  1024. /// Internal method to load the Font Asset Creation Settings
  1025. /// </summary>
  1026. /// <param name="settings"></param>
  1027. void LoadFontCreationSettings(FontAssetCreationSettings settings)
  1028. {
  1029. m_SourceFontFile = AssetDatabase.LoadAssetAtPath<Font>(AssetDatabase.GUIDToAssetPath(settings.sourceFontFileGUID));
  1030. m_PointSizeSamplingMode = settings.pointSizeSamplingMode;
  1031. m_PointSize = settings.pointSize;
  1032. m_Padding = settings.padding;
  1033. m_PackingMode = (FontPackingModes)settings.packingMode;
  1034. m_AtlasWidth = settings.atlasWidth;
  1035. m_AtlasHeight = settings.atlasHeight;
  1036. m_CharacterSetSelectionMode = settings.characterSetSelectionMode;
  1037. m_CharacterSequence = settings.characterSequence;
  1038. m_ReferencedFontAsset = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(AssetDatabase.GUIDToAssetPath(settings.referencedFontAssetGUID));
  1039. m_CharacterList = AssetDatabase.LoadAssetAtPath<TextAsset>(AssetDatabase.GUIDToAssetPath(settings.referencedTextAssetGUID));
  1040. m_FontStyle = (FaceStyles)settings.fontStyle;
  1041. m_FontStyleValue = settings.fontStyleModifier;
  1042. m_RenderMode = (RenderModes)settings.renderMode;
  1043. m_IncludeKerningPairs = settings.includeFontFeatures;
  1044. }
  1045. /// <summary>
  1046. /// Save the latest font asset creation settings to EditorPrefs.
  1047. /// </summary>
  1048. /// <param name="settings"></param>
  1049. void SaveCreationSettingsToEditorPrefs(FontAssetCreationSettings settings)
  1050. {
  1051. // Create new list if one does not already exist
  1052. if (m_FontAssetCreationSettingsContainer == null)
  1053. {
  1054. m_FontAssetCreationSettingsContainer = new FontAssetCreationSettingsContainer();
  1055. m_FontAssetCreationSettingsContainer.fontAssetCreationSettings = new List<FontAssetCreationSettings>();
  1056. }
  1057. // Add new creation settings to the list
  1058. m_FontAssetCreationSettingsContainer.fontAssetCreationSettings.Add(settings);
  1059. // Since list should only contain the most 4 recent settings, we remove the first element if list exceeds 4 elements.
  1060. if (m_FontAssetCreationSettingsContainer.fontAssetCreationSettings.Count > 4)
  1061. m_FontAssetCreationSettingsContainer.fontAssetCreationSettings.RemoveAt(0);
  1062. m_FontAssetCreationSettingsCurrentIndex = m_FontAssetCreationSettingsContainer.fontAssetCreationSettings.Count - 1;
  1063. // Serialize list to JSON
  1064. string serializedSettings = JsonUtility.ToJson(m_FontAssetCreationSettingsContainer, true);
  1065. EditorPrefs.SetString(k_FontAssetCreationSettingsContainerKey, serializedSettings);
  1066. }
  1067. void DrawPreview()
  1068. {
  1069. Rect pixelRect;
  1070. if (position.width > position.height && position.width > k_TwoColumnControlsWidth)
  1071. {
  1072. float minSide = Mathf.Min(position.height - 15f, position.width - k_TwoColumnControlsWidth);
  1073. EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.MaxWidth(minSide));
  1074. pixelRect = GUILayoutUtility.GetRect(minSide, minSide, GUILayout.ExpandHeight(false), GUILayout.ExpandWidth(false));
  1075. }
  1076. else
  1077. {
  1078. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  1079. pixelRect = GUILayoutUtility.GetAspectRect(1f);
  1080. }
  1081. if (m_FontAtlas != null)
  1082. {
  1083. EditorGUI.DrawTextureAlpha(pixelRect, m_FontAtlas, ScaleMode.StretchToFill);
  1084. }
  1085. else if (m_SavedFontAtlas != null)
  1086. {
  1087. EditorGUI.DrawTextureAlpha(pixelRect, m_SavedFontAtlas, ScaleMode.StretchToFill);
  1088. }
  1089. EditorGUILayout.EndVertical();
  1090. }
  1091. // Convert from FT_FaceInfo to FaceInfo
  1092. static FaceInfo GetFaceInfo(FT_FaceInfo ftFace, int scaleFactor)
  1093. {
  1094. FaceInfo face = new FaceInfo();
  1095. face.Name = ftFace.name;
  1096. face.PointSize = (float)ftFace.pointSize / scaleFactor;
  1097. face.Padding = ftFace.padding / scaleFactor;
  1098. face.LineHeight = ftFace.lineHeight / scaleFactor;
  1099. face.CapHeight = 0;
  1100. face.Baseline = 0;
  1101. face.Ascender = ftFace.ascender / scaleFactor;
  1102. face.Descender = ftFace.descender / scaleFactor;
  1103. face.CenterLine = ftFace.centerLine / scaleFactor;
  1104. face.Underline = ftFace.underline / scaleFactor;
  1105. face.UnderlineThickness = ftFace.underlineThickness == 0 ? 5 : ftFace.underlineThickness / scaleFactor; // Set Thickness to 5 if TTF value is Zero.
  1106. face.strikethrough = (face.Ascender + face.Descender) / 2.75f;
  1107. face.strikethroughThickness = face.UnderlineThickness;
  1108. face.SuperscriptOffset = face.Ascender;
  1109. face.SubscriptOffset = face.Underline;
  1110. face.SubSize = 0.5f;
  1111. //face.CharacterCount = ft_face.characterCount;
  1112. face.AtlasWidth = ftFace.atlasWidth / scaleFactor;
  1113. face.AtlasHeight = ftFace.atlasHeight / scaleFactor;
  1114. return face;
  1115. }
  1116. // Convert from FT_GlyphInfo[] to GlyphInfo[]
  1117. TMP_Glyph[] GetGlyphInfo(FT_GlyphInfo[] ftGlyphs, int scaleFactor)
  1118. {
  1119. List<TMP_Glyph> glyphs = new List<TMP_Glyph>();
  1120. List<int> kerningSet = new List<int>();
  1121. for (int i = 0; i < ftGlyphs.Length; i++)
  1122. {
  1123. TMP_Glyph g = new TMP_Glyph();
  1124. g.id = ftGlyphs[i].id;
  1125. g.x = ftGlyphs[i].x / scaleFactor;
  1126. g.y = ftGlyphs[i].y / scaleFactor;
  1127. g.width = ftGlyphs[i].width / scaleFactor;
  1128. g.height = ftGlyphs[i].height / scaleFactor;
  1129. g.xOffset = ftGlyphs[i].xOffset / scaleFactor;
  1130. g.yOffset = ftGlyphs[i].yOffset / scaleFactor;
  1131. g.xAdvance = ftGlyphs[i].xAdvance / scaleFactor;
  1132. // Filter out characters with missing glyphs.
  1133. if (g.x == -1)
  1134. continue;
  1135. glyphs.Add(g);
  1136. kerningSet.Add(g.id);
  1137. }
  1138. m_KerningSet = kerningSet.ToArray();
  1139. return glyphs.ToArray();
  1140. }
  1141. // Get Kerning Pairs
  1142. public KerningTable GetKerningTable(string fontFilePath, int pointSize)
  1143. {
  1144. KerningTable kerningInfo = new KerningTable();
  1145. kerningInfo.kerningPairs = new List<KerningPair>();
  1146. // Temporary Array to hold the kerning pairs from the Native Plug-in.
  1147. FT_KerningPair[] kerningPairs = new FT_KerningPair[7500];
  1148. int kpCount = TMPro_FontPlugin.FT_GetKerningPairs(fontFilePath, m_KerningSet, m_KerningSet.Length, kerningPairs);
  1149. for (int i = 0; i < kpCount; i++)
  1150. {
  1151. // Proceed to add each kerning pairs.
  1152. KerningPair kp = new KerningPair((uint)kerningPairs[i].ascII_Left, (uint)kerningPairs[i].ascII_Right, kerningPairs[i].xAdvanceOffset * pointSize);
  1153. // Filter kerning pairs to avoid duplicates
  1154. int index = kerningInfo.kerningPairs.FindIndex(item => item.firstGlyph == kp.firstGlyph && item.secondGlyph == kp.secondGlyph);
  1155. if (index == -1)
  1156. kerningInfo.kerningPairs.Add(kp);
  1157. else
  1158. if (!TMP_Settings.warningsDisabled) Debug.LogWarning("Kerning Key for [" + kp.firstGlyph + "] and [" + kp.secondGlyph + "] is a duplicate.");
  1159. }
  1160. return kerningInfo;
  1161. }
  1162. }
  1163. }