Browse Source

resm filename globbing

Benedikt Schroeder 7 years ago
parent
commit
ff230d4360

+ 18 - 0
samples/ControlCatalog/ControlCatalog.csproj

@@ -11,6 +11,24 @@
     </EmbeddedResource>
     <EmbeddedResource Include="Assets\*" />
   </ItemGroup>
+  <ItemGroup>
+    <None Remove="Fonts\Aspergit_Bold.ttf" />
+    <None Remove="Fonts\Aspergit_Bold_Italic.ttf" />
+    <None Remove="Fonts\Aspergit_Italic.ttf" />
+    <None Remove="Fonts\BolshevikBd.ttf" />
+    <None Remove="Fonts\PinkHighwayBold.ttf" />
+    <None Remove="Fonts\PixelOperator-Bold.ttf" />
+    <None Remove="Fonts\PixelOperator.ttf" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Fonts\Aspergit_Bold_Italic.ttf" />
+    <EmbeddedResource Include="Fonts\Aspergit_Bold.ttf" />
+    <EmbeddedResource Include="Fonts\Aspergit_Italic.ttf" />
+    <EmbeddedResource Include="Fonts\BolshevikBd.ttf" />
+    <EmbeddedResource Include="Fonts\PinkHighwayBold.ttf" />
+    <EmbeddedResource Include="Fonts\PixelOperator-Bold.ttf" />
+    <EmbeddedResource Include="Fonts\PixelOperator.ttf" />
+  </ItemGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />

BIN
samples/ControlCatalog/Fonts/Aspergit_Bold.ttf


BIN
samples/ControlCatalog/Fonts/Aspergit_Bold_Italic.ttf


BIN
samples/ControlCatalog/Fonts/Aspergit_Italic.ttf


BIN
samples/ControlCatalog/Fonts/BolshevikBd.ttf


BIN
samples/ControlCatalog/Fonts/PinkHighwayBold.ttf


BIN
samples/ControlCatalog/Fonts/PixelOperator-Bold.ttf


BIN
samples/ControlCatalog/Fonts/PixelOperator.ttf


+ 23 - 17
samples/ControlCatalog/Pages/BorderPage.xaml

@@ -1,32 +1,38 @@
 <UserControl xmlns="https://github.com/avaloniaui">
-  <StackPanel Orientation="Vertical" Gap="4">
-    <TextBlock Classes="h1">Border</TextBlock>
-    <TextBlock Classes="h2">A control which decorates a child with a border and background</TextBlock>
+    <StackPanel Orientation="Vertical" Gap="4">
+        <TextBlock Classes="h1">Border</TextBlock>
+        <TextBlock Classes="h2">A control which decorates a child with a border and background</TextBlock>
 
-    <StackPanel Orientation="Vertical"
+        <StackPanel Orientation="Vertical"
                 Margin="0,16,0,0"
                 HorizontalAlignment="Center"
                 Gap="16">
-      <Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16">
-        <TextBlock>Border</TextBlock>
-      </Border>
-      <Border Background="{DynamicResource ThemeAccentBrush2}"
+            <Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16">
+                <TextBlock>Border</TextBlock>
+            </Border>
+            <Border Background="{DynamicResource ThemeAccentBrush2}"
               BorderBrush="{DynamicResource ThemeAccentBrush}"
               BorderThickness="4"
               Padding="16">
-        <TextBlock>Border and Background</TextBlock>
-      </Border>
-      <Border BorderBrush="{DynamicResource ThemeAccentBrush}"
+                <TextBlock>Border and Background</TextBlock>
+            </Border>
+            <Border BorderBrush="{DynamicResource ThemeAccentBrush}"
               BorderThickness="4"
               CornerRadius="8"
               Padding="16">
-        <TextBlock>Rounded Corners</TextBlock>
-      </Border>
-      <Border Background="{DynamicResource ThemeAccentBrush2}"
+                <TextBlock>Rounded Corners</TextBlock>
+            </Border>
+            <Border Background="{DynamicResource ThemeAccentBrush2}"
               CornerRadius="8"
               Padding="16">
