diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
index 3d2b58683..5359861cf 100644
--- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs
+++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using Dalamud.Game.Text;
+using Dalamud.Interface.GameFonts;
using Dalamud.Interface.Style;
using Newtonsoft.Json;
using Serilog;
@@ -128,6 +129,11 @@ namespace Dalamud.Configuration.Internal
///
public float GlobalUiScale { get; set; } = 1.0f;
+ ///
+ /// Gets or sets the game font to use for Dalamud UI.
+ ///
+ public GameFont DefaultFontFromGame { get; set; } = GameFont.Undefined;
+
///
/// Gets or sets a value indicating whether or not plugin UI should be hidden.
///
diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs
index 8d313a646..537ffa516 100644
--- a/Dalamud/Dalamud.cs
+++ b/Dalamud/Dalamud.cs
@@ -18,6 +18,7 @@ using Dalamud.Game.Network.Internal;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Hooking.Internal;
using Dalamud.Interface;
+using Dalamud.Interface.GameFonts;
using Dalamud.Interface.Internal;
using Dalamud.IoC.Internal;
using Dalamud.Logging.Internal;
@@ -191,6 +192,9 @@ namespace Dalamud
Service.Set().Enable();
Log.Information("[T2] IM OK!");
+ Service.Set();
+ Log.Information("[T2] GFM OK!");
+
#pragma warning disable CS0618 // Type or member is obsolete
Service.Set();
#pragma warning restore CS0618 // Type or member is obsolete
diff --git a/Dalamud/Interface/GameFonts/FdtReader.cs b/Dalamud/Interface/GameFonts/FdtReader.cs
new file mode 100644
index 000000000..ceaca8096
--- /dev/null
+++ b/Dalamud/Interface/GameFonts/FdtReader.cs
@@ -0,0 +1,428 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Dalamud.Interface.GameFonts
+{
+ ///
+ /// Parses a game font file.
+ ///
+ public class FdtReader
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Content of a FDT file.
+ public FdtReader(byte[] data)
+ {
+ unsafe
+ {
+ fixed (byte* ptr = data)
+ {
+ this.FileHeader = *(FdtHeader*)ptr;
+ this.FontHeader = *(FontTableHeader*)(ptr + this.FileHeader.FontTableHeaderOffset);
+ this.KerningHeader = *(KerningTableHeader*)(ptr + this.FileHeader.KerningTableHeaderOffset);
+
+ var glyphs = (FontTableEntry*)(ptr + this.FileHeader.FontTableHeaderOffset + Marshal.SizeOf(this.FontHeader));
+ for (var i = 0; i < this.FontHeader.FontTableEntryCount; i++)
+ this.Glyphs.Add(glyphs[i]);
+
+ var kerns = (KerningTableEntry*)(ptr + this.FileHeader.KerningTableHeaderOffset + Marshal.SizeOf(this.KerningHeader));
+ for (var i = 0; i < this.FontHeader.FontTableEntryCount; i++)
+ this.Distances.Add(kerns[i]);
+ }
+ }
+ }
+
+ ///
+ /// Gets the header of this file.
+ ///
+ public FdtHeader FileHeader { get; init; }
+
+ ///
+ /// Gets the font header of this file.
+ ///
+ public FontTableHeader FontHeader { get; init; }
+
+ ///
+ /// Gets the kerning table header of this file.
+ ///
+ public KerningTableHeader KerningHeader { get; init; }
+
+ ///
+ /// Gets all the glyphs defined in this file.
+ ///
+ public List Glyphs { get; init; } = new();
+
+ ///
+ /// Gets all the kerning entries defined in this file.
+ ///
+ public List Distances { get; init; } = new();
+
+ ///
+ /// Finds glyph definition for corresponding codepoint.
+ ///
+ /// Unicode codepoint (UTF-32 value).
+ /// Corresponding FontTableEntry, or null if not found.
+ public FontTableEntry? FindGlyph(int codepoint)
+ {
+ var i = this.Glyphs.BinarySearch(new FontTableEntry { CharUtf8 = CodePointToUtf8int32(codepoint) });
+ if (i < 0 || i == this.Glyphs.Count)
+ return null;
+ return this.Glyphs[i];
+ }
+
+ ///
+ /// Returns glyph definition for corresponding codepoint.
+ ///
+ /// Unicode codepoint (UTF-32 value).
+ /// Corresponding FontTableEntry, or that of a fallback character.
+ public FontTableEntry GetGlyph(int codepoint)
+ {
+ return (this.FindGlyph(codepoint)
+ ?? this.FindGlyph('〓')
+ ?? this.FindGlyph('?')
+ ?? this.FindGlyph('='))!.Value;
+ }
+
+ ///
+ /// Returns distance adjustment between two adjacent characters.
+ ///
+ /// Left character.
+ /// Right character.
+ /// Supposed distance adjustment between given characters.
+ public int GetDistance(int codepoint1, int codepoint2)
+ {
+ var i = this.Distances.BinarySearch(new KerningTableEntry { LeftUtf8 = CodePointToUtf8int32(codepoint1), RightUtf8 = CodePointToUtf8int32(codepoint2) });
+ if (i < 0 || i == this.Distances.Count)
+ return 0;
+ return this.Distances[i].RightOffset;
+ }
+
+ private static int CodePointToUtf8int32(int codepoint)
+ {
+ if (codepoint <= 0x7F)
+ {
+ return codepoint;
+ }
+ else if (codepoint <= 0x7FF)
+ {
+ return ((0xC0 | (codepoint >> 6)) << 8)
+ | ((0x80 | ((codepoint >> 0) & 0x3F)) << 0);
+ }
+ else if (codepoint <= 0xFFFF)
+ {
+ return ((0xE0 | (codepoint >> 12)) << 16)
+ | ((0x80 | ((codepoint >> 6) & 0x3F)) << 8)
+ | ((0x80 | ((codepoint >> 0) & 0x3F)) << 0);
+ }
+ else if (codepoint <= 0x10FFFF)
+ {
+ return ((0xF0 | (codepoint >> 18)) << 24)
+ | ((0x80 | ((codepoint >> 12) & 0x3F)) << 16)
+ | ((0x80 | ((codepoint >> 6) & 0x3F)) << 8)
+ | ((0x80 | ((codepoint >> 0) & 0x3F)) << 0);
+ }
+ else
+ {
+ return 0xFFFE;
+ }
+ }
+
+ private static int Utf8Uint32ToCodePoint(int n)
+ {
+ if ((n & 0xFFFFFF80) == 0)
+ {
+ return n & 0x7F;
+ }
+ else if ((n & 0xFFFFE0C0) == 0xC080)
+ {
+ return
+ (((n >> 0x08) & 0x1F) << 6) |
+ (((n >> 0x00) & 0x3F) << 0);
+ }
+ else if ((n & 0xF0C0C0) == 0xE08080)
+ {
+ return
+ (((n >> 0x10) & 0x0F) << 12) |
+ (((n >> 0x08) & 0x3F) << 6) |
+ (((n >> 0x00) & 0x3F) << 0);
+ }
+ else if ((n & 0xF8C0C0C0) == 0xF0808080)
+ {
+ return
+ (((n >> 0x18) & 0x07) << 18) |
+ (((n >> 0x10) & 0x3F) << 12) |
+ (((n >> 0x08) & 0x3F) << 6) |
+ (((n >> 0x00) & 0x3F) << 0);
+ }
+ else
+ {
+ return 0xFFFF; // Guaranteed non-unicode
+ }
+ }
+
+ ///
+ /// Header of game font file format.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct FdtHeader
+ {
+ ///
+ /// Signature: "fcsv".
+ ///
+ public fixed byte Signature[8];
+
+ ///
+ /// Offset to FontTableHeader.
+ ///
+ public int FontTableHeaderOffset;
+
+ ///
+ /// Offset to KerningTableHeader.
+ ///
+ public int KerningTableHeaderOffset;
+
+ ///
+ /// Unused/unknown.
+ ///
+ public fixed byte Padding[0x10];
+ }
+
+ ///
+ /// Header of glyph table.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct FontTableHeader
+ {
+ ///
+ /// Signature: "fthd".
+ ///
+ public fixed byte Signature[4];
+
+ ///
+ /// Number of glyphs defined in this file.
+ ///
+ public int FontTableEntryCount;
+
+ ///
+ /// Number of kerning informations defined in this file.
+ ///
+ public int KerningTableEntryCount;
+
+ ///
+ /// Unused/unknown.
+ ///
+ public fixed byte Padding[0x04];
+
+ ///
+ /// Width of backing texture.
+ ///
+ public ushort TextureWidth;
+
+ ///
+ /// Height of backing texture.
+ ///
+ public ushort TextureHeight;
+
+ ///
+ /// Size of the font defined from this file, in points unit.
+ ///
+ public float Size;
+
+ ///
+ /// Line height of the font defined forom this file, in pixels unit.
+ ///
+ public int LineHeight;
+
+ ///
+ /// Ascent of the font defined from this file, in pixels unit.
+ ///
+ public int Ascent;
+
+ ///
+ /// Gets descent of the font defined from this file, in pixels unit.
+ ///
+ public int Descent => this.LineHeight - this.Ascent;
+ }
+
+ ///
+ /// Glyph table entry.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct FontTableEntry : IComparable
+ {
+ ///
+ /// Mapping of texture channel index to byte index.
+ ///
+ public static readonly int[] TextureChannelOrder = { 2, 1, 0, 3 };
+
+ ///
+ /// Integer representation of a Unicode character in UTF-8 in reverse order, read in little endian.
+ ///
+ public int CharUtf8;
+
+ ///
+ /// Integer representation of a Shift_JIS character in reverse order, read in little endian.
+ ///
+ public ushort CharSjis;
+
+ ///
+ /// Index of backing texture.
+ ///
+ public ushort TextureIndex;
+
+ ///
+ /// Horizontal offset of glyph image in the backing texture.
+ ///
+ public ushort TextureOffsetX;
+
+ ///
+ /// Vertical offset of glyph image in the backing texture.
+ ///
+ public ushort TextureOffsetY;
+
+ ///
+ /// Bounding width of this glyph.
+ ///
+ public byte BoundingWidth;
+
+ ///
+ /// Bounding height of this glyph.
+ ///
+ public byte BoundingHeight;
+
+ ///
+ /// Distance adjustment for drawing next character.
+ ///
+ public sbyte NextOffsetX;
+
+ ///
+ /// Distance adjustment for drawing current character.
+ ///
+ public sbyte CurrentOffsetY;
+
+ ///
+ /// Gets the index of the file among all the backing texture files.
+ ///
+ public int TextureFileIndex => this.TextureIndex / 4;
+
+ ///
+ /// Gets the channel index in the backing texture file.
+ ///
+ public int TextureChannelIndex => this.TextureIndex % 4;
+
+ ///
+ /// Gets the byte index in a multichannel pixel corresponding to the channel.
+ ///
+ public int TextureChannelByteIndex => TextureChannelOrder[this.TextureChannelIndex];
+
+ ///
+ /// Gets the advance width of this character.
+ ///
+ public int AdvanceWidth => this.BoundingWidth + this.NextOffsetX;
+
+ ///
+ /// Gets the Unicode codepoint of the character for this entry in int type.
+ ///
+ public int CharInt => Utf8Uint32ToCodePoint(this.CharUtf8);
+
+ ///
+ /// Gets the Unicode codepoint of the character for this entry in char type.
+ ///
+ public char Char => (char)Utf8Uint32ToCodePoint(this.CharUtf8);
+
+ ///
+ public int CompareTo(FontTableEntry other)
+ {
+ return this.CharUtf8 - other.CharUtf8;
+ }
+ }
+
+ ///
+ /// Header of kerning table.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct KerningTableHeader
+ {
+ ///
+ /// Signature: "knhd".
+ ///
+ public fixed byte Signature[4];
+
+ ///
+ /// Number of kerning entries in this table.
+ ///
+ public int Count;
+
+ ///
+ /// Unused/unknown.
+ ///
+ public fixed byte Padding[0x08];
+ }
+
+ ///
+ /// Kerning table entry.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct KerningTableEntry : IComparable
+ {
+ ///
+ /// Integer representation of a Unicode character in UTF-8 in reverse order, read in little endian, for the left character.
+ ///
+ public int LeftUtf8;
+
+ ///
+ /// Integer representation of a Unicode character in UTF-8 in reverse order, read in little endian, for the right character.
+ ///
+ public int RightUtf8;
+
+ ///
+ /// Integer representation of a Shift_JIS character in reverse order, read in little endian, for the left character.
+ ///
+ public ushort LeftSjis;
+
+ ///
+ /// Integer representation of a Shift_JIS character in reverse order, read in little endian, for the right character.
+ ///
+ public ushort RightSjis;
+
+ ///
+ /// Horizontal offset adjustment for the right character.
+ ///
+ public int RightOffset;
+
+ ///
+ /// Gets the Unicode codepoint of the character for this entry in int type.
+ ///
+ public int LeftInt => Utf8Uint32ToCodePoint(this.LeftUtf8);
+
+ ///
+ /// Gets the Unicode codepoint of the character for this entry in char type.
+ ///
+ public char Left => (char)Utf8Uint32ToCodePoint(this.LeftUtf8);
+
+ ///
+ /// Gets the Unicode codepoint of the character for this entry in int type.
+ ///
+ public int RightInt => Utf8Uint32ToCodePoint(this.RightUtf8);
+
+ ///
+ /// Gets the Unicode codepoint of the character for this entry in char type.
+ ///
+ public char Right => (char)Utf8Uint32ToCodePoint(this.RightUtf8);
+
+ ///
+ public int CompareTo(KerningTableEntry other)
+ {
+ if (this.LeftUtf8 == other.LeftUtf8)
+ return this.RightUtf8 - other.RightUtf8;
+ else
+ return this.LeftUtf8 - other.LeftUtf8;
+ }
+ }
+ }
+}
diff --git a/Dalamud/Interface/GameFonts/GameFont.cs b/Dalamud/Interface/GameFonts/GameFont.cs
new file mode 100644
index 000000000..f095b6fcc
--- /dev/null
+++ b/Dalamud/Interface/GameFonts/GameFont.cs
@@ -0,0 +1,174 @@
+namespace Dalamud.Interface.GameFonts
+{
+ ///
+ /// Enum of available game fonts.
+ ///
+ public enum GameFont : int
+ {
+ ///
+ /// Placeholder meaning unused.
+ ///
+ Undefined,
+
+ ///
+ /// AXIS (9.6pt)
+ ///
+ /// Contains Japanese characters in addition to Latin characters. Used in game for the whole UI.
+ ///
+ Axis96,
+
+ ///
+ /// AXIS (12pt)
+ ///
+ /// Contains Japanese characters in addition to Latin characters. Used in game for the whole UI.
+ ///
+ Axis12,
+
+ ///
+ /// AXIS (14pt)
+ ///
+ /// Contains Japanese characters in addition to Latin characters. Used in game for the whole UI.
+ ///
+ Axis14,
+
+ ///
+ /// AXIS (18pt)
+ ///
+ /// Contains Japanese characters in addition to Latin characters. Used in game for the whole UI.
+ ///
+ Axis18,
+
+ ///
+ /// AXIS (36pt)
+ ///
+ /// Contains Japanese characters in addition to Latin characters. Used in game for the whole UI.
+ ///
+ Axis36,
+
+ ///
+ /// Jupiter (16pt)
+ ///
+ /// Serif font. Contains mostly ASCII range. Used in game for job names.
+ ///
+ Jupiter16,
+
+ ///
+ /// Jupiter (20pt)
+ ///
+ /// Serif font. Contains mostly ASCII range. Used in game for job names.
+ ///
+ Jupiter20,
+
+ ///
+ /// Jupiter (23pt)
+ ///
+ /// Serif font. Contains mostly ASCII range. Used in game for job names.
+ ///
+ Jupiter23,
+
+ ///
+ /// Jupiter (45pt)
+ ///
+ /// Serif font. Contains mostly numbers. Used in game for flying texts.
+ ///
+ Jupiter45,
+
+ ///
+ /// Jupiter (46pt)
+ ///
+ /// Serif font. Contains mostly ASCII range. Used in game for job names.
+ ///
+ Jupiter46,
+
+ ///
+ /// Jupiter (90pt)
+ ///
+ /// Serif font. Contains mostly numbers. Used in game for flying texts.
+ ///
+ Jupiter90,
+
+ ///
+ /// Meidinger (16pt)
+ ///
+ /// Horizontally wide. Contains mostly numbers. Used in game for HP/MP/IL stuff.
+ ///
+ Meidinger16,
+
+ ///
+ /// Meidinger (20pt)
+ ///
+ /// Horizontally wide. Contains mostly numbers. Used in game for HP/MP/IL stuff.
+ ///
+ Meidinger20,
+
+ ///
+ /// Meidinger (40pt)
+ ///
+ /// Horizontally wide. Contains mostly numbers. Used in game for HP/MP/IL stuff.
+ ///
+ Meidinger40,
+
+ ///
+ /// MiedingerMid (10pt)
+ ///
+ /// Horizontally wide. Contains mostly ASCII range.
+ ///
+ MiedingerMid10,
+
+ ///
+ /// MiedingerMid (12pt)
+ ///
+ /// Horizontally wide. Contains mostly ASCII range.
+ ///
+ MiedingerMid12,
+
+ ///
+ /// MiedingerMid (14pt)
+ ///
+ /// Horizontally wide. Contains mostly ASCII range.
+ ///
+ MiedingerMid14,
+
+ ///
+ /// MiedingerMid (18pt)
+ ///
+ /// Horizontally wide. Contains mostly ASCII range.
+ ///
+ MiedingerMid18,
+
+ ///
+ /// MiedingerMid (36pt)
+ ///
+ /// Horizontally wide. Contains mostly ASCII range.
+ ///
+ MiedingerMid36,
+
+ ///
+ /// TrumpGothic (18.4pt)
+ ///
+ /// Horizontally narrow. Contains mostly ASCII range. Used for addon titles.
+ ///
+ TrumpGothic184,
+
+ ///
+ /// TrumpGothic (23pt)
+ ///
+ /// Horizontally narrow. Contains mostly ASCII range. Used for addon titles.
+ ///
+ TrumpGothic23,
+
+ ///
+ /// TrumpGothic (34pt)
+ ///
+ /// Horizontally narrow. Contains mostly ASCII range. Used for addon titles.
+ ///
+ TrumpGothic34,
+
+ ///
+ /// TrumpGothic (688pt)
+ ///
+ /// Horizontally narrow. Contains mostly ASCII range. Used for addon titles.
+ ///
+ TrumpGothic68,
+ }
+}
diff --git a/Dalamud/Interface/GameFonts/GameFontHandle.cs b/Dalamud/Interface/GameFonts/GameFontHandle.cs
new file mode 100644
index 000000000..6d9274ac0
--- /dev/null
+++ b/Dalamud/Interface/GameFonts/GameFontHandle.cs
@@ -0,0 +1,35 @@
+using System;
+
+using ImGuiNET;
+
+namespace Dalamud.Interface.GameFonts
+{
+ ///
+ /// Prepare and keep game font loaded for use in OnDraw.
+ ///
+ public class GameFontHandle : IDisposable
+ {
+ private readonly GameFontManager manager;
+ private readonly GameFont font;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// GameFontManager instance.
+ /// Font to use.
+ internal GameFontHandle(GameFontManager manager, GameFont font)
+ {
+ this.manager = manager;
+ this.font = font;
+ }
+
+ ///
+ /// Gets the font.
+ ///
+ /// Corresponding font or null.
+ public ImFontPtr? Get() => this.manager.GetFont(this.font);
+
+ ///
+ public void Dispose() => this.manager.DecreaseFontRef(this.font);
+ }
+}
diff --git a/Dalamud/Interface/GameFonts/GameFontManager.cs b/Dalamud/Interface/GameFonts/GameFontManager.cs
new file mode 100644
index 000000000..9cca00236
--- /dev/null
+++ b/Dalamud/Interface/GameFonts/GameFontManager.cs
@@ -0,0 +1,407 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+
+using Dalamud.Data;
+using Dalamud.Interface.Internal;
+using ImGuiNET;
+using Lumina.Data.Files;
+using Serilog;
+
+namespace Dalamud.Interface.GameFonts
+{
+ ///
+ /// Loads game font for use in ImGui.
+ ///
+ internal class GameFontManager : IDisposable
+ {
+ private static readonly string[] FontNames =
+ {
+ null,
+ "AXIS_96", "AXIS_12", "AXIS_14", "AXIS_18", "AXIS_36",
+ "Jupiter_16", "Jupiter_20", "Jupiter_23", "Jupiter_45", "Jupiter_46", "Jupiter_90",
+ "Meidinger_16", "Meidinger_20", "Meidinger_40",
+ "MiedingerMid_10", "MiedingerMid_12", "MiedingerMid_14", "MiedingerMid_18", "MiedingerMid_36",
+ "TrumpGothic_184", "TrumpGothic_23", "TrumpGothic_34", "TrumpGothic_68",
+ };
+
+ private readonly object syncRoot = new();
+
+ private readonly InterfaceManager interfaceManager;
+
+ private readonly FdtReader?[] fdts;
+ private readonly List texturePixels;
+ private readonly ImFontPtr?[] fonts = new ImFontPtr?[FontNames.Length];
+
+ private readonly int[] fontUseCounter = new int[FontNames.Length];
+ private readonly List>> glyphRectIds = new();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GameFontManager()
+ {
+ var dataManager = Service.Get();
+
+ this.fdts = FontNames.Select(fontName =>
+ {
+ var file = fontName == null ? null : dataManager.GetFile($"common/font/{fontName}.fdt");
+ return file == null ? null : new FdtReader(file!.Data);
+ }).ToArray();
+ this.texturePixels = Enumerable.Range(1, 1 + this.fdts.Where(x => x != null).Select(x => x.Glyphs.Select(x => x.TextureFileIndex).Max()).Max()).Select(x => dataManager.GameData.GetFile($"common/font/font{x}.tex").ImageData).ToList();
+
+ this.interfaceManager = Service.Get();
+ }
+
+ ///
+ /// Describe font into a string.
+ ///
+ /// Font to describe.
+ /// A string in a form of "FontName (NNNpt)".
+ public static string DescribeFont(GameFont font)
+ {
+ return font switch
+ {
+ GameFont.Undefined => "-",
+ GameFont.Axis96 => "AXIS (9.6pt)",
+ GameFont.Axis12 => "AXIS (12pt)",
+ GameFont.Axis14 => "AXIS (14pt)",
+ GameFont.Axis18 => "AXIS (18pt)",
+ GameFont.Axis36 => "AXIS (36pt)",
+ GameFont.Jupiter16 => "Jupiter (16pt)",
+ GameFont.Jupiter20 => "Jupiter (20pt)",
+ GameFont.Jupiter23 => "Jupiter (23pt)",
+ GameFont.Jupiter45 => "Jupiter Numeric (45pt)",
+ GameFont.Jupiter46 => "Jupiter (46pt)",
+ GameFont.Jupiter90 => "Jupiter Numeric (90pt)",
+ GameFont.Meidinger16 => "Meidinger Numeric (16pt)",
+ GameFont.Meidinger20 => "Meidinger Numeric (20pt)",
+ GameFont.Meidinger40 => "Meidinger Numeric (40pt)",
+ GameFont.MiedingerMid10 => "MiedingerMid (10pt)",
+ GameFont.MiedingerMid12 => "MiedingerMid (12pt)",
+ GameFont.MiedingerMid14 => "MiedingerMid (14pt)",
+ GameFont.MiedingerMid18 => "MiedingerMid (18pt)",
+ GameFont.MiedingerMid36 => "MiedingerMid (36pt)",
+ GameFont.TrumpGothic184 => "Trump Gothic (18.4pt)",
+ GameFont.TrumpGothic23 => "Trump Gothic (23pt)",
+ GameFont.TrumpGothic34 => "Trump Gothic (34pt)",
+ GameFont.TrumpGothic68 => "Trump Gothic (68pt)",
+ _ => throw new ArgumentOutOfRangeException(nameof(font), font, "Invalid argument"),
+ };
+ }
+
+ ///
+ /// Determines whether a font should be able to display most of stuff.
+ ///
+ /// Font to check.
+ /// True if it can.
+ public static bool IsGenericPurposeFont(GameFont font)
+ {
+ return font switch
+ {
+ GameFont.Axis96 => true,
+ GameFont.Axis12 => true,
+ GameFont.Axis14 => true,
+ GameFont.Axis18 => true,
+ GameFont.Axis36 => true,
+ _ => false,
+ };
+ }
+
+ ///
+ /// Fills missing glyphs in target font from source font, if both are not null.
+ ///
+ /// Source font.
+ /// Target font.
+ /// Whether to copy missing glyphs only.
+ /// Whether to call target.BuildLookupTable().
+ public static void CopyGlyphsAcrossFonts(ImFontPtr? source, ImFontPtr? target, bool missingOnly, bool rebuildLookupTable)
+ {
+ if (!source.HasValue || !target.HasValue)
+ return;
+
+ unsafe
+ {
+ var glyphs = (ImFontGlyphReal*)source.Value!.Glyphs.Data;
+ for (int j = 0, j_ = source.Value!.Glyphs.Size; j < j_; j++)
+ {
+ var glyph = &glyphs[j];
+ if (glyph->Codepoint < 32 || glyph->Codepoint >= 0xFFFF)
+ continue;
+
+ var prevGlyphPtr = (ImFontGlyphReal*)target.Value!.FindGlyphNoFallback((ushort)glyph->Codepoint).NativePtr;
+ if ((IntPtr)prevGlyphPtr == IntPtr.Zero)
+ {
+ target.Value!.AddGlyph(
+ target.Value!.ConfigData,
+ (ushort)glyph->Codepoint,
+ glyph->X0,
+ glyph->Y0,
+ glyph->X0 + ((glyph->X1 - glyph->X0) * target.Value!.FontSize / source.Value!.FontSize),
+ glyph->Y0 + ((glyph->Y1 - glyph->Y0) * target.Value!.FontSize / source.Value!.FontSize),
+ glyph->U0,
+ glyph->V0,
+ glyph->U1,
+ glyph->V1,
+ glyph->AdvanceX * target.Value!.FontSize / source.Value!.FontSize);
+ }
+ else if (!missingOnly)
+ {
+ prevGlyphPtr->X0 = glyph->X0;
+ prevGlyphPtr->Y0 = glyph->Y0;
+ prevGlyphPtr->X1 = glyph->X0 + ((glyph->X1 - glyph->X0) * target.Value!.FontSize / source.Value!.FontSize);
+ prevGlyphPtr->Y1 = glyph->Y0 + ((glyph->Y1 - glyph->Y0) * target.Value!.FontSize / source.Value!.FontSize);
+ prevGlyphPtr->U0 = glyph->U0;
+ prevGlyphPtr->V0 = glyph->V0;
+ prevGlyphPtr->U1 = glyph->U1;
+ prevGlyphPtr->V1 = glyph->V1;
+ prevGlyphPtr->AdvanceX = glyph->AdvanceX * target.Value!.FontSize / source.Value!.FontSize;
+ }
+ }
+ }
+
+ if (rebuildLookupTable)
+ target.Value!.BuildLookupTable();
+ }
+
+ ///
+ public void Dispose()
+ {
+ }
+
+ ///
+ /// Creates a new GameFontHandle, and increases internal font reference counter, and if it's first time use, then the font will be loaded on next font building process.
+ ///
+ /// Font to use.
+ /// Handle to game font that may or may not be ready yet.
+ public GameFontHandle NewFontRef(GameFont gameFont)
+ {
+ var fontIndex = (int)gameFont;
+ var needRebuild = false;
+
+ lock (this.syncRoot)
+ {
+ var prev = this.fontUseCounter[fontIndex] == 0;
+ this.fontUseCounter[fontIndex] += 1;
+ needRebuild = prev != (this.fontUseCounter[fontIndex] == 0);
+ }
+
+ if (needRebuild)
+ this.interfaceManager.RebuildFonts();
+
+ return new(this, gameFont);
+ }
+
+ ///
+ /// Gets the font.
+ ///
+ /// Font to get.
+ /// Corresponding font or null.
+ public ImFontPtr? GetFont(GameFont gameFont) => this.fonts[(int)gameFont];
+
+ ///
+ /// Fills missing glyphs in target font from source font, if both are not null.
+ ///
+ /// Source font.
+ /// Target font.
+ /// Whether to copy missing glyphs only.
+ /// Whether to call target.BuildLookupTable().
+ public void CopyGlyphsAcrossFonts(ImFontPtr? source, GameFont target, bool missingOnly, bool rebuildLookupTable)
+ {
+ GameFontManager.CopyGlyphsAcrossFonts(source, this.fonts[(int)target], missingOnly, rebuildLookupTable);
+ }
+
+ ///
+ /// Fills missing glyphs in target font from source font, if both are not null.
+ ///
+ /// Source font.
+ /// Target font.
+ /// Whether to copy missing glyphs only.
+ /// Whether to call target.BuildLookupTable().
+ public void CopyGlyphsAcrossFonts(GameFont source, ImFontPtr? target, bool missingOnly, bool rebuildLookupTable)
+ {
+ GameFontManager.CopyGlyphsAcrossFonts(this.fonts[(int)source], target, missingOnly, rebuildLookupTable);
+ }
+
+ ///
+ /// Fills missing glyphs in target font from source font, if both are not null.
+ ///
+ /// Source font.
+ /// Target font.
+ /// Whether to copy missing glyphs only.
+ /// Whether to call target.BuildLookupTable().
+ public void CopyGlyphsAcrossFonts(GameFont source, GameFont target, bool missingOnly, bool rebuildLookupTable)
+ {
+ GameFontManager.CopyGlyphsAcrossFonts(this.fonts[(int)source], this.fonts[(int)target], missingOnly, rebuildLookupTable);
+ }
+
+ ///
+ /// Build fonts before plugins do something more. To be called from InterfaceManager.
+ ///
+ public void BuildFonts()
+ {
+ this.glyphRectIds.Clear();
+ var io = ImGui.GetIO();
+ io.Fonts.TexDesiredWidth = 4096;
+
+ for (var i = 0; i < FontNames.Length; i++)
+ {
+ this.fonts[i] = null;
+ this.glyphRectIds.Add(new());
+
+ var fdt = this.fdts[i];
+ if (this.fontUseCounter[i] == 0 || fdt == null)
+ continue;
+
+ Log.Information($"GameFontManager BuildFont: {FontNames[i]}");
+
+ var font = io.Fonts.AddFontDefault();
+ this.fonts[i] = font;
+ foreach (var glyph in fdt.Glyphs)
+ {
+ var c = glyph.Char;
+ if (c < 32 || c >= 0xFFFF)
+ continue;
+
+ this.glyphRectIds[i][c] = Tuple.Create(io.Fonts.AddCustomRectFontGlyph(font, c, glyph.BoundingWidth + 1, glyph.BoundingHeight + 1, glyph.BoundingWidth + glyph.NextOffsetX, new Vector2(0, glyph.CurrentOffsetY)), glyph);
+ }
+ }
+ }
+
+ ///
+ /// Post-build fonts before plugins do something more. To be called from InterfaceManager.
+ ///
+ public unsafe void AfterBuildFonts()
+ {
+ var io = ImGui.GetIO();
+ io.Fonts.GetTexDataAsRGBA32(out byte* pixels8, out var width, out var height);
+ var pixels32 = (uint*)pixels8;
+
+ for (var i = 0; i < this.fonts.Length; i++)
+ {
+ if (!this.fonts[i].HasValue)
+ continue;
+
+ var font = this.fonts[i]!.Value;
+ var fdt = this.fdts[i];
+ var fontPtr = font.NativePtr;
+ fontPtr->ConfigData->SizePixels = fontPtr->FontSize = fdt.FontHeader.LineHeight;
+ fontPtr->Ascent = fdt.FontHeader.Ascent;
+ fontPtr->Descent = fdt.FontHeader.Descent;
+ fontPtr->EllipsisChar = '…';
+ foreach (var fallbackCharCandidate in "〓?!")
+ {
+ var glyph = font.FindGlyphNoFallback(fallbackCharCandidate);
+ if ((IntPtr)glyph.NativePtr != IntPtr.Zero)
+ {
+ font.SetFallbackChar(fallbackCharCandidate);
+ break;
+ }
+ }
+
+ fixed (char* c = FontNames[i])
+ {
+ for (var j = 0; j < 40; j++)
+ fontPtr->ConfigData->Name[j] = 0;
+ Encoding.UTF8.GetBytes(c, FontNames[i].Length, fontPtr->ConfigData->Name, 40);
+ }
+
+ foreach (var (c, (rectId, glyph)) in this.glyphRectIds[i])
+ {
+ var rc = io.Fonts.GetCustomRectByIndex(rectId);
+ var sourceBuffer = this.texturePixels[glyph.TextureFileIndex];
+ var sourceBufferDelta = glyph.TextureChannelByteIndex;
+ for (var y = 0; y < glyph.BoundingHeight; y++)
+ {
+ for (var x = 0; x < glyph.BoundingWidth; x++)
+ {
+ var a = sourceBuffer[sourceBufferDelta + (4 * (((glyph.TextureOffsetY + y) * fdt.FontHeader.TextureWidth) + glyph.TextureOffsetX + x))];
+ pixels32[((rc.Y + y) * width) + rc.X + x] = (uint)(a << 24) | 0xFFFFFFu;
+ }
+ }
+ }
+ }
+
+ this.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, GameFont.Axis96, true, false);
+ this.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, GameFont.Axis12, true, false);
+ this.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, GameFont.Axis14, true, false);
+ this.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, GameFont.Axis18, true, false);
+ this.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, GameFont.Axis36, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis18, GameFont.Jupiter16, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Jupiter20, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Jupiter23, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Jupiter45, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Jupiter46, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Jupiter90, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis18, GameFont.Meidinger16, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Meidinger20, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.Meidinger40, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis96, GameFont.MiedingerMid10, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis12, GameFont.MiedingerMid12, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis14, GameFont.MiedingerMid14, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis18, GameFont.MiedingerMid18, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.MiedingerMid36, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis18, GameFont.TrumpGothic184, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.TrumpGothic23, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.TrumpGothic34, true, false);
+ this.CopyGlyphsAcrossFonts(GameFont.Axis36, GameFont.TrumpGothic68, true, false);
+
+ foreach (var font in this.fonts)
+ font?.BuildLookupTable();
+ }
+
+ ///
+ /// Decrease font reference counter and release if nobody is using it.
+ ///
+ /// Font to release.
+ internal void DecreaseFontRef(GameFont gameFont)
+ {
+ var fontIndex = (int)gameFont;
+ var needRebuild = false;
+
+ lock (this.syncRoot)
+ {
+ var prev = this.fontUseCounter[fontIndex] == 0;
+ this.fontUseCounter[fontIndex] -= 1;
+ needRebuild = prev != (this.fontUseCounter[fontIndex] == 0);
+ }
+
+ if (needRebuild)
+ this.interfaceManager.RebuildFonts();
+ }
+
+ private struct ImFontGlyphReal
+ {
+ public uint ColoredVisibleCodepoint;
+ public float AdvanceX;
+ public float X0;
+ public float Y0;
+ public float X1;
+ public float Y1;
+ public float U0;
+ public float V0;
+ public float U1;
+ public float V1;
+
+ public bool Colored
+ {
+ get => ((this.ColoredVisibleCodepoint >> 0) & 1) != 0;
+ set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 0xFFFFFFFEu) | (value ? 1u : 0u);
+ }
+
+ public bool Visible
+ {
+ get => ((this.ColoredVisibleCodepoint >> 1) & 1) != 0;
+ set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 0xFFFFFFFDu) | (value ? 2u : 0u);
+ }
+
+ public int Codepoint
+ {
+ get => (int)(this.ColoredVisibleCodepoint >> 2);
+ set => this.ColoredVisibleCodepoint = (this.ColoredVisibleCodepoint & 3u) | ((uint)this.Codepoint << 2);
+ }
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index b285c60ed..1e9654dc0 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -16,6 +16,7 @@ using Dalamud.Game.Gui.Internal;
using Dalamud.Game.Internal.DXGI;
using Dalamud.Hooking;
using Dalamud.Hooking.Internal;
+using Dalamud.Interface.GameFonts;
using Dalamud.Interface.Internal.ManagedAsserts;
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.Internal.Windows.StyleEditor;
@@ -56,6 +57,9 @@ namespace Dalamud.Interface.Internal
private readonly SwapChainVtableResolver address;
private RawDX11Scene? scene;
+ private GameFont overwriteDefaultFontFromGameFont = GameFont.Undefined;
+ private GameFontHandle? overwriteDefaultFontFromGameFontHandle;
+
// can't access imgui IO before first present call
private bool lastWantCapture = false;
private bool isRebuildingFonts = false;
@@ -128,10 +132,15 @@ namespace Dalamud.Interface.Internal
public event Action ResizeBuffers;
///
- /// Gets or sets an action that is executed when fonts are rebuilt.
+ /// Gets or sets an action that is executed right before fonts are rebuilt.
///
public event Action BuildFonts;
+ ///
+ /// Gets or sets an action that is executed right after fonts are rebuilt.
+ ///
+ public event Action AfterBuildFonts;
+
///
/// Gets the default ImGui font.
///
@@ -304,6 +313,16 @@ namespace Dalamud.Interface.Internal
if (!this.isRebuildingFonts)
{
Log.Verbose("[FONT] RebuildFonts() trigger");
+ var configuration = Service.Get();
+ if (this.overwriteDefaultFontFromGameFont != configuration.DefaultFontFromGame)
+ {
+ this.overwriteDefaultFontFromGameFont = configuration.DefaultFontFromGame;
+ this.overwriteDefaultFontFromGameFontHandle?.Dispose();
+ if (configuration.DefaultFontFromGame == GameFont.Undefined)
+ this.overwriteDefaultFontFromGameFontHandle = null;
+ else
+ this.overwriteDefaultFontFromGameFontHandle = Service.Get().NewFontRef(configuration.DefaultFontFromGame);
+ }
this.isRebuildingFonts = true;
this.scene.OnNewRenderFrame += this.RebuildFontsInternal;
@@ -384,6 +403,16 @@ namespace Dalamud.Interface.Internal
this.scene.OnBuildUI += this.Display;
this.scene.OnNewInputFrame += this.OnNewInputFrame;
+ if (this.overwriteDefaultFontFromGameFont != configuration.DefaultFontFromGame)
+ {
+ this.overwriteDefaultFontFromGameFont = configuration.DefaultFontFromGame;
+ this.overwriteDefaultFontFromGameFontHandle?.Dispose();
+ if (configuration.DefaultFontFromGame == GameFont.Undefined)
+ this.overwriteDefaultFontFromGameFontHandle = null;
+ else
+ this.overwriteDefaultFontFromGameFontHandle = Service.Get().NewFontRef(configuration.DefaultFontFromGame);
+ }
+
this.SetupFonts();
StyleModel.TransferOldModels();
@@ -496,7 +525,6 @@ namespace Dalamud.Interface.Internal
ImGui.GetIO().Fonts.Clear();
ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig();
- fontConfig.MergeMode = true;
fontConfig.PixelSnapH = true;
var fontPathJp = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "NotoSansCJKjp-Medium.otf");
@@ -522,7 +550,9 @@ namespace Dalamud.Interface.Internal
},
GCHandleType.Pinned);
+ fontConfig.MergeMode = false;
ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathGame, 17.0f, fontConfig, gameRangeHandle.AddrOfPinnedObject());
+ fontConfig.MergeMode = true;
var fontPathIcon = Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "FontAwesome5FreeSolid.otf");
@@ -546,6 +576,9 @@ namespace Dalamud.Interface.Internal
MonoFont = ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathMono, 16.0f);
+ var gameFontManager = Service.Get();
+ gameFontManager.BuildFonts();
+
Log.Verbose("[FONT] Invoke OnBuildFonts");
this.BuildFonts?.Invoke();
Log.Verbose("[FONT] OnBuildFonts OK!");
@@ -557,6 +590,13 @@ namespace Dalamud.Interface.Internal
ImGui.GetIO().Fonts.Build();
+ gameFontManager.AfterBuildFonts();
+ GameFontManager.CopyGlyphsAcrossFonts(this.overwriteDefaultFontFromGameFontHandle?.Get(), DefaultFont, false, true);
+
+ Log.Verbose("[FONT] Invoke OnAfterBuildFonts");
+ this.AfterBuildFonts?.Invoke();
+ Log.Verbose("[FONT] OnAfterBuildFonts OK!");
+
Log.Verbose("[FONT] Fonts built!");
this.fontBuildSignal.Set();
diff --git a/Dalamud/Interface/Internal/Windows/SettingsWindow.cs b/Dalamud/Interface/Internal/Windows/SettingsWindow.cs
index d02dae56d..6fa6df808 100644
--- a/Dalamud/Interface/Internal/Windows/SettingsWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/SettingsWindow.cs
@@ -12,6 +12,7 @@ using Dalamud.Game.Gui.Dtr;
using Dalamud.Game.Text;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
+using Dalamud.Interface.GameFonts;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Internal;
using Dalamud.Utility;
@@ -27,6 +28,8 @@ namespace Dalamud.Interface.Internal.Windows
private const float MinScale = 0.3f;
private const float MaxScale = 2.0f;
+ private readonly List validFontChoices;
+ private readonly string[] validFontNames;
private readonly string[] languages;
private readonly string[] locLanguages;
private int langIndex;
@@ -66,6 +69,8 @@ namespace Dalamud.Interface.Internal.Windows
private bool doButtonsSystemMenu;
private bool disableRmtFiltering;
+ private int validFontIndex;
+
#region Experimental
private bool doPluginTest;
@@ -111,6 +116,10 @@ namespace Dalamud.Interface.Internal.Windows
this.doButtonsSystemMenu = configuration.DoButtonsSystemMenu;
this.disableRmtFiltering = configuration.DisableRmtFiltering;
+ this.validFontChoices = Enum.GetValues().Where(x => x == GameFont.Undefined || GameFontManager.IsGenericPurposeFont(x)).ToList();
+ this.validFontNames = this.validFontChoices.Select(x => GameFontManager.DescribeFont(x)).ToArray();
+ this.validFontIndex = Math.Max(0, this.validFontChoices.IndexOf(configuration.DefaultFontFromGame));
+
this.languages = Localization.ApplicableLangCodes.Prepend("en").ToArray();
try
{
@@ -286,6 +295,11 @@ namespace Dalamud.Interface.Internal.Windows
ImGuiHelpers.ScaledDummy(10, 16);
+ ImGui.Text(Loc.Localize("DalamudSettingsGlobalFont", "Global Font"));
+ ImGui.Combo("##DalamudSettingsGlobalFontDrag", ref this.validFontIndex, this.validFontNames, this.validFontNames.Length);
+
+ ImGuiHelpers.ScaledDummy(10, 16);
+
if (ImGui.Button(Loc.Localize("DalamudSettingsOpenStyleEditor", "Open Style Editor")))
{
Service.Get().OpenStyleEditor();
@@ -800,6 +814,8 @@ namespace Dalamud.Interface.Internal.Windows
configuration.IsFocusManagementEnabled = this.doFocus;
configuration.ShowTsm = this.doTsm;
+ configuration.DefaultFontFromGame = this.validFontChoices[this.validFontIndex];
+
// This is applied every frame in InterfaceManager::CheckViewportState()
configuration.IsDisableViewport = !this.doViewport;
@@ -842,6 +858,8 @@ namespace Dalamud.Interface.Internal.Windows
configuration.Save();
+ Service.Get().RebuildFonts();
+
_ = Service.Get().ReloadPluginMastersAsync();
}
}
diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs
index 445ac3618..35c82102b 100644
--- a/Dalamud/Interface/UiBuilder.cs
+++ b/Dalamud/Interface/UiBuilder.cs
@@ -5,6 +5,7 @@ using System.Diagnostics;
using Dalamud.Configuration.Internal;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.Gui;
+using Dalamud.Interface.GameFonts;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.ManagedAsserts;
using Dalamud.Interface.Internal.Notifications;
@@ -39,6 +40,7 @@ namespace Dalamud.Interface
var interfaceManager = Service.Get();
interfaceManager.Draw += this.OnDraw;
interfaceManager.BuildFonts += this.OnBuildFonts;
+ interfaceManager.AfterBuildFonts += this.OnAfterBuildFonts;
interfaceManager.ResizeBuffers += this.OnResizeBuffers;
}
@@ -67,6 +69,15 @@ namespace Dalamud.Interface
///
public event Action BuildFonts;
+ ///
+ /// Gets or sets an action that is called any time right after ImGui fonts are rebuilt.
+ /// Any ImFontPtr objects that you store can be invalidated when fonts are rebuilt
+ /// (at any time), so you should both reload your custom fonts and restore those
+ /// pointers inside this handler.
+ /// PLEASE remove this handler inside Dispose, or when you no longer need your fonts!
+ ///
+ public event Action AfterBuildFonts;
+
///
/// Gets the default Dalamud font based on Noto Sans CJK Medium in 17pt - supporting all game languages and icons.
///
@@ -201,6 +212,13 @@ namespace Dalamud.Interface
public TextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels)
=> Service.Get().LoadImageRaw(imageData, width, height, numChannels);
+ ///
+ /// Gets a game font.
+ ///
+ /// Font to get.
+ /// Handle to the game font which may or may not be available for use yet.
+ public GameFontHandle GetGameFontHandle(GameFont gameFont) => Service.Get().NewFontRef(gameFont);
+
///
/// Call this to queue a rebuild of the font atlas.
/// This will invoke any handlers and ensure that any loaded fonts are
@@ -320,6 +338,11 @@ namespace Dalamud.Interface
this.BuildFonts?.Invoke();
}
+ private void OnAfterBuildFonts()
+ {
+ this.AfterBuildFonts?.Invoke();
+ }
+
private void OnResizeBuffers()
{
this.ResizeBuffers?.Invoke();