diff --git a/Dalamud/Interface/Internal/DalamudIme.cs b/Dalamud/Interface/Internal/DalamudIme.cs index f44c885ce..f8d7fb690 100644 --- a/Dalamud/Interface/Internal/DalamudIme.cs +++ b/Dalamud/Interface/Internal/DalamudIme.cs @@ -5,8 +5,10 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using System.Text.Unicode; using Dalamud.Game.Text; +using Dalamud.Interface.GameFonts; using Dalamud.Interface.Utility; using Dalamud.Logging.Internal; @@ -26,6 +28,26 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType { private static readonly ModuleLog Log = new("IME"); + private static readonly UnicodeRange[] HanRange = + { + UnicodeRanges.CjkRadicalsSupplement, + UnicodeRanges.CjkSymbolsandPunctuation, + UnicodeRanges.CjkUnifiedIdeographsExtensionA, + UnicodeRanges.CjkUnifiedIdeographs, + UnicodeRanges.CjkCompatibilityIdeographs, + UnicodeRanges.CjkCompatibilityForms, + // No more; Extension B~ are outside BMP range + }; + + private static readonly UnicodeRange[] HangulRange = + { + UnicodeRanges.HangulJamo, + UnicodeRanges.HangulSyllables, + UnicodeRanges.HangulCompatibilityJamo, + UnicodeRanges.HangulJamoExtendedA, + UnicodeRanges.HangulJamoExtendedB, + }; + private readonly ImGuiSetPlatformImeDataDelegate setPlatformImeDataDelegate; [ServiceManager.ServiceConstructor] @@ -38,6 +60,16 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType private delegate void ImGuiSetPlatformImeDataDelegate(ImGuiViewportPtr viewport, ImGuiPlatformImeDataPtr data); + /// + /// Gets a value indicating whether Han(Chinese) input has been detected. + /// + public bool EncounteredHan { get; private set; } + + /// + /// Gets a value indicating whether Hangul(Korean) input has been detected. + /// + public bool EncounteredHangul { get; private set; } + /// /// Gets a value indicating whether to display the cursor in input text. This also deals with blinking. /// @@ -116,6 +148,39 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType GC.SuppressFinalize(this); } + /// + /// Looks for the characters inside and enables fonts accordingly. + /// + /// The string. + public void ReflectCharacterEncounters(string str) + { + foreach (var chr in str) + { + if (HanRange.Any(x => x.FirstCodePoint <= chr && chr < x.FirstCodePoint + x.Length)) + { + if (Service.Get() + .GetFdtReader(GameFontFamilyAndSize.Axis12) + ?.FindGlyph(chr) is null) + { + if (!this.EncounteredHan) + { + this.EncounteredHan = true; + Service.Get().RebuildFonts(); + } + } + } + + if (HangulRange.Any(x => x.FirstCodePoint <= chr && chr < x.FirstCodePoint + x.Length)) + { + if (!this.EncounteredHangul) + { + this.EncounteredHangul = true; + Service.Get().RebuildFonts(); + } + } + } + } + /// /// Processes window messages. /// @@ -308,6 +373,8 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType ? ImmGetCompositionString(hImc, GCS.GCS_RESULTSTR) : ImmGetCompositionString(hImc, GCS.GCS_COMPSTR); + this.ReflectCharacterEncounters(newString); + if (s != e) textState.DeleteChars(s, e - s); textState.InsertChars(s, newString); @@ -402,6 +469,7 @@ internal sealed unsafe class DalamudIme : IDisposable, IServiceType (int)Math.Min(candlist.dwCount - candlist.dwPageStart, candlist.dwPageSize))) { this.ImmCand.Add(new((char*)(pStorage + candlist.dwOffset[i]))); + this.ReflectCharacterEncounters(this.ImmCand[^1]); } } diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index d7ab5ba9d..49dfdb248 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using System.Text.Unicode; using System.Threading; using Dalamud.Configuration.Internal; @@ -786,10 +787,22 @@ internal class InterfaceManager : IDisposable, IServiceType var fontPathKr = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "NotoSansCJKkr-Regular.otf"); if (!File.Exists(fontPathKr)) fontPathKr = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "NotoSansKR-Regular.otf"); + if (!File.Exists(fontPathKr)) + fontPathKr = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Fonts", "malgun.ttf"); if (!File.Exists(fontPathKr)) fontPathKr = null; Log.Verbose("[FONT] fontPathKr = {0}", fontPathKr); + var fontPathChs = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Fonts", "msyh.ttc"); + if (!File.Exists(fontPathChs)) + fontPathChs = null; + Log.Verbose("[FONT] fontPathChs = {0}", fontPathChs); + + var fontPathCht = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Fonts", "msjh.ttc"); + if (!File.Exists(fontPathCht)) + fontPathCht = null; + Log.Verbose("[FONT] fontPathChs = {0}", fontPathCht); + // Default font Log.Verbose("[FONT] SetupFonts - Default font"); var fontInfo = new TargetFontModification( @@ -817,7 +830,8 @@ internal class InterfaceManager : IDisposable, IServiceType this.loadedFontInfo[DefaultFont] = fontInfo; } - if (fontPathKr != null && Service.Get().EffectiveLanguage == "ko") + if (fontPathKr != null + && (Service.Get().EffectiveLanguage == "ko" || this.dalamudIme.EncounteredHangul)) { fontConfig.MergeMode = true; fontConfig.GlyphRanges = ioFonts.GetGlyphRangesKorean(); @@ -826,6 +840,46 @@ internal class InterfaceManager : IDisposable, IServiceType fontConfig.MergeMode = false; } + if (fontPathCht != null && Service.Get().EffectiveLanguage == "tw") + { + fontConfig.MergeMode = true; + var rangeHandle = GCHandle.Alloc(new ushort[] + { + (ushort)UnicodeRanges.CjkUnifiedIdeographs.FirstCodePoint, + (ushort)(UnicodeRanges.CjkUnifiedIdeographs.FirstCodePoint + + (UnicodeRanges.CjkUnifiedIdeographs.Length - 1)), + (ushort)UnicodeRanges.CjkUnifiedIdeographsExtensionA.FirstCodePoint, + (ushort)(UnicodeRanges.CjkUnifiedIdeographsExtensionA.FirstCodePoint + + (UnicodeRanges.CjkUnifiedIdeographsExtensionA.Length - 1)), + 0, + }, GCHandleType.Pinned); + garbageList.Add(rangeHandle); + fontConfig.GlyphRanges = rangeHandle.AddrOfPinnedObject(); + fontConfig.PixelSnapH = true; + ioFonts.AddFontFromFileTTF(fontPathCht, fontConfig.SizePixels, fontConfig); + fontConfig.MergeMode = false; + } + else if (fontPathChs != null && (Service.Get().EffectiveLanguage == "zh" + || this.dalamudIme.EncounteredHan)) + { + fontConfig.MergeMode = true; + var rangeHandle = GCHandle.Alloc(new ushort[] + { + (ushort)UnicodeRanges.CjkUnifiedIdeographs.FirstCodePoint, + (ushort)(UnicodeRanges.CjkUnifiedIdeographs.FirstCodePoint + + (UnicodeRanges.CjkUnifiedIdeographs.Length - 1)), + (ushort)UnicodeRanges.CjkUnifiedIdeographsExtensionA.FirstCodePoint, + (ushort)(UnicodeRanges.CjkUnifiedIdeographsExtensionA.FirstCodePoint + + (UnicodeRanges.CjkUnifiedIdeographsExtensionA.Length - 1)), + 0, + }, GCHandleType.Pinned); + garbageList.Add(rangeHandle); + fontConfig.GlyphRanges = rangeHandle.AddrOfPinnedObject(); + fontConfig.PixelSnapH = true; + ioFonts.AddFontFromFileTTF(fontPathChs, fontConfig.SizePixels, fontConfig); + fontConfig.MergeMode = false; + } + // FontAwesome icon font Log.Verbose("[FONT] SetupFonts - FontAwesome icon font"); {