Browse Source

Merge pull request #10620 from Gillibald/feature/invariantFamilyNames

FontManager fixes
pull/10630/head
Max Katz 4 years ago
committed by GitHub
parent
commit
8423e16658
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      src/Avalonia.Base/Media/FontManager.cs
  2. 15
      src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs
  3. 2
      src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
  4. 92
      tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
  5. 34
      tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs
  6. 8
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

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

@ -119,7 +119,9 @@ namespace Avalonia.Media
} }
} }
if (fontCollection != null && fontCollection.TryGetGlyphTypeface(fontFamily.FamilyNames.PrimaryFamilyName, var familyName = fontFamily.FamilyNames.PrimaryFamilyName.ToUpperInvariant();
if (fontCollection != null && fontCollection.TryGetGlyphTypeface(familyName,
typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface)) typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface))
{ {
return true; return true;
@ -133,13 +135,13 @@ namespace Avalonia.Media
foreach (var familyName in fontFamily.FamilyNames) foreach (var familyName in fontFamily.FamilyNames)
{ {
if (SystemFonts.TryGetGlyphTypeface(familyName, typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface)) if (SystemFonts.TryGetGlyphTypeface(familyName.ToUpperInvariant(), typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface))
{ {
return true; return true;
} }
} }
return SystemFonts.TryGetGlyphTypeface(DefaultFontFamilyName, typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface); return TryGetGlyphTypeface(new Typeface(DefaultFontFamilyName, typeface.Style, typeface.Weight, typeface.Stretch), out glyphTypeface);
} }
/// <summary> /// <summary>

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

@ -3,7 +3,6 @@ using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Avalonia.Platform; using Avalonia.Platform;
namespace Avalonia.Media.Fonts namespace Avalonia.Media.Fonts
@ -43,11 +42,13 @@ namespace Avalonia.Media.Fonts
if (fontManager.TryCreateGlyphTypeface(stream, out var glyphTypeface)) if (fontManager.TryCreateGlyphTypeface(stream, out var glyphTypeface))
{ {
if (!_glyphTypefaceCache.TryGetValue(glyphTypeface.FamilyName, out var glyphTypefaces)) var familyName = glyphTypeface.FamilyName.ToUpperInvariant();
if (!_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))
{ {
glyphTypefaces = new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface>(); glyphTypefaces = new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface>();
if (_glyphTypefaceCache.TryAdd(glyphTypeface.FamilyName, glyphTypefaces)) if (_glyphTypefaceCache.TryAdd(familyName, glyphTypefaces))
{ {
_fontFamilies.Add(new FontFamily(_key, glyphTypeface.FamilyName)); _fontFamilies.Add(new FontFamily(_key, glyphTypeface.FamilyName));
} }
@ -86,6 +87,8 @@ namespace Avalonia.Media.Fonts
public bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight, public bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface)
{ {
familyName = familyName.ToUpperInvariant();
var key = new FontCollectionKey(style, weight, stretch); var key = new FontCollectionKey(style, weight, stretch);
if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces)) if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))
@ -101,9 +104,11 @@ namespace Avalonia.Media.Fonts
{ {
var fontFamily = _fontFamilies[i]; var fontFamily = _fontFamilies[i];
if (fontFamily.Name.ToLower(CultureInfo.InvariantCulture).StartsWith(familyName.ToLower(CultureInfo.InvariantCulture))) if (fontFamily.Name.ToUpperInvariant().StartsWith(familyName.ToUpperInvariant()))
{ {
if (_glyphTypefaceCache.TryGetValue(fontFamily.Name, out glyphTypefaces) && familyName = fontFamily.Name.ToUpperInvariant();
if (_glyphTypefaceCache.TryGetValue(familyName, out glyphTypefaces) &&
TryGetNearestMatch(glyphTypefaces, key, out glyphTypeface)) TryGetNearestMatch(glyphTypefaces, key, out glyphTypeface))
{ {
return true; return true;

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

@ -42,6 +42,8 @@ namespace Avalonia.Media.Fonts
familyName = _fontManager.DefaultFontFamilyName; familyName = _fontManager.DefaultFontFamilyName;
} }
familyName = familyName.ToUpperInvariant();
var key = new FontCollectionKey(style, weight, stretch); var key = new FontCollectionKey(style, weight, stretch);
if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces)) if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))

