Преглед изворни кода

FontLoading adjustments (#19538)

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

* Change relative Uri detection so it uses Contains
Benedikt Stebner пре 1 месец
родитељ
комит
5bed0cb7b6

+ 23 - 23
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;
                         }
                 }

+ 2 - 2
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) &&

+ 2 - 2
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;
                     }

+ 75 - 3
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 - 0
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);
+                }
+            }
+        }
     }
 }

+ 3 - 3
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")
         {