-        <TextBlock>Rounded Corners</TextBlock>
-      </Border>
+                <StackPanel>
+                    <TextBlock FontFamily="resm:ControlCatalog.Fonts?assembly=ControlCatalog#Pixel Operator">Rounded Corners</TextBlock>
+                    <TextBlock FontFamily="resm:ControlCatalog.Fonts?assembly=ControlCatalog#Pixel Operator" FontWeight="Bold">Rounded Corners</TextBlock>
+                    <TextBlock FontFamily="resm:ControlCatalog.Fonts.Aspergit*.ttf?assembly=ControlCatalog#Aspergit" FontWeight="Bold">Rounded Corners</TextBlock>
+                    <TextBlock FontFamily="resm:ControlCatalog.Fonts.Aspergit*.ttf?assembly=ControlCatalog#Aspergit" FontWeight="Bold" FontStyle="Italic">Rounded Corners</TextBlock>
+                    <TextBlock FontFamily="resm:ControlCatalog.Fonts.Aspergit*.ttf?assembly=ControlCatalog#Aspergit" FontStyle="Italic">Rounded Corners</TextBlock>
+                </StackPanel>
+            </Border>
+        </StackPanel>
     </StackPanel>
-  </StackPanel>
 </UserControl>

+ 271 - 116
src/Avalonia.Visuals/Media/FontFamily.cs

