Skip to content

Commit 12feba3

Browse files
author
Unity Technologies
committed
com.unity.textmeshpro@3.2.0-pre.12
## [3.2.0-pre.12] - 2025-03-07 - Fix for recently uncovered issues related to character caching which can occur when multiple text objects are present in a scene when fallback font assets are used in conjunction with font styles (ie. bold, italic, etc.).(UUM-97348) - Fix crash when reading fonts. (UUM-78322)
1 parent 49623db commit 12feba3

23 files changed

+192
-41
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Changelog
22
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
33

4+
## [3.2.0-pre.12] - 2025-03-07
5+
- Fix for recently uncovered issues related to character caching which can occur when multiple text objects are present in a scene when fallback font assets are used in conjunction with font styles (ie. bold, italic, etc.).(UUM-97348)
6+
- Fix crash when reading fonts. (UUM-78322)
7+
48
## [3.2.0-pre.11] - 2024-12-09
59
### Changes
610
- Fix TMP crash on Hyphen wrapping.
Binary file not shown.
Binary file not shown.

Scripts/Editor/TMP_EditorResourceManager.cs

-3
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,6 @@ void OnPreRenderCanvases()
104104

105105
#if UNITY_2023_3_OR_NEWER
106106
void OnEndOfFrame(ScriptableRenderContext renderContext, List<Camera> cameras)
107-
{
108-
DoPostRenderUpdates();
109-
}
110107
#else
111108
void OnEndOfFrame(ScriptableRenderContext renderContext, Camera[] cameras)
112109
{

Scripts/Editor/TMPro_ContextMenus.cs

+25-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
using UnityEditor;
33
using System.IO;
44
using System.Collections;
5-
5+
using UnityEngine.TextCore.LowLevel;
6+
using UnityEngine.TextCore.Text;
67

78
namespace TMPro.EditorUtilities
89
{
@@ -420,6 +421,29 @@ static void ClearFontCharacterData(MenuCommand command)
420421
TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset);
421422
}
422423

424+
[MenuItem("CONTEXT/TMP_FontAsset/Reset FaceInfo", priority = 101)]
425+
static void ResetFaceInfo(MenuCommand command)
426+
{
427+
TMP_FontAsset fontAsset = command.context as TMP_FontAsset;
428+
429+
if (fontAsset == null)
430+
return;
431+
432+
if (Selection.activeObject != fontAsset)
433+
Selection.activeObject = fontAsset;
434+
435+
if (fontAsset.LoadFontFace() != FontEngineError.Success)
436+
return;
437+
438+
fontAsset.faceInfo = FontEngine.GetFaceInfo();
439+
TextResourceManager.RebuildFontAssetCache();
440+
TextEventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset);
441+
442+
EditorUtility.SetDirty(fontAsset);
443+
AssetDatabase.SaveAssetIfDirty(fontAsset);
444+
AssetDatabase.Refresh();
445+
}
446+
423447
/// <summary>
424448
/// Import all font features
425449
/// </summary>

Scripts/Editor/TMPro_FontAssetCreatorWindow.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -474,13 +474,13 @@ void DrawControls()
474474
GUILayout.BeginHorizontal();
475475
EditorGUI.BeginChangeCheck();
476476

477-
m_PaddingFieldValue = EditorGUILayout.FloatField("Padding", m_PaddingFieldValue);
477+
m_PaddingFieldValue = Mathf.Max(EditorGUILayout.FloatField("Padding", m_PaddingFieldValue), 0);
478478

479479
int selection = m_PaddingMode == PaddingMode.Undefined || m_PaddingMode == PaddingMode.Pixel ? 1 : 0;
480480
selection = GUILayout.SelectionGrid(selection, k_PaddingOptionLabels, 2);
481481

482482
if (m_PaddingMode == PaddingMode.Percentage)
483-
m_PaddingFieldValue = (int)(m_PaddingFieldValue + 0.5f);
483+
m_PaddingFieldValue = Mathf.Min((int)(m_PaddingFieldValue + 0.5f), float.MaxValue);
484484

