diff --git a/CHANGELOG.md b/CHANGELOG.md index 2100a64..3fb1b0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ # Changelog These are the release notes for the TextMesh Pro UPM package which was first introduced with Unity 2018.1. Please see the following link for the Release Notes for prior versions of TextMesh Pro. http://digitalnativestudios.com/forum/index.php?topic=1363.0 +## [1.5.0-preview.6] - 2020-03-06 +## [2.1.0-preview.6] +## [3.0.0-preview.6] +### Changes +- Reverted recent change to the TMP_SubMeshUI OnDisable() function that could result in a Missing Reference Exception in the GraphicRaycaster.cs script. See the following [forum post](https://forum.unity.com/threads/version-1-5-0-2-1-0-preview-5-now-available-for-testing.753587/page-2#post-5523412). +- Fixed glyph drawing issue in the Font Asset Inspector Glyph Adjustment Table when Multi Atlas Texture is enabled and the glyph is not located in the main atlas texture or at atlasTextures[0]. +- Added support for <ZWSP> tag which is internally replaced by a zero width space or \u200B. +- Improved line breaking handling when using <NBSP> and / or <NOBR> tags where instead of breaking these line segments per character, they will break at any possible soft breaking space when these segments exceed the width of the text container. +- Improved PreferredHeight calculations and handling when using Text Auto Size. +- Fixed incorrect color being applied to the underline or strikethrough line segments when using and / or tags along with a tag while at the same time applying an Underline or Strikethrough font style on the whole text object. +- Fixed SDF Scale not getting updated when using SetText() with StringBuilder when the lossyScale of the text object changes. Case #1216007 +- Added Non Breaking Space \u00A0 and Soft Hyphen \u00AD to list of synthesized characters in the event they are not present in the source font file. +- Fixed stack overflow issue when using TMP_FontAsset.HasCharacter and TMP_FontAsset.HasCharacters function on font assets that have circular fallback references. Case #1222574 +- Fixed atlas texture not getting set correctly to IsReadable when switching a static font asset to dynamic in the Generation Settings of the Font Asset Inspector. +- Added check for RectTransform.sizeDelta change when OnRectTransformDimensionsChange() is called by the Canvas system to get around potential rounding error that erroneously trigger this callback when the RectTransform is using Stretch Anchor mode. +- As requested by a few users, TMP_FontAsset.faceInfo setter is now public instead of internal. + ## [1.5.0-preview.5] - 2020-02-25 ## [2.1.0-preview.5] ## [3.0.0-preview.5] diff --git a/Scripts/Editor/TMP_FontAssetEditor.cs b/Scripts/Editor/TMP_FontAssetEditor.cs index 6d5fa2c..fd1099b 100644 --- a/Scripts/Editor/TMP_FontAssetEditor.cs +++ b/Scripts/Editor/TMP_FontAssetEditor.cs @@ -419,7 +419,7 @@ public override void OnInspectorGUI() { #if UNITY_EDITOR && UNITY_2018_4_OR_NEWER #if !(UNITY_2018_4_0 || UNITY_2018_4_1 || UNITY_2018_4_2 || UNITY_2018_4_3 || UNITY_2018_4_4) - FontEngineEditorUtilities.SetAtlasTextureIsReadable(tex, false); + FontEngineEditorUtilities.SetAtlasTextureIsReadable(tex, true); #endif #endif } diff --git a/Scripts/Editor/TMP_GlyphPairAdjustmentRecordPropertyDrawer.cs b/Scripts/Editor/TMP_GlyphPairAdjustmentRecordPropertyDrawer.cs index fd4eb3e..69ac6f8 100644 --- a/Scripts/Editor/TMP_GlyphPairAdjustmentRecordPropertyDrawer.cs +++ b/Scripts/Editor/TMP_GlyphPairAdjustmentRecordPropertyDrawer.cs @@ -316,7 +316,7 @@ uint GetUnicodeCharacter (string source) void DrawGlyph(uint glyphIndex, Rect position, SerializedProperty property) { - // Get a reference to the sprite texture + // Get a reference to the font asset TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset; if (fontAsset == null) @@ -324,12 +324,12 @@ void DrawGlyph(uint glyphIndex, Rect position, SerializedProperty property) Glyph glyph; - // Check if glyph currently exists in the atlas texture. + // Check if glyph is present in the atlas texture. if (!fontAsset.glyphLookupTable.TryGetValue(glyphIndex, out glyph)) return; - // Get reference to atlas texture. - int atlasIndex = fontAsset.m_AtlasTextureIndex; + // Get the atlas index of the glyph and lookup its atlas texture + int atlasIndex = glyph.atlasIndex; Texture2D atlasTexture = fontAsset.atlasTextures.Length > atlasIndex ? fontAsset.atlasTextures[atlasIndex] : null; if (atlasTexture == null) @@ -361,18 +361,25 @@ void DrawGlyph(uint glyphIndex, Rect position, SerializedProperty property) GlyphRect glyphRect = glyph.glyphRect; + int padding = fontAsset.atlasPadding; + + int glyphOriginX = glyphRect.x - padding; + int glyphOriginY = glyphRect.y - padding; + int glyphWidth = glyphRect.width + padding * 2; + int glyphHeight = glyphRect.height + padding * 2; + float normalizedHeight = fontAsset.faceInfo.ascentLine - fontAsset.faceInfo.descentLine; float scale = glyphDrawPosition.width / normalizedHeight; // Compute the normalized texture coordinates - Rect texCoords = new Rect((float)glyphRect.x / atlasTexture.width, (float)glyphRect.y / atlasTexture.height, (float)glyphRect.width / atlasTexture.width, (float)glyphRect.height / atlasTexture.height); + Rect texCoords = new Rect((float)glyphOriginX / atlasTexture.width, (float)glyphOriginY / atlasTexture.height, (float)glyphWidth / atlasTexture.width, (float)glyphHeight / atlasTexture.height); if (Event.current.type == EventType.Repaint) { - glyphDrawPosition.x += (glyphDrawPosition.width - glyphRect.width * scale) / 2; - glyphDrawPosition.y += (glyphDrawPosition.height - glyphRect.height * scale) / 2; - glyphDrawPosition.width = glyphRect.width * scale; - glyphDrawPosition.height = glyphRect.height * scale; + glyphDrawPosition.x += (glyphDrawPosition.width - glyphWidth * scale) / 2; + glyphDrawPosition.y += (glyphDrawPosition.height - glyphHeight * scale) / 2; + glyphDrawPosition.width = glyphWidth * scale; + glyphDrawPosition.height = glyphHeight * scale; // Could switch to using the default material of the font asset which would require passing scale to the shader. Graphics.DrawTexture(glyphDrawPosition, atlasTexture, texCoords, 0, 0, 0, 0, new Color(1f, 1f, 1f), mat); @@ -381,4 +388,4 @@ void DrawGlyph(uint glyphIndex, Rect position, SerializedProperty property) } -} \ No newline at end of file +} diff --git a/Scripts/Runtime/TMP_FontAsset.cs b/Scripts/Runtime/TMP_FontAsset.cs index 8e10332..a3e0a55 100644 --- a/Scripts/Runtime/TMP_FontAsset.cs +++ b/Scripts/Runtime/TMP_FontAsset.cs @@ -6,6 +6,7 @@ using UnityEngine.Profiling; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.Linq; #if UNITY_EDITOR && UNITY_2018_4_OR_NEWER @@ -90,7 +91,7 @@ public AtlasPopulationMode atlasPopulationMode public FaceInfo faceInfo { get { return m_FaceInfo; } - internal set { m_FaceInfo = value; } + set { m_FaceInfo = value; } } [SerializeField] internal FaceInfo m_FaceInfo; @@ -220,7 +221,11 @@ public Texture2D[] atlasTextures /// /// /// - public bool isMultiAtlasTexturesEnabled { get { return m_IsMultiAtlasTexturesEnabled; } set { m_IsMultiAtlasTexturesEnabled = value; } } + public bool isMultiAtlasTexturesEnabled + { + get { return m_IsMultiAtlasTexturesEnabled; } + set { m_IsMultiAtlasTexturesEnabled = value; } + } [SerializeField] private bool m_IsMultiAtlasTexturesEnabled; @@ -709,6 +714,22 @@ internal void AddSynthesizedCharactersAndFaceMetrics() m_CharacterLookupDictionary.Add(3, new TMP_Character(3, glyph)); } + // Add Non Breaking Space \u00A0 + if (m_CharacterLookupDictionary.ContainsKey(0x00A0) == false) + { + TMP_Character character; + if (m_CharacterLookupDictionary.TryGetValue(0x0020, out character)) + m_CharacterLookupDictionary.Add(0x00A0, new TMP_Character(0x00A0, character.glyph)); + } + + // Add Soft Hyphen \u00AD + if (m_CharacterLookupDictionary.ContainsKey(0x00AD) == false) + { + TMP_Character character; + if (m_CharacterLookupDictionary.TryGetValue(0x002D, out character)) + m_CharacterLookupDictionary.Add(0x00AD, new TMP_Character(0x00AD, character.glyph)); + } + // Add Arabic Letter Mark \u061C if (m_CharacterLookupDictionary.ContainsKey(0x061C) == false) { @@ -762,7 +783,7 @@ internal void AddSynthesizedCharactersAndFaceMetrics() if (m_CharacterLookupDictionary.ContainsKey(0x2011) == false) { TMP_Character character; - if (m_CharacterLookupDictionary.TryGetValue(45, out character)) + if (m_CharacterLookupDictionary.TryGetValue(0x002D, out character)) m_CharacterLookupDictionary.Add(0x2011, new TMP_Character(0x2011, character.glyph)); } @@ -815,48 +836,35 @@ internal void SortAllTables() SortFontFeatureTable(); } - /// - /// Function to check if a certain character exists in the font asset. + /// HashSet of font asset instance ID used in the process of searching for through fallback font assets for a given character or characters. /// - /// - /// - public bool HasCharacter(int character) - { - if (m_CharacterLookupDictionary == null) - return false; - - if (m_CharacterLookupDictionary.ContainsKey((uint)character)) - return true; - - return false; - } - + private static HashSet k_SearchedFontAssetLookup; /// /// Function to check if a certain character exists in the font asset. /// /// /// - public bool HasCharacter(char character) + public bool HasCharacter(int character) { if (m_CharacterLookupDictionary == null) return false; - if (m_CharacterLookupDictionary.ContainsKey(character)) + if (m_CharacterLookupDictionary.ContainsKey((uint)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) + public bool HasCharacter(char character, bool searchFallbacks = false, bool tryAddCharacter = false) { // Read font asset definition if it hasn't already been done. if (m_CharacterLookupDictionary == null) @@ -872,7 +880,7 @@ public bool HasCharacter(char character, bool searchFallbacks) return true; // Check if font asset is dynamic and if so try to add the requested character to it. - if (m_AtlasPopulationMode == AtlasPopulationMode.Dynamic) + if (tryAddCharacter && m_AtlasPopulationMode == AtlasPopulationMode.Dynamic) { TMP_Character returnedCharacter; @@ -882,13 +890,29 @@ public bool HasCharacter(char character, bool searchFallbacks) if (searchFallbacks) { + // Initialize or clear font asset lookup + if (k_SearchedFontAssetLookup == null) + k_SearchedFontAssetLookup = new HashSet(); + else + k_SearchedFontAssetLookup.Clear(); + + // Add current font asset to lookup + k_SearchedFontAssetLookup.Add(GetInstanceID()); + // Check font asset fallbacks if (fallbackFontAssetTable != null && fallbackFontAssetTable.Count > 0) { for (int i = 0; i < fallbackFontAssetTable.Count && fallbackFontAssetTable[i] != null; i++) { - if (fallbackFontAssetTable[i].HasCharacter_Internal(character, searchFallbacks)) - return true; + TMP_FontAsset fallback = fallbackFontAssetTable[i]; + int fallbackID = fallback.GetInstanceID(); + + // Search fallback if not already contained in lookup + if (k_SearchedFontAssetLookup.Add(fallbackID)) + { + if (fallback.HasCharacter_Internal(character, true, tryAddCharacter)) + return true; + } } } @@ -897,22 +921,30 @@ public bool HasCharacter(char character, bool searchFallbacks) { for (int i = 0; i < TMP_Settings.fallbackFontAssets.Count && TMP_Settings.fallbackFontAssets[i] != null; i++) { - if (TMP_Settings.fallbackFontAssets[i].m_CharacterLookupDictionary == null) - TMP_Settings.fallbackFontAssets[i].ReadFontAssetDefinition(); + TMP_FontAsset fallback = TMP_Settings.fallbackFontAssets[i]; + int fallbackID = fallback.GetInstanceID(); - if (TMP_Settings.fallbackFontAssets[i].m_CharacterLookupDictionary != null && TMP_Settings.fallbackFontAssets[i].HasCharacter_Internal(character, searchFallbacks)) - return true; + // Search fallback if not already contained in lookup + if (k_SearchedFontAssetLookup.Add(fallbackID)) + { + if (fallback.HasCharacter_Internal(character, true, tryAddCharacter)) + return true; + } } } // Check TMP Settings Default Font Asset if (TMP_Settings.defaultFontAsset != null) { - if (TMP_Settings.defaultFontAsset.m_CharacterLookupDictionary == null) - TMP_Settings.defaultFontAsset.ReadFontAssetDefinition(); + TMP_FontAsset fallback = TMP_Settings.defaultFontAsset; + int fallbackID = fallback.GetInstanceID(); - if (TMP_Settings.defaultFontAsset.m_CharacterLookupDictionary != null && TMP_Settings.defaultFontAsset.HasCharacter_Internal(character, searchFallbacks)) - return true; + // Search fallback if it has not already been searched + if (k_SearchedFontAssetLookup.Add(fallbackID)) + { + if (fallback.HasCharacter_Internal(character, true, tryAddCharacter)) + return true; + } } } @@ -926,8 +958,9 @@ public bool HasCharacter(char character, bool searchFallbacks) /// /// /// + /// /// - bool HasCharacter_Internal(uint character, bool searchFallbacks) + bool HasCharacter_Internal(uint character, bool searchFallbacks = false, bool tryAddCharacter = false) { // Read font asset definition if it hasn't already been done. if (m_CharacterLookupDictionary == null) @@ -942,14 +975,30 @@ bool HasCharacter_Internal(uint character, bool searchFallbacks) if (m_CharacterLookupDictionary.ContainsKey(character)) return true; + // Check if fallback is dynamic and if so try to add the requested character to it. + if (tryAddCharacter && atlasPopulationMode == AtlasPopulationMode.Dynamic) + { + TMP_Character returnedCharacter; + + if (TryAddCharacterInternal(character, out returnedCharacter)) + return true; + } + if (searchFallbacks) { // Check Font Asset Fallback fonts. - if (fallbackFontAssetTable != null && fallbackFontAssetTable.Count > 0) + if (fallbackFontAssetTable == null || fallbackFontAssetTable.Count == 0) + return false; + + for (int i = 0; i < fallbackFontAssetTable.Count && fallbackFontAssetTable[i] != null; i++) { - for (int i = 0; i < fallbackFontAssetTable.Count && fallbackFontAssetTable[i] != null; i++) + TMP_FontAsset fallback = fallbackFontAssetTable[i]; + int fallbackID = fallback.GetInstanceID(); + + // Search fallback if it has not already been searched + if (k_SearchedFontAssetLookup.Add(fallbackID)) { - if (fallbackFontAssetTable[i].HasCharacter_Internal(character, searchFallbacks)) + if (fallback.HasCharacter_Internal(character, true, tryAddCharacter)) return true; } } @@ -963,6 +1012,7 @@ bool HasCharacter_Internal(uint character, bool searchFallbacks) /// 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) { @@ -992,8 +1042,9 @@ public bool HasCharacters(string text, out List missingCharacters) /// /// /// + /// /// - public bool HasCharacters(string text, out uint[] missingCharacters, bool searchFallbacks = false) + public bool HasCharacters(string text, out uint[] missingCharacters, bool searchFallbacks = false, bool tryAddCharacter = false) { missingCharacters = null; @@ -1014,55 +1065,86 @@ public bool HasCharacters(string text, out uint[] missingCharacters, bool search bool isMissingCharacter = true; uint character = text[i]; - if (!m_CharacterLookupDictionary.ContainsKey(character)) + if (m_CharacterLookupDictionary.ContainsKey(character)) + continue; + + // Check if fallback is dynamic and if so try to add the requested character to it. + if (tryAddCharacter && atlasPopulationMode == AtlasPopulationMode.Dynamic) + { + TMP_Character returnedCharacter; + + if (TryAddCharacterInternal(character, out returnedCharacter)) + continue; + } + + if (searchFallbacks) { - if (searchFallbacks) + // Initialize or clear font asset lookup + if (k_SearchedFontAssetLookup == null) + k_SearchedFontAssetLookup = new HashSet(); + else + k_SearchedFontAssetLookup.Clear(); + + // Add current font asset to lookup + k_SearchedFontAssetLookup.Add(GetInstanceID()); + + // Check font asset fallbacks + if (fallbackFontAssetTable != null && fallbackFontAssetTable.Count > 0) { - // Check font asset fallbacks - if (fallbackFontAssetTable != null && fallbackFontAssetTable.Count > 0) + for (int j = 0; j < fallbackFontAssetTable.Count && fallbackFontAssetTable[j] != null; j++) { - for (int j = 0; j < fallbackFontAssetTable.Count && fallbackFontAssetTable[j] != null; j++) - { - if (fallbackFontAssetTable[j].HasCharacter_Internal(character, searchFallbacks)) - { - isMissingCharacter = false; - break; - } - } - } + TMP_FontAsset fallback = fallbackFontAssetTable[j]; + int fallbackID = fallback.GetInstanceID(); - // Check general fallback font assets. - if (isMissingCharacter && TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) - { - for (int j = 0; j < TMP_Settings.fallbackFontAssets.Count && TMP_Settings.fallbackFontAssets[j] != null; j++) + // Search fallback if it has not already been searched + if (k_SearchedFontAssetLookup.Add(fallbackID)) { - if (TMP_Settings.fallbackFontAssets[j].m_CharacterLookupDictionary == null) - TMP_Settings.fallbackFontAssets[j].ReadFontAssetDefinition(); - - if (TMP_Settings.fallbackFontAssets[j].m_CharacterLookupDictionary != null && TMP_Settings.fallbackFontAssets[j].HasCharacter_Internal(character, searchFallbacks)) - { - isMissingCharacter = false; - break; - } + if (fallback.HasCharacter_Internal(character, true, tryAddCharacter) == false) + continue; + + isMissingCharacter = false; + break; } } + } - // Check TMP Settings Default Font Asset - if (isMissingCharacter && TMP_Settings.defaultFontAsset != null) + // Check general fallback font assets. + if (isMissingCharacter && TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) + { + for (int j = 0; j < TMP_Settings.fallbackFontAssets.Count && TMP_Settings.fallbackFontAssets[j] != null; j++) { - if (TMP_Settings.defaultFontAsset.m_CharacterLookupDictionary == null) - TMP_Settings.defaultFontAsset.ReadFontAssetDefinition(); + TMP_FontAsset fallback = TMP_Settings.fallbackFontAssets[j]; + int fallbackID = fallback.GetInstanceID(); + + // Search fallback if it has not already been searched + if (k_SearchedFontAssetLookup.Add(fallbackID)) + { + if (fallback.HasCharacter_Internal(character, true, tryAddCharacter) == false) + continue; - if (TMP_Settings.defaultFontAsset.m_CharacterLookupDictionary != null && TMP_Settings.defaultFontAsset.HasCharacter_Internal(character, searchFallbacks)) isMissingCharacter = false; + break; + } } } - if (isMissingCharacter) + // Check TMP Settings Default Font Asset + if (isMissingCharacter && TMP_Settings.defaultFontAsset != null) { - s_MissingCharacterList.Add(character); + TMP_FontAsset fallback = TMP_Settings.defaultFontAsset; + int fallbackID = fallback.GetInstanceID(); + + // Search fallback if it has not already been searched + if (k_SearchedFontAssetLookup.Add(fallbackID)) + { + if (fallback.HasCharacter_Internal(character, true, tryAddCharacter)) + isMissingCharacter = false; + } } } + + if (isMissingCharacter) + s_MissingCharacterList.Add(character); } if (s_MissingCharacterList.Count > 0) @@ -1130,6 +1212,23 @@ public static int[] GetCharactersArray(TMP_FontAsset fontAsset) return characters; } + /// + /// Internal function used to get the glyph index for the given Unicode. + /// + /// + /// + internal uint GetGlyphIndex(uint unicode) + { + // Check if glyph already exists in font asset. + if (m_CharacterLookupDictionary.ContainsKey(unicode)) + return m_CharacterLookupDictionary[unicode].glyphIndex; + + // Load font face. + if (FontEngine.LoadFontFace(sourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success) + return 0; + + return FontEngine.GetGlyphIndex(unicode); + } // ================================================================================ // Properties and functions related to character and glyph additions as well as @@ -1143,6 +1242,65 @@ public static int[] GetCharactersArray(TMP_FontAsset fontAsset) private static List k_FontAssets_AtlasTexturesUpdateQueue = new List(); private static HashSet k_FontAssets_AtlasTexturesUpdateQueueLookup = new HashSet(); + /// + /// + /// + /// + internal static void RegisterFontAssetForFontFeatureUpdate(TMP_FontAsset fontAsset) + { + int instanceID = fontAsset.instanceID; + + if (k_FontAssets_FontFeaturesUpdateQueueLookup.Add(instanceID)) + k_FontAssets_FontFeaturesUpdateQueue.Add(fontAsset); + } + + /// + /// Function called to update the font atlas texture and character data of font assets to which + /// new characters were added. + /// + internal static void UpdateFontFeaturesForFontAssetsInQueue() + { + int count = k_FontAssets_FontFeaturesUpdateQueue.Count; + + for (int i = 0; i < count; i++) + k_FontAssets_FontFeaturesUpdateQueue[i].UpdateGlyphAdjustmentRecords(); + + if (count > 0) + { + k_FontAssets_FontFeaturesUpdateQueue.Clear(); + k_FontAssets_FontFeaturesUpdateQueueLookup.Clear(); + } + } + + /// + /// + /// + /// + internal static void RegisterFontAssetForAtlasTextureUpdate(TMP_FontAsset fontAsset) + { + int instanceID = fontAsset.instanceID; + + if (k_FontAssets_AtlasTexturesUpdateQueueLookup.Add(instanceID)) + k_FontAssets_AtlasTexturesUpdateQueue.Add(fontAsset); + } + + /// + /// + /// + internal static void UpdateAtlasTexturesForFontAssetsInQueue() + { + int count = k_FontAssets_AtlasTexturesUpdateQueueLookup.Count; + + for (int i = 0; i < count; i++) + k_FontAssets_AtlasTexturesUpdateQueue[i].TryAddGlyphsToAtlasTextures(); + + if (count > 0) + { + k_FontAssets_AtlasTexturesUpdateQueue.Clear(); + k_FontAssets_AtlasTexturesUpdateQueueLookup.Clear(); + } + } + /// /// /// @@ -1281,18 +1439,12 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes, bool i } // Make sure glyph index has not already been added to list of glyphs to add. - if (m_GlyphsToAddLookup.Contains(glyphIndex) == false) - { - m_GlyphsToAddLookup.Add(glyphIndex); + if (m_GlyphsToAddLookup.Add(glyphIndex)) m_GlyphsToAdd.Add(glyphIndex); - } // Make sure unicode / character has not already been added. - if (m_CharactersToAddLookup.Contains(unicode) == false) - { - m_CharactersToAddLookup.Add(unicode); + if (m_CharactersToAddLookup.Add(unicode)) m_CharactersToAdd.Add(character); - } } if (m_GlyphsToAdd.Count == 0) @@ -1481,18 +1633,12 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo } // Make sure glyph index has not already been added to list of glyphs to add. - if (m_GlyphsToAddLookup.Contains(glyphIndex) == false) - { - m_GlyphsToAddLookup.Add(glyphIndex); + if (m_GlyphsToAddLookup.Add(glyphIndex)) m_GlyphsToAdd.Add(glyphIndex); - } // Make sure unicode / character has not already been added. - if (m_CharactersToAddLookup.Contains(unicode) == false) - { - m_CharactersToAddLookup.Add(unicode); + if (m_CharactersToAddLookup.Add(unicode)) m_CharactersToAdd.Add(character); - } } if (m_GlyphsToAdd.Count == 0) @@ -2164,25 +2310,6 @@ void SetupNewAtlasTexture() } - /// - /// Internal function used to get the glyph index for the given Unicode. - /// - /// - /// - internal uint GetGlyphIndex(uint unicode) - { - // Check if glyph already exists in font asset. - if (m_CharacterLookupDictionary.ContainsKey(unicode)) - return m_CharacterLookupDictionary[unicode].glyphIndex; - - // Load font face. - if (FontEngine.LoadFontFace(sourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success) - return 0; - - return FontEngine.GetGlyphIndex(unicode); - } - - /// /// Not used currently /// @@ -2253,68 +2380,9 @@ internal void UpdateAtlasTexture() #endif } - internal static void RegisterFontAssetForFontFeatureUpdate(TMP_FontAsset fontAsset) - { - int instanceID = fontAsset.instanceID; - - if (k_FontAssets_FontFeaturesUpdateQueueLookup.Add(instanceID)) - k_FontAssets_FontFeaturesUpdateQueue.Add(fontAsset); - } - - internal static void UpdateFontFeaturesForFontAssetsInQueue() - { - int count = k_FontAssets_FontFeaturesUpdateQueue.Count; - - for (int i = 0; i < count; i++) - k_FontAssets_FontFeaturesUpdateQueue[i].UpdateGlyphAdjustmentRecords(); - - if (count > 0) - { - k_FontAssets_FontFeaturesUpdateQueue.Clear(); - k_FontAssets_FontFeaturesUpdateQueueLookup.Clear(); - } - } - - internal static void RegisterFontAssetForAtlasTextureUpdate(TMP_FontAsset fontAsset) - { - int instanceID = fontAsset.instanceID; - - if (k_FontAssets_AtlasTexturesUpdateQueueLookup.Add(instanceID)) - k_FontAssets_AtlasTexturesUpdateQueue.Add(fontAsset); - } - - internal static void UpdateAtlasTexturesForFontAssetsInQueue() - { - int count = k_FontAssets_AtlasTexturesUpdateQueueLookup.Count; - - for (int i = 0; i < count; i++) - k_FontAssets_AtlasTexturesUpdateQueue[i].TryAddGlyphsToAtlasTextures(); - - if (count > 0) - { - k_FontAssets_AtlasTexturesUpdateQueue.Clear(); - k_FontAssets_AtlasTexturesUpdateQueueLookup.Clear(); - } - } - /// - /// Function called to update the font atlas texture and character data of font assets to which - /// new characters were added. + /// /// - public static void UpdateFontAssets() - { - int count = k_FontAssets_FontFeaturesUpdateQueue.Count; - - for (int i = 0; i < count; i++) - k_FontAssets_FontFeaturesUpdateQueue[i].UpdateGlyphAdjustmentRecords(); - - if (count > 0) - { - k_FontAssets_FontFeaturesUpdateQueue.Clear(); - k_FontAssets_FontFeaturesUpdateQueueLookup.Clear(); - } - } - internal void UpdateGlyphAdjustmentRecords() { Profiler.BeginSample("TMP.UpdateGlyphAdjustmentRecords"); @@ -2353,14 +2421,9 @@ internal void UpdateGlyphAdjustmentRecords() m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(pairKey, record); } - #if UNITY_EDITOR - m_FontFeatureTable.SortGlyphPairAdjustmentRecords(); - #endif - Profiler.EndSample(); } - /// /// Function used for debugging and performance testing. /// @@ -2398,10 +2461,6 @@ internal void UpdateGlyphAdjustmentRecords(uint[] glyphIndexes) m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(pairKey, record); } - #if UNITY_EDITOR - m_FontFeatureTable.SortGlyphPairAdjustmentRecords(); - #endif - Profiler.EndSample(); } @@ -2592,7 +2651,9 @@ internal void UpdateFontAssetData() Profiler.EndSample(); } - + /// + /// + /// internal void ClearFontAssetTables() { // Clear glyph and character tables diff --git a/Scripts/Runtime/TMP_SubMeshUI.cs b/Scripts/Runtime/TMP_SubMeshUI.cs index c3c43aa..cd6ad3b 100644 --- a/Scripts/Runtime/TMP_SubMeshUI.cs +++ b/Scripts/Runtime/TMP_SubMeshUI.cs @@ -35,7 +35,7 @@ public TMP_SpriteAsset spriteAsset /// - /// + /// /// public override Texture mainTexture { @@ -88,7 +88,7 @@ public Material sharedMaterial /// - /// + /// /// public Material fallbackMaterial { @@ -240,7 +240,7 @@ public static TMP_SubMeshUI AddSubTextObject(TextMeshProUGUI textComponent, Mate /// - /// + /// /// protected override void OnEnable() { @@ -274,13 +274,7 @@ protected override void OnEnable() protected override void OnDisable() { //Debug.Log("*** SubObject OnDisable() ***"); - //base.OnDisable(); - - //m_canvasRenderer.Clear(); - //TMP_UpdateRegistry.UnRegisterCanvasElementForRebuild(this); - - if (canvasRenderer != null) - canvasRenderer.Clear(); + base.OnDisable(); if (m_MaskMaterial != null) { @@ -385,7 +379,7 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) } else if (targetMaterialID == maskingMaterialID) { - // Update the padding + // Update the padding GetPaddingForMaterial(mat); m_sharedMaterial.CopyPropertiesFromMaterial(mat); @@ -484,7 +478,7 @@ void ON_TMP_SETTINGS_CHANGED() #endif /// - /// + /// /// protected override void OnTransformParentChanged() { @@ -552,7 +546,7 @@ public float GetPaddingForMaterial(Material mat) /// - /// + /// /// /// /// @@ -563,7 +557,7 @@ public void UpdateMeshPadding(bool isExtraPadding, bool isUsingBold) /// - /// + /// /// public override void SetAllDirty() { @@ -574,7 +568,7 @@ public override void SetAllDirty() /// - /// + /// /// public override void SetVerticesDirty() { @@ -591,7 +585,7 @@ public override void SetVerticesDirty() /// - /// + /// /// public override void SetLayoutDirty() { @@ -600,7 +594,7 @@ public override void SetLayoutDirty() /// - /// + /// /// public override void SetMaterialDirty() { @@ -624,7 +618,7 @@ public override void SetMaterialDirty() /// - /// + /// /// public void SetPivotDirty() { @@ -654,7 +648,7 @@ public override void Cull(Rect clipRect, bool validRect) /// - /// + /// /// protected override void UpdateGeometry() { @@ -664,7 +658,7 @@ protected override void UpdateGeometry() /// - /// + /// /// /// public override void Rebuild(CanvasUpdate update) @@ -689,7 +683,7 @@ public void RefreshMaterial() /// - /// + /// /// protected override void UpdateMaterial() { @@ -730,7 +724,7 @@ public override void RecalculateClipping() /// - /// + /// /// public override void RecalculateMasking() { @@ -778,7 +772,7 @@ Material GetMaterial(Material mat) m_sharedMaterial = m_material; - // Compute and Set new padding values for this new material. + // Compute and Set new padding values for this new material. m_padding = GetPaddingForMaterial(); SetVerticesDirty(); diff --git a/Scripts/Runtime/TMP_Text.cs b/Scripts/Runtime/TMP_Text.cs index 041ddea..9cbd9ef 100644 --- a/Scripts/Runtime/TMP_Text.cs +++ b/Scripts/Runtime/TMP_Text.cs @@ -100,6 +100,7 @@ public enum TextOverflowModes { Overflow = 0, Ellipsis = 1, Masking = 2, Truncat public enum MaskingOffsetMode { Percentage = 0, Pixel = 1 }; public enum TextureMappingOptions { Character = 0, Line = 1, Paragraph = 2, MatchAspect = 3 }; + [Flags] public enum FontStyles { Normal = 0x0, Bold = 0x1, Italic = 0x2, Underline = 0x4, LowerCase = 0x8, UpperCase = 0x10, SmallCaps = 0x20, Strikethrough = 0x40, Superscript = 0x80, Subscript = 0x100, Highlight = 0x200 }; public enum FontWeight { Thin = 100, ExtraLight = 200, Light = 300, Regular = 400, Medium = 500, SemiBold = 600, Bold = 700, Heavy = 800, Black = 900 }; @@ -1185,6 +1186,12 @@ public bool isUsingLegacyAnimationComponent protected RectTransform m_rectTransform; + /// + /// Used to track potential changes in RectTransform size to allow us to ignore OnRectTransformDimensionsChange getting called due to rounding errors when using Stretch Anchors. + /// + protected Vector2 m_RectTransformSizeDelta; + + /// /// Enables control over setting the size of the text container to match the text object. /// @@ -1507,7 +1514,7 @@ protected struct SpecialCharacter protected WordWrapState m_SavedLineState = new WordWrapState(); protected WordWrapState m_SavedEllipsisState = new WordWrapState(); protected WordWrapState m_SavedLastValidState = new WordWrapState(); - //protected WordWrapState m_SavedInvalidLineBreakingState = new WordWrapState(); + protected WordWrapState m_SavedSoftLineBreakState = new WordWrapState(); // Fields whose state is saved in conjunction with text parsing and word wrapping. protected int m_characterCount; @@ -1523,7 +1530,8 @@ protected struct SpecialCharacter protected float m_PageAscender; protected float m_maxAscender; protected float m_maxCapHeight; - protected float m_maxDescender; + protected float m_ElementAscender; + protected float m_ElementDescender; protected float m_maxLineAscender; protected float m_maxLineDescender; protected float m_startOfLineAscender; @@ -2083,26 +2091,38 @@ public void SetText(string text, float arg0, float arg1, float arg2, float arg3, } } - // Read Padding value + // Read formatting for integral part of the value if (readFlag == 2) { // Skip ':' separator if (c == ':') continue; - // Done reading padding value + // Done reading integral formatting and value if (c == '.') { readFlag = 3; continue; } + if (c == '#') + { + // do something + continue; + } + if (c == '0') { padding += 1; continue; } + if (c == ',') + { + // Use commas in the integral value + continue; + } + // Legacy mode if (c >= '1' && c <= '9') { @@ -2292,6 +2312,16 @@ public void SetCharArray(char[] sourceText) continue; } + else if (IsTagName(ref sourceText, "", i)) + { + if (writeIndex == m_InternalParsingBuffer.Length) ResizeInternalArray(ref m_InternalParsingBuffer); + + m_InternalParsingBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref sourceText, "", i)) + { + if (writeIndex == m_InternalParsingBuffer.Length) ResizeInternalArray(ref m_InternalParsingBuffer); + + m_InternalParsingBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref sourceText, "", i)) + { + if (writeIndex == m_InternalParsingBuffer.Length) ResizeInternalArray(ref m_InternalParsingBuffer); + + m_InternalParsingBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref sourceText, "", i)) + { + if (writeIndex == m_InternalParsingBuffer.Length) ResizeInternalArray(ref m_InternalParsingBuffer); + + m_InternalParsingBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref sourceText, "", i)) + { + if (writeIndex == internalParsingArray.Length) ResizeInternalArray(ref internalParsingArray); + + internalParsingArray[writeIndex].unicode = 0x200B; + internalParsingArray[writeIndex].stringIndex = i; + internalParsingArray[writeIndex].length = 1; + + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref sourceText, "", i)) + { + if (writeIndex == internalParsingArray.Length) ResizeInternalArray(ref internalParsingArray); + + internalParsingArray[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref sourceText, "", i)) + { + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref tagDefinition, "", i)) + { + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref tagDefinition, "", i)) + { + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref tagDefinition, "", i)) + { + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref tagDefinition, "", i)) + { + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref tagDefinition, "", i)) + { + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref tagDefinition, "", i)) + { + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref tagDefinition, "", i)) + { + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref tagDefinition, "", i)) + { + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref tagDefinition, "", i)) + { + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0x200B; + writeIndex += 1; + i += 5; + + continue; + } else if (IsTagName(ref tagDefinition, "(ref T[] array, int size) private readonly decimal[] k_Power = { 5e-1m, 5e-2m, 5e-3m, 5e-4m, 5e-5m, 5e-6m, 5e-7m, 5e-8m, 5e-9m, 5e-10m }; // Used by FormatText to enable rounding and avoid using Mathf.Pow. - protected void AddFloatToCharArray(float value, int padding, int precision, ref int writeIndex) + void AddFloatToCharArray(float value, int padding, int precision, ref int writeIndex) { if (value < 0) { @@ -4634,7 +4817,7 @@ protected void AddFloatToCharArray(float value, int padding, int precision, ref /// /// /// - protected void AddIntegerToCharArray(double number, int padding, ref int writeIndex) + void AddIntegerToCharArray(double number, int padding, ref int writeIndex) { int integralCount = 0; int i = writeIndex; @@ -4794,7 +4977,7 @@ protected float GetPreferredWidth() } m_AutoSizeIterationCount = 0; - float preferredWidth = CalculatePreferredValues(fontSize, margin, true, false).x; + float preferredWidth = CalculatePreferredValues(ref fontSize, margin, false, false).x; m_isPreferredWidthDirty = false; @@ -4809,7 +4992,7 @@ protected float GetPreferredWidth() /// /// /// - protected float GetPreferredWidth(Vector2 margin) + float GetPreferredWidth(Vector2 margin) { float fontSize = m_enableAutoSizing ? m_fontSizeMax : m_fontSize; @@ -4819,7 +5002,7 @@ protected float GetPreferredWidth(Vector2 margin) m_charWidthAdjDelta = 0; m_AutoSizeIterationCount = 0; - float preferredWidth = CalculatePreferredValues(fontSize, margin, true, false).x; + float preferredWidth = CalculatePreferredValues(ref fontSize, margin, false, false).x; //Debug.Log("GetPreferredWidth() Called. Returning width of " + preferredWidth); @@ -4850,8 +5033,19 @@ protected float GetPreferredHeight() ParseInputText(); } + // Reset Text Auto Size iteration tracking. + m_IsAutoSizePointSizeSet = false; m_AutoSizeIterationCount = 0; - float preferredHeight = CalculatePreferredValues(fontSize, margin, !m_enableAutoSizing, m_enableWordWrapping).y; + + // The CalculatePreferredValues function is potentially called repeatedly when text auto size is enabled. + // This is a revised implementation to remove the use of recursion which could potentially result in stack overflow issues. + float preferredHeight = 0; + + while (m_IsAutoSizePointSizeSet == false) + { + preferredHeight = CalculatePreferredValues(ref fontSize, margin, m_enableAutoSizing, m_enableWordWrapping).y; + m_AutoSizeIterationCount += 1; + } m_isPreferredHeightDirty = false; @@ -4866,7 +5060,7 @@ protected float GetPreferredHeight() /// /// /// - protected float GetPreferredHeight(Vector2 margin) + float GetPreferredHeight(Vector2 margin) { float fontSize = m_enableAutoSizing ? m_fontSizeMax : m_fontSize; @@ -4875,8 +5069,19 @@ protected float GetPreferredHeight(Vector2 margin) m_maxFontSize = m_fontSizeMax; m_charWidthAdjDelta = 0; + // Reset Text Auto Size iteration tracking. + m_IsAutoSizePointSizeSet = false; m_AutoSizeIterationCount = 0; - float preferredHeight = CalculatePreferredValues(fontSize, margin, true, m_enableWordWrapping).y; + + // The CalculatePreferredValues function is potentially called repeatedly when text auto size is enabled. + // This is a revised implementation to remove the use of recursion which could potentially result in stack overflow issues. + float preferredHeight = 0; + + while (m_IsAutoSizePointSizeSet == false) + { + preferredHeight = CalculatePreferredValues(ref fontSize, margin, m_enableAutoSizing, m_enableWordWrapping).y; + m_AutoSizeIterationCount += 1; + } //Debug.Log("GetPreferredHeight() Called. Returning height of " + preferredHeight); @@ -4908,7 +5113,7 @@ public Vector2 GetRenderedValues(bool onlyVisibleCharacters) /// Method returning the rendered width of the text object. /// /// - protected float GetRenderedWidth() + float GetRenderedWidth() { return GetRenderedValues().x; } @@ -4926,7 +5131,7 @@ protected float GetRenderedWidth(bool onlyVisibleCharacters) /// Method returning the rendered height of the text object. /// /// - protected float GetRenderedHeight() + float GetRenderedHeight() { return GetRenderedValues().y; } @@ -4945,7 +5150,7 @@ protected float GetRenderedHeight(bool onlyVisibleCharacters) /// Method to calculate the preferred width and height of the text object. /// /// - protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector2 marginSize, bool ignoreTextAutoSizing, bool isWordWrappingEnabled) + protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 marginSize, bool isTextAutoSizingEnabled, bool isWordWrappingEnabled) { //Debug.Log("*** CalculatePreferredValues() ***"); // ***** Frame: " + Time.frameCount); @@ -4956,12 +5161,14 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector { Debug.LogWarning("Can't Generate Mesh! No Font Asset has been assigned to Object ID: " + this.GetInstanceID()); + m_IsAutoSizePointSizeSet = true; return Vector2.zero; } // Early exit if we don't have any Text to generate. if (m_InternalParsingBuffer == null || m_InternalParsingBuffer.Length == 0 || m_InternalParsingBuffer[0].unicode == (char)0) { + m_IsAutoSizePointSizeSet = true; return Vector2.zero; } @@ -4978,23 +5185,20 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Calculate the scale of the font based on selected font size and sampling point size. // baseScale is calculated using the font asset assigned to the text object. - float baseScale = m_fontScale = (defaultFontSize / m_fontAsset.faceInfo.pointSize * m_fontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); + float baseScale = m_fontScale = (fontSize / m_fontAsset.faceInfo.pointSize * m_fontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); float currentElementScale = baseScale; - float currentEmScale = m_fontSize * 0.01f * (m_isOrthographic ? 1 : 0.1f); + float currentEmScale = fontSize * 0.01f * (m_isOrthographic ? 1 : 0.1f); m_fontScaleMultiplier = 1; - m_currentFontSize = defaultFontSize; + m_currentFontSize = fontSize; m_sizeStack.SetDefault(m_currentFontSize); - - int charCode = 0; // Holds the character code of the currently being processed character. + float fontSizeDelta = 0; m_FontStyleInternal = m_fontStyle; // Set the default style. m_lineJustification = m_HorizontalAlignment; // m_textAlignment; // Sets the line justification mode to match editor alignment. m_lineJustificationStack.SetDefault(m_lineJustification); - float boldSpacingAdjustment = 0; - m_baselineOffset = 0; // Used by subscript characters. m_baselineOffsetStack.Clear(); @@ -5045,21 +5249,23 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Tracking of the highest Ascender m_maxCapHeight = 0; m_maxAscender = 0; - m_maxDescender = 0; + m_ElementDescender = 0; float maxVisibleDescender = 0; bool isMaxVisibleDescenderSet = false; - // Initialize struct to track states of word wrapping bool isFirstWordOfLine = true; - bool isLastBreakingChar = false; + m_isNonBreakingSpace = false; + //bool isLastBreakingChar = false; + bool isLastCharacterCJK = false; + //int lastSoftLineBreak = 0; CharacterSubstitution characterToSubstitute = new CharacterSubstitution(-1, 0); bool isSoftHyphenIgnored = false; - bool isInjectingCharacter = false; WordWrapState internalWordWrapState = new WordWrapState(); WordWrapState internalLineState = new WordWrapState(); + WordWrapState internalSoftLineBreak = new WordWrapState(); // Counter to prevent recursive lockup when computing preferred values. m_AutoSizeIterationCount += 1; @@ -5067,7 +5273,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Parse through Character buffer to read HTML tags and begin creating mesh. for (int i = 0; i < m_InternalParsingBuffer.Length && m_InternalParsingBuffer[i].unicode != 0; i++) { - charCode = m_InternalParsingBuffer[i].unicode; + int charCode = m_InternalParsingBuffer[i].unicode; // Parse Rich Text Tag #region Parse Rich Text Tag @@ -5102,7 +5308,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Handle potential character substitutions #region Character Substitutions - isInjectingCharacter = false; + bool isInjectingCharacter = false; if (characterToSubstitute.index == m_characterCount) { @@ -5254,7 +5460,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Handle Soft Hyphen #region Handle Soft Hyphen - float unModifiedScale = currentElementScale; + float currentElementUnmodifiedScale = currentElementScale; if (charCode == 0xAD || charCode == 0x03) currentElementScale = 0; #endregion @@ -5332,34 +5538,45 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Set Padding based on selected font style #region Handle Style Padding + float boldSpacingAdjustment = 0; if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold)) // Checks for any combination of Bold Style. - { boldSpacingAdjustment = m_currentFontAsset.boldSpacing; - } - else - { - boldSpacingAdjustment = 0; - } #endregion Handle Style Padding m_internalCharacterInfo[m_characterCount].baseLine = 0 - m_lineOffset + m_baselineOffset; - // Compute and save text element Ascender and maximum line Ascender. + // Compute text metrics #region Compute Ascender & Descender values + // Element Ascender in line space float elementAscender = m_textElementType == TMP_TextElementType.Character ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementAscentLine * spriteScale + m_baselineOffset; - m_internalCharacterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; + if (isInjectingCharacter && charCode != 0x03) + elementAscender = m_maxLineAscender; - // Compute and save text element Descender and maximum line Descender. + // Element Descender in line space float elementDescender = m_textElementType == TMP_TextElementType.Character ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementDescentLine * spriteScale + m_baselineOffset; - float elementDescenderII = m_internalCharacterInfo[m_characterCount].descender = elementDescender - m_lineOffset; + if (isInjectingCharacter && charCode != 0x03) + elementDescender = m_maxLineDescender; + + // Element Ascender and Descender in object space + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + { + m_ElementAscender = m_internalCharacterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; + m_ElementDescender = m_internalCharacterInfo[m_characterCount].descender = elementDescender - m_lineOffset; + } + else + { + m_ElementAscender = m_internalCharacterInfo[m_characterCount].ascender = m_maxLineAscender - m_lineOffset; + m_ElementDescender = m_internalCharacterInfo[m_characterCount].descender = m_maxLineDescender - m_lineOffset; + } - if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) + // Max line ascender and descender in line space + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) { m_maxLineAscender = elementAscender > m_maxLineAscender ? elementAscender : m_maxLineAscender; m_maxLineDescender = elementDescender < m_maxLineDescender ? elementDescender : m_maxLineDescender; @@ -5368,37 +5585,38 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Adjust maxLineAscender and maxLineDescender if style is superscript or subscript if ((m_FontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript || (m_FontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript) { - float baseAscender = (elementAscender - m_baselineOffset) / m_currentFontAsset.faceInfo.subscriptSize; + float baseAscender = (elementAscender - m_baselineOffset) / m_currentFontAsset.m_FaceInfo.subscriptSize; elementAscender = m_maxLineAscender; m_maxLineAscender = baseAscender > m_maxLineAscender ? baseAscender : m_maxLineAscender; - float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.faceInfo.subscriptSize; - elementDescender = m_maxLineDescender; + float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.m_FaceInfo.subscriptSize; + //elementDescender = m_maxLineDescender; m_maxLineDescender = baseDescender < m_maxLineDescender ? baseDescender : m_maxLineDescender; } + // Max text object ascender and cap height if (m_lineNumber == 0 || m_isNewPage) { - if (charCode == 0x0A && m_characterCount != m_firstCharacterOfLine) - { - // Skip Line Feed that are not at the start of a line. - } - else + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) { m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender; m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier); } } - //if (m_lineOffset == 0) - // pageAscender = pageAscender > elementAscender ? pageAscender : elementAscender; + // Page ascender + if (m_lineOffset == 0) + { + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + m_PageAscender = m_PageAscender > elementAscender ? m_PageAscender : elementAscender; + } #endregion bool isJustifiedOrFlush = (m_lineJustification & HorizontalAlignmentOptions.Flush) == HorizontalAlignmentOptions.Flush || (m_lineJustification & HorizontalAlignmentOptions.Justified) == HorizontalAlignmentOptions.Justified; // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN. #region Handle Visible Characters - if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007 || (char.IsWhiteSpace((char)charCode) == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) + if (charCode == 9 || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) { //float marginLeft = m_marginLeft; //float marginRight = m_marginRight; @@ -5406,14 +5624,14 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Injected characters do not override margins //if (isInjectingCharacter) //{ - //marginLeft = m_textInfo.lineInfo[m_lineNumber].marginLeft; - //marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight; + // marginLeft = m_textInfo.lineInfo[m_lineNumber].marginLeft; + // marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight; //} widthOfTextArea = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - m_marginLeft - m_marginRight, m_width) : marginWidth + 0.0001f - m_marginLeft - m_marginRight; // Calculate the line breaking width of the text. - textWidth = Mathf.Abs(m_xAdvance) + currentGlyphMetrics.horizontalAdvance * (1 - m_charWidthAdjDelta) * (charCode != 0xAD ? currentElementScale : unModifiedScale); + textWidth = Mathf.Abs(m_xAdvance) + currentGlyphMetrics.horizontalAdvance * (1 - m_charWidthAdjDelta) * (charCode == 0xAD ? currentElementUnmodifiedScale : currentElementScale); int testedCharacterCount = m_characterCount; @@ -5429,7 +5647,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Replace Soft Hyphen by Hyphen Minus 0x2D #region Handle Soft Hyphenation - if (m_internalCharacterInfo[m_characterCount - 1].character == 0xAD && isSoftHyphenIgnored == false) + if (m_internalCharacterInfo[m_characterCount - 1].character == 0xAD && isSoftHyphenIgnored == false && m_overflowMode == TextOverflowModes.Overflow) { characterToSubstitute.index = m_characterCount - 1; characterToSubstitute.unicode = 0x2D; @@ -5449,14 +5667,63 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector } #endregion - // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well. + // Adjust character spacing before breaking up word if auto size is enabled + #region Handle Text Auto Size (if word wrapping is no longer possible) + if (isTextAutoSizingEnabled && isFirstWordOfLine) + { + // Handle Character Width Adjustments + #region Character Width Adjustments + if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) + { + float adjustedTextWidth = textWidth; + + // Determine full width of the text + if (m_charWidthAdjDelta > 0) + adjustedTextWidth /= 1f - m_charWidthAdjDelta; + + float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f); + m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth; + m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100); + + //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%"); + return Vector2.zero; + } + #endregion + + // Handle Text Auto-sizing resulting from text exceeding vertical bounds. + #region Text Auto-Sizing (Text greater than vertical bounds) + if (fontSize > m_fontSizeMin && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) + { + m_maxFontSize = fontSize; + + float sizeDelta = Mathf.Max((fontSize - m_minFontSize) / 2, 0.05f); + fontSize -= sizeDelta; + fontSize = Mathf.Max((int)(fontSize * 20 + 0.5f) / 20f, m_fontSizeMin); + + //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "]."); + return Vector2.zero; + } + #endregion Text Auto-Sizing + } + #endregion + + // Adjust line spacing if necessary + float baselineAdjustmentDelta = m_maxLineAscender - m_startOfLineAscender; + if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage) + { + //AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, baselineAdjustmentDelta); + m_ElementDescender -= baselineAdjustmentDelta; + m_lineOffset += baselineAdjustmentDelta; + } + + // Calculate line ascender and make sure if last character is superscript or subscript that we check that as well. float lineAscender = m_maxLineAscender - m_lineOffset; float lineDescender = m_maxLineDescender - m_lineOffset; // Update maxDescender and maxVisibleDescender - m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender; + m_ElementDescender = m_ElementDescender < lineDescender ? m_ElementDescender : lineDescender; if (!isMaxVisibleDescenderSet) - maxVisibleDescender = m_maxDescender; + maxVisibleDescender = m_ElementDescender; if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines)) isMaxVisibleDescenderSet = true; @@ -5469,7 +5736,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector renderedWidth += m_xAdvance; if (isWordWrappingEnabled) - renderedHeight = m_maxAscender - m_maxDescender; + renderedHeight = m_maxAscender - m_ElementDescender; else renderedHeight = Mathf.Max(renderedHeight, lineAscender - lineDescender); @@ -5496,6 +5763,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector m_startOfLineAscender = elementAscender; m_xAdvance = 0 + tag_Indent; + //isStartOfNewLine = true; isFirstWordOfLine = true; continue; } @@ -5511,17 +5779,17 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Check if Line Spacing of previous line needs to be adjusted. #region Adjust Line Spacing - if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage) + /*if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage) { float offsetDelta = m_maxLineAscender - m_startOfLineAscender; //AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta); - elementDescenderII -= offsetDelta; + m_ElementDescender -= offsetDelta; m_lineOffset += offsetDelta; m_startOfLineAscender += offsetDelta; internalWordWrapState.lineOffset = m_lineOffset; internalWordWrapState.startOfLineAscender = m_startOfLineAscender; - } + }*/ #endregion @@ -5537,14 +5805,14 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector { m_xAdvance += (m_monoSpacing - monoAdvance + ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment) * currentEmScale) + m_cSpacing) * (1 - m_charWidthAdjDelta); - if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B) + if (isWhiteSpace || charCode == 0x200B) m_xAdvance += m_wordSpacing * currentEmScale; } else { m_xAdvance += ((currentGlyphMetrics.horizontalAdvance + glyphAdjustments.xAdvance) * currentElementScale + (m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta); - if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B) + if (isWhiteSpace || charCode == 0x200B) m_xAdvance += m_wordSpacing * currentEmScale; } #endregion Tabulation & Stops @@ -5569,7 +5837,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) { float offsetDelta = m_maxLineAscender - m_startOfLineAscender; - elementDescenderII -= offsetDelta; + m_ElementDescender -= offsetDelta; m_lineOffset += offsetDelta; } @@ -5578,7 +5846,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector float lineDescender = m_maxLineDescender - m_lineOffset; // Update maxDescender and maxVisibleDescender - m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender; + m_ElementDescender = m_ElementDescender < lineDescender ? m_ElementDescender : lineDescender; // Store PreferredWidth paying attention to linefeed and last character of text. if (m_characterCount == totalCharacterCount - 1) @@ -5589,7 +5857,7 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector renderedWidth = 0; } - renderedHeight = m_maxAscender - m_maxDescender; + renderedHeight = m_maxAscender - m_ElementDescender; // Add new line if not last lines or character. if (charCode == 10 || charCode == 11 || charCode == 0x2D || charCode == 0x2028 || charCode == 0x2029) @@ -5636,33 +5904,70 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector #region Save Word Wrapping State if (isWordWrappingEnabled || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis) { - if ((char.IsWhiteSpace((char)charCode) || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && !m_isNonBreakingSpace && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060) + if ((isWhiteSpace || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && !m_isNonBreakingSpace && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060) { // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored // for Word Wrapping. SaveWordWrappingState(ref internalWordWrapState, i, m_characterCount); isFirstWordOfLine = false; + isLastCharacterCJK = false; + + // Reset soft line breaking point since we now have a valid hard break point. + internalSoftLineBreak.previous_WordBreak = -1; } // Handling for East Asian languages - else if ((charCode > 0x1100 && charCode < 0x11ff || /* Hangul Jamo */ - charCode > 0x2E80 && charCode < 0x9FFF || /* CJK */ - charCode > 0xA960 && charCode < 0xA97F || /* Hangul Jame Extended-A */ - charCode > 0xAC00 && charCode < 0xD7FF || /* Hangul Syllables */ - charCode > 0xF900 && charCode < 0xFAFF || /* CJK Compatibility Ideographs */ - charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */ - charCode > 0xFF00 && charCode < 0xFFEF) /* CJK Halfwidth */ - && !m_isNonBreakingSpace && !TMP_Settings.useModernHangulLineBreakingRules) - { - if (isFirstWordOfLine || isLastBreakingChar || TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode) == false && - (m_characterCount < totalCharacterCount - 1 && - TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_internalCharacterInfo[m_characterCount + 1].character) == false)) + else if (m_isNonBreakingSpace == false && + ((charCode > 0x1100 && charCode < 0x11ff || /* Hangul Jamo */ + charCode > 0xA960 && charCode < 0xA97F || /* Hangul Jamo Extended-A */ + charCode > 0xAC00 && charCode < 0xD7FF)&& /* Hangul Syllables */ + TMP_Settings.useModernHangulLineBreakingRules == false || + + (charCode > 0x2E80 && charCode < 0x9FFF || /* CJK */ + charCode > 0xF900 && charCode < 0xFAFF || /* CJK Compatibility Ideographs */ + charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */ + charCode > 0xFF00 && charCode < 0xFFEF))) /* CJK Halfwidth */ + { + bool isLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode); + bool isFollowingCharacter = m_characterCount < totalCharacterCount - 1 && TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_internalCharacterInfo[m_characterCount + 1].character); + + if (isFirstWordOfLine || isLeadingCharacter == false) { - SaveWordWrappingState(ref internalWordWrapState, i, m_characterCount); - isFirstWordOfLine = false; + if (isFollowingCharacter == false) + { + SaveWordWrappingState(ref internalWordWrapState, i, m_characterCount); + isFirstWordOfLine = false; + } + + if (isFirstWordOfLine) + { + // Special handling for non-breaking space and soft line breaks + if (isWhiteSpace) + SaveWordWrappingState(ref internalSoftLineBreak, i, m_characterCount); + + SaveWordWrappingState(ref internalWordWrapState, i, m_characterCount); + } } + + isLastCharacterCJK = true; } - else if ((isFirstWordOfLine || isLastBreakingChar)) + else if (isLastCharacterCJK) + { + bool isLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode); + + if (isLeadingCharacter == false) + SaveWordWrappingState(ref internalWordWrapState, i, m_characterCount); + + isLastCharacterCJK = false; + } + else if (isFirstWordOfLine) + { + // Special handling for non-breaking space and soft line breaks + if (isWhiteSpace || (charCode == 0xAD && isSoftHyphenIgnored == false)) + SaveWordWrappingState(ref internalSoftLineBreak, i, m_characterCount); + SaveWordWrappingState(ref internalWordWrapState, i, m_characterCount); + isLastCharacterCJK = false; + } } #endregion Save Word Wrapping State @@ -5671,17 +5976,26 @@ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector // Check Auto Sizing and increase font size to fill text container. #region Check Auto-Sizing (Upper Font Size Bounds) - //fontSizeDelta = m_maxFontSize - m_minFontSize; - //if (!m_isCharacterWrappingEnabled && ignoreTextAutoSizing == false && fontSizeDelta > 0.051f && defaultFontSize < m_fontSizeMax && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) - //{ - // m_minFontSize = defaultFontSize; - // defaultFontSize += Mathf.Max((m_maxFontSize - defaultFontSize) / 2, 0.05f); - // defaultFontSize = (int)(Mathf.Min(defaultFontSize, m_fontSizeMax) * 20 + 0.5f) / 20f; - - // return CalculatePreferredValues(defaultFontSize, marginSize, false); - //} + fontSizeDelta = m_maxFontSize - m_minFontSize; + if (isTextAutoSizingEnabled && fontSizeDelta > 0.051f && fontSize < m_fontSizeMax && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount) + { + // Reset character width adjustment delta + if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100) + m_charWidthAdjDelta = 0; + + m_minFontSize = fontSize; + + float sizeDelta = Mathf.Max((m_maxFontSize - fontSize) / 2, 0.05f); + fontSize += sizeDelta; + fontSize = Mathf.Min((int)(fontSize * 20 + 0.5f) / 20f, m_fontSizeMax); + + //Debug.Log("[" + m_AutoSizeIterationCount + "] Increasing Point Size from [" + m_minFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "]."); + return Vector2.zero; + } #endregion End Auto-sizing Check + m_IsAutoSizePointSizeSet = true; + m_isCalculatingPreferredValues = false; // Adjust Preferred Width and Height to account for Margins. @@ -5906,14 +6220,23 @@ public virtual void ComputeMarginSize() { } protected void InsertNewLine(int i, float baseScale, float currentEmScale, float characterSpacingAdjustment, float width, float lineGap, ref bool isMaxVisibleDescenderSet, ref float maxVisibleDescender) { + // Adjust line spacing if necessary + float baselineAdjustmentDelta = m_maxLineAscender - m_startOfLineAscender; + if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage) + { + AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, baselineAdjustmentDelta); + m_ElementDescender -= baselineAdjustmentDelta; + m_lineOffset += baselineAdjustmentDelta; + } + // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well. float lineAscender = m_maxLineAscender - m_lineOffset; float lineDescender = m_maxLineDescender - m_lineOffset; // Update maxDescender and maxVisibleDescender - m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender; + m_ElementDescender = m_ElementDescender < lineDescender ? m_ElementDescender : lineDescender; if (!isMaxVisibleDescenderSet) - maxVisibleDescender = m_maxDescender; + maxVisibleDescender = m_ElementDescender; if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines)) isMaxVisibleDescenderSet = true; @@ -6008,7 +6331,7 @@ protected void SaveWordWrappingState(ref WordWrapState state, int index, int cou state.xAdvance = m_xAdvance; state.maxCapHeight = m_maxCapHeight; state.maxAscender = m_maxAscender; - state.maxDescender = m_maxDescender; + state.maxDescender = m_ElementDescender; state.startOfLineAscender = m_startOfLineAscender; state.maxLineAscender = m_maxLineAscender; state.maxLineDescender = m_maxLineDescender; @@ -6099,7 +6422,7 @@ protected int RestoreWordWrappingState(ref WordWrapState state) m_xAdvance = state.xAdvance; m_maxCapHeight = state.maxCapHeight; m_maxAscender = state.maxAscender; - m_maxDescender = state.maxDescender; + m_ElementDescender = state.maxDescender; m_startOfLineAscender = state.startOfLineAscender; m_maxLineAscender = state.maxLineAscender; m_maxLineDescender = state.maxLineDescender; @@ -7900,6 +8223,8 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI if (m_fontStyleStack.Remove(FontStyles.Strikethrough) == 0) m_FontStyleInternal &= ~FontStyles.Strikethrough; } + + m_strikethroughColor = m_strikethroughColorStack.Remove(); return true; case 117: // case 85: // @@ -7926,6 +8251,8 @@ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endI if (m_fontStyleStack.Remove(FontStyles.Underline) == 0) m_FontStyleInternal &= ~FontStyles.Underline; } + + m_underlineColor = m_underlineColorStack.Remove(); return true; case 43045: // case 30245: // diff --git a/Scripts/Runtime/TMPro_Private.cs b/Scripts/Runtime/TMPro_Private.cs index 1062b0f..af59289 100644 --- a/Scripts/Runtime/TMPro_Private.cs +++ b/Scripts/Runtime/TMPro_Private.cs @@ -1289,7 +1289,13 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) unicode = unicodeChars[i].unicode = 32; character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); if (!TMP_Settings.warningsDisabled) - Debug.LogWarning("Character with ASCII value of " + srcGlyph + " was not found in the Font Asset Glyph Table. It was replaced by a space.", this); + { + string formatWarning = srcGlyph > 0xFFFF + ? "The character with Unicode value \\U{0:X8} was not found in {1}'s Glyph Table. It was replaced by a space in text object {2}." + : "The character with Unicode value \\u{0:X4} was not found in {1}'s Glyph Table. It was replaced by a space in text object {2}."; + + Debug.LogWarningFormat(formatWarning, srcGlyph, m_fontAsset.name, this); + } } } @@ -1512,6 +1518,13 @@ protected override void OnTransformParentChanged() protected override void OnRectTransformDimensionsChange() { //Debug.Log("*** OnRectTransformDimensionsChange() ***"); + + // Ignore changes to RectTransform SizeDelta that are very small and typically the result of rounding errors when using RectTransform in Anchor Stretch mode. + if (rectTransform != null && Mathf.Abs(m_rectTransform.sizeDelta.x - m_RectTransformSizeDelta.x) < 0.0001f && Mathf.Abs(m_rectTransform.sizeDelta.y - m_RectTransformSizeDelta.y) < 0.0001f) + return; + + m_RectTransformSizeDelta = m_rectTransform.sizeDelta; + ComputeMarginSize(); SetVerticesDirty(); @@ -1528,7 +1541,7 @@ internal override void InternalUpdate() if (m_havePropertiesChanged == false) { float lossyScaleY = m_rectTransform.lossyScale.y; - if (lossyScaleY != m_previousLossyScaleY && m_text != string.Empty && m_text != null) + if (lossyScaleY != m_previousLossyScaleY) { float scaleDelta = lossyScaleY / m_previousLossyScaleY; @@ -1593,7 +1606,7 @@ void OnPreRenderObject() ParseInputText(); - TMP_FontAsset.UpdateFontAssets(); + TMP_FontAsset.UpdateFontFeaturesForFontAssetsInQueue(); #if TMP_PROFILE_ON Profiler.EndSample(); @@ -1653,7 +1666,7 @@ protected override void GenerateTextMesh() m_textInfo.Clear(); // Early exit if we don't have any Text to generate. - if (m_InternalParsingBuffer == null || m_InternalParsingBuffer.Length == 0 || m_InternalParsingBuffer[0].unicode == (char)0) + if (m_InternalParsingBuffer == null || m_InternalParsingBuffer.Length == 0 || m_InternalParsingBuffer[0].unicode == 0) { // Clear mesh and upload changes to the mesh. ClearMesh(true); @@ -1802,7 +1815,7 @@ protected override void GenerateTextMesh() // Tracking of the highest Ascender m_maxCapHeight = 0; m_maxAscender = 0; - m_maxDescender = 0; + m_ElementDescender = 0; m_PageAscender = 0; float maxVisibleDescender = 0; bool isMaxVisibleDescenderSet = false; @@ -1813,6 +1826,7 @@ protected override void GenerateTextMesh() m_isNonBreakingSpace = false; bool ignoreNonBreakingSpace = false; bool isLastCharacterCJK = false; + int lastSoftLineBreak = 0; CharacterSubstitution characterToSubstitute = new CharacterSubstitution(-1, 0); bool isSoftHyphenIgnored = false; @@ -1823,6 +1837,7 @@ protected override void GenerateTextMesh() SaveWordWrappingState(ref m_SavedLineState, -1, -1); SaveWordWrappingState(ref m_SavedEllipsisState, -1, -1); SaveWordWrappingState(ref m_SavedLastValidState, -1, -1); + SaveWordWrappingState(ref m_SavedSoftLineBreakState, -1, -1); #if TMP_PROFILE_ON Profiler.BeginSample("TMP GenerateText() - Phase I"); @@ -2048,7 +2063,7 @@ protected override void GenerateTextMesh() // Handle Soft Hyphen #region Handle Soft Hyphen - float unModifiedScale = currentElementScale; + float currentElementUnmodifiedScale = currentElementScale; if (charCode == 0xAD || charCode == 0x03) currentElementScale = 0; #endregion @@ -2257,12 +2272,12 @@ protected override void GenerateTextMesh() m_textInfo.characterInfo[m_characterCount].aspectRatio = (top_right.x - bottom_left.x) / (top_left.y - bottom_left.y); - // Compute and save text element Ascender and maximum line Ascender. + // Compute text metrics #region Compute Ascender & Descender values #if TMP_PROFILE_ON Profiler.BeginSample("TMP - Compute Text Metrics"); #endif - + // Element Ascender in line space float elementAscender = m_textElementType == TMP_TextElementType.Character ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementAscentLine * spriteScale + m_baselineOffset; @@ -2270,9 +2285,7 @@ protected override void GenerateTextMesh() if (isInjectingCharacter && charCode != 0x03) elementAscender = m_maxLineAscender; - m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; - - // Compute and save text element Descender and maximum line Descender. + // Element Descender in line space float elementDescender = m_textElementType == TMP_TextElementType.Character ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementDescentLine * spriteScale + m_baselineOffset; @@ -2280,9 +2293,20 @@ protected override void GenerateTextMesh() if (isInjectingCharacter && charCode != 0x03) elementDescender = m_maxLineDescender; - float elementDescenderII = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset; + // Element Ascender and Descender in object space + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + { + m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; + m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset; + } + else + { + m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = m_maxLineAscender - m_lineOffset; + m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = m_maxLineDescender - m_lineOffset; + } - if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) + // Max line ascender and descender in line space + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) { m_maxLineAscender = elementAscender > m_maxLineAscender ? elementAscender : m_maxLineAscender; m_maxLineDescender = elementDescender < m_maxLineDescender ? elementDescender : m_maxLineDescender; @@ -2296,25 +2320,26 @@ protected override void GenerateTextMesh() m_maxLineAscender = baseAscender > m_maxLineAscender ? baseAscender : m_maxLineAscender; float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.m_FaceInfo.subscriptSize; - elementDescender = m_maxLineDescender; + //elementDescender = m_maxLineDescender; m_maxLineDescender = baseDescender < m_maxLineDescender ? baseDescender : m_maxLineDescender; } + // Max text object ascender and cap height if (m_lineNumber == 0 || m_isNewPage) { - if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) { m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender; m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier); } } + // Page ascender if (m_lineOffset == 0) { - if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) m_PageAscender = m_PageAscender > elementAscender ? m_PageAscender : elementAscender; } - #if TMP_PROFILE_ON Profiler.EndSample(); #endif @@ -2328,7 +2353,7 @@ protected override void GenerateTextMesh() // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN. #region Handle Visible Characters - if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007 || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) + if (charCode == 9 || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) { #if TMP_PROFILE_ON Profiler.BeginSample("TMP - Handle Visible Character"); @@ -2384,8 +2409,8 @@ protected override void GenerateTextMesh() widthOfTextArea = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight; // Calculate the line breaking width of the text. - float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode != 0xAD ? currentElementScale : unModifiedScale); - float textHeight = (m_maxAscender - elementDescenderII) + ((m_lineOffset > 0 && m_IsDrivenLineSpacing == false) ? m_maxLineAscender - m_startOfLineAscender : 0); + float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode == 0xAD ? currentElementUnmodifiedScale : currentElementScale); + float textHeight = m_maxAscender - m_ElementDescender + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); int testedCharacterCount = m_characterCount; @@ -2544,7 +2569,7 @@ protected override void GenerateTextMesh() if (m_lineHeight == TMP_Math.FLOAT_UNSET) { float ascender = m_textInfo.characterInfo[m_characterCount].ascender - m_textInfo.characterInfo[m_characterCount].baseLine; - lineOffsetDelta = 0 - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale; + lineOffsetDelta = (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0) - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale; } else { @@ -2619,6 +2644,29 @@ protected override void GenerateTextMesh() #endregion Text Auto-Sizing } + + // Special handling if first word of line and non breaking space + int savedSoftLineBreakingSpace = m_SavedSoftLineBreakState.previous_WordBreak; + if (isFirstWordOfLine && savedSoftLineBreakingSpace != -1) + { + if (savedSoftLineBreakingSpace != lastSoftLineBreak) + { + i = RestoreWordWrappingState(ref m_SavedSoftLineBreakState); + lastSoftLineBreak = savedSoftLineBreakingSpace; + + // check if soft hyphen + if (m_textInfo.characterInfo[m_characterCount - 1].character == 0xAD) + { + characterToSubstitute.index = m_characterCount - 1; + characterToSubstitute.unicode = 0x2D; + + i -= 1; + m_characterCount -= 1; + continue; + } + } + } + // Determine if new line of text would exceed the vertical bounds of text container if (newTextHeight > marginHeight + 0.0001f) { @@ -2914,15 +2962,12 @@ protected override void GenerateTextMesh() // Special handling of characters that are not ignored at the end of a line. - if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007) + if (charCode == 9) { m_textInfo.characterInfo[m_characterCount].isVisible = false; m_lastVisibleCharacterOfLine = m_characterCount; m_textInfo.lineInfo[m_lineNumber].spaceCount += 1; m_textInfo.spaceCount += 1; - - if (charCode == 0xA0) - m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1; } else if (charCode == 0xAD) { @@ -2977,46 +3022,25 @@ protected override void GenerateTextMesh() // This is white spacing / non visible characters. // Track # of spaces per line which is used for line justification. - if ((charCode == 10 || charCode == 11 || charCode == 0x2028 || charCode == 0x2029 || char.IsSeparator((char)charCode)) && charCode != 0xAD && charCode != 0x200B && charCode != 0x2060) + if ((charCode == 10 || charCode == 11 || charCode == 0xA0 || charCode == 0x2007 || charCode == 0x2028 || charCode == 0x2029 || char.IsSeparator((char)charCode)) && charCode != 0xAD && charCode != 0x200B && charCode != 0x2060) { m_textInfo.lineInfo[m_lineNumber].spaceCount += 1; m_textInfo.spaceCount += 1; } - #if TMP_PROFILE_ON - Profiler.EndSample(); - #endif - } - #endregion Handle Visible Characters - - - // Check if Line Spacing of previous line needs to be adjusted. - #region Adjust Line Spacing - if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage) - { - #if TMP_PROFILE_ON - Profiler.BeginSample("TMP - Handle Line Spacing Adjustments"); - #endif - - float offsetDelta = m_maxLineAscender - m_startOfLineAscender; - AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta); - elementDescenderII -= offsetDelta; - m_lineOffset += offsetDelta; - - m_startOfLineAscender += offsetDelta; - m_SavedWordWrapState.lineOffset = m_lineOffset; - m_SavedWordWrapState.startOfLineAscender = m_startOfLineAscender; + if (charCode == 0xA0) + m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1; #if TMP_PROFILE_ON Profiler.EndSample(); #endif } - #endregion + #endregion Handle Visible Characters // Tracking of potential insertion positions for Ellipsis character #region Track Potential Insertion Location for Ellipsis - if (m_overflowMode == TextOverflowModes.Ellipsis && isInjectingCharacter == false) + if (m_overflowMode == TextOverflowModes.Ellipsis && (isInjectingCharacter == false || charCode == 0x2D)) { float fontScale = m_currentFontSize / m_Ellipsis.fontAsset.m_FaceInfo.pointSize * m_Ellipsis.fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); float scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale; @@ -3032,7 +3056,7 @@ protected override void GenerateTextMesh() marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight; } - float textHeight = m_maxAscender - (m_maxLineDescender - m_lineOffset); + float textHeight = m_maxAscender - m_ElementDescender + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); // m_maxAscender - (m_maxLineDescender - m_lineOffset); float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_Ellipsis.character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale; float widthOfTextAreaForEllipsis = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight; @@ -3118,14 +3142,21 @@ protected override void GenerateTextMesh() Profiler.BeginSample("TMP - Handle Line & Text Termination"); #endif - // Check if Line Spacing of previous line needs to be adjusted. - if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) + // Adjust current line spacing (if necessary) before inserting new line + float baselineAdjustmentDelta = m_maxLineAscender - m_startOfLineAscender; + if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage) // && isInjectingCharacter == false) { //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber); - float offsetDelta = m_maxLineAscender - m_startOfLineAscender; - AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta); - elementDescenderII -= offsetDelta; - m_lineOffset += offsetDelta; + AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, baselineAdjustmentDelta); + m_ElementDescender -= baselineAdjustmentDelta; + m_lineOffset += baselineAdjustmentDelta; + + // Adjust saved ellipsis state only if we are adjusting the same line number + if (m_SavedEllipsisState.lineNumber == m_lineNumber) + { + m_SavedEllipsisState.startOfLineAscender += baselineAdjustmentDelta; + m_SavedEllipsisState.lineOffset += baselineAdjustmentDelta; + } } m_isNewPage = false; @@ -3134,9 +3165,9 @@ protected override void GenerateTextMesh() float lineDescender = m_maxLineDescender - m_lineOffset; // Update maxDescender and maxVisibleDescender - m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender; + m_ElementDescender = m_ElementDescender < lineDescender ? m_ElementDescender : lineDescender; if (!isMaxVisibleDescenderSet) - maxVisibleDescender = m_maxDescender; + maxVisibleDescender = m_ElementDescender; if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines)) isMaxVisibleDescenderSet = true; @@ -3258,8 +3289,8 @@ protected override void GenerateTextMesh() TMP_TextInfo.Resize(ref m_textInfo.pageInfo, m_pageNumber + 1, true); m_textInfo.pageInfo[m_pageNumber].ascender = m_PageAscender; - m_textInfo.pageInfo[m_pageNumber].descender = elementDescenderII < m_textInfo.pageInfo[m_pageNumber].descender - ? elementDescenderII + m_textInfo.pageInfo[m_pageNumber].descender = m_ElementDescender < m_textInfo.pageInfo[m_pageNumber].descender + ? m_ElementDescender : m_textInfo.pageInfo[m_pageNumber].descender; if (m_pageNumber == 0 && m_characterCount == 0) @@ -3293,17 +3324,21 @@ protected override void GenerateTextMesh() SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); isFirstWordOfLine = false; isLastCharacterCJK = false; + + // Reset soft line breaking point since we now have a valid hard break point. + m_SavedSoftLineBreakState.previous_WordBreak = -1; } // Handling for East Asian characters - else if (!m_isNonBreakingSpace && - (charCode > 0x1100 && charCode < 0x11ff || /* Hangul Jamo */ - charCode > 0xA960 && charCode < 0xA97F || /* Hangul Jamo Extended-A */ - charCode > 0xAC00 && charCode < 0xD7FF) /* Hangul Syllables */ && - !TMP_Settings.useModernHangulLineBreakingRules || - (charCode > 0x2E80 && charCode < 0x9FFF || /* CJK */ - charCode > 0xF900 && charCode < 0xFAFF || /* CJK Compatibility Ideographs */ - charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */ - charCode > 0xFF00 && charCode < 0xFFEF)) /* CJK Halfwidth */ + else if (m_isNonBreakingSpace == false && + ((charCode > 0x1100 && charCode < 0x11ff || /* Hangul Jamo */ + charCode > 0xA960 && charCode < 0xA97F || /* Hangul Jamo Extended-A */ + charCode > 0xAC00 && charCode < 0xD7FF)&& /* Hangul Syllables */ + TMP_Settings.useModernHangulLineBreakingRules == false || + + (charCode > 0x2E80 && charCode < 0x9FFF || /* CJK */ + charCode > 0xF900 && charCode < 0xFAFF || /* CJK Compatibility Ideographs */ + charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */ + charCode > 0xFF00 && charCode < 0xFFEF))) /* CJK Halfwidth */ { bool isLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode); bool isFollowingCharacter = m_characterCount < totalCharacterCount - 1 && TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_textInfo.characterInfo[m_characterCount + 1].character); @@ -3317,7 +3352,13 @@ protected override void GenerateTextMesh() } if (isFirstWordOfLine) + { + // Special handling for non-breaking space and soft line breaks + if (isWhiteSpace) + SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); + SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); + } } isLastCharacterCJK = true; @@ -3333,6 +3374,10 @@ protected override void GenerateTextMesh() } else if (isFirstWordOfLine) { + // Special handling for non-breaking space and soft line breaks + if (isWhiteSpace || (charCode == 0xAD && isSoftHyphenIgnored == false)) + SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); + SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); isLastCharacterCJK = false; } diff --git a/Scripts/Runtime/TMPro_UGUI_Private.cs b/Scripts/Runtime/TMPro_UGUI_Private.cs index e8ebe75..3f08e72 100644 --- a/Scripts/Runtime/TMPro_UGUI_Private.cs +++ b/Scripts/Runtime/TMPro_UGUI_Private.cs @@ -29,7 +29,7 @@ public partial class TextMeshProUGUI private bool m_isFirstAllocation; // Flag to determine if this is the first allocation of the buffers. private int m_max_characters = 8; // Determines the initial allocation and size of the character array / buffer. - //private int m_max_numberOfLines = 4; // Determines the initial allocation and maximum number of lines of text. + //private int m_max_numberOfLines = 4; // Determines the initial allocation and maximum number of lines of text. // MASKING RELATED PROPERTIES // This property is now obsolete and used for compatibility with previous releases (prior to release 0.1.54). @@ -39,7 +39,7 @@ public partial class TextMeshProUGUI private bool m_isScrollRegionSet; //private Mask m_mask; private int m_stencilID = 0; - + [SerializeField] private Vector4 m_maskOffset; @@ -86,12 +86,12 @@ protected override void Awake() // Cache Reference to RectTransform. m_rectTransform = gameObject.GetComponent(); - if (m_rectTransform == null) + if (m_rectTransform == null) m_rectTransform = gameObject.AddComponent(); // Cache a reference to the CanvasRenderer. m_canvasRenderer = GetComponent(); - if (m_canvasRenderer == null) + if (m_canvasRenderer == null) m_canvasRenderer = gameObject.AddComponent (); if (m_mesh == null) @@ -146,7 +146,7 @@ protected override void OnEnable() if (!m_isRegisteredForEvents) { //Debug.Log("Registering for Events."); - + #if UNITY_EDITOR // Register Callbacks for various events. TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Add(ON_MATERIAL_PROPERTY_CHANGED); @@ -193,7 +193,7 @@ protected override void OnDisable() return; if (m_MaskMaterial != null) - { + { TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial); m_MaskMaterial = null; } @@ -369,7 +369,7 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) if (m_canvasRenderer.GetMaterial() != m_sharedMaterial && m_fontAsset == null) // || m_renderer.sharedMaterials.Contains(mat)) { - //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED Called on Target ID: " + GetInstanceID() + ". Previous Material:" + m_sharedMaterial + " New Material:" + m_uiRenderer.GetMaterial()); // on Object ID:" + GetInstanceID() + ". m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial.name); + //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED Called on Target ID: " + GetInstanceID() + ". Previous Material:" + m_sharedMaterial + " New Material:" + m_uiRenderer.GetMaterial()); // on Object ID:" + GetInstanceID() + ". m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial.name); m_sharedMaterial = m_canvasRenderer.GetMaterial(); } @@ -400,7 +400,7 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) } else if (materialID == maskingMaterialID) { - // Update the padding + // Update the padding GetPaddingForMaterial(mat); m_sharedMaterial.CopyPropertiesFromMaterial(mat); @@ -442,7 +442,7 @@ void ON_FONT_PROPERTY_CHANGED(bool isChanged, TMP_FontAsset font) void ON_TEXTMESHPRO_UGUI_PROPERTY_CHANGED(bool isChanged, TextMeshProUGUI obj) { //Debug.Log("Event Received by " + obj); - + if (obj == this) { //Debug.Log("Undo / Redo Event Received by Object ID:" + GetInstanceID()); @@ -518,7 +518,7 @@ void ON_TMP_SETTINGS_CHANGED() protected override void LoadFontAsset() { //Debug.Log("***** LoadFontAsset() *****"); //TextMeshPro LoadFontAsset() has been called."); // Current Font Asset is " + (font != null ? font.name: "Null") ); - + ShaderUtilities.GetShaderPropertyIDs(); // Initialize & Get shader property IDs. if (m_fontAsset == null) @@ -668,14 +668,14 @@ void DisableMasking() m_sharedMaterial = m_MaskMaterial; else m_sharedMaterial = m_baseMaterial; - + m_canvasRenderer.SetMaterial(m_sharedMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex)); DestroyImmediate(m_fontMaterial); } - + m_isMaskingEnabled = false; - + /* if (m_maskingMaterial != null && m_stencilID == 0) { @@ -689,8 +689,8 @@ void DisableMasking() m_sharedMaterial.DisableKeyword("MASK_SOFT"); } */ - - + + /* Material mat = m_uiRenderer.GetMaterial(); if (mat.HasProperty(ShaderUtilities.ID_MaskCoord)) @@ -719,7 +719,7 @@ void UpdateMask() if (!ShaderUtilities.isInitialized) ShaderUtilities.GetShaderPropertyIDs(); - + //Debug.Log("Setting Mask for the first time."); m_isScrollRegionSet = true; @@ -733,9 +733,9 @@ void UpdateMask() float width = (m_rectTransform.rect.width - Mathf.Max(m_margin.x, 0) - Mathf.Max(m_margin.z, 0)) / 2 + softnessX; float height = (m_rectTransform.rect.height - Mathf.Max(m_margin.y, 0) - Mathf.Max(m_margin.w, 0)) / 2 + softnessY; - + Vector2 center = m_rectTransform.localPosition + new Vector3((0.5f - m_rectTransform.pivot.x) * m_rectTransform.rect.width + (Mathf.Max(m_margin.x, 0) - Mathf.Max(m_margin.z, 0)) / 2, (0.5f - m_rectTransform.pivot.y) * m_rectTransform.rect.height + (-Mathf.Max(m_margin.y, 0) + Mathf.Max(m_margin.w, 0)) / 2); - + //Vector2 center = m_rectTransform.localPosition + new Vector3((0.5f - m_rectTransform.pivot.x) * m_rectTransform.rect.width + (margin.x - margin.z) / 2, (0.5f - m_rectTransform.pivot.y) * m_rectTransform.rect.height + (-margin.y + margin.w) / 2); Vector4 mask = new Vector4(center.x, center.y, width, height); //Debug.Log(mask); @@ -754,7 +754,7 @@ protected override Material GetMaterial(Material mat) { // Get Shader PropertyIDs if they haven't been cached already. ShaderUtilities.GetShaderPropertyIDs(); - + // Check in case Object is disabled. If so, we don't have a valid reference to the Renderer. // This can occur when the Duplicate Material Context menu is used on an inactive object. //if (m_canvasRenderer == null) @@ -805,10 +805,10 @@ protected override Material[] GetMaterials(Material[] mats) // Function called internally when a new shared material is assigned via the fontSharedMaterial property. - protected override void SetSharedMaterial(Material mat) + protected override void SetSharedMaterial(Material mat) { // Check in case Object is disabled. If so, we don't have a valid reference to the Renderer. - // This can occur when the Duplicate Material Context menu is used on an inactive object. + // This can occur when the Duplicate Material Context menu is used on an inactive object. //if (m_canvasRenderer == null) // m_canvasRenderer = GetComponent(); @@ -934,7 +934,7 @@ protected override void SetOutlineColor(Color32 color) } - // Sets the Render Queue and Ztest mode + // Sets the Render Queue and Ztest mode protected override void SetShaderDepth() { if (m_canvas == null || m_sharedMaterial == null) @@ -1040,7 +1040,7 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) m_materialReferenceIndexLookup.Clear(); MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup); - // Set allocations for the text object's TextInfo + // Set allocations for the text object's TextInfo if (m_textInfo == null) m_textInfo = new TMP_TextInfo(m_InternalParsingBufferSize); else if (m_textInfo.characterInfo.Length < m_InternalParsingBufferSize) @@ -1345,7 +1345,13 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) unicode = unicodeChars[i].unicode = 32; character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); if (!TMP_Settings.warningsDisabled) - Debug.LogWarning("Character with ASCII value of " + srcGlyph + " was not found in the Font Asset Glyph Table. It was replaced by a space.", this); + { + string formatWarning = srcGlyph > 0xFFFF + ? "The character with Unicode value \\U{0:X8} was not found in {1}'s Glyph Table. It was replaced by a space in text object {2}." + : "The character with Unicode value \\u{0:X4} was not found in {1}'s Glyph Table. It was replaced by a space in text object {2}."; + + Debug.LogWarningFormat(formatWarning, srcGlyph, m_fontAsset.name, this); + } } } @@ -1553,7 +1559,7 @@ public override void ComputeMarginSize() /// - /// + /// /// protected override void OnDidApplyAnimationProperties() { @@ -1596,12 +1602,18 @@ protected override void OnTransformParentChanged() protected override void OnRectTransformDimensionsChange() { - //Debug.Log("*** OnRectTransformDimensionsChange() *** ActiveInHierarchy: " + this.gameObject.activeInHierarchy + " Frame: " + Time.frameCount); + //Debug.Log("*** OnRectTransformDimensionsChange() *** ActiveInHierarchy: " + this.gameObject.activeInHierarchy + " Frame: " + Time.frameCount); // Make sure object is active in Hierarchy if (!this.gameObject.activeInHierarchy) return; + // Ignore changes to RectTransform SizeDelta that are very small and typically the result of rounding errors when using RectTransform in Anchor Stretch mode. + if (rectTransform != null && Mathf.Abs(m_rectTransform.sizeDelta.x - m_RectTransformSizeDelta.x) < 0.0001f && Mathf.Abs(m_rectTransform.sizeDelta.y - m_RectTransformSizeDelta.y) < 0.0001f) + return; + + m_RectTransformSizeDelta = m_rectTransform.sizeDelta; + ComputeMarginSize(); UpdateSubObjectPivot(); @@ -1620,7 +1632,7 @@ internal override void InternalUpdate() if (m_havePropertiesChanged == false) { float lossyScaleY = m_rectTransform.lossyScale.y; - if (lossyScaleY != m_previousLossyScaleY && m_text != string.Empty && m_text != null) + if (lossyScaleY != m_previousLossyScaleY) { float scaleDelta = lossyScaleY / m_previousLossyScaleY; @@ -1644,7 +1656,7 @@ internal override void InternalUpdate() // Called just before the Canvas is rendered. void OnPreRenderCanvas() { - //Debug.Log("*** OnPreRenderCanvas() *** Frame: " + Time.frameCount); + //Debug.Log("*** OnPreRenderCanvas() *** Frame: " + Time.frameCount); // Make sure object is active and that we have a valid Canvas. if (!m_isAwake || (this.IsActive() == false && m_ignoreActiveState == false)) @@ -1692,7 +1704,7 @@ void OnPreRenderCanvas() ParseInputText(); - TMP_FontAsset.UpdateFontAssets(); + TMP_FontAsset.UpdateFontFeaturesForFontAssetsInQueue(); #if TMP_PROFILE_ON Profiler.EndSample(); @@ -1752,7 +1764,7 @@ protected override void GenerateTextMesh() m_textInfo.Clear(); // Early exit if we don't have any Text to generate. - if (m_InternalParsingBuffer == null || m_InternalParsingBuffer.Length == 0 || m_InternalParsingBuffer[0].unicode == (char)0) + if (m_InternalParsingBuffer == null || m_InternalParsingBuffer.Length == 0 || m_InternalParsingBuffer[0].unicode == 0) { // Clear mesh and upload changes to the mesh. ClearMesh(); @@ -1901,7 +1913,7 @@ protected override void GenerateTextMesh() // Tracking of the highest Ascender m_maxCapHeight = 0; m_maxAscender = 0; - m_maxDescender = 0; + m_ElementDescender = 0; m_PageAscender = 0; float maxVisibleDescender = 0; bool isMaxVisibleDescenderSet = false; @@ -1912,6 +1924,7 @@ protected override void GenerateTextMesh() m_isNonBreakingSpace = false; bool ignoreNonBreakingSpace = false; bool isLastCharacterCJK = false; + int lastSoftLineBreak = 0; CharacterSubstitution characterToSubstitute = new CharacterSubstitution(-1, 0); bool isSoftHyphenIgnored = false; @@ -1922,6 +1935,7 @@ protected override void GenerateTextMesh() SaveWordWrappingState(ref m_SavedLineState, -1, -1); SaveWordWrappingState(ref m_SavedEllipsisState, -1, -1); SaveWordWrappingState(ref m_SavedLastValidState, -1, -1); + SaveWordWrappingState(ref m_SavedSoftLineBreakState, -1, -1); #if TMP_PROFILE_ON Profiler.BeginSample("TMP GenerateText() - Phase I"); @@ -1994,7 +2008,7 @@ protected override void GenerateTextMesh() m_isTextTruncated = true; break; case 0x2D: - // + // break; case 0x2026: m_textInfo.characterInfo[m_characterCount].textElement = m_Ellipsis.character; @@ -2147,7 +2161,7 @@ protected override void GenerateTextMesh() // Handle Soft Hyphen #region Handle Soft Hyphen - float unModifiedScale = currentElementScale; + float currentElementUnmodifiedScale = currentElementScale; if (charCode == 0xAD || charCode == 0x03) currentElementScale = 0; #endregion @@ -2356,12 +2370,12 @@ protected override void GenerateTextMesh() m_textInfo.characterInfo[m_characterCount].aspectRatio = (top_right.x - bottom_left.x) / (top_left.y - bottom_left.y); - // Compute and save text element Ascender and maximum line Ascender. + // Compute text metrics #region Compute Ascender & Descender values #if TMP_PROFILE_ON Profiler.BeginSample("TMP - Compute Text Metrics"); #endif - + // Element Ascender in line space float elementAscender = m_textElementType == TMP_TextElementType.Character ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementAscentLine * spriteScale + m_baselineOffset; @@ -2369,9 +2383,7 @@ protected override void GenerateTextMesh() if (isInjectingCharacter && charCode != 0x03) elementAscender = m_maxLineAscender; - m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; - - // Compute and save text element Descender and maximum line Descender. + // Element Descender in line space float elementDescender = m_textElementType == TMP_TextElementType.Character ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementDescentLine * spriteScale + m_baselineOffset; @@ -2379,9 +2391,20 @@ protected override void GenerateTextMesh() if (isInjectingCharacter && charCode != 0x03) elementDescender = m_maxLineDescender; - float elementDescenderII = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset; + // Element Ascender and Descender in object space + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + { + m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; + m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset; + } + else + { + m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = m_maxLineAscender - m_lineOffset; + m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = m_maxLineDescender - m_lineOffset; + } - if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) + // Max line ascender and descender in line space + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) { m_maxLineAscender = elementAscender > m_maxLineAscender ? elementAscender : m_maxLineAscender; m_maxLineDescender = elementDescender < m_maxLineDescender ? elementDescender : m_maxLineDescender; @@ -2395,25 +2418,26 @@ protected override void GenerateTextMesh() m_maxLineAscender = baseAscender > m_maxLineAscender ? baseAscender : m_maxLineAscender; float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.m_FaceInfo.subscriptSize; - elementDescender = m_maxLineDescender; + //elementDescender = m_maxLineDescender; m_maxLineDescender = baseDescender < m_maxLineDescender ? baseDescender : m_maxLineDescender; } + // Max text object ascender and cap height if (m_lineNumber == 0 || m_isNewPage) { - if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) { m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender; m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier); } } + // Page ascender if (m_lineOffset == 0) { - if (charCode != 0x0A || m_characterCount == m_firstCharacterOfLine) + if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) m_PageAscender = m_PageAscender > elementAscender ? m_PageAscender : elementAscender; } - #if TMP_PROFILE_ON Profiler.EndSample(); #endif @@ -2427,7 +2451,7 @@ protected override void GenerateTextMesh() // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN. #region Handle Visible Characters - if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007 || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) + if (charCode == 9 || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) { #if TMP_PROFILE_ON Profiler.BeginSample("TMP - Handle Visible Character"); @@ -2483,8 +2507,8 @@ protected override void GenerateTextMesh() widthOfTextArea = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight; // Calculate the line breaking width of the text. - float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode != 0xAD ? currentElementScale : unModifiedScale); - float textHeight = (m_maxAscender - elementDescenderII) + ((m_lineOffset > 0 && m_IsDrivenLineSpacing == false) ? m_maxLineAscender - m_startOfLineAscender : 0); + float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode == 0xAD ? currentElementUnmodifiedScale : currentElementScale); + float textHeight = m_maxAscender - m_ElementDescender + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); int testedCharacterCount = m_characterCount; @@ -2601,7 +2625,7 @@ protected override void GenerateTextMesh() continue; } - // Go back to previous line and re-layout + // Go back to previous line and re-layout i = RestoreWordWrappingState(ref m_SavedLineState); m_isNewPage = true; @@ -2643,7 +2667,7 @@ protected override void GenerateTextMesh() if (m_lineHeight == TMP_Math.FLOAT_UNSET) { float ascender = m_textInfo.characterInfo[m_characterCount].ascender - m_textInfo.characterInfo[m_characterCount].baseLine; - lineOffsetDelta = 0 - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale; + lineOffsetDelta = (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0) - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale; } else { @@ -2718,6 +2742,29 @@ protected override void GenerateTextMesh() #endregion Text Auto-Sizing } + + // Special handling if first word of line and non breaking space + int savedSoftLineBreakingSpace = m_SavedSoftLineBreakState.previous_WordBreak; + if (isFirstWordOfLine && savedSoftLineBreakingSpace != -1) + { + if (savedSoftLineBreakingSpace != lastSoftLineBreak) + { + i = RestoreWordWrappingState(ref m_SavedSoftLineBreakState); + lastSoftLineBreak = savedSoftLineBreakingSpace; + + // check if soft hyphen + if (m_textInfo.characterInfo[m_characterCount - 1].character == 0xAD) + { + characterToSubstitute.index = m_characterCount - 1; + characterToSubstitute.unicode = 0x2D; + + i -= 1; + m_characterCount -= 1; + continue; + } + } + } + // Determine if new line of text would exceed the vertical bounds of text container if (newTextHeight > marginHeight + 0.0001f) { @@ -3013,15 +3060,12 @@ protected override void GenerateTextMesh() // Special handling of characters that are not ignored at the end of a line. - if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007) + if (charCode == 9) { m_textInfo.characterInfo[m_characterCount].isVisible = false; m_lastVisibleCharacterOfLine = m_characterCount; m_textInfo.lineInfo[m_lineNumber].spaceCount += 1; m_textInfo.spaceCount += 1; - - if (charCode == 0xA0) - m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1; } else if (charCode == 0xAD) { @@ -3076,46 +3120,25 @@ protected override void GenerateTextMesh() // This is white spacing / non visible characters. // Track # of spaces per line which is used for line justification. - if ((charCode == 10 || charCode == 11 || charCode == 0x2028 || charCode == 0x2029 || char.IsSeparator((char)charCode)) && charCode != 0xAD && charCode != 0x200B && charCode != 0x2060) + if ((charCode == 10 || charCode == 11 || charCode == 0xA0 || charCode == 0x2007 || charCode == 0x2028 || charCode == 0x2029 || char.IsSeparator((char)charCode)) && charCode != 0xAD && charCode != 0x200B && charCode != 0x2060) { m_textInfo.lineInfo[m_lineNumber].spaceCount += 1; m_textInfo.spaceCount += 1; } - #if TMP_PROFILE_ON - Profiler.EndSample(); - #endif - } - #endregion Handle Visible Characters - - - // Check if Line Spacing of previous line needs to be adjusted. - #region Adjust Line Spacing - if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage) - { - #if TMP_PROFILE_ON - Profiler.BeginSample("TMP - Handle Line Spacing Adjustments"); - #endif - - float offsetDelta = m_maxLineAscender - m_startOfLineAscender; - AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta); - elementDescenderII -= offsetDelta; - m_lineOffset += offsetDelta; - - m_startOfLineAscender += offsetDelta; - m_SavedWordWrapState.lineOffset = m_lineOffset; - m_SavedWordWrapState.startOfLineAscender = m_startOfLineAscender; + if (charCode == 0xA0) + m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1; #if TMP_PROFILE_ON Profiler.EndSample(); #endif } - #endregion + #endregion Handle Visible Characters // Tracking of potential insertion positions for Ellipsis character #region Track Potential Insertion Location for Ellipsis - if (m_overflowMode == TextOverflowModes.Ellipsis && isInjectingCharacter == false) + if (m_overflowMode == TextOverflowModes.Ellipsis && (isInjectingCharacter == false || charCode == 0x2D)) { float fontScale = m_currentFontSize / m_Ellipsis.fontAsset.m_FaceInfo.pointSize * m_Ellipsis.fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); float scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale; @@ -3131,7 +3154,7 @@ protected override void GenerateTextMesh() marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight; } - float textHeight = m_maxAscender - (m_maxLineDescender - m_lineOffset); + float textHeight = m_maxAscender - m_ElementDescender + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); // m_maxAscender - (m_maxLineDescender - m_lineOffset); float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_Ellipsis.character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale; float widthOfTextAreaForEllipsis = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight; @@ -3217,14 +3240,21 @@ protected override void GenerateTextMesh() Profiler.BeginSample("TMP - Handle Line & Text Termination"); #endif - // Check if Line Spacing of previous line needs to be adjusted. - if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) + // Adjust current line spacing (if necessary) before inserting new line + float baselineAdjustmentDelta = m_maxLineAscender - m_startOfLineAscender; + if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage) // && isInjectingCharacter == false) { //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber); - float offsetDelta = m_maxLineAscender - m_startOfLineAscender; - AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta); - elementDescenderII -= offsetDelta; - m_lineOffset += offsetDelta; + AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, baselineAdjustmentDelta); + m_ElementDescender -= baselineAdjustmentDelta; + m_lineOffset += baselineAdjustmentDelta; + + // Adjust saved ellipsis state only if we are adjusting the same line number + if (m_SavedEllipsisState.lineNumber == m_lineNumber) + { + m_SavedEllipsisState.startOfLineAscender += baselineAdjustmentDelta; + m_SavedEllipsisState.lineOffset += baselineAdjustmentDelta; + } } m_isNewPage = false; @@ -3233,9 +3263,9 @@ protected override void GenerateTextMesh() float lineDescender = m_maxLineDescender - m_lineOffset; // Update maxDescender and maxVisibleDescender - m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender; + m_ElementDescender = m_ElementDescender < lineDescender ? m_ElementDescender : lineDescender; if (!isMaxVisibleDescenderSet) - maxVisibleDescender = m_maxDescender; + maxVisibleDescender = m_ElementDescender; if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines)) isMaxVisibleDescenderSet = true; @@ -3357,8 +3387,8 @@ protected override void GenerateTextMesh() TMP_TextInfo.Resize(ref m_textInfo.pageInfo, m_pageNumber + 1, true); m_textInfo.pageInfo[m_pageNumber].ascender = m_PageAscender; - m_textInfo.pageInfo[m_pageNumber].descender = elementDescenderII < m_textInfo.pageInfo[m_pageNumber].descender - ? elementDescenderII + m_textInfo.pageInfo[m_pageNumber].descender = m_ElementDescender < m_textInfo.pageInfo[m_pageNumber].descender + ? m_ElementDescender : m_textInfo.pageInfo[m_pageNumber].descender; if (m_pageNumber == 0 && m_characterCount == 0) @@ -3387,22 +3417,26 @@ protected override void GenerateTextMesh() if ((isWhiteSpace || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060) { - // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored + // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored // for Word Wrapping. SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); isFirstWordOfLine = false; isLastCharacterCJK = false; + + // Reset soft line breaking point since we now have a valid hard break point. + m_SavedSoftLineBreakState.previous_WordBreak = -1; } // Handling for East Asian characters - else if (!m_isNonBreakingSpace && - (charCode > 0x1100 && charCode < 0x11ff || /* Hangul Jamo */ - charCode > 0xA960 && charCode < 0xA97F || /* Hangul Jamo Extended-A */ - charCode > 0xAC00 && charCode < 0xD7FF) /* Hangul Syllables */ && - !TMP_Settings.useModernHangulLineBreakingRules || - (charCode > 0x2E80 && charCode < 0x9FFF || /* CJK */ - charCode > 0xF900 && charCode < 0xFAFF || /* CJK Compatibility Ideographs */ - charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */ - charCode > 0xFF00 && charCode < 0xFFEF)) /* CJK Halfwidth */ + else if (m_isNonBreakingSpace == false && + ((charCode > 0x1100 && charCode < 0x11ff || /* Hangul Jamo */ + charCode > 0xA960 && charCode < 0xA97F || /* Hangul Jamo Extended-A */ + charCode > 0xAC00 && charCode < 0xD7FF)&& /* Hangul Syllables */ + TMP_Settings.useModernHangulLineBreakingRules == false || + + (charCode > 0x2E80 && charCode < 0x9FFF || /* CJK */ + charCode > 0xF900 && charCode < 0xFAFF || /* CJK Compatibility Ideographs */ + charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */ + charCode > 0xFF00 && charCode < 0xFFEF))) /* CJK Halfwidth */ { bool isLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode); bool isFollowingCharacter = m_characterCount < totalCharacterCount - 1 && TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_textInfo.characterInfo[m_characterCount + 1].character); @@ -3416,7 +3450,13 @@ protected override void GenerateTextMesh() } if (isFirstWordOfLine) + { + // Special handling for non-breaking space and soft line breaks + if (isWhiteSpace) + SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); + SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); + } } isLastCharacterCJK = true; @@ -3432,6 +3472,10 @@ protected override void GenerateTextMesh() } else if (isFirstWordOfLine) { + // Special handling for non-breaking space and soft line breaks + if (isWhiteSpace || (charCode == 0xAD && isSoftHyphenIgnored == false)) + SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); + SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); isLastCharacterCJK = false; } @@ -3533,12 +3577,12 @@ protected override void GenerateTextMesh() anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0, 0); break; - // Midline Vertically + // Midline Vertically case VerticalAlignmentOptions.Geometry: anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_meshExtents.max.y + margins.y + m_meshExtents.min.y - margins.w) / 2, 0); break; - // Capline Vertically + // Capline Vertically case VerticalAlignmentOptions.Capline: anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxCapHeight - margins.y - margins.w) / 2, 0); break; @@ -3861,7 +3905,7 @@ protected override void GenerateTextMesh() characterInfos[i].vertex_BR.uv2.x = PackUV(x1, y0); characterInfos[i].vertex_BR.uv2.y = xScale; #endregion break; - + // SPRITES case TMP_TextElementType.Sprite: // Nothing right now diff --git a/package.json b/package.json index 3f550c1..8254b13 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.textmeshpro", "displayName": "TextMeshPro", - "version": "1.5.0-preview.5", + "version": "1.5.0-preview.6", "unity": "2018.3", "description": "TextMeshPro is the ultimate text solution for Unity. It's the perfect replacement for Unity's UI Text and the legacy Text Mesh.\n\nPowerful and easy to use, TextMeshPro (also known as TMP) uses Advanced Text Rendering techniques along with a set of custom shaders; delivering substantial visual quality improvements while giving users incredible flexibility when it comes to text styling and texturing.\n\nTextMeshPro provides Improved Control over text formatting and layout with features like character, word, line and paragraph spacing, kerning, justified text, Links, over 30 Rich Text Tags available, support for Multi Font & Sprites, Custom Styles and more.\n\nGreat performance. Since the geometry created by TextMeshPro uses two triangles per character just like Unity's text components, this improved visual quality and flexibility comes at no additional performance cost.", "keywords": [ @@ -14,9 +14,9 @@ "category": "Text Rendering", "dependencies": {}, "repository": { - "footprint": "ea92fed994a895ff47fd221a9cd2fee93ca86b16", + "footprint": "af878f016980b5eb646103cace6589c59d61202b", "type": "git", "url": "https://github.cds.internal.unity3d.com/unity/com.unity.textmeshpro.git", - "revision": "873a6495a3b736304f380dbb071e8b06ecb05f4a" + "revision": "f094955d4ca1d3f1b5684551a149a5228230c54c" } }