@@ -2,41 +2,71 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Avalonia.Platform;
 
 namespace Avalonia.Media
 {
     public class FontFamily
     {
-        public FontFamily(string familyName = "Courier New", Uri baseUri = null)
+        public FontFamily(string name = "Courier New")
         {
-            if (familyName == null) throw new ArgumentNullException(nameof(familyName));
-
-            Key = new FontFamilyKey(familyName, baseUri);
+            Name = name ?? throw new ArgumentNullException(nameof(name));
         }
 
-        public string Name => Key.FriendlyName;
+        public FontFamily(string name, Uri source) : this(name)
+        {
+            Key = new FontFamilyKey(source);
+        }
 
-        public Uri BaseUri => Key.BaseUri;
+        public string Name { get; }
 
         public FontFamilyKey Key { get; }
 
         public override string ToString()
         {
-            return Key.ToString();
+            if (Key != null)
+            {
+                return Key + "#" + Name;
+            }
+
+            return Name;
         }
     }
 
     public class FontFamilyKey
     {
-        public FontFamilyKey(string friendlyName, Uri baseUri = null)
+        public FontFamilyKey(Uri source)
         {
-            FriendlyName = friendlyName;
-            BaseUri = baseUri;
+            if (source.AbsolutePath.Contains(".ttf"))
+            {
+                var filePathWithoutExtension = source.AbsolutePath.Replace(".ttf", "");
+
+                if (source.Scheme == "resm")
+                {
+                    var fileNameWithoutExtension = filePathWithoutExtension.Split('.').Last();
+                    FileName = fileNameWithoutExtension + ".ttf";
+                    Location = new Uri(source.OriginalString.Replace("." + FileName, ""), UriKind.RelativeOrAbsolute);
+                }
+
+                if (source.Scheme == "res")
+                {
+                    FileName = source.AbsolutePath.Split('/').Last();
+                }
+            }
+            else
+            {
+                Location = source;
+            }
         }
 
-        public string FriendlyName { get; }
+        public Uri Location { get; }
 
-        public Uri BaseUri { get; }
+        public string FileName { get; }
 
         public override int GetHashCode()
         {
@@ -44,14 +74,14 @@ namespace Avalonia.Media
             {
                 var hash = (int)2166136261;
 
-                if (FriendlyName != null)
+                if (Location != null)
                 {
-                    hash = (hash * 16777619) ^ FriendlyName.GetHashCode();
+                    hash = (hash * 16777619) ^ Location.GetHashCode();
                 }
 
-                if (BaseUri != null)
+                if (FileName != null)
                 {
-                    hash = (hash * 16777619) ^ BaseUri.GetHashCode();
+                    hash = (hash * 16777619) ^ FileName.GetHashCode();
                 }
 
                 return hash;
@@ -62,117 +92,242 @@ namespace Avalonia.Media
         {
             if (!(obj is FontFamilyKey other)) return false;
 
-            if (FriendlyName != other.FriendlyName) return false;
+            if (Location != other.Location) return false;
 
-            if (BaseUri != other.BaseUri) return false;
+            if (FileName != other.FileName) return false;
 
             return true;
         }
 
         public override string ToString()
         {
-            if (BaseUri != null)
+            if (FileName != null)
             {
-                return BaseUri + "#" + FriendlyName;
+                if (Location.Scheme == "resm")
+                {
+                    return Location + "." + FileName;
+                }
+
+                return Location + "/" + FileName;
             }
 
-            return FriendlyName;
+            return Location.ToString();
         }
     }
 
-    //public class FamilyTypeface
-    //{
-    //    public FamilyTypeface(Uri resourceUri = null, FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal)
-    //    {
-    //        ResourceUri = resourceUri;
-    //        FontStyle = fontStyle;
-    //        FontWeight = fontWeight;
-    //    }
-
-    //    public Uri ResourceUri { get; }
-    //    public FontWeight FontWeight { get; }
-    //    public FontStyle FontStyle { get; }
-    //}
-
-    //public class FamilyTypefaceKey
-    //{
-    //    public FamilyTypefaceKey(FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal)
-    //    {
-    //        FontWeight = fontWeight;
-    //        FontStyle = fontStyle;
-    //    }
-
-    //    public FontWeight FontWeight { get; }
-
-    //    public FontStyle FontStyle { get; }
-
-    //    public override int GetHashCode()
-    //    {
-    //        unchecked
-    //        {
-    //            var hash = (int)2166136261;
-
-    //            hash = (hash * 16777619) ^ FontWeight.GetHashCode();
-
-    //            hash = (hash * 16777619) ^ FontStyle.GetHashCode();
-
-    //            return hash;
-    //        }
-    //    }
-
-    //    public override bool Equals(object obj)
-    //    {
-    //        if (!(obj is FamilyTypefaceKey other)) return false;
-
-    //        if (FontWeight != other.FontWeight) return false;
-
-    //        if (FontStyle != other.FontStyle) return false;
-
-    //        return true;
-    //    }
-    //}
-
-    //public class CachedFontFamily
-    //{
-    //    private readonly ConcurrentDictionary<FamilyTypefaceKey, FamilyTypeface> _typefaces =
-    //        new ConcurrentDictionary<FamilyTypefaceKey, FamilyTypeface>();
-
-    //    public bool TryGetFamilyTypeface(out FamilyTypeface typeface, FontWeight fontWeight = FontWeight.Normal,
-    //        FontStyle fontStyle = FontStyle.Normal)
-    //    {
-    //        return _typefaces.TryGetValue(new FamilyTypefaceKey(fontWeight, fontStyle), out typeface);
-    //    }
-
-    //    public bool TryAddFamilyTypeface(Uri resourceUri, FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal)
-    //    {
-    //        var familyTypefaceKeytypefaceKey = new FamilyTypefaceKey(fontWeight, fontStyle);
-
-    //        return _typefaces.TryAdd(familyTypefaceKeytypefaceKey, CreateFamilyTypeface(familyTypefaceKeytypefaceKey, resourceUri));
-    //    }
-
-    //    private static FamilyTypeface CreateFamilyTypeface(FamilyTypefaceKey familyTypefaceKey, Uri resourceUri)
-    //    {
-    //        return new FamilyTypeface(resourceUri, familyTypefaceKey.FontWeight, familyTypefaceKey.FontStyle);
-    //    }
-    //}
-
-    //public class FontFamilyCache
-    //{
-    //    private readonly ConcurrentDictionary<FontFamilyKey, CachedFontFamily> _cachedFontFamilies = new ConcurrentDictionary<FontFamilyKey, CachedFontFamily>();
-
-    //    public bool TryGetCachedFontFamily(FontFamily fontFamily, out CachedFontFamily cachedFontFamily)
-    //    {
-    //        return _cachedFontFamilies.TryGetValue(fontFamily.Key, out cachedFontFamily);
-    //    }
-
-    //    public CachedFontFamily GetOrAddCachedFontFamily(FontFamily fontFamily)
-    //    {
-    //        return _cachedFontFamilies.GetOrAdd(fontFamily.Key, CreateCachedFontFamily);
-    //    }
-
-    //    private static CachedFontFamily CreateCachedFontFamily(FontFamilyKey fontFamilyKey)
-    //    {
-    //        return new CachedFontFamily();
-    //    }
-    //}
+    public class FontResource
+    {
+        public FontResource(Uri source)
+        {
+            Source = source;
+        }
+
+        public Uri Source { get; }
+    }
+
+    public class FontResourceCollection
+    {
+        private Dictionary<Uri, FontResource> _fontResources;
+        private readonly IFontResourceLoader _fontResourceLoader = new FontResourceLoader();
+
+        public FontResourceCollection(FontFamilyKey key)
+        {
+            Key = key;
+        }
+
+        public FontFamilyKey Key { get; }
+
+        public IEnumerable<FontResource> FontResources
+        {
+            get
+            {
+                if (_fontResources == null)
+                {
+                    _fontResources = CreateFontResources();
+                }
+
+                return _fontResources.Values;
+            }
+        }
+
+        private Dictionary<Uri, FontResource> CreateFontResources()
+        {
+            return _fontResourceLoader.GetFontResources(Key).ToDictionary(x => x.Source);
+        }
+    }
+
+    public interface IFontResourceLoader
+    {
+        IEnumerable<FontResource> GetFontResources(FontFamilyKey fontFamilyKey);
+    }
+
+    public class FontResourceLoader : IFontResourceLoader
+    {
+        private static readonly Dictionary<string, AssemblyDescriptor> s_assemblyNameCache
+            = new Dictionary<string, AssemblyDescriptor>();
+
+        private readonly AssemblyDescriptor _defaultAssembly;
+
+        public FontResourceLoader(Assembly assembly = null)
+        {
+            if (assembly == null)
+            {
+                assembly = Assembly.GetEntryAssembly();
+            }
+            if (assembly != null)
+            {
+                _defaultAssembly = new AssemblyDescriptor(assembly);
+            }
+        }
+
+        public IEnumerable<FontResource> GetFontResources(FontFamilyKey fontFamilyKey)
+        {
+            return fontFamilyKey.FileName != null
+                ? GetFontResourcesByFileName(fontFamilyKey.Location, fontFamilyKey.FileName)
+                : GetFontResourcesByLocation(fontFamilyKey.Location);
+        }
+
+        private IEnumerable<FontResource> GetFontResourcesByLocation(Uri location)
+        {
+            var assembly = GetAssembly(location);
+
+            var locationPath = GetLocationPath(location);
+
+            var matchingResources = assembly.Resources.Where(x => x.Contains(locationPath));
+
+            return matchingResources.Select(x => new FontResource(GetResourceUri(x, assembly.Name)));
+        }
+
+        private IEnumerable<FontResource> GetFontResourcesByFileName(Uri location, string fileName)
+        {
+            var assembly = GetAssembly(location);
+
+            var compareTo = fileName.Split('*').First();
+
+            var matchingResources = assembly.Resources.Where(x => x.Contains(compareTo));
+
+            return matchingResources.Select(x => new FontResource(GetResourceUri(x, assembly.Name)));
+        }
+
+        private static Uri GetResourceUri(string path, string assemblyName)
+        {
+            return new Uri("resm:" + path + "?assembly=" + assemblyName);
+        }
+
+        private static string GetLocationPath(Uri uri)
+        {
+            if (uri.Scheme == "resm") return uri.AbsolutePath;
+
+            var path = new StringBuilder();
+
+            for (var index = 0; index < uri.Segments.Length; index++)
+            {
+                path.Append(uri.Segments[index]);
+                if (index < uri.Segments.Length - 1)
+                {
+                    path.Append('.');
+                }
+            }
+
+            return path.ToString();
+        }
+
+        private AssemblyDescriptor GetAssembly(Uri uri)
+        {
+            if (uri == null) return null;
+
+            var parameters = ParseParameters(uri);
+
+            return parameters.TryGetValue("assembly", out var assemblyName) ? GetAssembly(assemblyName) : null;
+        }
+
+        private AssemblyDescriptor GetAssembly(string name)
+        {
+            if (name == null)
+            {
+                return _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;
+        }
+
+        private static Dictionary<string, string> ParseParameters(Uri uri)
+        {
+            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;
+            }
+
+            public string Name { get; }
+            public Assembly Assembly { get; }
+            public List<string> Resources { get; }
+        }
+    }
+
+    public class CachedFontFamily
+    {
+        private readonly FontResourceCollection _fontResourceCollection;
+
+        public CachedFontFamily(FontFamilyKey key, FontResourceCollection fontResourceCollection)
+        {
+            Key = key;
+            _fontResourceCollection = fontResourceCollection;
+        }
+
+        public FontFamilyKey Key { get; }
+
+        public IEnumerable<FontResource> FontResources => _fontResourceCollection.FontResources;
+    }
+
+    public class FontFamilyCache
+    {
+        private static readonly ConcurrentDictionary<FontFamilyKey, CachedFontFamily> s_cachedFontFamilies = new ConcurrentDictionary<FontFamilyKey, CachedFontFamily>();
+
+        public CachedFontFamily GetOrAddFontFamily(FontFamilyKey key)
+        {
+            return s_cachedFontFamilies.GetOrAdd(key, CreateCachedFontFamily);
+        }
+
+        private static CachedFontFamily CreateCachedFontFamily(FontFamilyKey fontFamilyKey)
+        {
+            return new CachedFontFamily(fontFamilyKey, new FontResourceCollection(fontFamilyKey));
+        }
+    }
 }

+ 8 - 5
src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs

@@ -18,13 +18,16 @@ namespace Avalonia.Markup.Xaml.Converters
 
             if (string.IsNullOrEmpty(s)) throw new ArgumentException("Specified family is not supported.");
 
-            if (!s.StartsWith("resm:")) return new FontFamily(s);
-
             var fontFamilyExpression = s.Split('#');
 
+            if (fontFamilyExpression.Length == 1)
+            {
+                return new FontFamily(s);
+            }
+
             string familyName;
 
-            Uri baseUri = null;
+            Uri source = null;
 
             switch (fontFamilyExpression.Length)
             {
@@ -35,7 +38,7 @@ namespace Avalonia.Markup.Xaml.Converters
                     }
                 case 2:
                     {
-                        baseUri = new Uri(fontFamilyExpression[0], UriKind.RelativeOrAbsolute);
+                        source = new Uri(fontFamilyExpression[0], UriKind.RelativeOrAbsolute);
                         familyName = fontFamilyExpression[1];
                         break;
                     }
@@ -45,7 +48,7 @@ namespace Avalonia.Markup.Xaml.Converters
                     }
             }
 