92
tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs

@ -68,66 +68,6 @@ namespace Avalonia.Skia.UnitTests.Media
return true; return true;
} }
public IGlyphTypeface CreateGlyphTypeface(Typeface typeface)
{
SKTypeface skTypeface;
Uri source = null;
switch (typeface.FontFamily.Name)
{
case "Twitter Color Emoji":
{
source = _emojiTypeface.FontFamily.Key.Source;
break;
}
case "Noto Sans":
{
source = _italicTypeface.FontFamily.Key.Source;
break;
}
case "Noto Sans Arabic":
{
source = _arabicTypeface.FontFamily.Key.Source;
break;
}
case "Noto Sans Hebrew":
{
source = _hebrewTypeface.FontFamily.Key.Source;
break;
}
case FontFamily.DefaultFontFamilyName:
case "Noto Mono":
{
source = _defaultTypeface.FontFamily.Key.Source;
break;
}
default:
{
break;
}
}
if (source is null)
{
skTypeface = SKTypeface.FromFamilyName(typeface.FontFamily.Name,
(SKFontStyleWeight)typeface.Weight, SKFontStyleWidth.Normal, (SKFontStyleSlant)typeface.Style);
}
else
{
var assetLoader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
var assetUri = FontFamilyLoader.LoadFontAssets(source).First();
var stream = assetLoader.Open(assetUri);
skTypeface = SKTypeface.FromStream(stream);
}
return new GlyphTypefaceImpl(skTypeface, FontSimulations.None);
}
public bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight, public bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface glyphTypeface) FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface glyphTypeface)
{ {
@ -137,54 +77,40 @@ namespace Avalonia.Skia.UnitTests.Media
switch (familyName) switch (familyName)
{ {
case "Twitter Color Emoji": case "TWITTER COLOR EMOJI":
{ {
source = _emojiTypeface.FontFamily.Key.Source; source = _emojiTypeface.FontFamily.Key.Source;
break; break;
} }
case "Noto Sans": case "NOTO SANS":
{ {
source = _italicTypeface.FontFamily.Key.Source; source = _italicTypeface.FontFamily.Key.Source;
break; break;
} }
case "Noto Sans Arabic": case "NOTO SANS ARABIC":
{ {
source = _arabicTypeface.FontFamily.Key.Source; source = _arabicTypeface.FontFamily.Key.Source;
break; break;
} }
case "Noto Sans Hebrew": case "NOTO SANS HEBREW":
{ {
source = _hebrewTypeface.FontFamily.Key.Source; source = _hebrewTypeface.FontFamily.Key.Source;
break; break;
} }
case FontFamily.DefaultFontFamilyName:
case "Noto Mono":
{
source = _defaultTypeface.FontFamily.Key.Source;
break;
}
default: default:
{ {
source = _defaultTypeface.FontFamily.Key.Source;
break; break;
} }
} }
if (source is null) var assetLoader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
{
skTypeface = SKTypeface.FromFamilyName(familyName,
(SKFontStyleWeight)weight, SKFontStyleWidth.Normal, (SKFontStyleSlant)style);
}
else
{
var assetLoader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
var assetUri = FontFamilyLoader.LoadFontAssets(source).First(); var assetUri = FontFamilyLoader.LoadFontAssets(source).First();
var stream = assetLoader.Open(assetUri); var stream = assetLoader.Open(assetUri);
skTypeface = SKTypeface.FromStream(stream); skTypeface = SKTypeface.FromStream(stream);
}
glyphTypeface = new GlyphTypefaceImpl(skTypeface, FontSimulations.None); glyphTypeface = new GlyphTypefaceImpl(skTypeface, FontSimulations.None);

34
tests/Avalonia.Skia.UnitTests/Media/FontManagerImplTests.cs → tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs

@ -1,5 +1,4 @@
using System; using System;
using System.Linq;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using SkiaSharp; using SkiaSharp;
@ -7,7 +6,7 @@ using Xunit;
namespace Avalonia.Skia.UnitTests.Media namespace Avalonia.Skia.UnitTests.Media
{ {
public class FontManagerImplTests public class FontManagerTests
{ {
private static string s_fontUri = "resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Mono"; private static string s_fontUri = "resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Mono";
@ -87,5 +86,36 @@ namespace Avalonia.Skia.UnitTests.Media
Assert.False(result); Assert.False(result);
} }
} }
[Fact]
public void Should_Load_Embedded_Font_With_Wrong_CharacterCasing()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new FontManagerImpl())))
{
var result = FontManager.Current.TryGetGlyphTypeface(new Typeface("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#noto mOnO"), out var glyphTypeface);
Assert.True(result);
Assert.Equal("Noto Mono", glyphTypeface.FamilyName);
}
}
[Fact]
public void Should_Load_Embedded_DefaultFontFamily()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new FontManagerImpl())))
{
using (AvaloniaLocator.EnterScope())
{
AvaloniaLocator.CurrentMutable.BindToSelf(new FontManagerOptions { DefaultFamilyName = s_fontUri });
var result = FontManager.Current.TryGetGlyphTypeface(new Typeface(FontFamily.DefaultFontFamilyName), out var glyphTypeface);
Assert.True(result);
Assert.Equal("Noto Mono", glyphTypeface.FamilyName);
}
}
}
} }
} }

