diff --git a/samples/ControlCatalog/Assets/Fonts/SourceSansPro-Bold.ttf b/samples/ControlCatalog/Assets/Fonts/SourceSansPro-Bold.ttf
new file mode 100644
index 0000000000..f6986468bd
Binary files /dev/null and b/samples/ControlCatalog/Assets/Fonts/SourceSansPro-Bold.ttf differ
diff --git a/samples/ControlCatalog/Assets/Fonts/SourceSansPro-BoldItalic.ttf b/samples/ControlCatalog/Assets/Fonts/SourceSansPro-BoldItalic.ttf
new file mode 100644
index 0000000000..5c00b64faf
Binary files /dev/null and b/samples/ControlCatalog/Assets/Fonts/SourceSansPro-BoldItalic.ttf differ
diff --git a/samples/ControlCatalog/Assets/Fonts/SourceSansPro-Italic.ttf b/samples/ControlCatalog/Assets/Fonts/SourceSansPro-Italic.ttf
new file mode 100644
index 0000000000..82e8762011
Binary files /dev/null and b/samples/ControlCatalog/Assets/Fonts/SourceSansPro-Italic.ttf differ
diff --git a/samples/ControlCatalog/Assets/Fonts/SourceSansPro-Regular.ttf b/samples/ControlCatalog/Assets/Fonts/SourceSansPro-Regular.ttf
new file mode 100644
index 0000000000..278ad8aa0a
Binary files /dev/null and b/samples/ControlCatalog/Assets/Fonts/SourceSansPro-Regular.ttf differ
diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj
index b8a8479a49..390d8d6bcb 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -11,6 +11,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml
index db1c9cb69d..fb0d8fb7b1 100644
--- a/samples/ControlCatalog/Pages/TextBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml
@@ -31,6 +31,12 @@
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Avalonia.Base/Platform/IAssetLoader.cs b/src/Avalonia.Base/Platform/IAssetLoader.cs
index ba30af60bf..26d79d5567 100644
--- a/src/Avalonia.Base/Platform/IAssetLoader.cs
+++ b/src/Avalonia.Base/Platform/IAssetLoader.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Collections.Generic;
using System.IO;
using System.Reflection;
@@ -58,6 +59,13 @@ namespace Avalonia.Platform
///
/// The resource was not found.
///
- Tuple OpenAndGetAssembly(Uri uri, Uri baseUri = null);
+ (Stream Stream, Assembly Assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null);
+
+ ///
+ /// Gets all assets at a specific location.
+ ///
+ /// The location of resources.
+ /// A tuple containing the absolute path to the resource and the owner assembly
+ IEnumerable<(string AbsolutePath, Assembly Assembly)> GetAssets(Uri location);
}
}
diff --git a/src/Avalonia.Visuals/Media/FontFamily.cs b/src/Avalonia.Visuals/Media/FontFamily.cs
index e8709138c8..cdd3161171 100644
--- a/src/Avalonia.Visuals/Media/FontFamily.cs
+++ b/src/Avalonia.Visuals/Media/FontFamily.cs
@@ -19,7 +19,8 @@ namespace Avalonia.Media
/// name
public FontFamily(string name)
{
- if (name == null) throw new ArgumentNullException();
+ Contract.Requires(name != null);
+
FamilyNames = new FamilyNameCollection(new[] { name });
}
@@ -30,14 +31,16 @@ namespace Avalonia.Media
/// name
public FontFamily(IEnumerable names)
{
- if (names == null) throw new ArgumentNullException();
+ Contract.Requires(names != null);
+
FamilyNames = new FamilyNameCollection(names);
}
+ ///
///
/// Initializes a new instance of the class.
///
- /// The name of the .
+ /// The name of the .
/// The source of font resources.
public FontFamily(string name, Uri source) : this(name)
{
@@ -64,7 +67,7 @@ namespace Avalonia.Media
}
///
- /// Gets the key for associated resources.
+ /// Gets the key for associated assets.
///
///
/// The family familyNames.
diff --git a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs b/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs
index 6296b947cc..839f2ce34b 100644
--- a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs
+++ b/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs
@@ -13,13 +13,23 @@ namespace Avalonia.Media.Fonts
{
private readonly ReadOnlyCollection _familyNames;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The family names.
+ /// familyNames
public FamilyNameCollection(IEnumerable familyNames)
{
- if (familyNames == null) throw new ArgumentNullException(nameof(familyNames));
+ Contract.Requires(familyNames != null);
+
var names = new List(familyNames);
+
if (names.Count == 0) throw new ArgumentException($"{nameof(familyNames)} must not be empty.");
+
_familyNames = new ReadOnlyCollection(names);
+
PrimaryFamilyName = _familyNames.First();
+
HasFallbacks = _familyNames.Count > 1;
}
@@ -39,11 +49,25 @@ namespace Avalonia.Media.Fonts
///
public bool HasFallbacks { get; }
+ ///
+ ///
+ /// Returns an enumerator that iterates through the collection.
+ ///
+ ///
+ /// An enumerator that can be used to iterate through the collection.
+ ///
public IEnumerator GetEnumerator()
{
return _familyNames.GetEnumerator();
}
+ ///
+ ///
+ /// Returns an enumerator that iterates through a collection.
+ ///
+ ///
+ /// An object that can be used to iterate through the collection.
+ ///
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
diff --git a/src/Avalonia.Visuals/Media/Fonts/FontResource.cs b/src/Avalonia.Visuals/Media/Fonts/FontAsset.cs
similarity index 73%
rename from src/Avalonia.Visuals/Media/Fonts/FontResource.cs
rename to src/Avalonia.Visuals/Media/Fonts/FontAsset.cs
index 84e2153b42..a1cff79371 100644
--- a/src/Avalonia.Visuals/Media/Fonts/FontResource.cs
+++ b/src/Avalonia.Visuals/Media/Fonts/FontAsset.cs
@@ -5,19 +5,19 @@ using System;
namespace Avalonia.Media.Fonts
{
- internal class FontResource
+ internal class FontAsset
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The source.
- public FontResource(Uri source)
+ public FontAsset(Uri source)
{
Source = source;
}
///
- /// Gets the source.
+ /// Gets the source of the asset.
///
///
/// The source.
diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs b/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs
index b248b5145d..852b39615a 100644
--- a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs
+++ b/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs
@@ -33,15 +33,21 @@ namespace Avalonia.Media.Fonts
}
///
- /// Location of stored that belong to a
+ /// Location of stored that belong to a
///
public Uri Location { get; }
///
- /// Optional filename for that belong to a
+ /// Optional filename for that belong to a
///
public string FileName { get; }
+ ///
+ /// Returns a hash code for this instance.
+ ///
+ ///
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ ///
public override int GetHashCode()
{
unchecked
diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs b/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs
index 89d5e07e5c..ffe6c10acb 100644
--- a/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs
+++ b/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs
@@ -11,181 +11,68 @@ namespace Avalonia.Media.Fonts
{
internal static class FontFamilyLoader
{
- private static readonly Dictionary s_assemblyNameCache
- = new Dictionary();
-
- 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();
}
- public static IEnumerable LoadFontResources(FontFamilyKey fontFamilyKey)
+ public static IEnumerable LoadFontAssets(FontFamilyKey fontFamilyKey)
{
return fontFamilyKey.FileName != null
- ? GetFontResourcesByFileName(fontFamilyKey.Location, fontFamilyKey.FileName)
- : GetFontResourcesByLocation(fontFamilyKey.Location);
+ ? GetFontAssetsByFileName(fontFamilyKey.Location, fontFamilyKey.FileName)
+ : GetFontAssetsByLocation(fontFamilyKey.Location);
}
///
- /// Searches for font resources at a given location and returns a quanity of found resources
+ /// Searches for font assets at a given location and returns a quanity of found assets
///
///
///
- private static IEnumerable GetFontResourcesByLocation(Uri location)
+ private static IEnumerable GetFontAssetsByLocation(Uri location)
{
- var assembly = GetAssembly(location);
-
- if (assembly == null) return Enumerable.Empty();
+ var availableAssets = s_assetLoader.GetAssets(location);
var locationPath = location.AbsolutePath;
- var matchingResources = assembly.Resources.Where(x => x.Contains(locationPath));
+ var mathchingAssets = availableAssets.Where(x => x.AbsolutePath.Contains(locationPath) && x.AbsolutePath.EndsWith(".ttf"));
- return matchingResources.Select(x => CreateResource(GetResourceUri(x, assembly.Name)));
+ return mathchingAssets.Select(x => CreateFontAsset(GetAssetUri(x.AbsolutePath, x.Assembly)));
}
///
- /// Searches for font resources at a given location and only accepts resources that fit to a given filename expression.
+ /// Searches for font assets at a given location and only accepts assets that fit to a given filename expression.
/// Filenames can target multible files with * wildcard. For example "FontFile*.ttf"
///
///
///
///
- private static IEnumerable GetFontResourcesByFileName(Uri location, string fileName)
+ private static IEnumerable GetFontAssetsByFileName(Uri location, string fileName)
{
- var assembly = GetAssembly(location);
-
- if (assembly == null) return Enumerable.Empty();
+ 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);
- }
-
- ///
- /// Returns a valid resource that follows the resm scheme
- ///
- ///
- ///
- ///
- private static Uri GetResourceUri(string path, string assemblyName)
- {
- return new Uri("resm:" + path + "?assembly=" + assemblyName);
- }
-
- ///
- /// Extracts a from a given
- ///
- ///
- ///
- 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)));
}
- ///
- /// Returns a that is identified by a given name.
- ///
- /// If name is null the default assembly is used.
- ///
- ///
- ///
- ///
- 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().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);
}
///
- /// Parses the parameters.
+ /// Returns a for a font asset that follows the resm scheme
///
- /// The URI.
+ ///
+ ///
///
- private static Dictionary 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;
- }
-
- ///
- /// Gets the name.
- ///
- ///
- /// The name.
- ///
- public string Name { get; }
-
- ///
- /// Gets the assembly.
- ///
- ///
- /// The assembly.
- ///
- public Assembly Assembly { get; }
-
- ///
- /// Gets the resources.
- ///
- ///
- /// The resources.
- ///
- public List Resources { get; }
+ return new Uri("resm:" + absolutePath + "?assembly=" + assembly.GetName().Name, UriKind.RelativeOrAbsolute);
}
}
}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
index 79523dd498..a6321f1452 100644
--- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
@@ -126,11 +126,11 @@ namespace Avalonia.Markup.Xaml
}
var asset = assetLocator.OpenAndGetAssembly(uri, baseUri);
- using (var stream = asset.Item1)
+ using (var stream = asset.Stream)
{
try
{
- return Load(stream, asset.Item2, rootInstance, uri);
+ return Load(stream, asset.Assembly, rootInstance, uri);
}
catch (Exception e)
{
diff --git a/src/Shared/PlatformSupport/AssetLoader.cs b/src/Shared/PlatformSupport/AssetLoader.cs
index 6fee855352..2e331daeda 100644
--- a/src/Shared/PlatformSupport/AssetLoader.cs
+++ b/src/Shared/PlatformSupport/AssetLoader.cs
@@ -67,8 +67,8 @@ namespace Avalonia.Shared.PlatformSupport
///
/// The resource was not found.
///
- public Stream Open(Uri uri, Uri baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1;
-
+ public Stream Open(Uri uri, Uri baseUri = null) => OpenAndGetAssembly(uri, baseUri).Stream;
+
///
/// Opens the resource with the requested URI and returns the resource string and the
/// assembly containing the resource.
@@ -83,7 +83,7 @@ namespace Avalonia.Shared.PlatformSupport
///
/// The resource was not found.
///
- public Tuple OpenAndGetAssembly(Uri uri, Uri baseUri = null)
+ public (Stream Stream, Assembly Assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null)
{
var asset = GetAsset(uri, baseUri);
@@ -92,9 +92,18 @@ namespace Avalonia.Shared.PlatformSupport
throw new FileNotFoundException($"The resource {uri} could not be found.");
}
- return Tuple.Create(asset.GetStream(), asset.Assembly);
+ return (asset.GetStream(), asset.Assembly);
+ }
+
+ public IEnumerable<(string AbsolutePath, Assembly Assembly)> GetAssets(Uri location)
+ {
+ var assembly = GetAssembly(location);
+
+ return assembly?.Resources.Select(x => (x.Key, x.Value.Assembly)) ??
+ Enumerable.Empty<(string AbsolutePath, Assembly Assembly)>();
}
+
private IAssetDescriptor GetAsset(Uri uri, Uri baseUri)
{
if (!uri.IsAbsoluteUri || uri.Scheme == "resm")
diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
index 254f6cff1b..13cce3f3da 100644
--- a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
+++ b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
@@ -22,17 +22,17 @@ namespace Avalonia.Skia
private static SKTypefaceCollection CreateCustomFontCollection(FontFamily fontFamily)
{
- var resources = FontFamilyLoader.LoadFontResources(fontFamily.Key);
+ var assets = FontFamilyLoader.LoadFontAssets(fontFamily.Key);
var typeFaceCollection = new SKTypefaceCollection();
var assetLoader = AvaloniaLocator.Current.GetService();
- foreach (var fontResource in resources)
+ foreach (var fontAsset in assets)
{
- var stream = assetLoader.Open(fontResource.Source);
+ var assetStream = assetLoader.Open(fontAsset.Source);
- var typeface = SKTypeface.FromStream(stream);
+ var typeface = SKTypeface.FromStream(assetStream);
typeFaceCollection.AddTypeFace(typeface);
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs
index da2ec8c0a6..ba8e48a02c 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs
@@ -16,20 +16,20 @@ namespace Avalonia.Direct2D1.Media
/// Initializes a new instance of the class.
///
/// The factory.
- ///
- public DWriteResourceFontLoader(Factory factory, IEnumerable fontResources)
+ ///
+ public DWriteResourceFontLoader(Factory factory, IEnumerable fontAssets)
{
var factory1 = factory;
var assetLoader = AvaloniaLocator.Current.GetService();
- foreach (var font in fontResources)
+ foreach (var font in fontAssets)
{
- var resourceStream = assetLoader.Open(font.Source);
+ var assetStream = assetLoader.Open(font.Source);
- var dataStream = new DataStream((int)resourceStream.Length, true, true);
+ var dataStream = new DataStream((int)assetStream.Length, true, true);
- resourceStream.CopyTo(dataStream);
+ assetStream.CopyTo(dataStream);
dataStream.Position = 0;
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs b/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs
index 881f27351a..4e80cd3ec1 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs
@@ -24,9 +24,9 @@ namespace Avalonia.Direct2D1.Media
private static SharpDX.DirectWrite.FontCollection CreateFontCollection(FontFamilyKey key)
{
- var resources = FontFamilyLoader.LoadFontResources(key);
+ var assets = FontFamilyLoader.LoadFontAssets(key);
- var fontLoader = new DWriteResourceFontLoader(s_factory, resources);
+ var fontLoader = new DWriteResourceFontLoader(s_factory, assets);
return new SharpDX.DirectWrite.FontCollection(s_factory, fontLoader, fontLoader.Key);
}
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs
index 10a841ba84..ef7171d651 100644
--- a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs
@@ -22,7 +22,7 @@ namespace Avalonia.Visuals.UnitTests.Media.Fonts
var key = new FontFamilyKey(source);
- var resources = FontFamilyLoader.LoadFontResources(key);
+ var resources = FontFamilyLoader.LoadFontAssets(key);
Assert.Single(resources);
}