-            return new FontFamily(familyName, baseUri);
+            return new FontFamily(familyName, source);
         }
     }
 }

+ 3 - 5
src/Shared/PlatformSupport/AssetLoader.cs

@@ -106,9 +106,8 @@ namespace Avalonia.Shared.PlatformSupport
             if (uri != null)
             {
                 var qs = ParseQueryString(uri);
-                string assemblyName;
 
-                if (qs.TryGetValue("assembly", out assemblyName))
+                if (qs.TryGetValue("assembly", out var assemblyName))
                 {
                     return GetAssembly(assemblyName);
                 }
@@ -124,8 +123,7 @@ namespace Avalonia.Shared.PlatformSupport
                 return _defaultAssembly;
             }
 
-            AssemblyDescriptor rv;
-            if (!AssemblyNameCache.TryGetValue(name, out rv))
+            if (!AssemblyNameCache.TryGetValue(name, out var rv))
             {
                 var loadedAssemblies = AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetLoadedAssemblies();
                 var match = loadedAssemblies.FirstOrDefault(a => a.GetName().Name == name);
@@ -199,5 +197,5 @@ namespace Avalonia.Shared.PlatformSupport
             public Dictionary<string, IAssetDescriptor> Resources { get; }
             public string Name { get; }
         }
-    }
+    }  
 }

