253 lines
12 KiB
C#
253 lines
12 KiB
C#
using Cryville.Common.Culture;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace Cryville.Common.Font {
|
|
public abstract class FontManager {
|
|
public IReadOnlyDictionary<string, IReadOnlyCollection<Typeface>> MapFullNameToTypeface { get; private set; }
|
|
public IReadOnlyDictionary<string, IReadOnlyCollection<Typeface>> MapNameToTypefaces { get; private set; }
|
|
public FontManager() {
|
|
var map1 = new Dictionary<string, List<Typeface>>();
|
|
var map2 = new Dictionary<string, List<Typeface>>();
|
|
foreach (var f in EnumerateAllTypefaces()) {
|
|
List<Typeface> set1;
|
|
if (!map1.TryGetValue(f.FullName, out set1)) {
|
|
map1.Add(f.FullName, set1 = new List<Typeface>());
|
|
}
|
|
set1.Add(f);
|
|
List<Typeface> set2;
|
|
if (!map2.TryGetValue(f.FamilyName, out set2)) {
|
|
map2.Add(f.FamilyName, set2 = new List<Typeface>());
|
|
}
|
|
set2.Add(f);
|
|
}
|
|
MapFullNameToTypeface = map1.ToDictionary(i => i.Key, i => (IReadOnlyCollection<Typeface>)i.Value);
|
|
MapNameToTypefaces = map2.ToDictionary(i => i.Key, i => (IReadOnlyCollection<Typeface>)i.Value);
|
|
}
|
|
protected abstract IEnumerable<Typeface> EnumerateAllTypefaces();
|
|
public abstract IEnumerable<Typeface> MatchScript(string script = null);
|
|
public abstract IEnumerable<Typeface> MatchFamily(string[] candidates);
|
|
protected static IEnumerable<Typeface> ScanDirectoryForTypefaces(string dir) {
|
|
foreach (var f in new DirectoryInfo(dir).EnumerateFiles()) {
|
|
FontFile file;
|
|
try {
|
|
file = FontFile.Create(f);
|
|
}
|
|
catch (InvalidDataException) {
|
|
continue;
|
|
}
|
|
if (file == null) continue;
|
|
var enumerator = file.GetEnumerator();
|
|
while (enumerator.MoveNext()) {
|
|
Typeface ret;
|
|
try {
|
|
ret = enumerator.Current;
|
|
}
|
|
catch (InvalidDataException) {
|
|
continue;
|
|
}
|
|
yield return ret;
|
|
}
|
|
file.Close();
|
|
}
|
|
}
|
|
}
|
|
public class FontManagerAndroid : FontManager {
|
|
protected override IEnumerable<Typeface> EnumerateAllTypefaces() {
|
|
return ScanDirectoryForTypefaces("/system/fonts");
|
|
}
|
|
|
|
public override IEnumerable<Typeface> MatchScript(string script = null) {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override IEnumerable<Typeface> MatchFamily(string[] candidates) {
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
public class FontManagerWindows : FontManager {
|
|
public static Dictionary<string, List<string>> MapScriptToTypefaces = new Dictionary<string, List<string>>();
|
|
|
|
static FontManagerWindows() {
|
|
if (Environment.OSVersion.Platform != PlatformID.Win32NT) return;
|
|
ScriptUtils.FillKeysWithScripts(MapScriptToTypefaces, () => new List<string>());
|
|
// Reference: https://github.com/chromium/chromium/blob/main/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
|
|
MapScriptToTypefaces["zyyy"].Insert(0, "SimSun"); // Custom
|
|
MapScriptToTypefaces["zyyy"].Insert(0, "SimHei"); // Custom
|
|
MapScriptToTypefaces["zyyy"].Insert(0, "Arial");
|
|
MapScriptToTypefaces["zyyy"].Insert(0, "Times New Roman");
|
|
MapScriptToTypefaces["zyyy"].Insert(0, "Segoe UI"); // Custom
|
|
MapScriptToTypefaces["arab"].Insert(0, "Tahoma");
|
|
MapScriptToTypefaces["cyrl"].Insert(0, "Times New Roman");
|
|
MapScriptToTypefaces["grek"].Insert(0, "Times New Roman");
|
|
MapScriptToTypefaces["hebr"].Insert(0, "David");
|
|
MapScriptToTypefaces["jpan"].Insert(0, "MS PGothic");
|
|
MapScriptToTypefaces["latn"].Insert(0, "Times New Roman");
|
|
MapScriptToTypefaces["hans"].Insert(0, "SimSun");
|
|
MapScriptToTypefaces["hans"].Insert(0, "SimHei"); // Custom
|
|
MapScriptToTypefaces["thai"].Insert(0, "Tahoma");
|
|
MapScriptToTypefaces["hans"].Insert(0, "PMingLiU");
|
|
// Reference: https://learn.microsoft.com/en-us/globalization/input/font-support
|
|
var ver = Environment.OSVersion.Version;
|
|
if (ver >= new Version(5, 0)) { // Windows 2000
|
|
MapScriptToTypefaces["armn"].Insert(0, "Sylfaen");
|
|
MapScriptToTypefaces["deva"].Insert(0, "Mangal");
|
|
MapScriptToTypefaces["geor"].Insert(0, "Sylfaen");
|
|
MapScriptToTypefaces["taml"].Insert(0, "Latha");
|
|
}
|
|
if (ver >= new Version(5, 1)) { // Windows XP
|
|
MapScriptToTypefaces["gujr"].Insert(0, "Shruti");
|
|
MapScriptToTypefaces["guru"].Insert(0, "Raavi");
|
|
MapScriptToTypefaces["knda"].Insert(0, "Tunga");
|
|
MapScriptToTypefaces["syrc"].Insert(0, "Estrangelo Edessa");
|
|
MapScriptToTypefaces["telu"].Insert(0, "Gautami");
|
|
MapScriptToTypefaces["thaa"].Insert(0, "MV Boli");
|
|
// SP2
|
|
MapScriptToTypefaces["beng"].Insert(0, "Vrinda");
|
|
MapScriptToTypefaces["mlym"].Insert(0, "Kartika");
|
|
}
|
|
if (ver >= new Version(6, 0)) { // Windows Vista
|
|
MapScriptToTypefaces["cans"].Insert(0, "Euphemia");
|
|
MapScriptToTypefaces["cher"].Insert(0, "Plantagenet");
|
|
MapScriptToTypefaces["ethi"].Insert(0, "Nyala");
|
|
MapScriptToTypefaces["khmr"].Insert(0, "DaunPenh MoolBoran");
|
|
MapScriptToTypefaces["laoo"].Insert(0, "DokChampa");
|
|
MapScriptToTypefaces["mong"].Insert(0, "Mongolian Baiti");
|
|
MapScriptToTypefaces["orya"].Insert(0, "Kalinga");
|
|
MapScriptToTypefaces["sinh"].Insert(0, "Iskoola Pota");
|
|
MapScriptToTypefaces["tibt"].Insert(0, "Microsoft Himalaya");
|
|
MapScriptToTypefaces["yiii"].Insert(0, "Microsoft Yi Baiti");
|
|
MapScriptToTypefaces["arab"].Insert(0, "Segoe UI");
|
|
MapScriptToTypefaces["cyrl"].Insert(0, "Segoe UI");
|
|
MapScriptToTypefaces["grek"].Insert(0, "Segoe UI");
|
|
MapScriptToTypefaces["latn"].Insert(0, "Segoe UI");
|
|
MapScriptToTypefaces["hans"].Add("SimSun-ExtB");
|
|
MapScriptToTypefaces["hant"].Add("MingLiU-ExtB");
|
|
MapScriptToTypefaces["hant"].Add("MingLiU_HKSCS-ExtB");
|
|
MapScriptToTypefaces["arab"].Add("Microsoft Uighur");
|
|
MapScriptToTypefaces["zmth"].Insert(0, "Cambria Math");
|
|
// Reference: https://en.wikipedia.org/wiki/List_of_CJK_fonts
|
|
MapScriptToTypefaces["jpan"].Insert(0, "Meiryo");
|
|
MapScriptToTypefaces["hans"].Insert(0, "Microsoft YaHei");
|
|
}
|
|
if (ver >= new Version(6, 1)) { // Windows 7
|
|
MapScriptToTypefaces["brai"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["dsrt"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["talu"].Insert(0, "Microsoft New Tai Lue");
|
|
MapScriptToTypefaces["ogam"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["osma"].Insert(0, "Ebrima");
|
|
MapScriptToTypefaces["phag"].Insert(0, "Microsoft PhagsPa");
|
|
MapScriptToTypefaces["runr"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["zsym"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["tale"].Insert(0, "Microsoft Tai Le");
|
|
MapScriptToTypefaces["tfng"].Insert(0, "Ebrima");
|
|
MapScriptToTypefaces["vaii"].Insert(0, "Ebrima");
|
|
}
|
|
if (ver >= new Version(6, 2)) { // Windows 8
|
|
MapScriptToTypefaces["glag"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["goth"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["hang"].Add("Malgun Gothic");
|
|
MapScriptToTypefaces["ital"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["lisu"].Insert(0, "Segoe UI");
|
|
MapScriptToTypefaces["mymr"].Insert(0, "Myanmar Text");
|
|
MapScriptToTypefaces["nkoo"].Insert(0, "Ebrima");
|
|
MapScriptToTypefaces["orkh"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["ethi"].Insert(0, "Ebrima");
|
|
MapScriptToTypefaces["cans"].Insert(0, "Gadugi");
|
|
MapScriptToTypefaces["hant"].Insert(0, "Microsoft JhengHei UI");
|
|
MapScriptToTypefaces["hans"].Insert(0, "Microsoft YaHei UI");
|
|
MapScriptToTypefaces["beng"].Insert(0, "Nirmala UI");
|
|
MapScriptToTypefaces["deva"].Insert(0, "Nirmala UI");
|
|
MapScriptToTypefaces["gujr"].Insert(0, "Nirmala UI");
|
|
MapScriptToTypefaces["guru"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
|
MapScriptToTypefaces["knda"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
|
MapScriptToTypefaces["mlym"].Insert(0, "Nirmala UI");
|
|
MapScriptToTypefaces["orya"].Insert(0, "Nirmala UI");
|
|
MapScriptToTypefaces["sinh"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
|
MapScriptToTypefaces["taml"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
|
MapScriptToTypefaces["telu"].Insert(0, "Nirmala UI");
|
|
MapScriptToTypefaces["armn"].Insert(0, "Segoe UI");
|
|
MapScriptToTypefaces["geor"].Insert(0, "Segoe UI");
|
|
MapScriptToTypefaces["hebr"].Insert(0, "Segoe UI");
|
|
}
|
|
if (ver >= new Version(6, 3)) { // Windows 8.1
|
|
MapScriptToTypefaces["bugi"].Insert(0, "Leelawadee UI");
|
|
MapScriptToTypefaces["copt"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["java"].Insert(0, "Javanese Text");
|
|
MapScriptToTypefaces["merc"].Insert(0, "Segoe UI Symbol");
|
|
MapScriptToTypefaces["olck"].Insert(0, "Nirmala UI");
|
|
MapScriptToTypefaces["sora"].Insert(0, "Nirmala UI");
|
|
MapScriptToTypefaces["khmr"].Insert(0, "Leelawadee UI");
|
|
MapScriptToTypefaces["laoo"].Insert(0, "Leelawadee UI");
|
|
MapScriptToTypefaces["thai"].Insert(0, "Leelawadee UI");
|
|
MapScriptToTypefaces["zsye"].Insert(0, "Segoe UI Emoji");
|
|
}
|
|
if (ver >= new Version(10, 0)) { // Windows 10
|
|
MapScriptToTypefaces["brah"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["cari"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["cprt"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["egyp"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["armi"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["phli"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["prti"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["khar"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["lyci"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["lydi"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["phnx"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["xpeo"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["sarb"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["shaw"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["xsux"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["ugar"].Insert(0, "Segoe UI Historic");
|
|
// Segoe UI Symbol -> Segoe UI Historic
|
|
MapScriptToTypefaces["glag"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["goth"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["merc"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["ogam"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["ital"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["orkh"].Insert(0, "Segoe UI Historic");
|
|
MapScriptToTypefaces["runr"].Insert(0, "Segoe UI Historic");
|
|
//
|
|
MapScriptToTypefaces["jpan"].Insert(0, "Yu Gothic UI");
|
|
MapScriptToTypefaces["zsym"].Add("Segoe MDL2 Assets");
|
|
}
|
|
}
|
|
|
|
protected override IEnumerable<Typeface> EnumerateAllTypefaces() {
|
|
return ScanDirectoryForTypefaces("C:/Windows/Fonts");
|
|
}
|
|
|
|
public override IEnumerable<Typeface> MatchScript(string script = null) {
|
|
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) {
|
|
Typeface typeface;
|
|
if (MapFullNameToTypeface.TryGetValue(candidate, out typeface)) {
|
|
yield return typeface;
|
|
}
|
|
IReadOnlyCollection<Typeface> typefaces;
|
|
if (MapNameToTypefaces.TryGetValue(candidate, out typefaces)) {
|
|
foreach (var typeface2 in typefaces) {
|
|
if (typeface2 == typeface) continue;
|
|
yield return typeface2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
candidateScripts = ScriptUtils.EnumerateFallbackScripts(script);
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<Typeface> MatchFamily(string[] candidates) {
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
}
|