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