+ 9 - 9
src/Skia/Avalonia.Skia/TypefaceCache.cs

@@ -56,21 +56,21 @@ namespace Avalonia.Skia
 
             if (!entry.TryGetValue(key, out var typeface))
             {
-                if (fontFamily.BaseUri != null)
-                {
-                    var stream = AvaloniaLocator.Current.GetService<IAssetLoader>().Open(fontFamily.BaseUri);
-
-                    typeface = SKTypeface.FromStream(stream);
-                }
-                else
-                {
+                //if (fontFamily.BaseUri != null)
+                //{
+                //    var stream = AvaloniaLocator.Current.GetService<IAssetLoader>().Open(fontFamily.BaseUri);
+
+                //    typeface = SKTypeface.FromStream(stream);
+                //}
+                //else
+                //{
                     typeface = SKTypeface.FromFamilyName(familyKey, key.Weight, SKFontStyleWidth.Normal, key.Slant);
 
                     if (typeface == null)
                     {
                         typeface = SKTypeface.FromFamilyName(null, SKTypefaceStyle.Normal);
                     }
-                }
+                //}
 
                 entry[key] = typeface;
             }

+ 25 - 22
src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs

@@ -4,7 +4,9 @@
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
+using System.Reflection;
 using Avalonia.Media;
 using Avalonia.Platform;
 using SharpDX;
