Browse Source

FontLoading adjustments (#19538)

* Adjust FontFamily parsing so non system fonts require a specific format

* Change relative Uri detection so it uses Contains
release/11.3.5
Benedikt Stebner 5 months ago
parent
commit
a80ff73ccc
  1. 46
      src/Avalonia.Base/Media/FontFamily.cs
  2. 4
      src/Avalonia.Base/Media/FontManager.cs
  3. 4
      src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs
  4. 78
      tests/Avalonia.Base.UnitTests/Media/FontFamilyTests.cs
  5. 18
      tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs
  6. 6
      tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs

46
src/Avalonia.Base/Media/FontFamily.cs

@ -36,6 +36,11 @@ namespace Avalonia.Media
throw new ArgumentNullException(nameof(name));
}
if (baseUri != null && !baseUri.IsAbsoluteUri)
{
throw new ArgumentException("Base uri must be an absolute uri.", nameof(baseUri));
}
var fontSources = GetFontSourceIdentifier(name);
FamilyNames = new FamilyNameCollection(fontSources);
@ -46,20 +51,15 @@ namespace Avalonia.Media
if (singleSource.Source is Uri source)
{
if (baseUri != null && !baseUri.IsAbsoluteUri)
if (source.IsAbsoluteUri)
{
throw new ArgumentException("Base uri must be an absolute uri.", nameof(baseUri));
Key = new FontFamilyKey(source);
}
Key = new FontFamilyKey(source, baseUri);
}
else
{
if(baseUri != null && baseUri.IsAbsoluteUri)
else
{
Key = new FontFamilyKey(baseUri);
Key = new FontFamilyKey(source, baseUri);
}
}
}
}
else
{
@ -138,7 +138,7 @@ namespace Avalonia.Media
var segment = segments[i];
var innerSegments = segment.Split('#');
FontSourceIdentifier identifier;
FontSourceIdentifier identifier = new FontSourceIdentifier(name, null);
switch (innerSegments.Length)
{
@ -159,19 +159,19 @@ namespace Avalonia.Media
}
else
{
var source = path.StartsWith("/", StringComparison.Ordinal)
? new Uri(path, UriKind.Relative)
: new Uri(path, UriKind.RelativeOrAbsolute);
identifier = new FontSourceIdentifier(innerName, source);
}
break;
}
if (path.Contains('/') && Uri.TryCreate(path, UriKind.Relative, out var source))
{
identifier = new FontSourceIdentifier(innerName, source);
}
else
{
if (Uri.TryCreate(path, UriKind.Absolute, out source))
{
identifier = new FontSourceIdentifier(innerName, source);
}
}
}
default:
{
identifier = new FontSourceIdentifier(name, null);
break;
}
}

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

