Browse Source

Add TypographicFamilyName to GlyphTypeface and use it for FontCollection (#16844)

* Introduce TypographicFamilyName

* Add TypographicFamilyName to the FontCollection cache if available
pull/16887/head
Benedikt Stebner 2 years ago
committed by GitHub
parent
commit
4f2dd6741a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 12
      src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs
  2. 41
      src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
  3. 5
      src/Avalonia.Base/Media/IGlyphTypeface2.cs
  4. 4
      src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
  5. 2
      tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj
  6. BIN
      tests/Avalonia.Skia.UnitTests/Fonts/Manrope-Light.ttf
  7. 27
      tests/Avalonia.Skia.UnitTests/Media/EmbeddedFontCollectionTests.cs

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

@ -131,11 +131,15 @@ namespace Avalonia.Media.Fonts
{ {
if (glyphTypeface is IGlyphTypeface2 glyphTypeface2) if (glyphTypeface is IGlyphTypeface2 glyphTypeface2)
{ {
foreach (var kvp in glyphTypeface2.FamilyNames) //Add the TypographicFamilyName to the cache
if (!string.IsNullOrEmpty(glyphTypeface2.TypographicFamilyName))
{ {
var familyName = kvp.Value; AddGlyphTypefaceByFamilyName(glyphTypeface2.TypographicFamilyName, glyphTypeface);
}
AddGlyphTypefaceByFamilyName(familyName, glyphTypeface); foreach (var kvp in glyphTypeface2.FamilyNames)
{
AddGlyphTypefaceByFamilyName(kvp.Value, glyphTypeface);
} }
} }
else else
@ -150,7 +154,7 @@ namespace Avalonia.Media.Fonts
var typefaces = _glyphTypefaceCache.GetOrAdd(familyName, var typefaces = _glyphTypefaceCache.GetOrAdd(familyName,
x => x =>
{ {
_fontFamilies.Add(new FontFamily(_key, glyphTypeface.FamilyName)); _fontFamilies.Add(new FontFamily(_key, familyName));
return new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>(); return new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>();
}); });

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

@ -160,26 +160,35 @@ namespace Avalonia.Media.Fonts
{ {
var stream = assetLoader.Open(fontAsset); var stream = assetLoader.Open(fontAsset);
if (fontManager.TryCreateGlyphTypeface(stream, FontSimulations.None, out var glyphTypeface)) if (!fontManager.TryCreateGlyphTypeface(stream, FontSimulations.None, out var glyphTypeface))
{ {
if (!_glyphTypefaceCache.TryGetValue(glyphTypeface.FamilyName, out var glyphTypefaces)) continue;
{ }
glyphTypefaces = new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>();
if (_glyphTypefaceCache.TryAdd(glyphTypeface.FamilyName, glyphTypefaces)) //Add TypographicFamilyName to the cache
{ if (glyphTypeface is IGlyphTypeface2 glyphTypeface2 && !string.IsNullOrEmpty(glyphTypeface2.TypographicFamilyName))
//Move the user defined system font to the start of the collection {
_familyNames.Insert(0, glyphTypeface.FamilyName); AddGlyphTypefaceByFamilyName(glyphTypeface2.TypographicFamilyName, glyphTypeface);
} }
}
AddGlyphTypefaceByFamilyName(glyphTypeface.FamilyName, glyphTypeface);
}
var key = new FontCollectionKey( return;
glyphTypeface.Style,
glyphTypeface.Weight,
glyphTypeface.Stretch);
glyphTypefaces.TryAdd(key, glyphTypeface); void AddGlyphTypefaceByFamilyName(string familyName, IGlyphTypeface glyphTypeface)
} {
var typefaces = _glyphTypefaceCache.GetOrAdd(familyName,
x =>
{
_familyNames.Insert(0, familyName);
return new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>();
});
typefaces.TryAdd(
new FontCollectionKey(glyphTypeface.Style, glyphTypeface.Weight, glyphTypeface.Stretch),
glyphTypeface);
} }
} }
} }

5
src/Avalonia.Base/Media/IGlyphTypeface2.cs