@@ -24,13 +26,17 @@ namespace Avalonia.Direct2D1.Media
         {
             Text = text;
 
-            var factory = AvaloniaLocator.Current.GetService<DWrite.Factory>();
+            var factory = AvaloniaLocator.Current.GetService<DWrite.Factory>();          
 
             DWrite.TextFormat textFormat;
 
-            if (typeface.FontFamily.BaseUri != null)
+            if (typeface.FontFamily.Key != null)
             {
-                var fontCollection = Direct2D1CustomFontResourceCache.GetOrAddCustomFontResource(typeface.FontFamily, factory);
+                var fontFamilyCache = new FontFamilyCache();
+
+                var fontFamily = fontFamilyCache.GetOrAddFontFamily(typeface.FontFamily.Key);
+
+                var fontCollection = Direct2D1CustomFontCollectionCache.GetOrAddCustomFontCollection(fontFamily, factory);
 
                 textFormat = new DWrite.TextFormat(
                         factory,
@@ -81,11 +87,6 @@ namespace Avalonia.Direct2D1.Media
 
         public DWrite.TextLayout TextLayout { get; }
 
-        //public void Dispose()
-        //{
-        //    TextLayout.Dispose();
-        //}
-
         public IEnumerable<FormattedTextLine> GetLines()
         {
             var result = TextLayout.GetLineMetrics();
@@ -148,19 +149,19 @@ namespace Avalonia.Direct2D1.Media
         }
     }
 
-    internal static class Direct2D1CustomFontResourceCache
+    internal static class Direct2D1CustomFontCollectionCache
     {
         private static readonly ConcurrentDictionary<FontFamilyKey, DWrite.FontCollection> s_cachedFonts =
             new ConcurrentDictionary<FontFamilyKey, DWrite.FontCollection>();
 
-        public static DWrite.FontCollection GetOrAddCustomFontResource(FontFamily fontFamily, DWrite.Factory factory)
+        public static DWrite.FontCollection GetOrAddCustomFontCollection(CachedFontFamily fontFamily, DWrite.Factory factory)
         {
-            return s_cachedFonts.GetOrAdd(fontFamily.Key, x => CreateCustomFontResource(x, factory));
+            return s_cachedFonts.GetOrAdd(fontFamily.Key, x => CreateCustomFontCollection(fontFamily, factory));
         }
 
-        private static DWrite.FontCollection CreateCustomFontResource(FontFamilyKey fontFamilyKey, DWrite.Factory factory)
+        private static DWrite.FontCollection CreateCustomFontCollection(CachedFontFamily fontFamily, DWrite.Factory factory)
         {
-            var fontLoader = new ResourceFontLoader(factory, fontFamilyKey.BaseUri);
+            var fontLoader = new ResourceFontLoader(factory, fontFamily.FontResources);
 
             return new DWrite.FontCollection(factory, fontLoader, fontLoader.Key);
         }
@@ -172,27 +173,29 @@ namespace Avalonia.Direct2D1.Media
         private readonly List<ResourceFontFileEnumerator> _enumerators = new List<ResourceFontFileEnumerator>();
         private readonly DataStream _keyStream;
 
-
         /// <summary>
         /// Initializes a new instance of the <see cref="ResourceFontLoader"/> class.
         /// </summary>
         /// <param name="factory">The factory.</param>
-        /// <param name="fontResource"></param>
-        public ResourceFontLoader(DWrite.Factory factory, Uri fontResource)
+        /// <param name="fontResources"></param>
+        public ResourceFontLoader(DWrite.Factory factory, IEnumerable<FontResource> fontResources)
         {
             var factory1 = factory;
 
-            var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
+            var assetLoader = AvaloniaLocator.Current.GetService<IAssetLoader>();
 
-            var resourceStream = assets.Open(fontResource);
+            foreach (var font in fontResources)
+            {
+                var resourceStream = assetLoader.Open(font.Source);
 
-            var dataStream = new DataStream((int)resourceStream.Length, true, true);
+                var dataStream = new DataStream((int)resourceStream.Length, true, true);
 
-            resourceStream.CopyTo(dataStream);
+                resourceStream.CopyTo(dataStream);
 
-            dataStream.Position = 0;
+                dataStream.Position = 0;
 
-            _fontStreams.Add(new ResourceFontFileStream(dataStream));
+                _fontStreams.Add(new ResourceFontFileStream(dataStream));
+            }
 
             // Build a Key storage that stores the index of the font
             _keyStream = new DataStream(sizeof(int) * _fontStreams.Count, true, true);