diff --git a/CHANGELOG.md b/CHANGELOG.md index 5173ca0..df45156 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # 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 +## [3.2.0-pre.11] - 2024-12-09 +### Changes +- Fix TMP crash on Hyphen wrapping. +- Fixed incorrect character caching when font styles and weights are used. UUM-87529 +- Fixed the iOS crash when using fallback fonts created at runtime. +- Fix exception thrown when pasting text into TMP inputfield with custom validator. +- Avoid creating a new submesh if the previous one still has space. +- Fixed the broken URL of the help button on the TextMesh Pro Settings page. + ## [3.2.0-pre.10] - 2024-05-27 ### Changes - Ensure space and underline are always added to Static FontAsset. (UUM-45512) diff --git a/Package Resources/TMP Examples & Extras.unitypackage b/Package Resources/TMP Examples & Extras.unitypackage index f1f6048..3fa2839 100644 Binary files a/Package Resources/TMP Examples & Extras.unitypackage and b/Package Resources/TMP Examples & Extras.unitypackage differ diff --git a/Scripts/Editor/TMP_BaseShaderGUI.cs b/Scripts/Editor/TMP_BaseShaderGUI.cs index bc0db5d..b712fc8 100644 --- a/Scripts/Editor/TMP_BaseShaderGUI.cs +++ b/Scripts/Editor/TMP_BaseShaderGUI.cs @@ -438,7 +438,7 @@ protected void DoColor(string name, string label) { MaterialProperty property = BeginProperty(name); s_TempLabel.text = label; - Color value = EditorGUI.ColorField(EditorGUILayout.GetControlRect(), s_TempLabel, property.colorValue, false, true, true); + Color value = EditorGUI.ColorField(EditorGUILayout.GetControlRect(), s_TempLabel, property.colorValue, false, true, false); if (EndProperty()) { property.colorValue = value; diff --git a/Scripts/Editor/TMP_EditorResourceManager.cs b/Scripts/Editor/TMP_EditorResourceManager.cs index a06de2f..c63485d 100644 --- a/Scripts/Editor/TMP_EditorResourceManager.cs +++ b/Scripts/Editor/TMP_EditorResourceManager.cs @@ -31,7 +31,7 @@ internal static void InitializeFontAssetResourceChangeCallBacks() string fontAssetPath = AssetDatabase.GUIDToAssetPath(fontAssetGUIDs[i]); TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath(fontAssetPath); - if (fontAsset != null && (fontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic || fontAsset.atlasPopulationMode == AtlasPopulationMode.DynamicOS) && fontAsset.clearDynamicDataOnBuild && fontAsset.atlasTexture.width != 0) + if (fontAsset != null && (fontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic || fontAsset.atlasPopulationMode == AtlasPopulationMode.DynamicOS) && fontAsset.clearDynamicDataOnBuild && fontAsset.atlasTexture.width > 1) { Debug.Log("Clearing [" + fontAsset.name + "] dynamic font asset data."); fontAsset.ClearCharacterAndGlyphTablesInternal(); diff --git a/Scripts/Editor/TMP_SettingsEditor.cs b/Scripts/Editor/TMP_SettingsEditor.cs index 8f618e0..be1a0a1 100644 --- a/Scripts/Editor/TMP_SettingsEditor.cs +++ b/Scripts/Editor/TMP_SettingsEditor.cs @@ -457,7 +457,7 @@ public override void OnGUI(string searchContext) { // Lazy creation that supports domain reload if (m_ResourceImporter == null) - m_ResourceImporter = new TMP_PackageResourceImporter(); + m_ResourceImporter = new TMP_PackageResourceImporter(logErrors: false); m_ResourceImporter.OnGUI(); } diff --git a/Scripts/Editor/TMP_SpriteAssetEditor.cs b/Scripts/Editor/TMP_SpriteAssetEditor.cs index ead6d1b..237810c 100644 --- a/Scripts/Editor/TMP_SpriteAssetEditor.cs +++ b/Scripts/Editor/TMP_SpriteAssetEditor.cs @@ -122,6 +122,7 @@ public override void OnInspectorGUI() if (GUI.Button(rect, new GUIContent("Update Sprite Asset"))) { TMP_SpriteAssetMenu.UpdateSpriteAsset(m_SpriteAsset); + ResetSelectionNextFrame(); } EditorGUI.indentLevel = 1; @@ -677,6 +678,19 @@ public override void OnInspectorGUI() } + private void ResetSelectionNextFrame() + { + var activeObject = Selection.activeObject; + EditorApplication.delayCall += () => + { + Selection.activeObject = null; + EditorApplication.delayCall += () => + { + Selection.activeObject = activeObject; + }; + }; + } + /// /// diff --git a/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs b/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs index b1d47cd..6543477 100644 --- a/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs +++ b/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs @@ -206,8 +206,7 @@ public void OnEnable() } // Get potential font face and styles for the current font. - if (m_SourceFont != null) - m_SourceFontFaces = GetFontFaces(); + m_SourceFontFaces = GetFontFaces(); ClearGeneratedData(); } @@ -438,8 +437,7 @@ void DrawControls() { m_SelectedFontAsset = null; m_IsFontAtlasInvalid = true; - if (m_SourceFont != null) - m_SourceFontFaces = GetFontFaces(); + m_SourceFontFaces = GetFontFaces(); m_SourceFontFaceIndex = 0; } @@ -1220,7 +1218,8 @@ void ClearGeneratedData() /// string[] GetFontFaces() { - FontEngine.LoadFontFace(m_SourceFont, 0, 0); + if (FontEngine.LoadFontFace(m_SourceFont, 0, 0) != FontEngineError.Success) + return Array.Empty(); return FontEngine.GetFontFaces(); } @@ -1845,8 +1844,7 @@ void LoadFontCreationSettings(FontAssetCreationSettings settings) { m_SourceFont = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(settings.sourceFontFileGUID)); m_SourceFontFaceIndex = settings.faceIndex; - if (m_SourceFont != null) - m_SourceFontFaces = GetFontFaces(); + m_SourceFontFaces = GetFontFaces(); m_PointSizeSamplingMode = settings.pointSizeSamplingMode; m_PointSize = settings.pointSize; m_Padding = settings.padding; diff --git a/Scripts/Runtime/TMP_FontAsset.cs b/Scripts/Runtime/TMP_FontAsset.cs index 2358dd9..b7f6bbc 100644 --- a/Scripts/Runtime/TMP_FontAsset.cs +++ b/Scripts/Runtime/TMP_FontAsset.cs @@ -678,6 +678,36 @@ static TMP_FontAsset CreateFontAssetInstance(Font font, int atlasPadding, GlyphR internal static Func SetSourceFontGUID; #endif + /// + /// Weak reference to all instances. + /// + static readonly List> s_CallbackInstances = new(); + + /// + /// Register an instance for static lookup. + /// + /// The instance to register. + void RegisterCallbackInstance(TMP_FontAsset instance) + { + // Verify that it is not already registered. + for (var i = 0; i < s_CallbackInstances.Count; i++) + { + if (s_CallbackInstances[i].TryGetTarget(out TMP_FontAsset fa) && fa == instance) + return; + } + + for (var i = 0; i < s_CallbackInstances.Count; i++) + { + if (!s_CallbackInstances[i].TryGetTarget(out _)) + { + s_CallbackInstances[i] = new WeakReference(instance); + return; + } + } + + s_CallbackInstances.Add(new WeakReference(this)); + } + // Profiler Marker declarations private static ProfilerMarker k_ReadFontAssetDefinitionMarker = new ProfilerMarker("TMP.ReadFontAssetDefinition"); private static ProfilerMarker k_AddSynthesizedCharactersMarker = new ProfilerMarker("TMP.AddSynthesizedCharacters"); @@ -794,6 +824,8 @@ public void ReadFontAssetDefinition() IsFontAssetLookupTablesDirty = false; + RegisterCallbackInstance(this); + k_ReadFontAssetDefinitionMarker.End(); } @@ -893,6 +925,28 @@ internal void InitializeCharacterLookupDictionary() m_MissingUnicodesFromFontFile.Clear(); } + internal void ClearFallbackCharacterTable() + { + var keysToRemove = new List(); + + foreach (var characterLookup in m_CharacterLookupDictionary) + { + var character = characterLookup.Value; + + // Collect the keys to remove + if (character.textAsset != this) + { + keysToRemove.Add(characterLookup.Key); + } + } + + // Now remove the collected keys + foreach (var key in keysToRemove) + { + m_CharacterLookupDictionary.Remove(key); + } + } + internal void InitializeLigatureSubstitutionLookupDictionary() { if (m_FontFeatureTable.m_LigatureSubstitutionRecordLookup == null) @@ -1091,12 +1145,15 @@ void AddSynthesizedCharacter(uint unicode, bool isFontFaceLoaded, bool addImmedi //internal HashSet FallbackSearchQueryLookup = new HashSet(); - internal void AddCharacterToLookupCache(uint unicode, TMP_Character character) + internal void AddCharacterToLookupCache(uint unicode, TMP_Character character, FontStyles fontStyle = FontStyles.Normal, FontWeight fontWeight = FontWeight.Regular, bool isAlternativeTypeface = false) { - m_CharacterLookupDictionary.Add(unicode, character); + uint lookupKey = unicode; + + // Compute composite lookup key if a font style or weight is used. + if (fontStyle != FontStyles.Normal || fontWeight != FontWeight.Regular) + lookupKey = (((isAlternativeTypeface ? 0x80u : 0u) | ((uint)fontStyle << 4) | ((uint)fontWeight / 100)) << 24) | unicode; - // Add font asset to fallback references. - //FallbackSearchQueryLookup.Add(character.textAsset.instanceID); + m_CharacterLookupDictionary.TryAdd(lookupKey, character); } /// @@ -2381,7 +2438,7 @@ internal bool TryAddGlyphInternal(uint glyphIndex, out Glyph glyph) } // Add glyph which did not fit in current atlas texture to new atlas texture. - if (m_IsMultiAtlasTexturesEnabled) + if (m_IsMultiAtlasTexturesEnabled && m_UsedGlyphRects.Count > 0) { // Create new atlas texture SetupNewAtlasTexture(); @@ -2588,7 +2645,7 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) } // Add glyph which did not fit in current atlas texture to new atlas texture. - if (m_IsMultiAtlasTexturesEnabled) + if (m_IsMultiAtlasTexturesEnabled && m_UsedGlyphRects.Count > 0) { // Create new atlas texture SetupNewAtlasTexture(); @@ -3375,6 +3432,13 @@ public void ClearFontAssetData(bool setAtlasSizeToZero = false) ReadFontAssetDefinition(); + // Clear fallback character table for all other fontAssets, in case they were refereing this one. + for (var i = 0; i < s_CallbackInstances.Count; i++) + if (s_CallbackInstances[i].TryGetTarget(out var target) && target != this) + target.ClearFallbackCharacterTable(); + + TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, this); + //TMP_ResourceManager.RebuildFontAssetCache(); #if UNITY_EDITOR diff --git a/Scripts/Runtime/TMP_FontAssetUtilities.cs b/Scripts/Runtime/TMP_FontAssetUtilities.cs index a5b5924..544b1b9 100644 --- a/Scripts/Runtime/TMP_FontAssetUtilities.cs +++ b/Scripts/Runtime/TMP_FontAssetUtilities.cs @@ -65,7 +65,7 @@ public static TMP_Character GetCharacterFromFontAsset(uint unicode, TMP_FontAsse private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TMP_FontAsset sourceFontAsset, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface) { isAlternativeTypeface = false; - TMP_Character character = null; + TMP_Character character; #region FONT WEIGHT AND FONT STYLE HANDLING // Determine if a font weight or style is used. If so check if an alternative typeface is assigned for the given weight and / or style. @@ -73,6 +73,31 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM if (isItalic || fontWeight != FontWeight.Regular) { + // Check if character is already cached using the composite unicode value the takes into consideration the font style and weight + uint compositeUnicodeLookupKey = ((0x80u | ((uint)fontStyle << 4) | ((uint)fontWeight / 100)) << 24) | unicode; + if (sourceFontAsset.characterLookupTable.TryGetValue(compositeUnicodeLookupKey, out character)) + { + // Set isAlternativeTypeface + isAlternativeTypeface = true; + + if (character.textAsset is not null) + return character; + + // Remove character from lookup table + sourceFontAsset.characterLookupTable.Remove(unicode); + } + else if (sourceFontAsset.characterLookupTable.TryGetValue(compositeUnicodeLookupKey & 0x7FFFFFFF, out character)) + { + // Set isAlternativeTypeface + isAlternativeTypeface = false; + + if (character.textAsset is not null) + return character; + + // Remove character from lookup table + sourceFontAsset.characterLookupTable.Remove(unicode); + } + // Get reference to the font weight pairs of the given font asset. TMP_FontWeightPair[] fontWeights = sourceFontAsset.fontWeightTable; @@ -114,7 +139,7 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM { if (temp.characterLookupTable.TryGetValue(unicode, out character)) { - if (character.textAsset != null) + if (character.textAsset is not null) { isAlternativeTypeface = true; return character; @@ -132,32 +157,21 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM return character; } - - // Check if the source font file contains the requested character. - //if (TryGetCharacterFromFontFile(unicode, fontAsset, out characterData)) - //{ - // isAlternativeTypeface = true; - - // return characterData; - //} - - // If we find the requested character, we add it to the font asset character table - // and return its character data. - // We also add this character to the list of characters we will need to add to the font atlas. - // We assume the font atlas has room otherwise this font asset should not be marked as dynamic. - // Alternatively, we could also add multiple pages of font atlas textures (feature consideration). } - - // At this point, we were not able to find the requested character in the alternative typeface - // so we check the source font asset and its potential fallbacks. } + + // Search potential fallbacks of the source font asset + if (includeFallbacks && sourceFontAsset.fallbackFontAssetTable != null) + return SearchFallbacksForCharacter(unicode, sourceFontAsset, fontStyle, fontWeight, out isAlternativeTypeface); + + return null; } #endregion - // Search the source font asset for the requested character. + // Search the source font asset for the requested character if (sourceFontAsset.characterLookupTable.TryGetValue(unicode, out character)) { - if (character.textAsset != null) + if (character.textAsset is not null) return character; // Remove character from lookup table @@ -171,36 +185,40 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM } // Search fallback font assets if we still don't have a valid character and include fallback is set to true. - if (character == null && includeFallbacks && sourceFontAsset.fallbackFontAssetTable != null) - { - // Get reference to the list of fallback font assets. - List fallbackFontAssets = sourceFontAsset.fallbackFontAssetTable; - int fallbackCount = fallbackFontAssets.Count; + if (includeFallbacks && sourceFontAsset.fallbackFontAssetTable != null) + return SearchFallbacksForCharacter(unicode, sourceFontAsset, fontStyle, fontWeight, out isAlternativeTypeface); + + return null; + } - if (fallbackCount == 0) - return null; + private static TMP_Character SearchFallbacksForCharacter(uint unicode, TMP_FontAsset sourceFontAsset, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface) + { + isAlternativeTypeface = false; - for (int i = 0; i < fallbackCount; i++) - { - TMP_FontAsset temp = fallbackFontAssets[i]; + // Get reference to the list of fallback font assets. + List fallbackFontAssets = sourceFontAsset.fallbackFontAssetTable; + int fallbackCount = fallbackFontAssets.Count; - if (temp == null) - continue; + if (fallbackCount == 0) + return null; - int id = temp.instanceID; + for (int i = 0; i < fallbackCount; i++) + { + TMP_FontAsset temp = fallbackFontAssets[i]; - // Try adding font asset to search list. If already present skip to the next one otherwise check if it contains the requested character. - if (k_SearchedAssets.Add(id) == false) - continue; + if (temp == null) + continue; - // Add reference to this search query - //sourceFontAsset.FallbackSearchQueryLookup.Add(id); + int id = temp.instanceID; - character = GetCharacterFromFontAsset_Internal(unicode, temp, true, fontStyle, fontWeight, out isAlternativeTypeface); + // Try adding font asset to search list. If already present skip to the next one otherwise check if it contains the requested character. + if (k_SearchedAssets.Add(id) == false) + continue; - if (character != null) - return character; - } + TMP_Character character = GetCharacterFromFontAsset_Internal(unicode, temp, true, fontStyle, fontWeight, out isAlternativeTypeface); + + if (character != null) + return character; } return null; diff --git a/Scripts/Runtime/TMP_InputField.cs b/Scripts/Runtime/TMP_InputField.cs index 3663d11..010e24b 100644 --- a/Scripts/Runtime/TMP_InputField.cs +++ b/Scripts/Runtime/TMP_InputField.cs @@ -19,7 +19,11 @@ namespace TMPro /// Editable text input field. /// [AddComponentMenu("UI/TextMeshPro - Input Field", 11)] + #if UNITY_2023_2_OR_NEWER + [HelpURL("https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/TextMeshPro/index.html")] + #else [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2")] + #endif public class TMP_InputField : Selectable, IUpdateSelectedHandler, IBeginDragHandler, @@ -1552,8 +1556,8 @@ void UpdateKeyboardStringPosition() if (m_HideMobileInput && m_SoftKeyboard != null && m_SoftKeyboard.canSetSelection && (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.tvOS)) { - var selectionStart = Mathf.Min(caretSelectPositionInternal, caretPositionInternal); - var selectionLength = Mathf.Abs(caretSelectPositionInternal - caretPositionInternal); + var selectionStart = Mathf.Min(stringSelectPositionInternal, stringPositionInternal); + var selectionLength = Mathf.Abs(stringSelectPositionInternal - stringPositionInternal); m_SoftKeyboard.selection = new RangeInt(selectionStart, selectionLength); } } @@ -1824,8 +1828,8 @@ protected virtual void LateUpdate() else if (m_HideMobileInput && m_SoftKeyboard != null && m_SoftKeyboard.canSetSelection && Application.platform != RuntimePlatform.IPhonePlayer && Application.platform != RuntimePlatform.tvOS) { - var selectionStart = Mathf.Min(caretSelectPositionInternal, caretPositionInternal); - var selectionLength = Mathf.Abs(caretSelectPositionInternal - caretPositionInternal); + var selectionStart = Mathf.Min(stringSelectPositionInternal, stringPositionInternal); + var selectionLength = Mathf.Abs(stringSelectPositionInternal - stringPositionInternal); m_SoftKeyboard.selection = new RangeInt(selectionStart, selectionLength); } else if (m_HideMobileInput && Application.platform == RuntimePlatform.Android || @@ -3307,6 +3311,9 @@ protected virtual void Append(char input) if (input == 0) return; + if (!char.IsHighSurrogate(input)) + m_CaretSelectPosition = m_CaretPosition += 1; + SendOnValueChanged(); UpdateLabel(); diff --git a/Scripts/Runtime/TMP_PackageResourceImporter.cs b/Scripts/Runtime/TMP_PackageResourceImporter.cs index 81187d0..d31995b 100644 --- a/Scripts/Runtime/TMP_PackageResourceImporter.cs +++ b/Scripts/Runtime/TMP_PackageResourceImporter.cs @@ -15,16 +15,18 @@ public class TMP_PackageResourceImporter bool m_ExamplesAndExtrasResourcesImported; bool m_EssentialResourcesNeedUpdate; bool m_ExamplesAndExtrasNeedUpdate; + bool m_LogErrors; internal bool m_IsImportingExamples; - public TMP_PackageResourceImporter() + public TMP_PackageResourceImporter(bool logErrors = true) { + m_LogErrors = logErrors; m_EssentialResourcesNeedUpdate = m_ExamplesAndExtrasNeedUpdate = !TMP_Settings.isTMPSettingsNull && TMP_Settings.instance.assetVersion != TMP_Settings.s_CurrentAssetVersion; } public void OnDestroy() { - if (TMP_Settings.isTMPSettingsNull || TMP_Settings.instance?.assetVersion != TMP_Settings.s_CurrentAssetVersion) + if (m_LogErrors && (TMP_Settings.isTMPSettingsNull || TMP_Settings.instance?.assetVersion != TMP_Settings.s_CurrentAssetVersion)) Debug.LogError("TextMesh Pro Essential Resources are missing, which are crucial for proper functionality. To import them, go to 'Window > Text Mesh Pro > Import TMP Essential Resources' in the menu."); } diff --git a/Scripts/Runtime/TMP_Text.cs b/Scripts/Runtime/TMP_Text.cs index 9b5b5bb..6b0b1f3 100644 --- a/Scripts/Runtime/TMP_Text.cs +++ b/Scripts/Runtime/TMP_Text.cs @@ -448,6 +448,16 @@ public float outlineWidth protected float m_outlineWidth = 0.0f; + /// + /// The rotation for the environment map lighting. + /// + protected Vector3 m_currentEnvMapRotation; + /// + /// Determine if the environment map property is valid. + /// + protected bool m_hasEnvMapProperty; + + /// /// The point size of the font. /// @@ -4667,8 +4677,8 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m if ((isWhiteSpace || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060) { - // Ignore Hyphen (0x2D) when preceded by a whitespace - if ((charCode == 0x2D && m_characterCount > 0 && char.IsWhiteSpace(m_textInfo.characterInfo[m_characterCount - 1].character)) == false) + // Case 1391990 - Text after hyphen breaks when the hyphen is connected to the text + if (!(charCode == 0x2D && m_characterCount > 0 && char.IsWhiteSpace(m_textInfo.characterInfo[m_characterCount - 1].character) && m_textInfo.characterInfo[m_characterCount - 1].lineNumber == m_lineNumber)) { isFirstWordOfLine = false; shouldSaveHardLineBreak = true; @@ -4968,7 +4978,7 @@ internal void InsertNewLine(int i, float baseScale, float currentElementScale, f m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1; m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount; - m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = m_lineVisibleSpaceCount; + m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = (m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex + 1 - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex) - m_lineVisibleCharacterCount; m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender); m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender); m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x; @@ -5403,6 +5413,8 @@ protected virtual void SaveSpriteVertexInfo(Color32 vertexColor) c3 = m_tintSprite ? c3.Multiply(m_colorGradientPreset.bottomRight) : c3; } + m_tintSprite = false; + m_textInfo.characterInfo[m_characterCount].vertex_BL.color = c0; m_textInfo.characterInfo[m_characterCount].vertex_TL.color = c1; m_textInfo.characterInfo[m_characterCount].vertex_TR.color = c2; @@ -6079,19 +6091,13 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F { //Debug.Log("Unicode: " + unicode.ToString("X8")); - TMP_Character character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, fontAsset, false, fontStyle, fontWeight, out isUsingAlternativeTypeface); - - if (character != null) - return character; - - // Search potential list of fallback font assets assigned to the font asset. - if (fontAsset.m_FallbackFontAssetTable != null && fontAsset.m_FallbackFontAssetTable.Count > 0) - character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, fontAsset, fontAsset.m_FallbackFontAssetTable, true, fontStyle, fontWeight, out isUsingAlternativeTypeface); + // Search the font asset and potential fallback for the requested character + TMP_Character character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, fontAsset, true, fontStyle, fontWeight, out isUsingAlternativeTypeface); if (character != null) { // Add character to font asset lookup cache - fontAsset.AddCharacterToLookupCache(unicode, character); + fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface); return character; } @@ -6105,11 +6111,8 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F // Use material and index of primary font asset. if (character != null) { - m_currentMaterialIndex = 0; - m_currentMaterial = m_materialReferences[0].material; - // Add character to font asset lookup cache - fontAsset.AddCharacterToLookupCache(unicode, character); + fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface); return character; } @@ -6121,7 +6124,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F if (character != null) { // Add character to font asset lookup cache - fontAsset.AddCharacterToLookupCache(unicode, character); + fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface); return character; } @@ -6143,7 +6146,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F if (character != null) { // Add character to font asset lookup cache - fontAsset.AddCharacterToLookupCache(unicode, character); + fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface); return character; } @@ -6155,7 +6158,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F if (character != null) { // Add character to font asset lookup cache - fontAsset.AddCharacterToLookupCache(unicode, character); + fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface); return character; } @@ -6169,6 +6172,44 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F return spriteCharacter; } + // Since we have been unable to locate the character thus far using the designated font style and weight. Attempt to locate this character using normal style and regular font weight in order to synthesize it. + if (fontStyle != FontStyles.Normal || fontWeight != FontWeight.Regular) + { + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, fontAsset, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); + + if (character != null) + { + // Add character to font asset lookup cache + fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface); + + return character; + } + + // Search potential Global fallback font assets. + if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) + character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, fontAsset, TMP_Settings.fallbackFontAssets, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); + + if (character != null) + { + // Add character to font asset lookup cache + fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface); + + return character; + } + + // Search for the character in the Default Font Asset assigned in the TMP Settings file. + if (TMP_Settings.defaultFontAsset != null) + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, TMP_Settings.defaultFontAsset, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); + + if (character != null) + { + // Add character to font asset lookup cache + fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface); + + return character; + } + } + return null; } diff --git a/Scripts/Runtime/TextMeshPro.cs b/Scripts/Runtime/TextMeshPro.cs index d374120..05abff2 100644 --- a/Scripts/Runtime/TextMeshPro.cs +++ b/Scripts/Runtime/TextMeshPro.cs @@ -17,7 +17,11 @@ namespace TMPro [RequireComponent(typeof(MeshRenderer))] [AddComponentMenu("Mesh/TextMeshPro - Text")] [ExecuteAlways] + #if UNITY_2023_2_OR_NEWER + [HelpURL("https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/TextMeshPro/index.html")] + #else [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2")] + #endif public class TextMeshPro : TMP_Text, ILayoutElement { // Public Properties and Serializable Properties @@ -832,7 +836,7 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) //m_sharedMaterialHashCode = TMP_TextUtilities.GetSimpleHashCode(m_sharedMaterial.name); UpdateMask(); - UpdateEnvMapMatrix(); + ValidateEnvMapProperty(); m_havePropertiesChanged = true; SetVerticesDirty(); @@ -972,6 +976,9 @@ protected override void LoadFontAsset() } } + // Cache environment map property validation. + ValidateEnvMapProperty(); + m_padding = GetPaddingForMaterial(); m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial); @@ -981,14 +988,32 @@ protected override void LoadFontAsset() SetMaterialDirty(); } + /// + /// Method to check if the environment map property is valid. + /// + void ValidateEnvMapProperty() + { + if (m_sharedMaterial != null) + m_hasEnvMapProperty = m_sharedMaterial.HasProperty(ShaderUtilities.ID_EnvMap) && m_sharedMaterial.GetTexture(ShaderUtilities.ID_EnvMap) != null; + else + m_hasEnvMapProperty = false; + } void UpdateEnvMapMatrix() { - if (!m_sharedMaterial.HasProperty(ShaderUtilities.ID_EnvMap) || m_sharedMaterial.GetTexture(ShaderUtilities.ID_EnvMap) == null) + if (!m_hasEnvMapProperty) return; //Debug.Log("Updating Env Matrix..."); Vector3 rotation = m_sharedMaterial.GetVector(ShaderUtilities.ID_EnvMatrixRotation); + #if !UNITY_EDITOR + // The matrix property is reverted on editor save because m_sharedMaterial will be replaced with a new material instance. + // Disable rotation change check if editor to handle this material change. + if (m_currentEnvMapRotation == rotation) + return; + #endif + + m_currentEnvMapRotation = rotation; m_EnvMapMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(rotation), Vector3.one); m_sharedMaterial.SetMatrix(ShaderUtilities.ID_EnvMatrix, m_EnvMapMatrix); @@ -1398,6 +1423,7 @@ void SetPerspectiveCorrection() } + Dictionary materialIndexPairs = new Dictionary(); // This function parses through the Char[] to determine how many characters will be visible. It then makes sure the arrays are large enough for all those characters. internal override int SetArraySizes(TextProcessingElement[] textProcessingArray) { @@ -1625,34 +1651,34 @@ internal override int SetArraySizes(TextProcessingElement[] textProcessingArray) unicode = textProcessingArray[i].unicode = (uint)TMP_Settings.missingGlyphCharacter == 0 ? 9633 : (uint)TMP_Settings.missingGlyphCharacter; // Check for the missing glyph character in the currently assigned font asset and its fallbacks - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); if (character == null) { // Search for the missing glyph character in the TMP Settings Fallback list. if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) - character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, m_currentFontAsset, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); + character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, m_currentFontAsset, TMP_Settings.fallbackFontAssets, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); } if (character == null) { // Search for the missing glyph in the TMP Settings Default Font Asset. if (TMP_Settings.defaultFontAsset != null) - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, TMP_Settings.defaultFontAsset, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); } if (character == null) { // Use Space (32) Glyph from the currently assigned font asset. unicode = textProcessingArray[i].unicode = 32; - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); } if (character == null) { // Use End of Text (0x03) Glyph from the currently assigned font asset. unicode = textProcessingArray[i].unicode = 0x03; - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); } if (!TMP_Settings.warningsDisabled) @@ -1674,6 +1700,7 @@ internal override int SetArraySizes(TextProcessingElement[] textProcessingArray) { isUsingFallbackOrAlternativeTypeface = true; m_currentFontAsset = character.textAsset as TMP_FontAsset; + m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentFontAsset.material, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); } #region VARIATION SELECTOR @@ -1809,6 +1836,21 @@ internal override int SetArraySizes(TextProcessingElement[] textProcessingArray) // Limit the mesh of the main text object to 65535 vertices and use sub objects for the overflow. if (m_materialReferences[m_currentMaterialIndex].referenceCount < 16383) m_materialReferences[m_currentMaterialIndex].referenceCount += 1; + else if (isUsingFallbackOrAlternativeTypeface) + { + if (materialIndexPairs.TryGetValue(m_currentMaterialIndex, out int prev_fallbackMaterialIndex) && m_materialReferences[prev_fallbackMaterialIndex].referenceCount < 16383) + { + m_currentMaterialIndex = prev_fallbackMaterialIndex; + } + else + { + int fallbackMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); + materialIndexPairs[m_currentMaterialIndex] = fallbackMaterialIndex; + m_currentMaterialIndex = fallbackMaterialIndex; + } + + m_materialReferences[m_currentMaterialIndex].referenceCount += 1; + } else { m_currentMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); @@ -2033,6 +2075,9 @@ internal override void InternalUpdate() m_havePropertiesChanged = true; OnPreRenderObject(); } + + // Update Environment Matrix property to support changing the rotation via a script. + UpdateEnvMapMatrix(); } @@ -3752,8 +3797,17 @@ protected virtual void GenerateTextMesh() if (charCode == 9) { float tabSize = m_currentFontAsset.m_FaceInfo.tabWidth * m_currentFontAsset.tabSize * currentElementScale; - float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize; - m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize; + // Adjust horizontal tab depending on RTL + if (m_isRightToLeft) + { + float tabs = Mathf.Floor(m_xAdvance / tabSize) * tabSize; + m_xAdvance = tabs < m_xAdvance ? tabs : m_xAdvance - tabSize; + } + else + { + float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize; + m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize; + } } else if (m_monoSpacing != 0) { @@ -3873,7 +3927,7 @@ protected virtual void GenerateTextMesh() m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1; m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount; - m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = (m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex + 1) - m_lineVisibleCharacterCount; + m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = (m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex + 1 - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex) - m_lineVisibleCharacterCount; m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender); m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender); m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x - (padding * currentElementScale); @@ -3982,8 +4036,8 @@ protected virtual void GenerateTextMesh() if ((isWhiteSpace || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060) { - // Ignore Hyphen (0x2D) when preceded by a whitespace - if ((charCode == 0x2D && m_characterCount > 0 && char.IsWhiteSpace(m_textInfo.characterInfo[m_characterCount - 1].character)) == false) + // Case 1391990 - Text after hyphen breaks when the hyphen is connected to the text + if (!(charCode == 0x2D && m_characterCount > 0 && char.IsWhiteSpace(m_textInfo.characterInfo[m_characterCount - 1].character) && m_textInfo.characterInfo[m_characterCount - 1].lineNumber == m_lineNumber)) { isFirstWordOfLine = false; shouldSaveHardLineBreak = true; diff --git a/Scripts/Runtime/TextMeshProUGUI.cs b/Scripts/Runtime/TextMeshProUGUI.cs index 0d3a213..1bfb474 100644 --- a/Scripts/Runtime/TextMeshProUGUI.cs +++ b/Scripts/Runtime/TextMeshProUGUI.cs @@ -21,7 +21,11 @@ namespace TMPro [RequireComponent(typeof(CanvasRenderer))] [AddComponentMenu("UI/TextMeshPro - Text (UI)", 11)] [ExecuteAlways] + #if UNITY_2023_2_OR_NEWER + [HelpURL("https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/TextMeshPro/index.html")] + #else [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2")] + #endif public class TextMeshProUGUI : TMP_Text, ILayoutElement { /// @@ -1113,6 +1117,7 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) } m_padding = GetPaddingForMaterial(); + ValidateEnvMapProperty(); m_havePropertiesChanged = true; SetVerticesDirty(); //SetMaterialDirty(); @@ -1252,6 +1257,8 @@ protected override void LoadFontAsset() } } + // Cache environment map property validation. + ValidateEnvMapProperty(); // Find and cache Underline & Ellipsis characters. GetSpecialCharacters(m_fontAsset); @@ -1289,18 +1296,36 @@ private Canvas GetCanvas() return canvas; } + /// + /// Method to check if the environment map property is valid. + /// + void ValidateEnvMapProperty() + { + if (m_sharedMaterial != null) + m_hasEnvMapProperty = m_sharedMaterial.HasProperty(ShaderUtilities.ID_EnvMap) && m_sharedMaterial.GetTexture(ShaderUtilities.ID_EnvMap) != null; + else + m_hasEnvMapProperty = false; + } /// /// Method used when animating the Env Map on the material. /// void UpdateEnvMapMatrix() { - if (!m_sharedMaterial.HasProperty(ShaderUtilities.ID_EnvMap) || m_sharedMaterial.GetTexture(ShaderUtilities.ID_EnvMap) == null) + if (!m_hasEnvMapProperty) return; //Debug.Log("Updating Env Matrix..."); Vector3 rotation = m_sharedMaterial.GetVector(ShaderUtilities.ID_EnvMatrixRotation); - m_EnvMapMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(rotation), Vector3.one); + #if !UNITY_EDITOR + // The matrix property is reverted on editor save because m_sharedMaterial will be replaced with a new material instance. + // Disable rotation change check if editor to handle this material change. + if (m_currentEnvMapRotation == rotation) + return; + #endif + + m_currentEnvMapRotation = rotation; + m_EnvMapMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(m_currentEnvMapRotation), Vector3.one); m_sharedMaterial.SetMatrix(ShaderUtilities.ID_EnvMatrix, m_EnvMapMatrix); } @@ -1701,6 +1726,7 @@ void SetMeshArrays(int size) } + Dictionary materialIndexPairs = new Dictionary(); // This function parses through the Char[] to determine how many characters will be visible. It then makes sure the arrays are large enough for all those characters. internal override int SetArraySizes(TextProcessingElement[] textProcessingArray) { @@ -1940,34 +1966,34 @@ internal override int SetArraySizes(TextProcessingElement[] textProcessingArray) unicode = textProcessingArray[i].unicode = (uint)TMP_Settings.missingGlyphCharacter == 0 ? 9633 : (uint)TMP_Settings.missingGlyphCharacter; // Check for the missing glyph character in the currently assigned font asset and its fallbacks - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); if (character == null) { // Search for the missing glyph character in the TMP Settings Fallback list. if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) - character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, m_currentFontAsset, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); + character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, m_currentFontAsset, TMP_Settings.fallbackFontAssets, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); } if (character == null) { // Search for the missing glyph in the TMP Settings Default Font Asset. if (TMP_Settings.defaultFontAsset != null) - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, TMP_Settings.defaultFontAsset, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); } if (character == null) { // Use Space (32) Glyph from the currently assigned font asset. unicode = textProcessingArray[i].unicode = 32; - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); } if (character == null) { // Use End of Text (0x03) Glyph from the currently assigned font asset. unicode = textProcessingArray[i].unicode = 0x03; - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, FontStyles.Normal, FontWeight.Regular, out isUsingAlternativeTypeface); } if (!TMP_Settings.warningsDisabled) @@ -1989,6 +2015,7 @@ internal override int SetArraySizes(TextProcessingElement[] textProcessingArray) { isUsingFallbackOrAlternativeTypeface = true; m_currentFontAsset = character.textAsset as TMP_FontAsset; + m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentFontAsset.material, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); } #region VARIATION SELECTOR @@ -2124,6 +2151,21 @@ internal override int SetArraySizes(TextProcessingElement[] textProcessingArray) // Limit the mesh of the main text object to 65535 vertices and use sub objects for the overflow. if (m_materialReferences[m_currentMaterialIndex].referenceCount < 16383) m_materialReferences[m_currentMaterialIndex].referenceCount += 1; + else if (isUsingFallbackOrAlternativeTypeface) + { + if (materialIndexPairs.TryGetValue(m_currentMaterialIndex, out int prev_fallbackMaterialIndex) && m_materialReferences[prev_fallbackMaterialIndex].referenceCount < 16383) + { + m_currentMaterialIndex = prev_fallbackMaterialIndex; + } + else + { + int fallbackMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); + materialIndexPairs[m_currentMaterialIndex] = fallbackMaterialIndex; + m_currentMaterialIndex = fallbackMaterialIndex; + } + + m_materialReferences[m_currentMaterialIndex].referenceCount += 1; + } else { m_currentMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup); @@ -2390,6 +2432,9 @@ internal override void InternalUpdate() m_havePropertiesChanged = true; OnPreRenderCanvas(); } + + // Update Environment Matrix property to support changing the rotation via a script. + UpdateEnvMapMatrix(); } @@ -4103,8 +4148,17 @@ protected virtual void GenerateTextMesh() if (charCode == 9) { float tabSize = m_currentFontAsset.m_FaceInfo.tabWidth * m_currentFontAsset.tabSize * currentElementScale; - float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize; - m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize; + // Adjust horizontal tab depending on RTL + if (m_isRightToLeft) + { + float tabs = Mathf.Floor(m_xAdvance / tabSize) * tabSize; + m_xAdvance = tabs < m_xAdvance ? tabs : m_xAdvance - tabSize; + } + else + { + float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize; + m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize; + } } else if (m_monoSpacing != 0) { @@ -4224,7 +4278,7 @@ protected virtual void GenerateTextMesh() m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1; m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount; - m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = (m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex + 1) - m_lineVisibleCharacterCount; + m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = (m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex + 1 - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex) - m_lineVisibleCharacterCount; m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender); m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender); m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x - (padding * currentElementScale); @@ -4333,8 +4387,8 @@ protected virtual void GenerateTextMesh() if ((isWhiteSpace || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060) { - // Ignore Hyphen (0x2D) when preceded by a whitespace - if ((charCode == 0x2D && m_characterCount > 0 && char.IsWhiteSpace(m_textInfo.characterInfo[m_characterCount - 1].character)) == false) + // Case 1391990 - Text after hyphen breaks when the hyphen is connected to the text + if (!(charCode == 0x2D && m_characterCount > 0 && char.IsWhiteSpace(m_textInfo.characterInfo[m_characterCount - 1].character) && m_textInfo.characterInfo[m_characterCount - 1].lineNumber == m_lineNumber)) { isFirstWordOfLine = false; shouldSaveHardLineBreak = true; @@ -4616,7 +4670,7 @@ protected virtual void GenerateTextMesh() { float gap = !m_isRightToLeft ? lineInfo.width - lineInfo.maxAdvance : lineInfo.width + lineInfo.maxAdvance; int visibleCount = lineInfo.visibleCharacterCount - 1 + lineInfo.controlCharacterCount; - int spaces = lineInfo.spaceCount - lineInfo.controlCharacterCount; + int spaces = lineInfo.visibleSpaceCount - lineInfo.controlCharacterCount; if (isFirstSeperator) { spaces -= 1; visibleCount += 1; } diff --git a/ValidationExceptions.json b/ValidationExceptions.json index 6bedbca..8e5821b 100644 --- a/ValidationExceptions.json +++ b/ValidationExceptions.json @@ -3,7 +3,7 @@ [ { "ValidationTest": "API Validation", - "PackageVersion": "3.2.0-pre.10" + "PackageVersion": "3.2.0-pre.11" } ] } diff --git a/package.json b/package.json index 221772b..4d59c4c 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "com.unity.textmeshpro", "displayName": "TextMeshPro", - "version": "3.2.0-pre.10", - "unity": "2020.3", + "version": "3.2.0-pre.11", + "unity": "2021.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.\n\n\n\nUPGRADE NOTE\n--------------------\nThis latest release of the TMP package includes updated TMP Essential Resources and TMP Examples & Extras. Be sure to update those via the \"Window - TextMeshPro - Import...\" menu options.", "keywords": [ "TextMeshPro", @@ -16,15 +16,15 @@ "com.unity.ugui": "1.0.0" }, "_upm": { - "changelog": "### Changes\n- Ensure space and underline are always added to Static FontAsset. (UUM-45512)\n- Fix bug that occurs with the Input Field, resulting in an incorrect cursor position when modifying long text input. (UUM-58685)\n- Fixed compile error on TMP_PostBuildProcessorHandler.cs when building for iOS with \"install into source code (UUM-57710)\n- Fixed opening style tag overflow and TextSettings upgrade (UUM-30205)\n- Fixed crash occurring in the FontAssetCreatorWindow. (UUM-66950)\n- Fixed incorrect handling of Emoji Presentation forms ensuring emojis such as ©️ are displayed in text form, whereas 🚀 is displayed in its color presentation form.\n- Added support for Emoji Variant Selectors u+FE0E and u+FE0F to control whether an emoji is displayed in text or presentation form: GitHub PR #48504" + "changelog": "### Changes\n- Fix TMP crash on Hyphen wrapping.\n- Fixed incorrect character caching when font styles and weights are used. UUM-87529\n- Fixed the iOS crash when using fallback fonts created at runtime.\n- Fix exception thrown when pasting text into TMP inputfield with custom validator.\n- Avoid creating a new submesh if the previous one still has space.\n- Fixed the broken URL of the help button on the TextMesh Pro Settings page." }, "upmCi": { - "footprint": "9ae25a969fffc8f9f1fda146dec38566dfcd1ba7" + "footprint": "db3d50dead0f904faf65dcb8f73b7225f4d338fc" }, "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2/manual/index.html", "repository": { "url": "https://github.cds.internal.unity3d.com/unity/unity.git", "type": "git", - "revision": "8fca90baea965bab48c88326f1d4ca248b64097b" + "revision": "68ef3a0b1270677875ef0b3139fe9cd282127d4e" } }