Import Cryville.Culture in favor of ScriptUtils.

This commit is contained in:
2023-11-02 12:15:38 +08:00
parent 395c094890
commit bfa1423f64
34 changed files with 13725 additions and 88 deletions

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 520554ce9a8205b4b91e0ff2b8011673
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,37 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Cryville.Common.Culture {
public static class ScriptUtils {
public static string[] Scripts = new string[] { "adlm", "afak", "aghb", "ahom", "arab", "aran", "armi", "armn", "avst", "bali", "bamu", "bass", "batk", "beng", "bhks", "blis", "bopo", "brah", "brai", "bugi", "buhd", "cakm", "cans", "cari", "cham", "cher", "chrs", "cirt", "copt", "cpmn", "cprt", "cyrl", "cyrs", "deva", "diak", "dogr", "dsrt", "dupl", "egyd", "egyh", "egyp", "elba", "elym", "ethi", "geok", "geor", "glag", "gong", "gonm", "goth", "gran", "grek", "gujr", "guru", "hanb", "hang", "hani", "hano", "hans", "hant", "hatr", "hebr", "hira", "hluw", "hmng", "hmnp", "hrkt", "hung", "inds", "ital", "jamo", "java", "jpan", "jurc", "kali", "kana", "khar", "khmr", "khoj", "kitl", "kits", "knda", "kore", "kpel", "kthi", "lana", "laoo", "latf", "latg", "latn", "leke", "lepc", "limb", "lina", "linb", "lisu", "loma", "lyci", "lydi", "mahj", "maka", "mand", "mani", "marc", "maya", "medf", "mend", "merc", "mero", "mlym", "modi", "mong", "moon", "mroo", "mtei", "mult", "mymr", "nand", "narb", "nbat", "newa", "nkdb", "nkgb", "nkoo", "nshu", "ogam", "olck", "orkh", "orya", "osge", "osma", "ougr", "palm", "pauc", "pcun", "pelm", "perm", "phag", "phli", "phlp", "phlv", "phnx", "piqd", "plrd", "prti", "psin", "qaaa", "qaai", "qabx", "ranj", "rjng", "rohg", "roro", "runr", "samr", "sara", "sarb", "saur", "sgnw", "shaw", "shrd", "shui", "sidd", "sind", "sinh", "sogd", "sogo", "sora", "soyo", "sund", "sylo", "syrc", "syre", "syrj", "syrn", "tagb", "takr", "tale", "talu", "taml", "tang", "tavt", "telu", "teng", "tfng", "tglg", "thaa", "thai", "tibt", "tirh", "toto", "ugar", "vaii", "visp", "wara", "wcho", "wole", "xpeo", "xsux", "yezi", "yiii", "zanb", "zinh", "zmth", "zsye", "zsym", "zxxx", "zyyy", "zzzz", };
public static string UltimateFallbackScript = "zyyy";
public static Dictionary<string, string[]> FallbackScriptMap = new Dictionary<string, string[]> {
{ "aran", new string[] { "arab" } }, { "cyrs", new string[] { "cyrl" } },
{ "hanb", new string[] { "hant", "bopo" } }, { "hans", new string[] { "hani" } }, { "hant", new string[] { "hani" } },
{ "hrkt", new string[] { "hira", "kana" } }, { "jpan", new string[] { "hani", "hira", "kana" } },
{ "jamo", new string[] { "hang" } }, { "kore", new string[] { "hang", "hani" } },
{ "latf", new string[] { "latn" } }, { "latg", new string[] { "latn" } },
{ "syre", new string[] { "syrc" } }, { "syrj", new string[] { "syrc" } }, { "syrn", new string[] { "syrc" } },
{ "zsye", new string[] { "zsym" } },
};
public static void FillKeysWithScripts(IDictionary dict, Func<object> value) {
foreach (var s in Scripts) dict.Add(s, value());
}
public static IEnumerable<string> EnumerateFallbackScripts(string script) {
if (string.IsNullOrEmpty(script)) throw new ArgumentNullException("script");
script = script.ToLower();
if (script == UltimateFallbackScript) {
yield return null;
yield break;
}
string[] fblist;
if (FallbackScriptMap.TryGetValue(script, out fblist)) {
foreach (var fb in fblist) {
yield return fb;
}
}
else yield return UltimateFallbackScript;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ae9dab8f520fadc4194032f523ca87c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,4 +1,5 @@
using Cryville.Common.Culture;
using Cryville.Common.Logging;
using Cryville.Culture;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -7,13 +8,15 @@ namespace Cryville.Common.Font {
public abstract class FontMatcher {
protected FontManager Manager { get; private set; }
public FontMatcher(FontManager manager) { Manager = manager; }
public abstract IEnumerable<Typeface> MatchScript(string script = null, bool distinctFamily = false);
public abstract IEnumerable<Typeface> MatchLanguage(LanguageId lang, bool distinctFamily = false);
}
public class FallbackListFontMatcher : FontMatcher {
readonly LanguageMatching _matcher;
static readonly string UltimateFallbackScript = "zyyy";
public Dictionary<string, List<string>> MapScriptToTypefaces = new Dictionary<string, List<string>>();
public static Dictionary<string, List<string>> GetDefaultWindowsFallbackMap() {
var map = new Dictionary<string, List<string>>();
ScriptUtils.FillKeysWithScripts(map, () => new List<string>());
var map = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
FillKeysWithScripts(map, () => new List<string>());
// Reference: https://github.com/chromium/chromium/blob/main/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
map["zyyy"].Insert(0, "SimSun"); // Custom
map["zyyy"].Insert(0, "SimHei"); // Custom
@@ -158,8 +161,8 @@ namespace Cryville.Common.Font {
return map;
}
public static Dictionary<string, List<string>> GetDefaultAndroidFallbackMap() {
var map = new Dictionary<string, List<string>>();
ScriptUtils.FillKeysWithScripts(map, () => new List<string>());
var map = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
FillKeysWithScripts(map, () => new List<string>());
map["zyyy"].Insert(0, "Noto Sans CJK TC"); // TODO Modify default fallback
map["zyyy"].Insert(0, "Noto Sans CJK JP");
map["zyyy"].Insert(0, "Noto Sans CJK SC");
@@ -275,9 +278,9 @@ namespace Cryville.Common.Font {
map["sund"].Insert(0, "Noto Sans Sundanese");
map["sylo"].Insert(0, "Noto Sans Syloti Nagri");
map["zsym"].Insert(0, "Noto Sans Symbols");
map["syrn"].Insert(0, "Noto Sans Syriac Eastern");
map["syre"].Insert(0, "Noto Sans Syriac Estrangela");
map["syrj"].Insert(0, "Noto Sans Syriac Western");
map["syrc"].Add("Noto Sans Syriac Eastern");
map["syrc"].Add("Noto Sans Syriac Western");
map["syrc"].Add("Noto Sans Syriac Estrangela");
map["tglg"].Insert(0, "Noto Sans Tagalog");
map["tagb"].Insert(0, "Noto Sans Tagbanwa");
map["tale"].Insert(0, "Noto Sans Tai Le");
@@ -296,33 +299,45 @@ namespace Cryville.Common.Font {
map["yiii"].Insert(0, "Noto Sans Yi");
return map;
}
public FallbackListFontMatcher(FontManager manager) : base(manager) { }
public override IEnumerable<Typeface> MatchScript(string script = null, bool distinctFamily = false) {
if (string.IsNullOrEmpty(script)) script = ScriptUtils.UltimateFallbackScript;
List<string> candidates;
IEnumerable<string> candidateScripts = new string[] { script };
while (candidateScripts != null) {
foreach (var candidateScript in candidateScripts) {
if (MapScriptToTypefaces.TryGetValue(candidateScript, out candidates)) {
foreach (var candidate in candidates) {
IReadOnlyCollection<Typeface> typefaces1;
if (Manager.MapFullNameToTypeface.TryGetValue(candidate, out typefaces1)) {
foreach (var typeface in typefaces1) {
yield return typeface;
}
}
if (distinctFamily) continue;
IReadOnlyCollection<Typeface> typefaces2;
if (Manager.MapNameToTypefaces.TryGetValue(candidate, out typefaces2)) {
foreach (var typeface in typefaces2) {
if (typefaces1.Contains(typeface)) continue;
yield return typeface;
}
}
}
static void FillKeysWithScripts<T>(IDictionary<string, T> map, Func<T> value) {
foreach (var s in IdValidity.Enumerate("script")) map.Add(s, value());
}
public FallbackListFontMatcher(LanguageMatching matcher, FontManager manager) : base(manager) {
_matcher = matcher;
}
public override IEnumerable<Typeface> MatchLanguage(LanguageId lang, bool distinctFamily = false) {
var supported = MapScriptToTypefaces.Keys.Select(i => new LanguageId(i)).ToList();
while (_matcher.Match(lang, supported, out var match, out var distance)) {
if (distance > 40) break;
Logger.Log("main", 0, "UI", "Matching fonts for language {0}, distance = {1}", match, distance);
var candidates = MapScriptToTypefaces[match.Script.ToLowerInvariant()];
foreach (var typeface in EnumerateTypefaces(candidates, distinctFamily)) {
yield return typeface;
}
supported.Remove(match);
}
Logger.Log("main", 0, "UI", "Matching fallback fonts");
foreach (var typeface in EnumerateTypefaces(MapScriptToTypefaces[UltimateFallbackScript], distinctFamily)) {
yield return typeface;
}
}
IEnumerable<Typeface> EnumerateTypefaces(List<string> candidates, bool distinctFamily) {
foreach (var candidate in candidates) {
IReadOnlyCollection<Typeface> typefaces1;
if (Manager.MapFullNameToTypeface.TryGetValue(candidate, out typefaces1)) {
foreach (var typeface in typefaces1) {
yield return typeface;
}
}
if (distinctFamily) continue;
IReadOnlyCollection<Typeface> typefaces2;
if (Manager.MapNameToTypefaces.TryGetValue(candidate, out typefaces2)) {
foreach (var typeface in typefaces2) {
if (typefaces1.Contains(typeface)) continue;
yield return typeface;
}
}
candidateScripts = ScriptUtils.EnumerateFallbackScripts(script);
}
}
}

View File

@@ -1,6 +1,8 @@
using Cryville.Common.Font;
using Cryville.Culture;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using TMPro;
using UnityEngine;
@@ -23,7 +25,7 @@ namespace Cryville.Common.Unity.UI {
if (FontMatcher == null) return;
_text = GetComponent<TextMeshProUGUI>();
if (_font == null) {
foreach (var typeface in FontMatcher.MatchScript(null, true)) {
foreach (var typeface in FontMatcher.MatchLanguage(new LanguageId(CultureInfo.CurrentCulture.Name), true)) {
try {
var ifont = CreateFontAsset(typeface.File.FullName, typeface.IndexInFile);
if (m_shader) ifont.material.shader = m_shader;

View File

@@ -5,6 +5,7 @@ using Cryville.Common.Logging;
using Cryville.Common.Unity;
using Cryville.Common.Unity.UI;
using Cryville.Crtr.UI;
using Cryville.Culture;
using Cryville.Input;
using Cryville.Input.Unity;
#if UNITY_ANDROID && !UNITY_EDITOR
@@ -18,6 +19,9 @@ using Newtonsoft.Json;
using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using UnityEngine;
using Logger = Cryville.Common.Logging.Logger;
using unity = UnityEngine;
@@ -61,7 +65,7 @@ namespace Cryville.Crtr {
Logger.Log("main", 1, "Game", "Culture: {0}, UI = {1}, System = {2}, Unity = {3}", CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture, CultureInfo.InstalledUICulture, Application.systemLanguage);
if (_bcflag) Logger.Log("main", 2, "Game", "Reset all settings");
GameDataPath = Settings.Default.GameDataPath;
UnityDataPath = Application.dataPath;
@@ -153,7 +157,13 @@ namespace Cryville.Crtr {
Settings.Default.Save();
Logger.Log("main", 1, "UI", "Initializing font manager");
TMPAutoFont.FontMatcher = new FallbackListFontMatcher(PlatformConfig.FontManager) {
foreach (var res in Resources.LoadAll<TextAsset>("cldr/common/validity")) {
IdValidity.Load(LoadXmlDocument(res));
}
var metadata = new SupplementalMetadata(LoadXmlDocument("cldr/common/supplemental/supplementalMetadata"));
var subtags = new LikelySubtags(LoadXmlDocument("cldr/common/supplemental/likelySubtags"), metadata);
var matcher = new LanguageMatching(LoadXmlDocument("cldr/common/supplemental/languageInfo"), subtags);
TMPAutoFont.FontMatcher = new FallbackListFontMatcher(matcher, PlatformConfig.FontManager) {
MapScriptToTypefaces = PlatformConfig.ScriptFontMap
};
TMPAutoFont.DefaultShader = Resources.Load<Shader>(PlatformConfig.TextShader);
@@ -161,6 +171,21 @@ namespace Cryville.Crtr {
Logger.Log("main", 1, "Game", "Initialized");
}
static readonly Encoding _encoding = new UTF8Encoding(false, true);
static readonly XmlReaderSettings _xmlSettings = new XmlReaderSettings {
DtdProcessing = DtdProcessing.Ignore,
};
static XDocument LoadXmlDocument(string path) {
return LoadXmlDocument(Resources.Load<TextAsset>(path));
}
static XDocument LoadXmlDocument(TextAsset asset) {
using (var stream = new MemoryStream(_encoding.GetBytes(asset.text))) {
using (var reader = XmlReader.Create(stream, _xmlSettings)) {
return XDocument.Load(reader);
}
}
}
static bool _shutdown;
public static void Shutdown() {
if (_shutdown) return;

View File

@@ -13,12 +13,12 @@ namespace Cryville.Crtr {
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
public static readonly string FileProtocolPrefix = "file:///";
public static readonly FontManager FontManager = new FontManagerWindows();
public static readonly Dictionary<string, List<string>> ScriptFontMap = FallbackListFontMatcher.GetDefaultWindowsFallbackMap();
public static Dictionary<string, List<string>> ScriptFontMap => FallbackListFontMatcher.GetDefaultWindowsFallbackMap();
public static readonly string TextShader = "TextMesh Pro/Shaders/TMP_SDF SSD";
#elif UNITY_ANDROID
public static readonly string FileProtocolPrefix = "file://";
public static readonly FontManager FontManager = new FontManagerAndroid();
public static readonly Dictionary<string, List<string>> ScriptFontMap = FallbackListFontMatcher.GetDefaultAndroidFallbackMap();
public static Dictionary<string, List<string>> ScriptFontMap => FallbackListFontMatcher.GetDefaultAndroidFallbackMap();
public static readonly string TextShader = "TextMesh Pro/Shaders/TMP_SDF-Mobile SSD";
#else
#error Unknown platform.