#nullable enable using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using Avalonia.Media; using Avalonia.Media.Fonts; using Avalonia.Platform; using SkiaSharp; using System.Diagnostics.CodeAnalysis; using System.IO; namespace Avalonia.Skia.UnitTests.Media { public class CustomFontManagerImpl : IFontManagerImpl, IDisposable { private readonly string _defaultFamilyName; private readonly string[] _bcp47 = { CultureInfo.CurrentCulture.ThreeLetterISOLanguageName, CultureInfo.CurrentCulture.TwoLetterISOLanguageName }; private IFontCollection? _systemFonts; public CustomFontManagerImpl() { _defaultFamilyName = FontManager.SystemFontsKey + "#Noto Mono"; } public IFontCollection SystemFonts { get { if(_systemFonts is null) { var source = new Uri("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests"); _systemFonts = new EmbeddedFontCollection(FontManager.SystemFontsKey, source); } return _systemFonts; } } public string GetDefaultFontFamilyName() { return _defaultFamilyName; } public string[] GetInstalledFontFamilyNames(bool checkForUpdates = false) { // Directly load from assets to avoid creating the full font collection try { var key = new Uri("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests"); var assetLoader = AvaloniaLocator.Current.GetRequiredService(); var fontAssets = FontFamilyLoader.LoadFontAssets(key); var names = new HashSet(StringComparer.InvariantCultureIgnoreCase); foreach (var fontAsset in fontAssets) { try { using var stream = assetLoader.Open(fontAsset); using var sk = SKTypeface.FromStream(stream); if (sk != null && !string.IsNullOrEmpty(sk.FamilyName)) { names.Add(sk.FamilyName); } } catch { // Ignore faulty assets } } return names.ToArray(); } catch { return Array.Empty(); } } public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, string? familyName, CultureInfo? culture, out Typeface typeface) { if(SystemFonts.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, familyName, culture, out typeface)) { return true; } var fallback = SKFontManager.Default.MatchCharacter(null, (SKFontStyleWeight)fontWeight, (SKFontStyleWidth)fontStretch, (SKFontStyleSlant)fontStyle, _bcp47, codepoint); typeface = new Typeface(fallback?.FamilyName ?? _defaultFamilyName, fontStyle, fontWeight); return true; } public bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { if (SystemFonts.TryGetGlyphTypeface(familyName, style, weight, stretch, out glyphTypeface)) { return true; } var skTypeface = SKTypeface.FromFamilyName(familyName, (SKFontStyleWeight)weight, SKFontStyleWidth.Normal, (SKFontStyleSlant)style); glyphTypeface = new GlyphTypefaceImpl(skTypeface, FontSimulations.None); return true; } public bool TryCreateGlyphTypeface(Stream stream, FontSimulations fontSimulations, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { var skTypeface = SKTypeface.FromStream(stream); glyphTypeface = new GlyphTypefaceImpl(skTypeface, fontSimulations); return true; } public void Dispose() { _systemFonts?.Dispose(); } } }