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:

File diff suppressed because one or more lines are too long

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.