@ -14,6 +14,11 @@ namespace Avalonia.Media
/// <returns>Returns <c>true</c> if the stream can be obtained, otherwise <c>false</c>.</returns> /// <returns>Returns <c>true</c> if the stream can be obtained, otherwise <c>false</c>.</returns>
bool TryGetStream([NotNullWhen(true)] out Stream? stream); bool TryGetStream([NotNullWhen(true)] out Stream? stream);
/// <summary>
/// Gets the typographic family name.
/// </summary>
string TypographicFamilyName { get; }
/// <summary> /// <summary>
/// Gets the localized family names. /// Gets the localized family names.
/// <para>Keys are culture identifiers.</para> /// <para>Keys are culture identifiers.</para>

4
src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs

@ -100,6 +100,8 @@ namespace Avalonia.Skia
_nameTable = NameTable.Load(this); _nameTable = NameTable.Load(this);
TypographicFamilyName = _nameTable.GetNameById((ushort)CultureInfo.InvariantCulture.LCID, KnownNameIds.TypographicFamilyName);
FamilyName = _nameTable.FontFamilyName((ushort)CultureInfo.InvariantCulture.LCID); FamilyName = _nameTable.FontFamilyName((ushort)CultureInfo.InvariantCulture.LCID);
var familyNames = new Dictionary<ushort, string>(_nameTable.Languages.Count); var familyNames = new Dictionary<ushort, string>(_nameTable.Languages.Count);
@ -112,6 +114,8 @@ namespace Avalonia.Skia
FamilyNames = familyNames; FamilyNames = familyNames;
} }
public string TypographicFamilyName { get; }
public IReadOnlyDictionary<ushort, string> FamilyNames { get; } public IReadOnlyDictionary<ushort, string> FamilyNames { get; }
public IReadOnlyList<OpenTypeTag> SupportedFeatures public IReadOnlyList<OpenTypeTag> SupportedFeatures

2
tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj

@ -11,7 +11,9 @@
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="..\Avalonia.RenderTests\*\*.ttf" /> <EmbeddedResource Include="..\Avalonia.RenderTests\*\*.ttf" />
<None Remove="Fonts\DejaVuSans.ttf" /> <None Remove="Fonts\DejaVuSans.ttf" />
<None Remove="Fonts\Manrope-Light.ttf" />
<EmbeddedResource Include="Fonts\DejaVuSans.ttf" /> <EmbeddedResource Include="Fonts\DejaVuSans.ttf" />
<EmbeddedResource Include="Fonts\Manrope-Light.ttf" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />

BIN
tests/Avalonia.Skia.UnitTests/Fonts/Manrope-Light.ttf

Binary file not shown.

27
tests/Avalonia.Skia.UnitTests/Media/EmbeddedFontCollectionTests.cs

@ -10,7 +10,9 @@ namespace Avalonia.Skia.UnitTests.Media
{ {
private const string s_notoMono = private const string s_notoMono =
"resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Mono"; "resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Mono";
private const string s_manrope = "resm:Avalonia.Skia.UnitTests.Fonts?assembly=Avalonia.Skia.UnitTests#Manrope";
[InlineData(FontWeight.SemiLight, FontStyle.Normal)] [InlineData(FontWeight.SemiLight, FontStyle.Normal)]
[InlineData(FontWeight.Bold, FontStyle.Italic)] [InlineData(FontWeight.Bold, FontStyle.Italic)]
[InlineData(FontWeight.Heavy, FontStyle.Oblique)] [InlineData(FontWeight.Heavy, FontStyle.Oblique)]
@ -64,5 +66,28 @@ namespace Avalonia.Skia.UnitTests.Media
Assert.Equal("Twitter Color Emoji", glyphTypeface.FamilyName); Assert.Equal("Twitter Color Emoji", glyphTypeface.FamilyName);
} }
} }
[Fact]
public void Should_Get_Typeface_For_TypographicFamilyName()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var source = new Uri(s_manrope, UriKind.Absolute);
var fontCollection = new EmbeddedFontCollection(source, source);
fontCollection.Initialize(new CustomFontManagerImpl());
Assert.True(fontCollection.TryGetGlyphTypeface("Manrope", FontStyle.Normal, FontWeight.Light, FontStretch.Normal, out var glyphTypeface));
Assert.Equal("Manrope Light", glyphTypeface.FamilyName);
Assert.True(glyphTypeface is IGlyphTypeface2);
var glyphTypeface2 = (IGlyphTypeface2)glyphTypeface;
Assert.Equal("Manrope", glyphTypeface2.TypographicFamilyName);
}
}
} }
} }

Loading…
Cancel
Save