Skip to content

Commit

Permalink
Fix FontCollection glyph typeface caching (#17519)
Browse files Browse the repository at this point in the history
* Make sure we always cache the created glyph typeface before we try to find the nearest match

* Cleanup usings

* Add failing test

* Cache the matched glyph typeface for EmbeddedFontCollection
  • Loading branch information
Gillibald authored Nov 19, 2024
1 parent 9091d66 commit 28c2107
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ public override bool TryGetGlyphTypeface(string familyName, FontStyle style, Fon
glyphTypeface = syntheticGlyphTypeface;
}

//Make sure we cache the found match
glyphTypefaces.TryAdd(key, glyphTypeface);

return true;
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ public override bool TryGetGlyphTypeface(string familyName, FontStyle style, Fon
//No exact match
if (createdKey != key)
{
//Add the created glyph typeface to the cache so we can match it.
glyphTypefaces.TryAdd(createdKey, glyphTypeface);

//Try to find nearest match if possible
if (TryGetNearestMatch(glyphTypefaces, key, out var nearestMatch))
{
Expand Down
35 changes: 35 additions & 0 deletions tests/Avalonia.Skia.UnitTests/Media/EmbeddedFontCollectionTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.Fonts;
using Avalonia.UnitTests;
Expand Down Expand Up @@ -89,5 +92,37 @@ public void Should_Get_Typeface_For_TypographicFamilyName()
Assert.Equal("Manrope", glyphTypeface2.TypographicFamilyName);
}
}

[Fact]
public void Should_Cache_Synthetic_GlyphTypeface()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var source = new Uri(s_manrope, UriKind.Absolute);

var fontCollection = new TestEmbeddedFontCollection(source, source);

fontCollection.Initialize(new CustomFontManagerImpl());

Assert.True(fontCollection.TryGetGlyphTypeface("Manrope", FontStyle.Normal, FontWeight.ExtraBlack, FontStretch.Normal, out var glyphTypeface));

Assert.True(fontCollection.GlyphTypefaceCache.TryGetValue("Manrope", out var glyphTypefaces));

Assert.Equal(2, glyphTypefaces.Count);

fontCollection.TryGetGlyphTypeface("Manrope", FontStyle.Normal, FontWeight.ExtraBlack, FontStretch.Normal, out var otherGlyphTypeface);

Assert.Equal(glyphTypeface, otherGlyphTypeface);
}
}

private class TestEmbeddedFontCollection : EmbeddedFontCollection
{
public TestEmbeddedFontCollection(Uri key, Uri source) : base(key, source)
{
}

public IDictionary<string, ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>> GlyphTypefaceCache => _glyphTypefaceCache;
}
}
}
40 changes: 39 additions & 1 deletion tests/Avalonia.Skia.UnitTests/Media/FontCollectionTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Avalonia.Media;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.Fonts;
using Avalonia.UnitTests;
using Xunit;

namespace Avalonia.Skia.UnitTests.Media
Expand All @@ -24,5 +27,40 @@ public void Should_Get_Implicit_Typeface(string input, string familyName, FontSt
Assert.Equal(weight, result.Weight);
Assert.Equal(FontStretch.Normal, result.Stretch);
}

[Win32Fact("Relies on some installed font family")]
public void Should_Cache_Nearest_Match()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new FontManagerImpl())))
{
var fontManager = FontManager.Current;

var fontCollection = new TestSystemFontCollection(FontManager.Current);

Assert.True(fontCollection.TryGetGlyphTypeface("Arial", FontStyle.Normal, FontWeight.ExtraBlack, FontStretch.Normal, out var glyphTypeface));

Assert.True(glyphTypeface.FontSimulations == FontSimulations.Bold);

Assert.True(fontCollection.GlyphTypfaceCache.TryGetValue("Arial", out var glyphTypefaces));

Assert.Equal(2, glyphTypefaces.Count);

Assert.True(glyphTypefaces.ContainsKey(new FontCollectionKey(FontStyle.Normal, FontWeight.Black, FontStretch.Normal)));

fontCollection.TryGetGlyphTypeface("Arial", FontStyle.Normal, FontWeight.ExtraBlack, FontStretch.Normal, out var otherGlyphTypeface);

Assert.Equal(glyphTypeface, otherGlyphTypeface);
}
}

private class TestSystemFontCollection : SystemFontCollection
{
public TestSystemFontCollection(FontManager fontManager) : base(fontManager)
{

}

public IDictionary<string, ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>> GlyphTypfaceCache => _glyphTypefaceCache;
}
}
}

0 comments on commit 28c2107

Please sign in to comment.