@ -1,7 +1,10 @@
# nullable enable
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Diagnostics.CodeAnalysis ;
using System.Globalization ;
using Avalonia.Media ;
using Avalonia.Media.Fonts ;
using Avalonia.UnitTests ;
@ -11,6 +14,9 @@ namespace Avalonia.Skia.UnitTests.Media
{
public class FontCollectionTests
{
private const string NotoMono =
"resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests" ;
[InlineData("Hello World 6", "Hello World 6", FontStyle.Normal, FontWeight.Normal)]
[InlineData("Hello World Italic", "Hello World", FontStyle.Italic, FontWeight.Normal)]
[InlineData("Hello World Italic Bold", "Hello World", FontStyle.Italic, FontWeight.Bold)]
@ -41,8 +47,6 @@ namespace Avalonia.Skia.UnitTests.Media
Assert . True ( fontCollection . TryGetGlyphTypeface ( "Arial" , FontStyle . Normal , FontWeight . ExtraBlack , FontStretch . Normal , out var glyphTypeface ) ) ;
Assert . True ( glyphTypeface . FontSimulations = = FontSimulations . Bold ) ;
Assert . True ( fontCollection . GlyphTypefaceCache . TryGetValue ( "Arial" , out var glyphTypefaces ) ) ;
Assert . Equal ( 2 , glyphTypefaces . Count ) ;
@ -64,5 +68,108 @@ namespace Avalonia.Skia.UnitTests.Media
public IDictionary < string , ConcurrentDictionary < FontCollectionKey , IGlyphTypeface ? > > GlyphTypefaceCache = > _ glyphTypefaceCache ;
}
[Fact]
public void Should_Use_Fallback ( )
{
using ( UnitTestApplication . Start ( TestServices . MockPlatformRenderInterface ) )
{
var source = new Uri ( NotoMono , UriKind . Absolute ) ;
var fallback = new FontFallback { FontFamily = new FontFamily ( "Arial" ) , UnicodeRange = new UnicodeRange ( 'A' , 'A' ) } ;
var fontCollection = new CustomizableFontCollection ( source , source , new [ ] { fallback } ) ;
fontCollection . Initialize ( new CustomFontManagerImpl ( ) ) ;
Assert . True ( fontCollection . TryMatchCharacter ( 'A' , FontStyle . Normal , FontWeight . Normal , FontStretch . Normal , null , null , out var match ) ) ;
Assert . Equal ( "Arial" , match . FontFamily . Name ) ;
}
}
[Fact]
public void Should_Ignore_FontFamily ( )
{
using ( UnitTestApplication . Start ( TestServices . MockPlatformRenderInterface ) )
{
var source = new Uri ( NotoMono + "#Noto Mono" , UriKind . Absolute ) ;
var ignorable = new FontFamily ( new Uri ( NotoMono , UriKind . Absolute ) , "Noto Mono" ) ;
var typeface = new Typeface ( ignorable ) ;
var fontCollection = new CustomizableFontCollection ( source , source , null , new [ ] { ignorable } ) ;
fontCollection . Initialize ( new CustomFontManagerImpl ( ) ) ;
Assert . False ( fontCollection . TryCreateSyntheticGlyphTypeface (
typeface . GlyphTypeface ,
FontStyle . Italic ,
FontWeight . DemiBold ,
FontStretch . Normal ,
out var syntheticGlyphTypeface ) ) ;
}
}
private class CustomizableFontCollection : EmbeddedFontCollection
{
private readonly IReadOnlyList < FontFallback > ? _f allbacks ;
private readonly IReadOnlyList < FontFamily > ? _ ignorables ;
public CustomizableFontCollection ( Uri key , Uri source , IReadOnlyList < FontFallback > ? fallbacks = null , IReadOnlyList < FontFamily > ? ignorables = null ) : base ( key , source )
{
_f allbacks = fallbacks ;
_ ignorables = ignorables ;
}
public override bool TryMatchCharacter (
int codepoint ,
FontStyle style ,
FontWeight weight ,
FontStretch stretch ,
string? familyName ,
CultureInfo ? culture ,
out Typeface match )
{
if ( _f allbacks is not null )
{
foreach ( var fallback in _f allbacks )
{
if ( fallback . UnicodeRange . IsInRange ( codepoint ) )
{
match = new Typeface ( fallback . FontFamily , style , weight , stretch ) ;
return true ;
}
}
}
return base . TryMatchCharacter ( codepoint , style , weight , stretch , familyName , culture , out match ) ;
}
public override bool TryCreateSyntheticGlyphTypeface (
IGlyphTypeface glyphTypeface ,
FontStyle style ,
FontWeight weight ,
FontStretch stretch ,
[NotNullWhen(true)] out IGlyphTypeface ? syntheticGlyphTypeface )
{
syntheticGlyphTypeface = null ;
if ( _ ignorables is not null )
{
foreach ( var ignorable in _ ignorables )
{
if ( glyphTypeface . FamilyName = = ignorable . Name | | glyphTypeface is IGlyphTypeface2 glyphTypeface2 & & glyphTypeface2 . TypographicFamilyName = = ignorable . Name )
{
return false ;
}
}
}
return base . TryCreateSyntheticGlyphTypeface ( glyphTypeface , style , weight , stretch , out syntheticGlyphTypeface ) ;
}
}
}
}