Browse Source

Try to find a fallback glyph typeface for unknown font families

pull/10455/head
Benedikt Stebner 4 years ago
parent
commit
2364c5d140
  1. 24
      src/Avalonia.Base/Media/FontManager.cs
  2. 1
      src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs
  3. 35
      src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
  4. 11
      tests/Avalonia.Direct2D1.UnitTests/Media/FontManagerImplTests.cs
  5. 11
      tests/Avalonia.Skia.UnitTests/Media/FontManagerImplTests.cs

24
src/Avalonia.Base/Media/FontManager.cs

@ -15,9 +15,10 @@ namespace Avalonia.Media
/// </summary>
public sealed class FontManager
{
internal static Uri SystemFontsKey = new Uri("fonts:SystemFonts");
public const string FontCollectionScheme = "fonts";
private readonly SystemFontCollection _systemFonts;
private readonly ConcurrentDictionary<Uri, IFontCollection> _fontCollections = new ConcurrentDictionary<Uri, IFontCollection>();
private readonly IReadOnlyList<FontFallback>? _fontFallbacks;
@ -36,7 +37,7 @@ namespace Avalonia.Media
throw new InvalidOperationException("Default font family name can't be null or empty.");
}
_systemFonts = new SystemFontCollection(this);
AddFontCollection(new SystemFontCollection(this));
}
public static FontManager Current
@ -71,7 +72,7 @@ namespace Avalonia.Media
/// <summary>
/// Get all system fonts.
/// </summary>
public IFontCollection SystemFonts => _systemFonts;
public IFontCollection SystemFonts => _fontCollections[SystemFontsKey];
internal IFontManagerImpl PlatformImpl { get; }
@ -120,6 +121,11 @@ namespace Avalonia.Media
{
return true;
}
if (!fontFamily.FamilyNames.HasFallbacks)
{
return false;
}
}
foreach (var familyName in fontFamily.FamilyNames)
@ -130,7 +136,7 @@ namespace Avalonia.Media
}
}
return false;
return SystemFonts.TryGetGlyphTypeface(DefaultFontFamilyName, typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface);
}
public void AddFontCollection(IFontCollection fontCollection)
@ -139,13 +145,15 @@ namespace Avalonia.Media
if (!fontCollection.Key.IsFontCollection())
{
throw new ArgumentException(nameof(fontCollection), "Font collection Key should follow the fontCollection: scheme.");
throw new ArgumentException(nameof(fontCollection), "Font collection Key should follow the fonts: scheme.");
}
if (!_fontCollections.TryAdd(key, fontCollection))
_fontCollections.AddOrUpdate(key, fontCollection, (_, oldCollection) =>
{
throw new ArgumentException(nameof(fontCollection), "Font collection is already registered.");
}
oldCollection.Dispose();
return fontCollection;
});
fontCollection.Initialize(PlatformImpl);
}

1
src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs

@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Avalonia.Platform;
using Avalonia.Utilities;
namespace Avalonia.Media.Fonts
{

35
src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs

@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Platform;
@ -8,8 +9,7 @@ namespace Avalonia.Media.Fonts
{
internal class SystemFontCollection : IFontCollection
{
private readonly Dictionary<string, Dictionary<FontCollectionKey, IGlyphTypeface>> _glyphTypefaceCache =
new Dictionary<string, Dictionary<FontCollectionKey, IGlyphTypeface>>();
private readonly ConcurrentDictionary<string, ConcurrentDictionary<FontCollectionKey, IGlyphTypeface>> _glyphTypefaceCache = new();
private readonly FontManager _fontManager;
private readonly string[] _familyNames;
@ -20,7 +20,7 @@ namespace Avalonia.Media.Fonts
_familyNames = fontManager.PlatformImpl.GetInstalledFontFamilyNames();
}
public Uri Key => new Uri("fontCollection:SystemFonts");
public Uri Key => FontManager.SystemFontsKey;
public FontFamily this[int index]
{
@ -44,23 +44,30 @@ namespace Avalonia.Media.Fonts
var key = new FontCollectionKey(style, weight, stretch);
if (!_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))
if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))
{
glyphTypefaces = new Dictionary<FontCollectionKey, IGlyphTypeface>();
_glyphTypefaceCache.Add(familyName, glyphTypefaces);
}
if (glyphTypefaces.TryGetValue(key, out glyphTypeface))
{
return true;
if (glyphTypefaces.TryGetValue(key, out glyphTypeface))
{
return true;
}
else
{
if (_fontManager.PlatformImpl.TryCreateGlyphTypeface(familyName, style, weight, stretch, out glyphTypeface) &&
glyphTypefaces.TryAdd(key, glyphTypeface))
{
return true;
}
}
}
if (_fontManager.PlatformImpl.TryCreateGlyphTypeface(familyName, style, weight, stretch, out glyphTypeface))
{
glyphTypefaces.Add(key, glyphTypeface);
glyphTypefaces = new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface>();
return true;
if (glyphTypefaces.TryAdd(key, glyphTypeface) && _glyphTypefaceCache.TryAdd(familyName, glyphTypefaces))
{
return true;
}
}
return false;

11
tests/Avalonia.Direct2D1.UnitTests/Media/FontManagerImplTests.cs

@ -42,18 +42,17 @@ namespace Avalonia.Direct2D1.UnitTests.Media
}
[Fact]
public void Should_Throw_InvalidOperationException_For_Unknown_Font()
public void Should_Create_Typeface_For_Unknown_Font()
{
using (AvaloniaLocator.EnterScope())
{
Direct2D1Platform.Initialize();
var fontManager = FontManager.Current;
var glyphTypeface = new Typeface(new FontFamily("Unknown")).GlyphTypeface;
Assert.Throws<InvalidOperationException>(() =>
{
var glyphTypeface =new Typeface(new FontFamily("Unknown")).GlyphTypeface;
});
var defaultName = FontManager.Current.DefaultFontFamilyName;
Assert.Equal(defaultName, glyphTypeface.FamilyName);
}
}

11
tests/Avalonia.Skia.UnitTests/Media/FontManagerImplTests.cs

@ -36,14 +36,13 @@ namespace Avalonia.Skia.UnitTests.Media
}
[Fact]
public void Should_Throw_InvalidOperationException_For_Invalid_FamilyName()
public void Should_Yield_Default_GlyphTypeface_For_Invalid_FamilyName()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new FontManagerImpl())))
{
Assert.Throws<InvalidOperationException>(() =>
{
var glyphTypeface = new Typeface(new FontFamily("Unknown")).GlyphTypeface;
});
{
var glyphTypeface = new Typeface(new FontFamily("Unknown")).GlyphTypeface;
Assert.Equal(FontManager.Current.DefaultFontFamilyName, glyphTypeface.FamilyName);
}
}

Loading…
Cancel
Save