485485
if (EditorGUI.EndChangeCheck())
486486
{

Scripts/Runtime/TMP_DefaultControls.cs

+4
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,11 @@ private static void SetParentAndAlign(GameObject child, GameObject parent)
8787
if (parent == null)
8888
return;
8989

90+
#if UNITY_EDITOR
91+
Undo.SetTransformParent(child.transform, parent.transform, "");
92+
#else
9093
child.transform.SetParent(parent.transform, false);
94+
#endif
9195
SetLayerRecursively(child, parent.layer);
9296
}
9397

Scripts/Runtime/TMP_FontAsset.cs

+15-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using UnityEngine.TextCore;
77
using UnityEngine.TextCore.LowLevel;
88
using Unity.Profiling;
9+
using Unity.Jobs.LowLevel.Unsafe;
910

1011
#if UNITY_EDITOR
1112
using UnityEditor;
@@ -807,11 +808,20 @@ public void ReadFontAssetDefinition()
807808
m_AtlasPadding = (int)material.GetFloat(ShaderUtilities.ID_GradientScale) - 1;
808809
}
809810

810-
#if TEXTCORE_FONT_ENGINE_1_5_OR_NEWER
811+
#if TEXTCORE_FONT_ENGINE_1_5_OR_NEWER
811812
// Update Units per EM for pre-existing font assets.
812-
if (m_FaceInfo.unitsPerEM == 0)
813-
m_FaceInfo.unitsPerEM = FontEngine.GetFaceInfo().unitsPerEM;
814-
#endif
813+
if (m_FaceInfo.unitsPerEM == 0 && atlasPopulationMode != AtlasPopulationMode.Static)
814+
{
815+
// Only retrieve Units Per EM if we are on the main thread.
816+
if (!JobsUtility.IsExecutingJob)
817+
{
818+
m_FaceInfo.unitsPerEM = FontEngine.GetFaceInfo().unitsPerEM;
819+
Debug.Log("Font Asset [" + name + "] Units Per EM set to " + m_FaceInfo.unitsPerEM + ". Please commit the newly serialized value.");
820+
}
821+
else
822+
Debug.LogError("Font Asset [" + name + "] is missing Units Per EM. Please select the 'Reset FaceInfo' menu item on Font Asset [" + name + "] to ensure proper serialization.");
823+
}
824+
#endif
815825