8
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@ -416,14 +416,14 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
} }
[InlineData("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor", [InlineData("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor",
new[] { "Lorem ipsum ", "dolor sit amet, ", "consectetur ", "adipisicing elit, ", "sed do eiusmod " })] new[] { "Lorem ipsum ", "dolor sit ", "amet, ", "consectetur ", "adipisicing ", "elit, sed do ", "eiusmod tempor" })]
[Theory] [Theory]
public void Should_Produce_Wrapped_And_Trimmed_Lines(string text, string[] expectedLines) public void Should_Produce_Wrapped_And_Trimmed_Lines(string text, string[] expectedLines)
{ {
using (Start()) using (Start())
{ {
var typeface = new Typeface("Verdana"); var typeface = Typeface.Default;
var defaultProperties = new GenericTextRunProperties(typeface, 32, foregroundBrush: Brushes.Black); var defaultProperties = new GenericTextRunProperties(typeface, 32, foregroundBrush: Brushes.Black);
@ -432,9 +432,9 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
new ValueSpan<TextRunProperties>(0, 5, new ValueSpan<TextRunProperties>(0, 5,
new GenericTextRunProperties(typeface, 48)), new GenericTextRunProperties(typeface, 48)),
new ValueSpan<TextRunProperties>(6, 11, new ValueSpan<TextRunProperties>(6, 11,
new GenericTextRunProperties(new Typeface("Verdana", weight: FontWeight.Bold), 32)), new GenericTextRunProperties(new Typeface(FontFamily.Default, weight: FontWeight.Bold), 32)),
new ValueSpan<TextRunProperties>(28, 28, new ValueSpan<TextRunProperties>(28, 28,
new GenericTextRunProperties(new Typeface("Verdana", FontStyle.Italic),32)) new GenericTextRunProperties(new Typeface(FontFamily.Default, FontStyle.Italic),32))
}; };
var textSource = new FormattedTextSource(text, defaultProperties, styleSpans); var textSource = new FormattedTextSource(text, defaultProperties, styleSpans);

Loading…
Cancel
Save