using Cryville.Common.Font; using Cryville.Culture; using System; using System.Collections.Generic; using System.Globalization; using System.Reflection; using TMPro; using UnityEngine; using UnityEngine.TextCore.LowLevel; using UnityEngine.TextCore.Text; namespace Cryville.Common.Unity.UI { [RequireComponent(typeof(TMP_Text))] public class TMPLocalizedText : MonoBehaviour { public static Shader DefaultShader; public static FontMatcher FontMatcher; public static int MaxFallbackCount = 4; static readonly Dictionary _cachedFonts = new(); [SerializeField] Shader m_shader; public TMP_Text Text { get; private set; } void Awake() { Text = GetComponent(); } public void SetText(string text, CultureInfo culture = null) { Text.text = text; SetCulture(culture ?? CultureInfo.CurrentCulture); } void SetCulture(CultureInfo culture) { if (FontMatcher == null) return; string cultureName = culture.Name; if (string.IsNullOrEmpty(cultureName)) cultureName = CultureInfo.CurrentCulture.Name; if (!_cachedFonts.TryGetValue(culture, out var font)) { foreach (var typeface in FontMatcher.MatchLanguage(new LanguageId(cultureName), true)) { try { var ifont = CreateFontAsset(typeface.File.FullName, typeface.IndexInFile); if (m_shader) ifont.material.shader = m_shader; else if (DefaultShader) ifont.material.shader = DefaultShader; if (font == null) { font = ifont; if (MaxFallbackCount <= 0) break; } else { font.fallbackFontAssetTable ??= new List(); font.fallbackFontAssetTable.Add(ifont); if (font.fallbackFontAssetTable.Count >= MaxFallbackCount) break; } } catch (Exception) { } } _cachedFonts.Add(culture, font); } Text.font = font; } static MethodInfo _methodCreateFontAsset; static readonly object[] _paramsCreateFontAsset = new object[] { null, null, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024, Type.Missing, Type.Missing }; static FontAsset CreateFontAsset(string path, int index) { if (_methodCreateFontAsset == null) { _methodCreateFontAsset = typeof(FontAsset).GetMethod( "CreateFontAsset", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(string), typeof(int), typeof(int), typeof(int), typeof(GlyphRenderMode), typeof(int), typeof(int), typeof(AtlasPopulationMode), typeof(bool) }, null ); } _paramsCreateFontAsset[0] = path; _paramsCreateFontAsset[1] = index; return (FontAsset)_methodCreateFontAsset.Invoke(null, _paramsCreateFontAsset); } } }