@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis ;
using System.Globalization ;
using System.IO ;
using Avalonia.Media.Fonts.Tables ;
using Avalonia.Platform ;
namespace Avalonia.Media.Fonts
@ -55,35 +56,40 @@ namespace Avalonia.Media.Fonts
}
}
//Try to find a match in any font family
foreach ( var pair in _ glyphTypefaceCache )
return TryMatchInAnyFamily ( isLastResort : false , out match ) | |
TryMatchInAnyFamily ( isLastResort : true , out match ) ;
bool TryMatchInAnyFamily ( bool isLastResort , out Typeface match )
{
if ( pair . Key = = familyName )
//Try to find a match in any font family
foreach ( var pair in _ glyphTypefaceCache )
{
//We already tried this before
continue ;
}
glyphTypefaces = pair . Value ;
if ( pair . Key = = familyName )
{
//We already tried this before
continue ;
}
if ( TryGetNearestMatch ( glyphTypefaces , key , out var glyphTypeface ) )
{
if ( glyphTypeface . CharacterToGlyphMap . TryGetGlyph ( codepoint , out _ ) )
if ( TryGetNearestMatchCore ( pair . Value , key , isLastResort , out var glyphTypeface ) )
{
var platformTypeface = glyphTypeface . PlatformTypeface ;
if ( glyphTypeface . CharacterToGlyphMap . TryGetGlyph ( codepoint , out _ ) )
{
var platformTypeface = glyphTypeface . PlatformTypeface ;
// Found a match
match = new Typeface ( new FontFamily ( null , Key . AbsoluteUri + "#" + glyphTypeface . FamilyName ) ,
platformTypeface . Style ,
platformTypeface . Weight ,
platformTypeface . Stretch ) ;
// Found a match
match = new Typeface ( new FontFamily ( null , Key . AbsoluteUri + "#" + glyphTypeface . FamilyName ) ,
platformTypeface . Style ,
platformTypeface . Weight ,
platformTypeface . Stretch ) ;
return true ;
return true ;
}
}
}
}
return false ;
match = default ;
return false ;
}
}
public virtual bool TryCreateSyntheticGlyphTypeface (
@ -570,7 +576,19 @@ namespace Avalonia.Media.Fonts
protected bool TryGetNearestMatch ( IDictionary < FontCollectionKey , GlyphTypeface ? > glyphTypefaces ,
FontCollectionKey key , [ NotNullWhen ( true ) ] out GlyphTypeface ? glyphTypeface )
{
if ( glyphTypefaces . TryGetValue ( key , out glyphTypeface ) & & glyphTypeface ! = null )
return TryGetNearestMatchCore ( glyphTypefaces , key , isLastResort : false , out glyphTypeface )
| | TryGetNearestMatchCore ( glyphTypefaces , key , isLastResort : true , out glyphTypeface ) ;
}
private static bool TryGetNearestMatchCore (
IDictionary < FontCollectionKey , GlyphTypeface ? > glyphTypefaces ,
FontCollectionKey key ,
bool isLastResort ,
[NotNullWhen(true)] out GlyphTypeface ? glyphTypeface )
{
if ( glyphTypefaces . TryGetValue ( key , out glyphTypeface ) & &
glyphTypeface ! = null & &
glyphTypeface . IsLastResort = = isLastResort )
{
return true ;
}
@ -582,14 +600,14 @@ namespace Avalonia.Media.Fonts
if ( key . Stretch ! = FontStretch . Normal )
{
if ( TryFindStretchFallback ( glyphTypefaces , key , out glyphTypeface ) )
if ( TryFindStretchFallback ( glyphTypefaces , key , isLastResort , out glyphTypeface ) )
{
return true ;
}
if ( key . Weight ! = FontWeight . Normal )
{
if ( TryFindStretchFallback ( glyphTypefaces , key with { Weight = FontWeight . Normal } , out glyphTypeface ) )
if ( TryFindStretchFallback ( glyphTypefaces , key with { Weight = FontWeight . Normal } , isLastResort , out glyphTypeface ) )
{
return true ;
}
@ -598,12 +616,12 @@ namespace Avalonia.Media.Fonts
key = key with { Stretch = FontStretch . Normal } ;
}
if ( TryFindWeightFallback ( glyphTypefaces , key , out glyphTypeface ) )
if ( TryFindWeightFallback ( glyphTypefaces , key , isLastResort , out glyphTypeface ) )
{
return true ;
}
if ( TryFindStretchFallback ( glyphTypefaces , key , out glyphTypeface ) )
if ( TryFindStretchFallback ( glyphTypefaces , key , isLastResort , out glyphTypeface ) )
{
return true ;
}
@ -611,7 +629,7 @@ namespace Avalonia.Media.Fonts
//Take the first glyph typeface we can find.
foreach ( var typeface in glyphTypefaces . Values )
{
if ( typeface ! = null )
if ( typeface ! = null & & isLastResort = = typeface . IsLastResort )
{
glyphTypeface = typeface ;
@ -695,12 +713,14 @@ namespace Avalonia.Media.Fonts
/// <param name="glyphTypefaces">A dictionary mapping font collection keys to their corresponding glyph typefaces. Used as the source for
/// searching fallback typefaces.</param>
/// <param name="key">The font collection key specifying the desired font stretch and other font attributes to match.</param>
/// <param name="isLastResort">Whether to match last resort fonts.</param>
/// <param name="glyphTypeface">When this method returns, contains the found glyph typeface with a similar stretch if one exists; otherwise,
/// null.</param>
/// <returns>true if a suitable fallback glyph typeface is found; otherwise, false.</returns>
private static bool TryFindStretchFallback (
IDictionary < FontCollectionKey , GlyphTypeface ? > glyphTypefaces ,
FontCollectionKey key ,
bool isLastResort ,
[NotNullWhen(true)] out GlyphTypeface ? glyphTypeface )
{
glyphTypeface = null ;
@ -711,7 +731,7 @@ namespace Avalonia.Media.Fonts
{
for ( var i = 0 ; stretch + i < 9 ; i + + )
{
if ( glyphTypefaces . TryGetValue ( key with { Stretch = ( FontStretch ) ( stretch + i ) } , out glyphTypeface ) & & glyphTypeface ! = null )
if ( TryGetWithStretch ( stretch , out glyphTypeface ) )
{
return true ;
}
@ -721,13 +741,18 @@ namespace Avalonia.Media.Fonts
{
for ( var i = 0 ; stretch - i > 1 ; i + + )
{
if ( glyphTypefaces . TryGetValue ( key with { Stretch = ( FontStretch ) ( stretch - i ) } , out glyphTypeface ) & & glyphTypeface ! = null )
if ( TryGetWithStretch ( stretch , out glyphTypeface ) )
{
return true ;
}
}
}
bool TryGetWithStretch ( int effectiveStretch , [ NotNullWhen ( true ) ] out GlyphTypeface ? glyphTypeface )
= > glyphTypefaces . TryGetValue ( key with { Stretch = ( FontStretch ) effectiveStretch } , out glyphTypeface ) & &
glyphTypeface ! = null & &
glyphTypeface . IsLastResort = = isLastResort ;
return false ;
}
@ -742,12 +767,14 @@ namespace Avalonia.Media.Fonts
/// for a suitable fallback.</param>
/// <param name="key">The font collection key specifying the desired font attributes, including weight, for which a fallback glyph
/// typeface is sought.</param>
/// <param name="isLastResort">Whether to match last resort fonts.</param>
/// <param name="glyphTypeface">When this method returns, contains the matching glyph typeface if a suitable fallback is found; otherwise,
/// null.</param>
/// <returns>true if a fallback glyph typeface matching the requested weight is found; otherwise, false.</returns>
private static bool TryFindWeightFallback (
IDictionary < FontCollectionKey , GlyphTypeface ? > glyphTypefaces ,
FontCollectionKey key ,
bool isLastResort ,
[NotNullWhen(true)] out GlyphTypeface ? glyphTypeface )
{
glyphTypeface = null ;
@ -759,7 +786,7 @@ namespace Avalonia.Media.Fonts
//Look for available weights between the target and 500, in ascending order.
for ( var i = 0 ; weight + i < = 5 0 0 ; i + = 5 0 )
{
if ( glyphTypefaces . TryGetValue ( key with { Weight = ( FontWeight ) ( weight + i ) } , out glyphTypeface ) & & glyphTypeface ! = null )
if ( TryGetWithWeight ( weight , out glyphTypeface ) )
{
return true ;
}
@ -768,7 +795,7 @@ namespace Avalonia.Media.Fonts
//If no match is found, look for available weights less than the target, in descending order.
for ( var i = 0 ; weight - i > = 1 0 0 ; i + = 5 0 )
{
if ( glyphTypefaces . TryGetValue ( key with { Weight = ( FontWeight ) ( weight - i ) } , out glyphTypeface ) & & glyphTypeface ! = null )
if ( TryGetWithWeight ( weight , out glyphTypeface ) )
{
return true ;
}
@ -777,7 +804,7 @@ namespace Avalonia.Media.Fonts
//If no match is found, look for available weights greater than 500, in ascending order.
for ( var i = 0 ; weight + i < = 9 0 0 ; i + = 5 0 )
{
if ( glyphTypefaces . TryGetValue ( key with { Weight = ( FontWeight ) ( weight + i ) } , out glyphTypeface ) & & glyphTypeface ! = null )
if ( TryGetWithWeight ( weight , out glyphTypeface ) )
{
return true ;
}
@ -789,7 +816,7 @@ namespace Avalonia.Media.Fonts
{
for ( var i = 0 ; weight - i > = 1 0 0 ; i + = 5 0 )
{
if ( glyphTypefaces . TryGetValue ( key with { Weight = ( FontWeight ) ( weight - i ) } , out glyphTypeface ) & & glyphTypeface ! = null )
if ( TryGetWithWeight ( weight , out glyphTypeface ) )
{
return true ;
}
@ -798,7 +825,7 @@ namespace Avalonia.Media.Fonts
//If no match is found, look for available weights less than the target, in descending order.
for ( var i = 0 ; weight + i < = 9 0 0 ; i + = 5 0 )
{
if ( glyphTypefaces . TryGetValue ( key with { Weight = ( FontWeight ) ( weight + i ) } , out glyphTypeface ) & & glyphTypeface ! = null )
if ( TryGetWithWeight ( weight , out glyphTypeface ) )
{
return true ;
}
@ -810,7 +837,7 @@ namespace Avalonia.Media.Fonts
{
for ( var i = 0 ; weight + i < = 9 0 0 ; i + = 5 0 )
{
if ( glyphTypefaces . TryGetValue ( key with { Weight = ( FontWeight ) ( weight + i ) } , out glyphTypeface ) & & glyphTypeface ! = null )
if ( TryGetWithWeight ( weight , out glyphTypeface ) )
{
return true ;
}
@ -819,7 +846,7 @@ namespace Avalonia.Media.Fonts
//If no match is found, look for available weights less than the target, in descending order.
for ( var i = 0 ; weight - i > = 1 0 0 ; i + = 5 0 )
{
if ( glyphTypefaces . TryGetValue ( key with { Weight = ( FontWeight ) ( weight - i ) } , out glyphTypeface ) & & glyphTypeface ! = null )
if ( TryGetWithWeight ( weight , out glyphTypeface ) )
{
return true ;
}
@ -827,6 +854,11 @@ namespace Avalonia.Media.Fonts
}
return false ;
bool TryGetWithWeight ( int effectiveWeight , [ NotNullWhen ( true ) ] out GlyphTypeface ? glyphTypeface )
= > glyphTypefaces . TryGetValue ( key with { Weight = ( FontWeight ) effectiveWeight } , out glyphTypeface ) & &
glyphTypeface ! = null & &
glyphTypeface . IsLastResort = = isLastResort ;
}
void IDisposable . Dispose ( )