From e6a9dcd74001b14e4c8f2915a63cfba066bb9e54 Mon Sep 17 00:00:00 2001 From: Loskh <1020612624@qq.com> Date: Fri, 12 Dec 2025 16:58:48 +0800 Subject: [PATCH 1/6] feat: Refactor CJK font resources into a TTC collection and add support for multi-language switching. --- Dalamud/DalamudAsset.cs | 14 ++--- .../Interface/FontIdentifier/IFontFamilyId.cs | 3 +- Dalamud/Interface/FontIdentifier/IFontSpec.cs | 5 ++ .../FontIdentifier/SingleFontSpec.cs | 7 +++ .../SingleFontChooserDialog.cs | 18 +++++- .../FontAtlasFactory.BuildToolkit.cs | 58 ++++++++++--------- .../Internals/GamePrebakedFontHandle.cs | 4 +- 7 files changed, 70 insertions(+), 39 deletions(-) diff --git a/Dalamud/DalamudAsset.cs b/Dalamud/DalamudAsset.cs index 27771116e..49fee005f 100644 --- a/Dalamud/DalamudAsset.cs +++ b/Dalamud/DalamudAsset.cs @@ -1,4 +1,4 @@ -using Dalamud.Storage.Assets; +using Dalamud.Storage.Assets; using TerraFX.Interop.DirectX; @@ -128,17 +128,17 @@ public enum DalamudAsset /// : Noto Sans CJK JP Medium. /// [DalamudAsset(DalamudAssetPurpose.Font)] - [DalamudAssetPath("UIRes", "NotoSansCJKjp-Regular.otf")] - [DalamudAssetPath("UIRes", "NotoSansCJKjp-Medium.otf")] - NotoSansJpMedium = 2000, + //[DalamudAssetPath("UIRes", "NotoSansCJKjp-Regular.otf")] + [DalamudAssetPath("UIRes", "NotoSansCJK-Medium.ttc")] + NotoSansCJKMedium = 2000, /// /// : Noto Sans CJK KR Regular. /// [DalamudAsset(DalamudAssetPurpose.Font)] - [DalamudAssetPath("UIRes", "NotoSansCJKkr-Regular.otf")] - [DalamudAssetPath("UIRes", "NotoSansKR-Regular.otf")] - NotoSansKrRegular = 2001, + //[DalamudAssetPath("UIRes", "NotoSansCJKkr-Regular.otf")] + [DalamudAssetPath("UIRes", "NotoSansCJK-Regular.ttc")] + NotoSansCJKRegular = 2001, /// /// : Inconsolata Regular. diff --git a/Dalamud/Interface/FontIdentifier/IFontFamilyId.cs b/Dalamud/Interface/FontIdentifier/IFontFamilyId.cs index 991716f74..163d7e905 100644 --- a/Dalamud/Interface/FontIdentifier/IFontFamilyId.cs +++ b/Dalamud/Interface/FontIdentifier/IFontFamilyId.cs @@ -38,7 +38,8 @@ public interface IFontFamilyId : IObjectWithLocalizableName public static List ListDalamudFonts() => new() { - new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansJpMedium), + new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCJKMedium), + new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCJKRegular), new DalamudAssetFontAndFamilyId(DalamudAsset.InconsolataRegular), new DalamudAssetFontAndFamilyId(DalamudAsset.FontAwesomeFreeSolid), }; diff --git a/Dalamud/Interface/FontIdentifier/IFontSpec.cs b/Dalamud/Interface/FontIdentifier/IFontSpec.cs index c597ed4dd..8c7b94a9c 100644 --- a/Dalamud/Interface/FontIdentifier/IFontSpec.cs +++ b/Dalamud/Interface/FontIdentifier/IFontSpec.cs @@ -24,6 +24,11 @@ public interface IFontSpec /// float LineHeightPx { get; } + /// + /// Gets the font no. + /// + int FontNo { get; } + /// /// Creates a font handle corresponding to this font specification. /// diff --git a/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs b/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs index 070b1c1e1..96b566dfe 100644 --- a/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs +++ b/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs @@ -65,6 +65,12 @@ public record SingleFontSpec : IFontSpec [JsonProperty] public ushort[]? GlyphRanges { get; init; } + /// + /// Gets the font no. + /// + [JsonProperty] + public int FontNo { get; init; } + /// public string ToLocalizedString(string localeCode) { @@ -99,6 +105,7 @@ public record SingleFontSpec : IFontSpec tk, new() { + FontNo = this.FontNo, SizePx = this.SizePx, GlyphRanges = this.GlyphRanges, MergeFont = mergeFont, diff --git a/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs b/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs index 6a381f5b2..cc22f358a 100644 --- a/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs +++ b/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs @@ -128,7 +128,7 @@ public sealed class SingleFontChooserDialog : IDisposable this.popupImGuiName = $"{this.title}##{nameof(SingleFontChooserDialog)}[{this.counter}]"; this.atlas = newAsyncAtlas; this.selectedFont = new() { FontId = DalamudDefaultFontAndFamilyId.Instance }; - Encoding.UTF8.GetBytes("Font preview.\n0123456789!", this.fontPreviewText); + Encoding.UTF8.GetBytes("Font preview.\n0123456789!\n遍角次亮采之门,门上插刀、直字拐弯、天上平板、船顶漏雨。\n다람쥐 헌 쳇바퀴에 타고파", this.fontPreviewText); } /// Called when the selected font spec has changed. @@ -891,7 +891,21 @@ public sealed class SingleFontChooserDialog : IDisposable this.selectedFontWeight = font.Weight; this.selectedFontStretch = font.Stretch; this.selectedFontStyle = font.Style; - this.selectedFont = this.selectedFont with { FontId = font }; + int fontNo = 0; + if (family is DalamudAssetFontAndFamilyId { Asset: DalamudAsset.NotoSansCJKRegular or DalamudAsset.NotoSansCJKMedium }) + { + var dalamudConfiguration = Service.Get(); + fontNo = dalamudConfiguration.EffectiveLanguage switch + { + "jp" => 0, + "tw" => 1, + "zh" => 2, + "ko" => 3, + _ => 0, + }; + } + + this.selectedFont = this.selectedFont with { FontId = font, FontNo = fontNo }; } return changed; diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs index 2a93cf093..58c2c953d 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs @@ -371,10 +371,7 @@ internal sealed partial class FontAtlasFactory return this.factory.AddFont( this, asset, - fontConfig with - { - FontNo = 0, - }); + fontConfig); } } @@ -562,32 +559,39 @@ internal sealed partial class FontAtlasFactory return; var dalamudConfiguration = Service.Get(); - if (dalamudConfiguration.EffectiveLanguage == "ko" - || Service.GetNullable()?.EncounteredHangul is true) + var ime = Service.GetNullable(); + + string langTag = null; + // fontNo: 0 = japanese, 1 = traditional chinese, 2 = simplified chinese, 3 = korean + int fontNo = 0; + + if (dalamudConfiguration.EffectiveLanguage == "tw") { - this.AddDalamudAssetFont( - DalamudAsset.NotoSansKrRegular, - fontConfig with - { - MergeFont = targetFont, - GlyphRanges = default(FluentGlyphRangeBuilder).WithLanguage("ko-kr").BuildExact(), - }); + langTag = "zh-hant"; + fontNo = 1; + } + else if (dalamudConfiguration.EffectiveLanguage == "zh" || ime?.EncounteredHan is true) + { + langTag = "zh-hans"; + fontNo = 2; + } + else if (dalamudConfiguration.EffectiveLanguage == "ko" || ime?.EncounteredHangul is true) + { + langTag = "ko-kr"; + fontNo = 3; } - if (Service.Get().EffectiveLanguage == "tw") + Log.Debug($"Loading extra glyphs for language tag '{langTag}' (font no {fontNo})"); + if (langTag != null) { - this.AttachWindowsDefaultFont(CultureInfo.GetCultureInfo("zh-hant"), fontConfig with - { - GlyphRanges = default(FluentGlyphRangeBuilder).WithLanguage("zh-hant").BuildExact(), - }); - } - else if (Service.Get().EffectiveLanguage == "zh" - || Service.GetNullable()?.EncounteredHan is true) - { - this.AttachWindowsDefaultFont(CultureInfo.GetCultureInfo("zh-hans"), fontConfig with - { - GlyphRanges = default(FluentGlyphRangeBuilder).WithLanguage("zh-hans").BuildExact(), - }); + this.AddDalamudAssetFont( + DalamudAsset.NotoSansCJKRegular, + fontConfig with + { + FontNo = fontNo, + MergeFont = targetFont, + GlyphRanges = default(FluentGlyphRangeBuilder).WithLanguage(langTag).BuildExact(), + }); } } @@ -629,7 +633,7 @@ internal sealed partial class FontAtlasFactory if (this.data.ConfigData.Length == 0) { this.AddDalamudAssetFont( - DalamudAsset.NotoSansJpMedium, + DalamudAsset.NotoSansCJKRegular, new() { GlyphRanges = new ushort[] { ' ', ' ', '\0' }, SizePx = 1 }); } diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs index f6904db7c..8bd3212dd 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs @@ -412,14 +412,14 @@ internal class GamePrebakedFontHandle : FontHandle private ImFontPtr CreateTemplateFont(IFontAtlasBuildToolkitPreBuild toolkitPreBuild, float sizePx) { var font = toolkitPreBuild.AddDalamudAssetFont( - DalamudAsset.NotoSansJpMedium, + DalamudAsset.NotoSansCJKMedium, new() { GlyphRanges = new ushort[] { ' ', ' ', '\0' }, SizePx = sizePx, }); this.templatedFonts.Add(font); - return font; + return font; } private unsafe void PatchFontMetricsIfNecessary(GameFontStyle style, ImFontPtr font, float atlasScale) From 205cb965d5af81b7093cec2dee29962b6b217b6b Mon Sep 17 00:00:00 2001 From: Loskh <1020612624@qq.com> Date: Sat, 13 Dec 2025 22:27:29 +0800 Subject: [PATCH 2/6] deprecate: add Obsolete for NotoSansJpMedium and NotoSansKrRegular --- Dalamud/DalamudAsset.cs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Dalamud/DalamudAsset.cs b/Dalamud/DalamudAsset.cs index 49fee005f..760c71a91 100644 --- a/Dalamud/DalamudAsset.cs +++ b/Dalamud/DalamudAsset.cs @@ -125,21 +125,35 @@ public enum DalamudAsset TitleScreenMenuShade = 1013, /// - /// : Noto Sans CJK JP Medium. + /// : Noto Sans CJK Medium. /// [DalamudAsset(DalamudAssetPurpose.Font)] - //[DalamudAssetPath("UIRes", "NotoSansCJKjp-Regular.otf")] [DalamudAssetPath("UIRes", "NotoSansCJK-Medium.ttc")] NotoSansCJKMedium = 2000, /// - /// : Noto Sans CJK KR Regular. + /// : Noto Sans CJK JP Medium. + /// + [Obsolete("Use NotoSansCJKMedium instead.")] + [DalamudAsset(DalamudAssetPurpose.Font)] + [DalamudAssetPath("UIRes", "NotoSansCJK-Medium.ttc")] + NotoSansJpMedium = 2000, + + /// + /// : Noto Sans CJK Regular. /// [DalamudAsset(DalamudAssetPurpose.Font)] - //[DalamudAssetPath("UIRes", "NotoSansCJKkr-Regular.otf")] [DalamudAssetPath("UIRes", "NotoSansCJK-Regular.ttc")] NotoSansCJKRegular = 2001, + /// + /// : Noto Sans CJK KR Regular. + /// + [Obsolete("Use NotoSansCJKRegular instead.")] + [DalamudAsset(DalamudAssetPurpose.Font)] + [DalamudAssetPath("UIRes", "NotoSansCJK-Regular.ttc")] + NotoSansKrRegular = 2001, + /// /// : Inconsolata Regular. /// From bf422e01b2df8207ad2939b11aff7ada4dabd0fd Mon Sep 17 00:00:00 2001 From: Loskh <1020612624@qq.com> Date: Mon, 22 Dec 2025 23:14:12 +0800 Subject: [PATCH 3/6] docs: expand documentation for FontNo with TTC index details --- lib/FFXIVClientStructs | 2 +- lib/Lumina.Excel | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 9c5f93cf3..90168316b 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 9c5f93cf3ac57236656cd2323b93cd258ea84a88 +Subproject commit 90168316b4c5e3af2746a1bdea52fb10f9113862 diff --git a/lib/Lumina.Excel b/lib/Lumina.Excel index d6ff8cf46..d8d0b53e2 160000 --- a/lib/Lumina.Excel +++ b/lib/Lumina.Excel @@ -1 +1 @@ -Subproject commit d6ff8cf46c7e341989843c28c7550f8d50bee851 +Subproject commit d8d0b53e27393f509ac5397511cb8d251d562277 From 8ab99a0a916b29224f3b457be1744d17ca377aba Mon Sep 17 00:00:00 2001 From: Loskh <1020612624@qq.com> Date: Mon, 22 Dec 2025 23:27:19 +0800 Subject: [PATCH 4/6] Merge branch 'master' into font-family --- lib/FFXIVClientStructs | 2 +- lib/Lumina.Excel | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 90168316b..9c5f93cf3 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 90168316b4c5e3af2746a1bdea52fb10f9113862 +Subproject commit 9c5f93cf3ac57236656cd2323b93cd258ea84a88 diff --git a/lib/Lumina.Excel b/lib/Lumina.Excel index d8d0b53e2..d6ff8cf46 160000 --- a/lib/Lumina.Excel +++ b/lib/Lumina.Excel @@ -1 +1 @@ -Subproject commit d8d0b53e27393f509ac5397511cb8d251d562277 +Subproject commit d6ff8cf46c7e341989843c28c7550f8d50bee851 From c55e47ee4d30be0b0f7ceefae069e7aa605d1a33 Mon Sep 17 00:00:00 2001 From: Loskh <1020612624@qq.com> Date: Mon, 22 Dec 2025 23:34:22 +0800 Subject: [PATCH 5/6] docs: expand documentation for FontNo with TTC index details --- Dalamud/DalamudAsset.cs | 3 ++- Dalamud/Interface/FontIdentifier/IFontSpec.cs | 20 ++++++++++++++++- .../FontIdentifier/SingleFontSpec.cs | 4 +--- .../ManagedFontAtlas/SafeFontConfig.cs | 22 +++++++++++++++++-- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/Dalamud/DalamudAsset.cs b/Dalamud/DalamudAsset.cs index 0dbd4cef1..07f0c0f0a 100644 --- a/Dalamud/DalamudAsset.cs +++ b/Dalamud/DalamudAsset.cs @@ -130,7 +130,8 @@ public enum DalamudAsset [DalamudAsset(DalamudAssetPurpose.Font)] [DalamudAssetPath("UIRes", "NotoSansCJK-Medium.ttc")] NotoSansCJKMedium = 2000, - + + /// /// : Atlas containing badges. /// [DalamudAsset(DalamudAssetPurpose.TextureFromPng)] diff --git a/Dalamud/Interface/FontIdentifier/IFontSpec.cs b/Dalamud/Interface/FontIdentifier/IFontSpec.cs index 8c7b94a9c..535909b54 100644 --- a/Dalamud/Interface/FontIdentifier/IFontSpec.cs +++ b/Dalamud/Interface/FontIdentifier/IFontSpec.cs @@ -25,8 +25,26 @@ public interface IFontSpec float LineHeightPx { get; } /// - /// Gets the font no. + /// Gets or sets the font face index within a TrueType Collection (TTC) file. /// + /// + /// This property only applies to and + /// , which are TTC fonts bundling + /// multiple language-specific CJK glyph sets (Japanese, Traditional Chinese, + /// Simplified Chinese, Korean) into a single file. + /// + /// The index corresponds to the font face order in the TTC: + /// + /// 0 = Japanese + /// 1 = Traditional Chinese + /// 2 = Simplified Chinese + /// 3 = Korean + /// + /// + /// This value is ignored for all other entries. + /// Only one glyph set can be active at a time. In most cases, you can omit this— + /// Dalamud automatically selects the appropriate face based on the UI language. + /// int FontNo { get; } /// diff --git a/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs b/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs index 96b566dfe..986fa4db2 100644 --- a/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs +++ b/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs @@ -65,9 +65,7 @@ public record SingleFontSpec : IFontSpec [JsonProperty] public ushort[]? GlyphRanges { get; init; } - /// - /// Gets the font no. - /// + /// [JsonProperty] public int FontNo { get; init; } diff --git a/Dalamud/Interface/ManagedFontAtlas/SafeFontConfig.cs b/Dalamud/Interface/ManagedFontAtlas/SafeFontConfig.cs index 484c3540b..61646af3f 100644 --- a/Dalamud/Interface/ManagedFontAtlas/SafeFontConfig.cs +++ b/Dalamud/Interface/ManagedFontAtlas/SafeFontConfig.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using System.Numerics; using System.Runtime.CompilerServices; using System.Text; @@ -48,8 +48,26 @@ public struct SafeFontConfig } /// - /// Gets or sets the index of font within a TTF/OTF file. + /// Gets or sets the font face index within a TrueType Collection (TTC) file. /// + /// + /// This property only applies to and + /// , which are TTC fonts bundling + /// multiple language-specific CJK glyph sets (Japanese, Traditional Chinese, + /// Simplified Chinese, Korean) into a single file. + /// + /// The index corresponds to the font face order in the TTC: + /// + /// 0 = Japanese + /// 1 = Traditional Chinese + /// 2 = Simplified Chinese + /// 3 = Korean + /// + /// + /// This value is ignored for all other entries. + /// Only one glyph set can be active at a time. In most cases, you can omit this— + /// Dalamud automatically selects the appropriate face based on the UI language. + /// public int FontNo { get => this.Raw.FontNo; From 35508be552f3583fd2febc0708f29704c2e71463 Mon Sep 17 00:00:00 2001 From: Loskh <1020612624@qq.com> Date: Mon, 22 Dec 2025 23:36:56 +0800 Subject: [PATCH 6/6] doc: remove sets in the comment of IFontSpec.FontNo --- Dalamud/Interface/FontIdentifier/IFontSpec.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Interface/FontIdentifier/IFontSpec.cs b/Dalamud/Interface/FontIdentifier/IFontSpec.cs index 535909b54..c0138aadd 100644 --- a/Dalamud/Interface/FontIdentifier/IFontSpec.cs +++ b/Dalamud/Interface/FontIdentifier/IFontSpec.cs @@ -25,7 +25,7 @@ public interface IFontSpec float LineHeightPx { get; } /// - /// Gets or sets the font face index within a TrueType Collection (TTC) file. + /// Gets the font face index within a TrueType Collection (TTC) file. /// /// /// This property only applies to and