B1: dedupe CJK/symbols merge via AddCjkAndSymbols, trim fallback range; FontsReady + report self-test
This commit is contained in:
+76
-39
@@ -61,6 +61,17 @@ public sealed class FontManager : IDisposable
|
|||||||
private ushort[] Ranges = [];
|
private ushort[] Ranges = [];
|
||||||
private ushort[] JpRange = [];
|
private ushort[] JpRange = [];
|
||||||
|
|
||||||
|
// B1: trimmed remainder the NotoSansCjk fallback is the sole source for
|
||||||
|
// (Hangul + Simplified-Han); excludes the Default/Latin block already merged
|
||||||
|
// by the global font, so the fallback no longer re-merges the full Ranges array.
|
||||||
|
private ushort[] CjkFallbackGlyphRange = [];
|
||||||
|
|
||||||
|
// Report accessor for the ctor self-test: built glyph-range array lengths so
|
||||||
|
// the step can show the B1 dedup effect (a small trimmed fallback vs the large
|
||||||
|
// primary range) in its on-disk report instead of a bare Pass.
|
||||||
|
internal (int Ranges, int JpRange, int CjkFallback) GlyphRangeLengths =>
|
||||||
|
(Ranges.Length, JpRange.Length, CjkFallbackGlyphRange.Length);
|
||||||
|
|
||||||
public static readonly HashSet<float> AxisFontSizeList =
|
public static readonly HashSet<float> AxisFontSizeList =
|
||||||
[
|
[
|
||||||
9.6f,
|
9.6f,
|
||||||
@@ -170,6 +181,42 @@ public sealed class FontManager : IDisposable
|
|||||||
|
|
||||||
// Instance method so Ranges / JpRange are reachable without parameter
|
// Instance method so Ranges / JpRange are reachable without parameter
|
||||||
// plumbing; PascalCase field names follow the existing class style.
|
// plumbing; PascalCase field names follow the existing class style.
|
||||||
|
// B1: shared CJK + symbols tail for both the regular and italic delegate
|
||||||
|
// fonts. Earlier-merged fonts win for shared codepoints (imgui MergeMode),
|
||||||
|
// so this runs AFTER the primary font is set as config.MergeFont. The CJK
|
||||||
|
// fallback is the sole Hangul/Simplified-Han source when UseHellionFont=true
|
||||||
|
// (global=Inter-Light), so it stays in the chain — only its glyph range is
|
||||||
|
// trimmed (CjkFallbackGlyphRange) to drop the Default-block/endonym overlap.
|
||||||
|
// basePt sizes the fallback to match the primary font; the Japanese merge
|
||||||
|
// keeps its own configured size and the full JpRange (which owns Traditional
|
||||||
|
// Han such as 體 U+9AD4), so japanese↔fallback no longer overlap.
|
||||||
|
private void AddCjkAndSymbols(
|
||||||
|
IFontAtlasBuildToolkitPreBuild tk,
|
||||||
|
SafeFontConfig config,
|
||||||
|
float basePt
|
||||||
|
)
|
||||||
|
{
|
||||||
|
config.SizePt = Plugin.Config.JapaneseFontV2.SizePt;
|
||||||
|
config.GlyphRanges = JpRange;
|
||||||
|
AddFontWithFallback(tk, Plugin.Config.JapaneseFontV2.FontId, config, "japanese");
|
||||||
|
|
||||||
|
// v1.5.3: NotoSansCjk fallback covers Hangul, Simplified-Chinese-specific
|
||||||
|
// Han (e.g. 简) and other CJK glyphs the primary (Inter Light / global font)
|
||||||
|
// and the FFXIV Japanese font do not ship. B1: trimmed to CjkFallbackGlyphRange
|
||||||
|
// so it no longer re-merges the Default block. Merged last so earlier fonts win.
|
||||||
|
config.SizePt = basePt;
|
||||||
|
config.GlyphRanges = CjkFallbackGlyphRange;
|
||||||
|
AddFontWithFallback(
|
||||||
|
tk,
|
||||||
|
new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular),
|
||||||
|
config,
|
||||||
|
"noto-cjk-fallback"
|
||||||
|
);
|
||||||
|
|
||||||
|
config.SizePt = ResolveSymbolsFontPt();
|
||||||
|
tk.AddGameSymbol(config);
|
||||||
|
}
|
||||||
|
|
||||||
private IFontHandle BuildRegularFontHandle(IFontAtlas atlas) =>
|
private IFontHandle BuildRegularFontHandle(IFontAtlas atlas) =>
|
||||||
atlas.NewDelegateFontHandle(e =>
|
atlas.NewDelegateFontHandle(e =>
|
||||||
e.OnPreBuild(tk =>
|
e.OnPreBuild(tk =>
|
||||||
@@ -183,25 +230,7 @@ public sealed class FontManager : IDisposable
|
|||||||
? tk.AddFontFromMemory(bundledBytes, config, "Inter-Light")
|
? tk.AddFontFromMemory(bundledBytes, config, "Inter-Light")
|
||||||
: AddFontWithFallback(tk, Plugin.Config.GlobalFontV2.FontId, config, "global");
|
: AddFontWithFallback(tk, Plugin.Config.GlobalFontV2.FontId, config, "global");
|
||||||
|
|
||||||
config.SizePt = Plugin.Config.JapaneseFontV2.SizePt;
|
AddCjkAndSymbols(tk, config, basePt);
|
||||||
config.GlyphRanges = JpRange;
|
|
||||||
AddFontWithFallback(tk, Plugin.Config.JapaneseFontV2.FontId, config, "japanese");
|
|
||||||
|
|
||||||
// v1.5.3: NotoSansCjk fallback covers Hangul, Simplified-Chinese
|
|
||||||
// -specific Han (e.g. 简) and other CJK glyphs that the primary
|
|
||||||
// (Inter Light / global font) and the FFXIV Japanese font do not
|
|
||||||
// ship. Merged last so earlier fonts win for shared codepoints.
|
|
||||||
config.SizePt = basePt;
|
|
||||||
config.GlyphRanges = Ranges;
|
|
||||||
AddFontWithFallback(
|
|
||||||
tk,
|
|
||||||
new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular),
|
|
||||||
config,
|
|
||||||
"noto-cjk-fallback"
|
|
||||||
);
|
|
||||||
|
|
||||||
config.SizePt = ResolveSymbolsFontPt();
|
|
||||||
tk.AddGameSymbol(config);
|
|
||||||
|
|
||||||
tk.Font = config.MergeFont;
|
tk.Font = config.MergeFont;
|
||||||
})
|
})
|
||||||
@@ -223,22 +252,7 @@ public sealed class FontManager : IDisposable
|
|||||||
"italic"
|
"italic"
|
||||||
);
|
);
|
||||||
|
|
||||||
config.SizePt = Plugin.Config.JapaneseFontV2.SizePt;
|
AddCjkAndSymbols(tk, config, Plugin.Config.ItalicFontV2.SizePt);
|
||||||
config.GlyphRanges = JpRange;
|
|
||||||
AddFontWithFallback(tk, Plugin.Config.JapaneseFontV2.FontId, config, "japanese");
|
|
||||||
|
|
||||||
// v1.5.3: NotoSansCjk fallback (see BuildRegularFontHandle).
|
|
||||||
config.SizePt = Plugin.Config.ItalicFontV2.SizePt;
|
|
||||||
config.GlyphRanges = Ranges;
|
|
||||||
AddFontWithFallback(
|
|
||||||
tk,
|
|
||||||
new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular),
|
|
||||||
config,
|
|
||||||
"noto-cjk-fallback"
|
|
||||||
);
|
|
||||||
|
|
||||||
config.SizePt = ResolveSymbolsFontPt();
|
|
||||||
tk.AddGameSymbol(config);
|
|
||||||
|
|
||||||
tk.Font = config.MergeFont;
|
tk.Font = config.MergeFont;
|
||||||
})
|
})
|
||||||
@@ -282,7 +296,11 @@ public sealed class FontManager : IDisposable
|
|||||||
|
|
||||||
private unsafe void SetUpRanges()
|
private unsafe void SetUpRanges()
|
||||||
{
|
{
|
||||||
ushort[] BuildRange(IReadOnlyList<ushort>? chars, params nint[] ranges)
|
ushort[] BuildRange(
|
||||||
|
IReadOnlyList<ushort>? chars,
|
||||||
|
bool includeCommonExtras,
|
||||||
|
params nint[] ranges
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var builder = new ImFontGlyphRangesBuilderPtr(ImGuiNative.ImFontGlyphRangesBuilder());
|
var builder = new ImFontGlyphRangesBuilderPtr(ImGuiNative.ImFontGlyphRangesBuilder());
|
||||||
foreach (var range in ranges)
|
foreach (var range in ranges)
|
||||||
@@ -300,8 +318,16 @@ public sealed class FontManager : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Common extras (Axis ingame glyphs, endonyms, enclosed alphanumerics)
|
||||||
|
// belong to the primary/Japanese ranges only. The trimmed CJK fallback
|
||||||
|
// (B1) skips them so it stays a pure Hangul/Simplified-Han remainder and
|
||||||
|
// does not re-merge the Default-block work the global font already did.
|
||||||
|
if (includeCommonExtras)
|
||||||
|
{
|
||||||
// Ingame supported ranges
|
// Ingame supported ranges
|
||||||
var reader = new FdtReader(Plugin.DataManager.GetFile("common/font/axis_12.fdt")!.Data);
|
var reader = new FdtReader(
|
||||||
|
Plugin.DataManager.GetFile("common/font/axis_12.fdt")!.Data
|
||||||
|
);
|
||||||
foreach (var c in reader.Glyphs)
|
foreach (var c in reader.Glyphs)
|
||||||
builder.AddChar(c.Char);
|
builder.AddChar(c.Char);
|
||||||
|
|
||||||
@@ -327,6 +353,8 @@ public sealed class FontManager : IDisposable
|
|||||||
builder.AddChar((char)i);
|
builder.AddChar((char)i);
|
||||||
|
|
||||||
builder.AddChar('⓪');
|
builder.AddChar('⓪');
|
||||||
|
}
|
||||||
|
|
||||||
return builder.BuildRangesToArray();
|
return builder.BuildRangesToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,8 +384,17 @@ public sealed class FontManager : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ranges = BuildRange(customChars.Count > 0 ? customChars : null, ranges.ToArray());
|
Ranges = BuildRange(
|
||||||
JpRange = BuildRange(GlyphRangesJapanese.GlyphRanges);
|
customChars.Count > 0 ? customChars : null,
|
||||||
|
includeCommonExtras: true,
|
||||||
|
ranges.ToArray()
|
||||||
|
);
|
||||||
|
JpRange = BuildRange(GlyphRangesJapanese.GlyphRanges, includeCommonExtras: true);
|
||||||
|
|
||||||
|
// B1: the fallback gets only the trimmed Hangul/Simplified-Han remainder.
|
||||||
|
// No Default block, no endonyms — those are already merged by the global and
|
||||||
|
// Japanese fonts, so re-merging them on the fallback was wasted atlas work.
|
||||||
|
CjkFallbackGlyphRange = BuildRange(CjkFallbackRange.Pairs, includeCommonExtras: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add font with fallback to NotoSansCjkRegular if unavailable
|
// Add font with fallback to NotoSansCjkRegular if unavailable
|
||||||
|
|||||||
@@ -86,6 +86,49 @@ internal sealed class FontManagerCtorSmokeStep : ISelfTestStep
|
|||||||
return SelfTestStepResult.Fail;
|
return SelfTestStepResult.Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// B1: assert the atlas actually finished building all required handles,
|
||||||
|
// not just that the references are non-null. FontsReady is the observable
|
||||||
|
// state the trimmed-fallback rebuild must still reach; a half-built atlas
|
||||||
|
// would pass the null/exception checks above but fail here.
|
||||||
|
if (!fm.FontsReady)
|
||||||
|
{
|
||||||
|
ImGui.Text("FontManager.FontsReady is false (atlas not fully built).");
|
||||||
|
SelfTestReport.Append(
|
||||||
|
Name,
|
||||||
|
"FAIL",
|
||||||
|
new[] { "FontsReady is false — atlas not fully built." }
|
||||||
|
);
|
||||||
|
return SelfTestStepResult.Fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report what was actually verified (Flo's request: don't just show Pass).
|
||||||
|
// The glyph-range entry counts make the B1 dedup visible — the cjk-fallback
|
||||||
|
// range is now a small trimmed remainder next to the large primary range.
|
||||||
|
var counts = fm.GlyphRangeLengths;
|
||||||
|
var italicState =
|
||||||
|
fm.ItalicFont is null ? "disabled (null)"
|
||||||
|
: fm.ItalicFont.Available ? "available"
|
||||||
|
: "NOT available";
|
||||||
|
var path = SelfTestReport.Append(
|
||||||
|
Name,
|
||||||
|
"PASS",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
$"Axis available: {fm.Axis.Available}",
|
||||||
|
$"AxisItalic available: {fm.AxisItalic.Available}",
|
||||||
|
$"FontAwesome available: {fm.FontAwesome.Available}",
|
||||||
|
$"RegularFont available: {fm.RegularFont.Available}",
|
||||||
|
$"ItalicFont: {italicState}",
|
||||||
|
$"FontsReady: {fm.FontsReady}",
|
||||||
|
$"Glyph-range entries: primary={counts.Ranges}, jp={counts.JpRange}, "
|
||||||
|
+ $"cjk-fallback={counts.CjkFallback} (B1 trimmed)",
|
||||||
|
$"UseHellionFont={Plugin.Config.UseHellionFont}, ItalicEnabled={Plugin.Config.ItalicEnabled}",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ImGui.Text(
|
||||||
|
$"PASS — FontsReady, ranges primary={counts.Ranges}/jp={counts.JpRange}/"
|
||||||
|
+ $"cjk-fallback={counts.CjkFallback}. Report: {path}"
|
||||||
|
);
|
||||||
return SelfTestStepResult.Pass;
|
return SelfTestStepResult.Pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user