├── TextMeshProReplacer ├── Editor │ ├── CanvasUIEditor.cs │ └── TextReplacer.cs └── TextMeshReplacerReadme.pdf └── TextMeshReplacer.unitypackage /TextMeshProReplacer/Editor/CanvasUIEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | using UnityEngine.UI; 4 | 5 | namespace TextMeshProReplacer 6 | { 7 | //note: since it's impossible to extends text editor now, use canvas renderer instead 8 | //it was textmesh pro replacer but i extended it to store text properties 9 | [CustomEditor(typeof(CanvasRenderer))] 10 | class CanvasUIEditor :Editor 11 | { 12 | private CanvasRenderer canvasRenderer; 13 | private void OnEnable() 14 | { 15 | canvasRenderer = (CanvasRenderer)target; 16 | } 17 | 18 | public override void OnInspectorGUI() 19 | { 20 | base.OnInspectorGUI(); 21 | 22 | GUILayout.BeginHorizontal(); 23 | if(canvasRenderer.GetComponent()!=null && 24 | GUILayout.Button("Replace with TextMeshPro")) 25 | { 26 | TextReplacer.ReplaceUnityText(canvasRenderer.GetComponent()); 27 | GUIUtility.ExitGUI(); 28 | 29 | 30 | } 31 | GUILayout.EndHorizontal(); 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /TextMeshProReplacer/Editor/TextReplacer.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using TMPro; 6 | using UnityEditor; 7 | using UnityEditor.SceneManagement; 8 | using UnityEngine; 9 | using UnityEngine.SceneManagement; 10 | using UnityEngine.UI; 11 | 12 | namespace TextMeshProReplacer 13 | { 14 | internal class TextReplacer 15 | { 16 | [MenuItem("Text Mesh Replacer/Replace Current Scene")] 17 | internal static void ReplaceCurrentScene() 18 | { 19 | 20 | GameObject[] rootGameObjects = SceneManager.GetActiveScene().GetRootGameObjects(); 21 | for (int i = 0; i < rootGameObjects.Length; i++) 22 | { 23 | GameObject root = rootGameObjects[i]; 24 | for (int j = 0; j < root.transform.childCount; j++) 25 | { 26 | Text text = root.transform.GetChild(j).GetComponent(); 27 | if (text) 28 | ReplaceUnityText(text); 29 | } 30 | } 31 | 32 | } 33 | 34 | [MenuItem("Text Mesh Replacer/Replace All Scene")] 35 | internal static void ReplaceAllScene() 36 | { 37 | SceneAsset[] scenes = FindAssets(); 38 | for (int i = 0; i < scenes.Length; i++) 39 | { 40 | SceneAsset scene = scenes[i]; 41 | EditorUtility.DisplayProgressBar("Replacing in scene...", scene.name, (float)i / scenes.Length); 42 | Scene loadedScene=EditorSceneManager.OpenScene(AssetDatabase.GetAssetPath(scene),OpenSceneMode.Single); 43 | while (!loadedScene.isLoaded) 44 | { 45 | //wait 46 | } 47 | 48 | ReplaceCurrentScene(); 49 | 50 | EditorSceneManager.SaveScene(loadedScene); 51 | } 52 | EditorUtility.ClearProgressBar(); 53 | } 54 | [MenuItem("Text Mesh Replacer/Test/Test Canvas")] 55 | internal static void GenerateDemoCanvas() 56 | { 57 | //generate new canvas 58 | GameObject newCanvas=new GameObject("TestCanvas"); 59 | Canvas canvas=newCanvas.AddComponent(); 60 | canvas.renderMode=RenderMode.ScreenSpaceOverlay; 61 | 62 | //find all fonts 63 | Font[] allFonts = FindAssets(); 64 | //generate texts inside canvas 65 | StringBuilder stringBuilder=new StringBuilder(); 66 | 67 | for (int i = 0; i < 100; i++) 68 | { 69 | Text testText=new GameObject("test text").AddComponent(); 70 | testText.transform.SetParent(newCanvas.transform,false); 71 | testText.rectTransform.anchoredPosition=new Vector2(Random.Range(-300,300), Random.Range(-300, 300)); 72 | if (allFonts.Length > 0) 73 | testText.font = allFonts[Random.Range(0, allFonts.Length)]; 74 | //generate random string 75 | int stringLength = Random.Range(5, 20); 76 | stringBuilder.Length = 0; 77 | while (stringLength>0) 78 | { 79 | stringBuilder.Append(char.ConvertFromUtf32(Random.Range(48, 123))); 80 | stringLength--; 81 | } 82 | testText.text = stringBuilder.ToString(); 83 | 84 | } 85 | 86 | EditorUtility.SetDirty(canvas); 87 | 88 | ReplaceUnityText(canvas.GetComponentsInChildren()); 89 | } 90 | internal static void ReplaceUnityText(Text[] unityTexts) 91 | { 92 | TMP_FontAsset[] fonts = FindAssets(); 93 | if(fonts.Length==0) 94 | return; 95 | List missingFonts=new List(); 96 | for (int i = 0; i < unityTexts.Length; i++) 97 | { 98 | Text text = unityTexts[i]; 99 | 100 | if (!missingFonts.Contains(text.font)) 101 | { 102 | TMP_FontAsset font = GetTMPFont(text.font, fonts); 103 | if (font != null) 104 | ReplaceUnityText(text, font); 105 | else 106 | missingFonts.Add(text.font); 107 | } 108 | } 109 | //log missing fonts if any 110 | if (missingFonts.Count==0) 111 | return; 112 | Debug.LogWarningFormat("Missing {0} fonts",missingFonts.Count); 113 | for (int i = 0; i < missingFonts.Count; i++) 114 | { 115 | Debug.LogWarningFormat("Text Mesh pro Font {0} is missing", missingFonts[i].name); 116 | } 117 | } 118 | 119 | internal static void ReplaceUnityText(Text unityText) 120 | { 121 | TMP_FontAsset[] fonts = FindAssets(); 122 | if (fonts.Length == 0) 123 | return; 124 | TMP_FontAsset font = GetTMPFont(unityText.font, fonts); 125 | if (font != null) 126 | ReplaceUnityText(unityText, font); 127 | else 128 | Debug.LogWarningFormat("Text Mesh pro Font {0} is missing",unityText.font.name); 129 | } 130 | private static void ReplaceUnityText(Text unityText,TMP_FontAsset font) 131 | { 132 | Text currentText = unityText; 133 | //check if tmpro text already exist, if not dont replace 134 | 135 | //for some reason adding tmpro component messed up the rect transform size 136 | //this will fix it 137 | Vector2 size = currentText.rectTransform.sizeDelta; 138 | 139 | TextData textData = new TextData(); 140 | GameObject obj = unityText.gameObject; 141 | textData.write(currentText,font); 142 | 143 | //Selection.activeObject = obj; 144 | Undo.DestroyObjectImmediate(currentText); 145 | 146 | 147 | TextMeshProUGUI tmproText = Undo.AddComponent(obj); 148 | tmproText.autoSizeTextContainer = false; 149 | textData.read(tmproText); 150 | tmproText.rectTransform.sizeDelta = size; 151 | 152 | EditorUtility.SetDirty(tmproText); 153 | } 154 | private static T[] FindAssets() where T : Object 155 | { 156 | List result = new List(); 157 | string[] assetList = AssetDatabase.FindAssets(string.Format("t:{0}", typeof(T).Name)); 158 | for (int i = 0; i < assetList.Length; i++) 159 | { 160 | string assetPath = AssetDatabase.GUIDToAssetPath(assetList[i]); 161 | T asset = AssetDatabase.LoadAssetAtPath(assetPath); 162 | if (asset != null) 163 | { 164 | result.Add(asset); 165 | } 166 | } 167 | return result.ToArray(); 168 | } 169 | 170 | private static TMP_FontAsset GetTMPFont(Font m_font, TMP_FontAsset[] fonts) 171 | { 172 | 173 | for (int i = 0; i < fonts.Length; i++) 174 | { 175 | TMP_FontAsset fontAsset = fonts[i]; 176 | if (fontAsset.faceInfo.familyName.Equals(m_font.name, System.StringComparison.OrdinalIgnoreCase)) 177 | return fontAsset; 178 | 179 | if (System.Array.Exists(m_font.fontNames,(s)=>s.Equals(fontAsset.faceInfo.familyName,System.StringComparison.OrdinalIgnoreCase))) 180 | { 181 | return fontAsset; 182 | } 183 | } 184 | return null; 185 | } 186 | } 187 | internal struct TextData 188 | { 189 | private string text; 190 | private TextAlignmentOptions anchor; 191 | private TMP_FontAsset tmProfont; 192 | private Color color; 193 | private float fontSize; 194 | private FontStyles fontStyle; 195 | private bool autoResize; 196 | private Vector2 minMaxSize; 197 | public void write(Text textObject,TMP_FontAsset tmpFont) 198 | { 199 | 200 | color = textObject.color; 201 | fontSize = textObject.fontSize; 202 | fontStyle = GetFontStyle(textObject.fontStyle); 203 | tmProfont = tmpFont; 204 | anchor = GetTextAlignment(textObject.alignment); 205 | autoResize = textObject.resizeTextForBestFit; 206 | minMaxSize = new Vector2(textObject.resizeTextMinSize, textObject.resizeTextMaxSize); 207 | if (textObject.fontStyle == FontStyle.BoldAndItalic) 208 | text = string.Format("{0}", textObject.text); 209 | else 210 | text = textObject.text; 211 | } 212 | 213 | public void read(TextMeshProUGUI textObject) 214 | { 215 | textObject.text = text; 216 | textObject.color = color; 217 | textObject.fontSize = fontSize; 218 | textObject.fontStyle = fontStyle; 219 | 220 | textObject.alignment = anchor; 221 | 222 | textObject.enableAutoSizing = autoResize; 223 | textObject.fontSizeMin = minMaxSize.x; 224 | textObject.fontSizeMax = minMaxSize.y; 225 | 226 | if (tmProfont != null) 227 | textObject.font = tmProfont; 228 | 229 | } 230 | private FontStyles GetFontStyle(FontStyle style) 231 | { 232 | switch (style) 233 | { 234 | case FontStyle.Bold: 235 | return FontStyles.Bold; 236 | case FontStyle.Italic: 237 | return FontStyles.Italic; 238 | case FontStyle.BoldAndItalic: 239 | //this might not work because it's protected inside tmpro internal 240 | //and i'm too lazy too look it up :P 241 | //so i added tags around it 242 | return FontStyles.Bold; 243 | } 244 | return FontStyles.Normal; 245 | } 246 | private TextAlignmentOptions GetTextAlignment(TextAnchor m_anchor) 247 | { 248 | switch (m_anchor) 249 | { 250 | case TextAnchor.LowerCenter: 251 | return TextAlignmentOptions.Bottom; 252 | case TextAnchor.LowerLeft: 253 | return TextAlignmentOptions.BottomLeft; 254 | case TextAnchor.LowerRight: 255 | return TextAlignmentOptions.BottomRight; 256 | case TextAnchor.MiddleCenter: 257 | return TextAlignmentOptions.Center; 258 | case TextAnchor.MiddleLeft: 259 | return TextAlignmentOptions.MidlineLeft; 260 | case TextAnchor.MiddleRight: 261 | return TextAlignmentOptions.MidlineRight; 262 | case TextAnchor.UpperCenter: 263 | return TextAlignmentOptions.Top; 264 | case TextAnchor.UpperLeft: 265 | return TextAlignmentOptions.TopLeft; 266 | case TextAnchor.UpperRight: 267 | return TextAlignmentOptions.TopRight; 268 | default: 269 | return TextAlignmentOptions.Baseline; 270 | } 271 | } 272 | 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /TextMeshProReplacer/TextMeshReplacerReadme.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackisgames/TextMeshProReplacer/85f6285701f87ca6d649e31f58986dcf8fa49c0d/TextMeshProReplacer/TextMeshReplacerReadme.pdf -------------------------------------------------------------------------------- /TextMeshReplacer.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackisgames/TextMeshProReplacer/85f6285701f87ca6d649e31f58986dcf8fa49c0d/TextMeshReplacer.unitypackage --------------------------------------------------------------------------------