| 1 | #nullable enable |
| 2 | |
| 3 | using CascadeIDE.Views.SkiaKit; |
| 4 | using SkiaSharp; |
| 5 | |
| 6 | namespace CascadeIDE.Views.Chat.Skia; |
| 7 | |
| 8 | /// <summary>SKTypeface/SKFont из списка семейств (comma-separated) с fallback.</summary> |
| 9 | internal static class SkiaChatFeedFontResolver |
| 10 | { |
| 11 | public static SKTypeface ResolveTypeface(string familyList, SKFontStyle style) |
| 12 | { |
| 13 | foreach (var name in SplitFamilies(familyList)) |
| 14 | { |
| 15 | var tf = SKTypeface.FromFamilyName(name, style); |
| 16 | if (IsUsable(tf, name)) |
| 17 | return tf; |
| 18 | } |
| 19 | |
| 20 | return SKTypeface.FromFamilyName("Segoe UI", style) ?? SKTypeface.Default; |
| 21 | } |
| 22 | |
| 23 | public static SKFont CreateFont(string familyList, float size, SKFontStyle? style = null) |
| 24 | { |
| 25 | var font = new SKFont(ResolveTypeface(familyList, style ?? SKFontStyle.Normal), size); |
| 26 | SkiaKitFonts.ApplyTextQuality(font); |
| 27 | return font; |
| 28 | } |
| 29 | |
| 30 | private static IEnumerable<string> SplitFamilies(string familyList) |
| 31 | { |
| 32 | if (string.IsNullOrWhiteSpace(familyList)) |
| 33 | { |
| 34 | yield return "Segoe UI"; |
| 35 | yield break; |
| 36 | } |
| 37 | |
| 38 | foreach (var part in familyList.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)) |
| 39 | yield return part; |
| 40 | } |
| 41 | |
| 42 | private static bool IsUsable(SKTypeface? typeface, string requested) |
| 43 | { |
| 44 | if (typeface is null) |
| 45 | return false; |
| 46 | |
| 47 | var family = typeface.FamilyName ?? ""; |
| 48 | if (family.Length == 0) |
| 49 | return false; |
| 50 | |
| 51 | return family.Contains(requested, StringComparison.OrdinalIgnoreCase) |
| 52 | || !string.Equals(family, "sans-serif", StringComparison.OrdinalIgnoreCase); |
| 53 | } |
| 54 | } |
| 55 | |