using System; using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Linq; namespace TMPro { /// /// Contains the font asset for the specified font weight styles. /// [Serializable] public struct TMP_FontWeights { public TMP_FontAsset regularTypeface; public TMP_FontAsset italicTypeface; } [Serializable] public class TMP_FontAsset : TMP_Asset { /// /// Default Font Asset used as last resort when glyphs are missing. /// public static TMP_FontAsset defaultFontAsset { get { if (s_defaultFontAsset == null) { s_defaultFontAsset = Resources.Load("Fonts & Materials/LiberationSans SDF"); } return s_defaultFontAsset; } } private static TMP_FontAsset s_defaultFontAsset; public enum FontAssetTypes { None = 0, SDF = 1, Bitmap = 2 }; public FontAssetTypes fontAssetType; /// /// The general information about the font. /// public FaceInfo fontInfo { get { return m_fontInfo; } } [SerializeField] private FaceInfo m_fontInfo; [SerializeField] public Texture2D atlas; // Should add a property to make this read-only. // Glyph Info [SerializeField] private List m_glyphInfoList; public Dictionary characterDictionary { get { if (m_characterDictionary == null) ReadFontDefinition(); return m_characterDictionary; } } private Dictionary m_characterDictionary; /// /// Dictionary containing the kerning data /// public Dictionary kerningDictionary { get { return m_kerningDictionary; } } private Dictionary m_kerningDictionary; /// /// /// public KerningTable kerningInfo { get { return m_kerningInfo; } } [SerializeField] private KerningTable m_kerningInfo; [SerializeField] #pragma warning disable 0169 // Property is used to create an empty Kerning Pair in the editor. private KerningPair m_kerningPair; // Used for creating a new kerning pair in Editor Panel. /// /// List containing the Fallback font assets for this font. /// [SerializeField] public List fallbackFontAssets; /// /// The settings used in the Font Asset Creator when this font asset was created or edited. /// public FontAssetCreationSettings creationSettings { get { return m_CreationSettings; } set { m_CreationSettings = value; } } [SerializeField] public FontAssetCreationSettings m_CreationSettings; // FONT WEIGHTS [SerializeField] public TMP_FontWeights[] fontWeights = new TMP_FontWeights[10]; private int[] m_characterSet; // Array containing all the characters in this font asset. public float normalStyle = 0; public float normalSpacingOffset = 0; public float boldStyle = 0.75f; public float boldSpacing = 7f; public byte italicStyle = 35; public byte tabSize = 10; private byte m_oldTabSize; void OnEnable() { //Debug.Log("OnEnable has been called on " + this.name); } void OnDisable() { //Debug.Log("TextMeshPro Font Asset [" + this.name + "] has been disabled!"); } #if UNITY_EDITOR /// /// /// void OnValidate() { if (m_oldTabSize != tabSize) { m_oldTabSize = tabSize; ReadFontDefinition(); } } #endif /// /// /// /// public void AddFaceInfo(FaceInfo faceInfo) { m_fontInfo = faceInfo; } /// /// /// /// public void AddGlyphInfo(TMP_Glyph[] glyphInfo) { m_glyphInfoList = new List(); int characterCount = glyphInfo.Length; m_fontInfo.CharacterCount = characterCount; m_characterSet = new int[characterCount]; for (int i = 0; i < characterCount; i++) { TMP_Glyph g = new TMP_Glyph(); g.id = glyphInfo[i].id; g.x = glyphInfo[i].x; g.y = glyphInfo[i].y; g.width = glyphInfo[i].width; g.height = glyphInfo[i].height; g.xOffset = glyphInfo[i].xOffset; g.yOffset = (glyphInfo[i].yOffset); g.xAdvance = glyphInfo[i].xAdvance; g.scale = 1; m_glyphInfoList.Add(g); // While iterating through list of glyphs, find the Descender & Ascender for this GlyphSet. //m_fontInfo.Ascender = Mathf.Max(m_fontInfo.Ascender, glyphInfo[i].yOffset); //m_fontInfo.Descender = Mathf.Min(m_fontInfo.Descender, glyphInfo[i].yOffset - glyphInfo[i].height); //Debug.Log(m_fontInfo.Ascender + " " + m_fontInfo.Descender); m_characterSet[i] = g.id; // Add Character ID to Array to make it easier to get the kerning pairs. } // Sort List by ID. m_glyphInfoList = m_glyphInfoList.OrderBy(s => s.id).ToList(); } /// /// /// /// public void AddKerningInfo(KerningTable kerningTable) { m_kerningInfo = kerningTable; } /// /// /// public void ReadFontDefinition() { //Debug.Log("Reading Font Definition for " + this.name + "."); // Make sure that we have a Font Asset file assigned. if (m_fontInfo == null) { return; } // Check Font Asset type //Debug.Log(name + " " + fontAssetType); // Create new instance of GlyphInfo Dictionary for fast access to glyph info. m_characterDictionary = new Dictionary(); for (int i = 0; i < m_glyphInfoList.Count; i++) { TMP_Glyph glyph = m_glyphInfoList[i]; if (!m_characterDictionary.ContainsKey(glyph.id)) m_characterDictionary.Add(glyph.id, glyph); // Compatibility if (glyph.scale == 0) glyph.scale = 1; } //Debug.Log("PRE: BaseLine:" + m_fontInfo.Baseline + " Ascender:" + m_fontInfo.Ascender + " Descender:" + m_fontInfo.Descender); // + " Centerline:" + m_fontInfo.CenterLine); TMP_Glyph temp_charInfo = new TMP_Glyph(); // Add Character (10) LineFeed, (13) Carriage Return & Space (32) to Dictionary if they don't exists. if (m_characterDictionary.ContainsKey(32)) { m_characterDictionary[32].width = m_characterDictionary[32].xAdvance; // m_fontInfo.Ascender / 5; m_characterDictionary[32].height = m_fontInfo.Ascender - m_fontInfo.Descender; m_characterDictionary[32].yOffset= m_fontInfo.Ascender; m_characterDictionary[32].scale = 1; } else { //Debug.Log("Adding Character 32 (Space) to Dictionary for Font (" + m_fontInfo.Name + ")."); temp_charInfo = new TMP_Glyph(); temp_charInfo.id = 32; temp_charInfo.x = 0; temp_charInfo.y = 0; temp_charInfo.width = m_fontInfo.Ascender / 5; temp_charInfo.height = m_fontInfo.Ascender - m_fontInfo.Descender; temp_charInfo.xOffset = 0; temp_charInfo.yOffset = m_fontInfo.Ascender; temp_charInfo.xAdvance = m_fontInfo.PointSize / 4; temp_charInfo.scale = 1; m_characterDictionary.Add(32, temp_charInfo); } // Add Non-Breaking Space (160) if (!m_characterDictionary.ContainsKey(160)) { temp_charInfo = TMP_Glyph.Clone(m_characterDictionary[32]); m_characterDictionary.Add(160, temp_charInfo); } // Add Zero Width Space (8203) if (!m_characterDictionary.ContainsKey(8203)) { temp_charInfo = TMP_Glyph.Clone(m_characterDictionary[32]); temp_charInfo.width = 0; temp_charInfo.xAdvance = 0; m_characterDictionary.Add(8203, temp_charInfo); } //Add Zero Width no-break space (8288) if (!m_characterDictionary.ContainsKey(8288)) { temp_charInfo = TMP_Glyph.Clone(m_characterDictionary[32]); temp_charInfo.width = 0; temp_charInfo.xAdvance = 0; m_characterDictionary.Add(8288, temp_charInfo); } // Add Linefeed (10) if (m_characterDictionary.ContainsKey(10) == false) { //Debug.Log("Adding Character 10 (Linefeed) to Dictionary for Font (" + m_fontInfo.Name + ")."); temp_charInfo = new TMP_Glyph(); temp_charInfo.id = 10; temp_charInfo.x = 0; // m_characterDictionary[32].x; temp_charInfo.y = 0; // m_characterDictionary[32].y; temp_charInfo.width = 10; // m_characterDictionary[32].width; temp_charInfo.height = m_characterDictionary[32].height; temp_charInfo.xOffset = 0; // m_characterDictionary[32].xOffset; temp_charInfo.yOffset = m_characterDictionary[32].yOffset; temp_charInfo.xAdvance = 0; temp_charInfo.scale = 1; m_characterDictionary.Add(10, temp_charInfo); if (!m_characterDictionary.ContainsKey(13)) m_characterDictionary.Add(13, temp_charInfo); } // Add Tab Character to Dictionary. Tab is Tab Size * Space Character Width. if (m_characterDictionary.ContainsKey(9) == false) { //Debug.Log("Adding Character 9 (Tab) to Dictionary for Font (" + m_fontInfo.Name + ")."); temp_charInfo = new TMP_Glyph(); temp_charInfo.id = 9; temp_charInfo.x = m_characterDictionary[32].x; temp_charInfo.y = m_characterDictionary[32].y; temp_charInfo.width = m_characterDictionary[32].width * tabSize + (m_characterDictionary[32].xAdvance - m_characterDictionary[32].width) * (tabSize - 1); temp_charInfo.height = m_characterDictionary[32].height; temp_charInfo.xOffset = m_characterDictionary[32].xOffset; temp_charInfo.yOffset = m_characterDictionary[32].yOffset; temp_charInfo.xAdvance = m_characterDictionary[32].xAdvance * tabSize; temp_charInfo.scale = 1; m_characterDictionary.Add(9, temp_charInfo); } // Centerline is located at the center of character like { or in the middle of the lowercase o. //m_fontInfo.CenterLine = m_characterDictionary[111].yOffset - m_characterDictionary[111].height * 0.5f; // Tab Width is using the same xAdvance as space (32). m_fontInfo.TabWidth = m_characterDictionary[9].xAdvance; // Set Cap Height if (m_fontInfo.CapHeight == 0 && m_characterDictionary.ContainsKey(72)) m_fontInfo.CapHeight = m_characterDictionary[72].yOffset; // Adjust Font Scale for compatibility reasons if (m_fontInfo.Scale == 0) m_fontInfo.Scale = 1.0f; // Set Strikethrough Offset (if needed) if (m_fontInfo.strikethrough == 0) m_fontInfo.strikethrough = m_fontInfo.CapHeight / 2.5f; // Set Padding value for legacy font assets. if (m_fontInfo.Padding == 0) { if (material.HasProperty(ShaderUtilities.ID_GradientScale)) m_fontInfo.Padding = material.GetFloat(ShaderUtilities.ID_GradientScale) - 1; } // Populate Dictionary with Kerning Information m_kerningDictionary = new Dictionary(); List pairs = m_kerningInfo.kerningPairs; //Debug.Log(m_fontInfo.Name + " has " + pairs.Count + " Kerning Pairs."); for (int i = 0; i < pairs.Count; i++) { KerningPair pair = pairs[i]; // Convert legacy kerning data if (pair.xOffset != 0) pairs[i].ConvertLegacyKerningData(); KerningPairKey uniqueKey = new KerningPairKey(pair.firstGlyph, pair.secondGlyph); if (m_kerningDictionary.ContainsKey((int)uniqueKey.key) == false) { m_kerningDictionary.Add((int)uniqueKey.key, pair); } else { if (!TMP_Settings.warningsDisabled) Debug.LogWarning("Kerning Key for [" + uniqueKey.ascii_Left + "] and [" + uniqueKey.ascii_Right + "] already exists."); } } // Compute Hashcode for the font asset name hashCode = TMP_TextUtilities.GetSimpleHashCode(this.name); // Compute Hashcode for the material name materialHashCode = TMP_TextUtilities.GetSimpleHashCode(material.name); // Unload font atlas texture //ShaderUtilities.GetShaderPropertyIDs(); //Resources.UnloadAsset(material.GetTexture(ShaderUtilities.ID_MainTex)); // Initialize Font Weights if needed //InitializeFontWeights(); } /// /// Function to sort the list of glyphs. /// public void SortGlyphs() { if (m_glyphInfoList == null || m_glyphInfoList.Count == 0) return; m_glyphInfoList = m_glyphInfoList.OrderBy(item => item.id).ToList(); } /// /// Function to check if a certain character exists in the font asset. /// /// /// public bool HasCharacter(int character) { if (m_characterDictionary == null) return false; if (m_characterDictionary.ContainsKey(character)) return true; return false; } /// /// Function to check if a certain character exists in the font asset. /// /// /// public bool HasCharacter(char character) { if (m_characterDictionary == null) return false; if (m_characterDictionary.ContainsKey(character)) return true; return false; } /// /// Function to check if a character is contained in the font asset with the option to also check through fallback font assets. /// /// /// /// public bool HasCharacter(char character, bool searchFallbacks) { // Read font asset definition if it hasn't already been done. if (m_characterDictionary == null) { ReadFontDefinition(); if (m_characterDictionary == null) return false; } // Check font asset if (m_characterDictionary.ContainsKey(character)) return true; if (searchFallbacks) { // Check font asset fallbacks if (fallbackFontAssets != null && fallbackFontAssets.Count > 0) { for (int i = 0; i < fallbackFontAssets.Count && fallbackFontAssets[i] != null; i++) { if (fallbackFontAssets[i].HasCharacter_Internal(character, searchFallbacks)) return true; } } // Check general fallback font assets. if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) { for (int i = 0; i < TMP_Settings.fallbackFontAssets.Count && TMP_Settings.fallbackFontAssets[i] != null; i++) { if (TMP_Settings.fallbackFontAssets[i].characterDictionary == null) TMP_Settings.fallbackFontAssets[i].ReadFontDefinition(); if (TMP_Settings.fallbackFontAssets[i].characterDictionary != null && TMP_Settings.fallbackFontAssets[i].HasCharacter_Internal(character, searchFallbacks)) return true; } } // Check TMP Settings Default Font Asset if (TMP_Settings.defaultFontAsset != null) { if (TMP_Settings.defaultFontAsset.characterDictionary == null) TMP_Settings.defaultFontAsset.ReadFontDefinition(); if (TMP_Settings.defaultFontAsset.characterDictionary != null && TMP_Settings.defaultFontAsset.HasCharacter_Internal(character, searchFallbacks)) return true; } } return false; } /// /// Function to check if a character is contained in a font asset with the option to also check through fallback font assets. /// This private implementation does not search the fallback font asset in the TMP Settings file. /// /// /// /// bool HasCharacter_Internal(char character, bool searchFallbacks) { // Read font asset definition if it hasn't already been done. if (m_characterDictionary == null) { ReadFontDefinition(); if (m_characterDictionary == null) return false; } // Check font asset if (m_characterDictionary.ContainsKey(character)) return true; if (searchFallbacks) { // Check Font Asset Fallback fonts. if (fallbackFontAssets != null && fallbackFontAssets.Count > 0) { for (int i = 0; i < fallbackFontAssets.Count && fallbackFontAssets[i] != null; i++) { if (fallbackFontAssets[i].HasCharacter_Internal(character, searchFallbacks)) return true; } } } return false; } /// /// Function to check if certain characters exists in the font asset. Function returns a list of missing characters. /// /// /// public bool HasCharacters(string text, out List missingCharacters) { if (m_characterDictionary == null) { missingCharacters = null; return false; } missingCharacters = new List(); for (int i = 0; i < text.Length; i++) { if (!m_characterDictionary.ContainsKey(text[i])) missingCharacters.Add(text[i]); } if (missingCharacters.Count == 0) return true; return false; } /// /// Function to check if certain characters exists in the font asset. Function returns false if any characters are missing. /// /// String containing the characters to check /// public bool HasCharacters(string text) { if (m_characterDictionary == null) return false; for (int i = 0; i < text.Length; i++) { if (!m_characterDictionary.ContainsKey(text[i])) return false; } return true; } /// /// Function to extract all the characters from a font asset. /// /// /// public static string GetCharacters(TMP_FontAsset fontAsset) { string characters = string.Empty; for (int i = 0; i < fontAsset.m_glyphInfoList.Count; i++) { characters += (char)fontAsset.m_glyphInfoList[i].id; } return characters; } /// /// Function which returns an array that contains all the characters from a font asset. /// /// /// public static int[] GetCharactersArray(TMP_FontAsset fontAsset) { int[] characters = new int[fontAsset.m_glyphInfoList.Count]; for (int i = 0; i < fontAsset.m_glyphInfoList.Count; i++) { characters[i] = fontAsset.m_glyphInfoList[i].id; } return characters; } } }