@ -11,181 +11,68 @@ namespace Avalonia.Media.Fonts
{
internal static class FontFamilyLoader
{
private static readonly Dictionary < string , AssemblyDescriptor > s_assemblyNameCache
= new Dictionary < string , AssemblyDescriptor > ( ) ;
private static readonly AssemblyDescriptor s_defaultAssembly ;
private static readonly IAssetLoader s_assetLoader ;
static FontFamilyLoader ( )
{
s_defaultAssembly = new AssemblyDescriptor ( Assembly . GetEntryAssembly ( ) ) ;
s_assetLoader = AvaloniaLocator . Current . GetService < IAssetLoader > ( ) ;
}
public static IEnumerable < FontResource > LoadFontResource s ( FontFamilyKey fontFamilyKey )
public static IEnumerable < FontAsset > LoadFontAsset s ( FontFamilyKey fontFamilyKey )
{
return fontFamilyKey . FileName ! = null
? GetFontResource sByFileName ( fontFamilyKey . Location , fontFamilyKey . FileName )
: GetFontResource sByLocation ( fontFamilyKey . Location ) ;
? GetFontAsset sByFileName ( fontFamilyKey . Location , fontFamilyKey . FileName )
: GetFontAsset sByLocation ( fontFamilyKey . Location ) ;
}
/// <summary>
/// Searches for font resources at a given location and returns a quanity of found resource s
/// Searches for font assets at a given location and returns a quanity of found asset s
/// </summary>
/// <param name="location"></param>
/// <returns></returns>
private static IEnumerable < FontResource > GetFontResource sByLocation ( Uri location )
private static IEnumerable < FontAsset > GetFontAsset sByLocation ( Uri location )
{
var assembly = GetAssembly ( location ) ;
if ( assembly = = null ) return Enumerable . Empty < FontResource > ( ) ;
var availableAssets = s_assetLoader . GetAssets ( location ) ;
var locationPath = location . AbsolutePath ;
var matchingResources = assembly . Resource s . Where ( x = > x . Contains ( locationPath ) ) ;
var mathchingAssets = availableAsset s . Where ( x = > x . AbsolutePath . Contains ( locationPath ) & & x . AbsolutePath . EndsWith ( ".ttf" ) ) ;
return matchingResource s . Select ( x = > CreateResource ( GetResourceUri ( x , assembly . Name ) ) ) ;
return mathchingAsset s . Select ( x = > CreateFontAsset ( GetAssetUri ( x . AbsolutePath , x . Assembly ) ) ) ;
}
/// <summary>
/// Searches for font resources at a given location and only accepts resource s that fit to a given filename expression.
/// Searches for font assets at a given location and only accepts asset s that fit to a given filename expression.
/// <para>Filenames can target multible files with * wildcard. For example "FontFile*.ttf"</para>
/// </summary>
/// <param name="location"></param>
/// <param name="fileName"></param>
/// <returns></returns>
private static IEnumerable < FontResource > GetFontResource sByFileName ( Uri location , string fileName )
private static IEnumerable < FontAsset > GetFontAsset sByFileName ( Uri location , string fileName )
{
var assembly = GetAssembly ( location ) ;
if ( assembly = = null ) return Enumerable . Empty < FontResource > ( ) ;
var availableResources = s_assetLoader . GetAssets ( location ) ;
var compareTo = location . AbsolutePath + "." + fileName . Split ( '*' ) . First ( ) ;
var matchingResources = assembly . Resources . Where ( x = > x . Contains ( compareTo ) ) ;
return matchingResources . Select ( x = > CreateResource ( GetResourceUri ( x , assembly . Name ) ) ) ;
}
private static FontResource CreateResource ( Uri source )
{
return new FontResource ( source ) ;
}
/// <summary>
/// Returns a valid resource <see cref="Uri"/> that follows the resm scheme
/// </summary>
/// <param name="path"></param>
/// <param name="assemblyName"></param>
/// <returns></returns>
private static Uri GetResourceUri ( string path , string assemblyName )
{
return new Uri ( "resm:" + path + "?assembly=" + assemblyName ) ;
}
/// <summary>
/// Extracts a <see cref="AssemblyDescriptor"/> from a given <see cref="Uri"/>
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
private static AssemblyDescriptor GetAssembly ( Uri uri )
{
if ( uri = = null ) return null ;
var parameters = ParseParameters ( uri ) ;
var matchingResources = availableResources . Where ( x = > x . AbsolutePath . Contains ( compareTo ) ) ;
return parameters . TryGetValue ( "assembly" , out var assemblyName ) ? GetAssembly ( assemblyName ) : s_defaultAssembly ;
return matchingResources . Select ( x = > CreateFontAsset ( GetAssetUri ( x . AbsolutePath , x . Assembly ) ) ) ;
}
/// <summary>
/// Returns a <see cref="AssemblyDescriptor"/> that is identified by a given name.
/// <para>
/// If name is <value>null</value> the default assembly is used.
/// </para>
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
private static AssemblyDescriptor GetAssembly ( string name )
private static FontAsset CreateFontAsset ( Uri source )
{
if ( name = = null )
{
return s_defaultAssembly ;
}
if ( ! s_assemblyNameCache . TryGetValue ( name , out var rv ) )
{
var loadedAssemblies = AvaloniaLocator . Current . GetService < IRuntimePlatform > ( ) . GetLoadedAssemblies ( ) ;
var match = loadedAssemblies . FirstOrDefault ( a = > a . GetName ( ) . Name = = name ) ;
if ( match ! = null )
{
s_assemblyNameCache [ name ] = rv = new AssemblyDescriptor ( match ) ;
}
else
{
// iOS does not support loading assemblies dynamically!
//
#if NETCOREAPP1_0
s_assemblyNameCache [ name ] = rv = new AssemblyDescriptor ( Assembly . Load ( new AssemblyName ( name ) ) ) ;
#elif __IOS__
throw new InvalidOperationException (
$"Assembly {name} needs to be referenced and explicitly loaded before loading resources" ) ;
#else
s_assemblyNameCache [ name ] = rv = new AssemblyDescriptor ( Assembly . Load ( name ) ) ;
#endif
}
}
return rv ;
return new FontAsset ( source ) ;
}
/// <summary>
/// Parses the parameters.
/// Returns a <see cref="Uri"/> for a font asset that follows the resm scheme
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="absolutePath"></param>
/// <param name="assembly"></param>
/// <returns></returns>
private static Dictionary < string , string > ParseParameters ( Uri uri )
private static Uri GetAssetUri ( string absolutePath , Assembly assembly )
{
return uri . Query . TrimStart ( '?' )
. Split ( new [ ] { '&' } , StringSplitOptions . RemoveEmptyEntries )
. Select ( p = > p . Split ( '=' ) )
. ToDictionary ( p = > p [ 0 ] , p = > p [ 1 ] ) ;
}
private class AssemblyDescriptor
{
public AssemblyDescriptor ( Assembly assembly )
{
Assembly = assembly ;
if ( Assembly = = null ) return ;
Resources = assembly . GetManifestResourceNames ( ) . ToList ( ) ;
Name = Assembly . GetName ( ) . Name ;
}
/// <summary>
/// Gets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get ; }
/// <summary>
/// Gets the assembly.
/// </summary>
/// <value>
/// The assembly.
/// </value>
public Assembly Assembly { get ; }
/// <summary>
/// Gets the resources.
/// </summary>
/// <value>
/// The resources.
/// </value>
public List < string > Resources { get ; }
return new Uri ( "resm:" + absolutePath + "?assembly=" + assembly . GetName ( ) . Name , UriKind . RelativeOrAbsolute ) ;
}
}
}