@ -273,8 +273,6 @@ namespace Avalonia.Media
//Try to match against fallbacks first
if (fontFamily?.Key != null)
{
var fontUri = fontFamily.Key.Source.EnsureAbsolute(fontFamily.Key.BaseUri);
if (fontFamily.Key is CompositeFontFamilyKey compositeKey)
{
for (int i = 0; i < compositeKey.Keys.Count; i++)
@ -296,6 +294,8 @@ namespace Avalonia.Media
}
}
var fontUri = fontFamily.Key.Source.EnsureAbsolute(fontFamily.Key.BaseUri);
if (fontUri.IsFontCollection())
{
if (TryGetFontCollection(fontUri, out var fontCollection) &&

4
src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs

@ -35,7 +35,7 @@ namespace Avalonia.Media.Fonts
{
if (glyphTypeface.TryGetGlyph((uint)codepoint, out _))
{
match = new Typeface(new FontFamily(Key, "#" + glyphTypeface.FamilyName), style, weight, stretch);
match = new Typeface(new FontFamily(null, Key.AbsoluteUri + "#" + glyphTypeface.FamilyName), style, weight, stretch);
return true;
}
@ -57,7 +57,7 @@ namespace Avalonia.Media.Fonts
{
if (glyphTypeface.TryGetGlyph((uint)codepoint, out _))
{
match = new Typeface(new FontFamily(Key, "#" + glyphTypeface.FamilyName) , style, weight, stretch);
match = new Typeface(new FontFamily(null, Key.AbsoluteUri + "#" + glyphTypeface.FamilyName), style, weight, stretch);
return true;
}

78
tests/Avalonia.Base.UnitTests/Media/FontFamilyTests.cs

@ -121,12 +121,12 @@ namespace Avalonia.Base.UnitTests.Media
}
[Theory]
[InlineData("resm:Avalonia.Visuals.UnitTests.Assets.Fonts", "#MyFont")]
[InlineData("avares://Avalonia.Visuals.UnitTests/Assets/Fonts", "#MyFont")]
[InlineData(null, "resm:Avalonia.Visuals.UnitTests.Assets.Fonts#MyFont")]
[InlineData("avares://Avalonia.Visuals.UnitTests/Assets/Fonts", "/#MyFont")]
[InlineData("avares://Avalonia.Visuals.UnitTests", "/Assets/Fonts#MyFont")]
public void Should_Create_FontFamily_From_Uri_With_Base_Uri(string @base, string name)
{
var baseUri = new Uri(@base);
var baseUri = @base != null ? new Uri(@base) : null;
var fontFamily = new FontFamily(baseUri, name);
@ -134,5 +134,77 @@ namespace Avalonia.Base.UnitTests.Media
Assert.NotNull(fontFamily.Key);
}
[InlineData(null, "Arial", "Arial", null)]
[InlineData(null, "resm:Avalonia.Skia.UnitTests.Fonts?assembly=Avalonia.Skia.UnitTests#Manrope", "Manrope", "resm:Avalonia.Skia.UnitTests.Fonts?assembly=Avalonia.Skia.UnitTests")]
[InlineData(null, "avares://Avalonia.Fonts.Inter/Assets#Inter", "Inter", null)]
[InlineData("avares://Avalonia.Fonts.Inter", "/Assets#Inter", "Inter", "avares://Avalonia.Fonts.Inter/Assets")]
[InlineData("avares://ControlCatalog/MainWindow.xaml", "avares://Avalonia.Fonts.Inter/Assets#Inter", "Inter", "avares://Avalonia.Fonts.Inter/Assets")]
[Theory]
public void Should_Parse_FontFamily_With_BaseUri(string baseUri, string s, string expectedName, string expectedUri)
{
var b = baseUri is not null ? new Uri(baseUri) : null;
expectedUri = expectedUri is not null ? new Uri(expectedUri).AbsoluteUri : null;
var fontFamily = FontFamily.Parse(s, b);
Assert.Equal(expectedName, fontFamily.Name);
var key = fontFamily.Key;
if (expectedUri is not null)
{
Assert.NotNull(key);
if (key.BaseUri is not null)
{
Assert.True(key.BaseUri.IsAbsoluteUri);
}
if (key.BaseUri is null)
{
Assert.NotNull(key.Source);
Assert.True(key.Source.IsAbsoluteUri);
}
var fontUri = key.BaseUri;
if (key.Source is Uri sourceUri)
{
if (sourceUri.IsAbsoluteUri)
{
fontUri = sourceUri;
}
else
{
fontUri = new Uri(fontUri, sourceUri);
}
}
Assert.Equal(expectedUri, fontUri.AbsoluteUri);
}
}
[InlineData("avares://MyAssembly/", "Some/Path/#FontName", "avares://MyAssembly/Some/Path/"), ]
[InlineData("avares://MyAssembly/", "./Some/Path/#FontName", "avares://MyAssembly/Some/Path/")]
[InlineData("avares://MyAssembly/sub/", "../Some/Path/#FontName", "avares://MyAssembly/Some/Path/")]
[Theory]
public void Should_Parse_Relative_Path(string baseUriString, string path, string expected)
{
var baseUri = new Uri(baseUriString, UriKind.Absolute);
var fontFamily = FontFamily.Parse(path, baseUri);
Assert.NotNull(fontFamily.Key);
Assert.NotNull(fontFamily.Key.BaseUri);
Assert.NotNull(fontFamily.Key.Source);
var actual = new Uri(fontFamily.Key.BaseUri, fontFamily.Key.Source);
Assert.Equal(expected, actual.AbsoluteUri);
}
}
}

18
tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs

@ -409,5 +409,23 @@ namespace Avalonia.Skia.UnitTests.Media
}
}
}
[InlineData("Arial")]
[InlineData("#Arial")]
[Win32Theory("Windows specific font")]
public void Should_Get_SystemFont_With_BaseUri(string name)
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new FontManagerImpl())))
{
using (AvaloniaLocator.EnterScope())
{
var fontFamily = new FontFamily(new Uri("avares://Avalonia.Skia.UnitTests/NotFound"), name);
var glyphTypeface = new Typeface(fontFamily).GlyphTypeface;
Assert.Equal("Arial", glyphTypeface.FamilyName);
}
}
}
}
}

6
tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs

@ -14,11 +14,11 @@ namespace Avalonia.UnitTests
private readonly string _defaultFamilyName;
private static readonly Typeface _defaultTypeface =
new Typeface(new FontFamily(new Uri("resm:Avalonia.UnitTests.Assets?assembly=Avalonia.UnitTests", UriKind.Absolute), "Noto Mono"));
new Typeface(new FontFamily("resm:Avalonia.UnitTests.Assets?assembly=Avalonia.UnitTests#Noto Mono"));
private static readonly Typeface _italicTypeface =
new Typeface(new FontFamily(new Uri("resm:Avalonia.UnitTests.Assets?assembly=Avalonia.UnitTests", UriKind.Absolute), "Noto Sans"));
new Typeface(new FontFamily("resm:Avalonia.UnitTests.Assets?assembly=Avalonia.UnitTests#Noto Sans"));
private static readonly Typeface _emojiTypeface =
new Typeface(new FontFamily(new Uri("resm:Avalonia.UnitTests.Assets?assembly=Avalonia.UnitTests"), "Twitter Color Emoji"));
new Typeface(new FontFamily("resm:Avalonia.UnitTests.Assets?assembly=Avalonia.UnitTests#Twitter Color Emoji"));
public HarfBuzzFontManagerImpl(string defaultFamilyName = "Noto Mono")
{

Loading…
Cancel
Save