816826
// Compute hash codes for various properties of the font asset used for lookup.
817827
hashCode = TMP_TextUtilities.GetHashCode(this.name);
@@ -1160,7 +1170,7 @@ internal void AddCharacterToLookupCache(uint unicode, TMP_Character character, F
11601170
///
11611171
/// </summary>
11621172
/// <returns></returns>
1163-
FontEngineError LoadFontFace()
1173+
internal FontEngineError LoadFontFace()
11641174
{
11651175
if (m_AtlasPopulationMode == AtlasPopulationMode.Dynamic)
11661176
{

Scripts/Runtime/TMP_FontAssetUtilities.cs

+4-15
Original file line numberDiff line numberDiff line change
@@ -73,25 +73,14 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM
7373

7474
if (isItalic || fontWeight != FontWeight.Regular)
7575
{
76-
// Check if character is already cached using the composite unicode value the takes into consideration the font style and weight
76+
// Check if character is already cached using the composite Unicode value the takes into consideration the font style and weight
7777
uint compositeUnicodeLookupKey = ((0x80u | ((uint)fontStyle << 4) | ((uint)fontWeight / 100)) << 24) | unicode;
7878
if (sourceFontAsset.characterLookupTable.TryGetValue(compositeUnicodeLookupKey, out character))
7979
{
8080
// Set isAlternativeTypeface
8181
isAlternativeTypeface = true;
8282

83-
if (character.textAsset is not null)
84-
return character;
85-
86-
// Remove character from lookup table
87-
sourceFontAsset.characterLookupTable.Remove(unicode);
88-
}
89-
else if (sourceFontAsset.characterLookupTable.TryGetValue(compositeUnicodeLookupKey & 0x7FFFFFFF, out character))
90-
{
91-
// Set isAlternativeTypeface
92-
isAlternativeTypeface = false;
93-
94-
if (character.textAsset is not null)
83+
if (character.textAsset != null)
9584
return character;
9685

9786
// Remove character from lookup table
@@ -139,7 +128,7 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM
139128
{
140129
if (temp.characterLookupTable.TryGetValue(unicode, out character))
141130
{
142-
if (character.textAsset is not null)
131+
if (character.textAsset != null)
143132
{
144133
isAlternativeTypeface = true;
145134
return character;
@@ -171,7 +160,7 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM
171160
// Search the source font asset for the requested character
172161
if (sourceFontAsset.characterLookupTable.TryGetValue(unicode, out character))
173162
{
174-
if (character.textAsset is not null)
163+
if (character.textAsset != null)
175164
return character;
176165

177166
// Remove character from lookup table

Scripts/Runtime/TMP_InputField.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,7 @@ private bool TouchScreenKeyboardShouldBeUsed()
15381538
switch (platform)
15391539
{
15401540
case RuntimePlatform.Android:
1541+
case RuntimePlatform.WebGLPlayer:
15411542
if (s_IsQuestDevice)
15421543
return TouchScreenKeyboard.isSupported;
15431544

@@ -4191,6 +4192,9 @@ protected char Validate(string text, int pos, char ch)
41914192

41924193
var separator = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;
41934194
if (ch == Convert.ToChar(separator) && characterValidation == CharacterValidation.Decimal && !text.Contains(separator)) return ch;
4195+
4196+
//Some keyboards including Samsung require double tapping a . to get a - this allows these keyboards to input negative integers
4197+
if (characterValidation == CharacterValidation.Integer && ch == '.' && (pos == 0 || selectionAtStart)) return '-';
41944198
}
41954199
}
41964200
else if (characterValidation == CharacterValidation.Digit)
@@ -4523,7 +4527,7 @@ private void EnforceContentType()
45234527
{
45244528
m_LineType = LineType.SingleLine;
45254529
m_InputType = InputType.Standard;
4526-
m_KeyboardType = TouchScreenKeyboardType.NumberPad;
4530+
m_KeyboardType = TouchScreenKeyboardType.NumbersAndPunctuation;
45274531
m_CharacterValidation = CharacterValidation.Integer;
45284532
break;
45294533
}

Scripts/Runtime/TMP_InputValidator.cs

+7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ namespace TMPro
1010
[System.Serializable]
1111
public abstract class TMP_InputValidator : ScriptableObject
1212
{
13+
/// <summary>
14+
/// Customs text input validation function.
15+
/// </summary>
16+
/// <param name="text">The original text</param>
17+
/// <param name="pos">The position in the string to add the caharcter</param>
18+
/// <param name="ch">The character to add</param>
19+
/// <returns>The character added</returns>
1320
public abstract char Validate(ref string text, ref int pos, char ch);
1421
}
1522
}

Scripts/Runtime/TMP_SpriteAsset.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ public int GetSpriteIndexFromName (string name)
237237
if (m_NameLookup == null)
238238
UpdateLookupTables();
239239

240-
int hashCode = TMP_TextUtilities.GetSimpleHashCode(name);
240+
int hashCode = TMP_TextUtilities.GetHashCode(name);
241241

242242
return GetSpriteIndexFromHashcode(hashCode);
243243
}

Scripts/Runtime/TMP_Text.cs

+9-5
Original file line numberDiff line numberDiff line change
@@ -6180,7 +6180,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F
61806180
if (character != null)
61816181
{
61826182
// Add character to font asset lookup cache
6183-
fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface);
6183+
fontAsset.AddCharacterToLookupCache(unicode, character, FontStyles.Normal, FontWeight.Regular, isUsingAlternativeTypeface);
61846184

61856185
return character;
61866186
}
@@ -6192,7 +6192,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F
61926192
if (character != null)
61936193
{
61946194
// Add character to font asset lookup cache
6195-
fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface);
6195+
fontAsset.AddCharacterToLookupCache(unicode, character, FontStyles.Normal, FontWeight.Regular, isUsingAlternativeTypeface);
61966196

61976197
return character;
61986198
}
@@ -6204,7 +6204,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F
62046204
if (character != null)
62056205
{
62066206
// Add character to font asset lookup cache
6207-
fontAsset.AddCharacterToLookupCache(unicode, character, fontStyle, fontWeight, isUsingAlternativeTypeface);
6207+
fontAsset.AddCharacterToLookupCache(unicode, character, FontStyles.Normal, FontWeight.Regular, isUsingAlternativeTypeface);
62086208

62096209
return character;
62106210
}
@@ -7132,6 +7132,7 @@ internal bool ValidateHtmlTag(TextProcessingElement[] chars, int startIndex, out
71327132
case MarkupTag.SUBSCRIPT:
71337133
m_fontScaleMultiplier *= m_currentFontAsset.faceInfo.subscriptSize > 0 ? m_currentFontAsset.faceInfo.subscriptSize : 1;
71347134
m_baselineOffsetStack.Push(m_baselineOffset);
7135+
m_materialReferenceStack.Push(m_materialReferences[m_currentMaterialIndex]);
71357136
fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
71367137
m_baselineOffset += m_currentFontAsset.faceInfo.subscriptOffset * fontScale * m_fontScaleMultiplier;
71377138

@@ -7141,10 +7142,11 @@ internal bool ValidateHtmlTag(TextProcessingElement[] chars, int startIndex, out
71417142
case MarkupTag.SLASH_SUBSCRIPT:
71427143
if ((m_FontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript)
71437144
{
7145+
var previousFontAsset = m_materialReferenceStack.Pop().fontAsset;
71447146
if (m_fontScaleMultiplier < 1)
71457147
{
71467148
m_baselineOffset = m_baselineOffsetStack.Pop();
7147-
m_fontScaleMultiplier /= m_currentFontAsset.faceInfo.subscriptSize > 0 ? m_currentFontAsset.faceInfo.subscriptSize : 1;
7149+
m_fontScaleMultiplier /= previousFontAsset.faceInfo.subscriptSize > 0 ? previousFontAsset.faceInfo.subscriptSize : 1;
71487150
}
71497151

71507152
if (m_fontStyleStack.Remove(FontStyles.Subscript) == 0)
@@ -7154,6 +7156,7 @@ internal bool ValidateHtmlTag(TextProcessingElement[] chars, int startIndex, out
71547156
case MarkupTag.SUPERSCRIPT:
71557157
m_fontScaleMultiplier *= m_currentFontAsset.faceInfo.superscriptSize > 0 ? m_currentFontAsset.faceInfo.superscriptSize : 1;
71567158
m_baselineOffsetStack.Push(m_baselineOffset);
7159+
m_materialReferenceStack.Push(m_materialReferences[m_currentMaterialIndex]);
71577160
fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
71587161
m_baselineOffset += m_currentFontAsset.faceInfo.superscriptOffset * fontScale * m_fontScaleMultiplier;
71597162

@@ -7163,10 +7166,11 @@ internal bool ValidateHtmlTag(TextProcessingElement[] chars, int startIndex, out
71637166
case MarkupTag.SLASH_SUPERSCRIPT:
71647167
if ((m_FontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript)
71657168
{
7169+
var previousFontAsset = m_materialReferenceStack.Pop().fontAsset;
71667170
if (m_fontScaleMultiplier < 1)
71677171
{
71687172
m_baselineOffset = m_baselineOffsetStack.Pop();
7169-
m_fontScaleMultiplier /= m_currentFontAsset.faceInfo.superscriptSize > 0 ? m_currentFontAsset.faceInfo.superscriptSize : 1;
7173+
m_fontScaleMultiplier /= previousFontAsset.faceInfo.superscriptSize > 0 ? previousFontAsset.faceInfo.superscriptSize : 1;
71707174
}
71717175

71727176
if (m_fontStyleStack.Remove(FontStyles.Superscript) == 0)

Scripts/Runtime/TMP_TextUtilities.cs

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ public static bool IsIntersectingRectTransform(RectTransform rectTransform, Vect
321321

322322
/// <summary>
323323
/// Function returning the index of the character at the given position (if any).
324+
/// Returns @@-1@@ if no character is found at the specified position.
324325
/// </summary>
325326
/// <param name="text">A reference to the TextMeshPro component.</param>
326327
/// <param name="position">Position to check for intersection.</param>

Scripts/Runtime/TextMeshPro.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,9 @@ public override void UpdateVertexData()
477477
}
478478
}
479479

480+
/// <summary>
481+
/// Loads either the default font or a newly assigned font asset, and assigns the appropriate material to the renderer.
482+
/// </summary>
480483
public void UpdateFontAsset()
481484
{
482485
LoadFontAsset();
@@ -1700,7 +1703,7 @@ internal override int SetArraySizes(TextProcessingElement[] textProcessingArray)
17001703
{
17011704
isUsingFallbackOrAlternativeTypeface = true;
17021705
m_currentFontAsset = character.textAsset as TMP_FontAsset;
1703-
m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentFontAsset.material, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
1706+
//m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentFontAsset.material, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
17041707
}
17051708

17061709
#region VARIATION SELECTOR

Scripts/Runtime/TextMeshProUGUI.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2015,7 +2015,7 @@ internal override int SetArraySizes(TextProcessingElement[] textProcessingArray)
20152015
{
20162016
isUsingFallbackOrAlternativeTypeface = true;
20172017
m_currentFontAsset = character.textAsset as TMP_FontAsset;
2018-
m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentFontAsset.material, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
2018+
//m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentFontAsset.material, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
20192019
}
20202020

20212021
#region VARIATION SELECTOR
@@ -2379,7 +2379,7 @@ protected override void OnRectTransformDimensionsChange()
23792379

23802380
// Check if Canvas scale factor has changed as this requires an update of the SDF Scale.
23812381
bool hasCanvasScaleFactorChanged = false;
2382-
if (m_canvas != null && m_CanvasScaleFactor != m_canvas.scaleFactor)
2382+
if (m_canvas != null && !Mathf.Approximately(m_CanvasScaleFactor, m_canvas.scaleFactor))
23832383
{
23842384
m_CanvasScaleFactor = m_canvas.scaleFactor;
23852385
hasCanvasScaleFactorChanged = true;

Tests/Editor/FontEngineTests.cs

+13
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ public void CreateFontAsset_from_FontObject(string fontFileGUID, string familyNa
5454
Assert.AreEqual(fontAsset.faceInfo.styleName, styleName);
5555
}
5656

57+
[TestCase("e3265ab4bf004d28a9537516768c1c75", "Liberation Sans", "Regular")]
58+
public void TryAddCharacters_SanityCheck(string fontFileGUID, string familyName, string styleName)
59+
{
60+
string filePath = AssetDatabase.GUIDToAssetPath(fontFileGUID);
61+
62+
TMP_FontAsset fontAsset = TMP_FontAsset.CreateFontAsset(filePath, 0, 90, 9, GlyphRenderMode.SDFAA, 512, 512);
63+
64+
Assert.NotNull(fontAsset);
65+
66+
fontAsset.TryAddCharacters("abc");
67+
Assert.IsTrue(fontAsset.HasCharacters("abc"));
68+
}
69+
5770
// =============================================
5871
// FONT ENGINE - OPENTYPE TESTS
5972
// =============================================

0 commit comments

Comments
 (0)