From 2c8953de977829060db11eda7dc1bf1ea886b1b2 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Mon, 21 May 2018 22:54:24 +0200 Subject: [PATCH] Requested changes --- src/Avalonia.Base/Platform/IAssetLoader.cs | 6 +- src/Avalonia.Visuals/Media/FontFamily.cs | 116 ++++++++++-------- .../Media/Fonts/FamilyNameCollection.cs | 69 +++++++++-- src/Avalonia.Visuals/Media/Fonts/FontAsset.cs | 27 ---- .../Media/Fonts/FontFamilyKey.cs | 12 +- .../Media/Fonts/FontFamilyLoader.cs | 23 ++-- src/Avalonia.Visuals/Media/IFontFamily.cs | 35 ++++++ src/Avalonia.Visuals/Media/Typeface.cs | 14 ++- .../Properties/AssemblyInfo.cs | 2 - .../AvaloniaXamlLoader.cs | 4 +- src/Shared/PlatformSupport/AssetLoader.cs | 6 +- .../Avalonia.Skia/SKTypefaceCollection.cs | 68 +++++----- .../SKTypefaceCollectionCache.cs | 20 ++- .../Media/DWriteResourceFontFileStream.cs | 1 - .../Media/DWriteResourceFontLoader.cs | 8 +- .../Media/Direct2D1FontCollectionCache.cs | 34 +++-- .../Media/FormattedTextImpl.cs | 8 +- tests/Avalonia.UnitTests/MockAssetLoader.cs | 4 +- .../Media/FontFamilyTests.cs | 14 ++- .../Media/Fonts/FamilyNameCollectionTests.cs | 8 ++ 20 files changed, 300 insertions(+), 179 deletions(-) delete mode 100644 src/Avalonia.Visuals/Media/Fonts/FontAsset.cs create mode 100644 src/Avalonia.Visuals/Media/IFontFamily.cs diff --git a/src/Avalonia.Base/Platform/IAssetLoader.cs b/src/Avalonia.Base/Platform/IAssetLoader.cs index 26d79d5567..767ce4ad8e 100644 --- a/src/Avalonia.Base/Platform/IAssetLoader.cs +++ b/src/Avalonia.Base/Platform/IAssetLoader.cs @@ -59,13 +59,13 @@ namespace Avalonia.Platform /// /// The resource was not found. /// - (Stream Stream, Assembly Assembly) 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. + /// The location of assets. /// A tuple containing the absolute path to the resource and the owner assembly - IEnumerable<(string AbsolutePath, Assembly Assembly)> GetAssets(Uri location); + 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 cdd3161171..d7b7edd96f 100644 --- a/src/Avalonia.Visuals/Media/FontFamily.cs +++ b/src/Avalonia.Visuals/Media/FontFamily.cs @@ -8,10 +8,8 @@ using Avalonia.Media.Fonts; namespace Avalonia.Media { - public class FontFamily + public class FontFamily : IFontFamily { - public static FontFamily Default => new FontFamily("Courier New"); - /// /// Initializes a new instance of the class. /// @@ -47,56 +45,25 @@ namespace Avalonia.Media Key = new FontFamilyKey(source); } - /// - /// Gets the name of the font family. - /// - /// - /// The name of the font family. - /// + public static FontFamily Default => new FontFamily("Courier New"); + public string Name => FamilyNames.PrimaryFamilyName; + + FamilyNameCollection IFontFamily.FamilyNames => FamilyNames; - /// - /// Gets the family names. - /// - /// - /// The family familyNames. - /// - internal FamilyNameCollection FamilyNames - { - get; - } + FontFamilyKey IFontFamily.Key => Key; - /// - /// Gets the key for associated assets. - /// - /// - /// The family familyNames. - /// - internal FontFamilyKey Key { get; } + internal FamilyNameCollection FamilyNames { get; } - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - if (Key != null) - { - return Key + "#" + Name; - } - - return Name; - } + internal FontFamilyKey Key { get; } /// - /// Implicit conversion of FontFamily to string + /// Implicit conversion of string to FontFamily /// /// - public static implicit operator string(FontFamily fontFamily) + public static implicit operator FontFamily(string fontFamily) { - return fontFamily.ToString(); + return new FontFamily(fontFamily); } /// @@ -116,21 +83,74 @@ namespace Avalonia.Media switch (segments.Length) { case 1: - { - var names = segments[0].Split(',') - .Select(x => x.Trim()) - .Where(x => !string.IsNullOrWhiteSpace(x)); + { + var names = segments[0].Split(',') + .Select(x => x.Trim()) + .Where(x => !string.IsNullOrWhiteSpace(x)); return new FontFamily(names); } + case 2: { return new FontFamily(segments[1], new Uri(segments[0], UriKind.RelativeOrAbsolute)); } + default: { throw new ArgumentException("Specified family is not supported."); } } } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + if (Key != null) + { + return Key + "#" + FamilyNames; + } + + return FamilyNames.ToString(); + } + + /// + /// 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 + { + var hash = (int)2186146271; + + hash = (hash * 15768619) ^ FamilyNames.GetHashCode(); + + if (Key != null) + { + hash = (hash * 15768619) ^ Key.GetHashCode(); + } + + return hash; + } + } + + public override bool Equals(object obj) + { + if (!(obj is FontFamily other)) return false; + + if (Key != null) + { + return other.FamilyNames.Equals(FamilyNames) && other.Key.Equals(Key); + } + + return other.FamilyNames.Equals(FamilyNames); + } } } diff --git a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs b/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs index 839f2ce34b..50511d2fb7 100644 --- a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs +++ b/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs @@ -9,10 +9,10 @@ using System.Linq; namespace Avalonia.Media.Fonts { - internal class FamilyNameCollection : IEnumerable - { - private readonly ReadOnlyCollection _familyNames; + using System.Text; + public class FamilyNameCollection : IEnumerable + { /// /// Initializes a new instance of the class. /// @@ -26,11 +26,11 @@ namespace Avalonia.Media.Fonts if (names.Count == 0) throw new ArgumentException($"{nameof(familyNames)} must not be empty."); - _familyNames = new ReadOnlyCollection(names); + Names = new ReadOnlyCollection(names); - PrimaryFamilyName = _familyNames.First(); + PrimaryFamilyName = Names.First(); - HasFallbacks = _familyNames.Count > 1; + HasFallbacks = Names.Count > 1; } /// @@ -49,6 +49,14 @@ namespace Avalonia.Media.Fonts /// public bool HasFallbacks { get; } + /// + /// Gets the internal collection of names. + /// + /// + /// The names. + /// + internal ReadOnlyCollection Names { get; } + /// /// /// Returns an enumerator that iterates through the collection. @@ -58,7 +66,7 @@ namespace Avalonia.Media.Fonts /// public IEnumerator GetEnumerator() { - return _familyNames.GetEnumerator(); + return Names.GetEnumerator(); } /// @@ -72,5 +80,52 @@ namespace Avalonia.Media.Fonts { return GetEnumerator(); } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + var builder = new StringBuilder(); + + for (var index = 0; index < Names.Count; index++) + { + builder.Append(Names[index]); + + if (index == Names.Count - 1) break; + + builder.Append(", "); + } + + return builder.ToString(); + } + + /// + /// 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() + { + return ToString().GetHashCode(); + } + + /// + /// Determines whether the specified , is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (!(obj is FamilyNameCollection other)) return false; + + return other.ToString().Equals(ToString()); + } } } \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/Fonts/FontAsset.cs b/src/Avalonia.Visuals/Media/Fonts/FontAsset.cs deleted file mode 100644 index a1cff79371..0000000000 --- a/src/Avalonia.Visuals/Media/Fonts/FontAsset.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) The Avalonia Project. All rights reserved. -// Licensed under the MIT license. See licence.md file in the project root for full license information. - -using System; - -namespace Avalonia.Media.Fonts -{ - internal class FontAsset - { - /// - /// Initializes a new instance of the class. - /// - /// The source. - public FontAsset(Uri source) - { - Source = source; - } - - /// - /// Gets the source of the asset. - /// - /// - /// The source. - /// - public Uri Source { get; } - } -} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs b/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs index 852b39615a..c2005c9acb 100644 --- a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs +++ b/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs @@ -7,9 +7,9 @@ using System.Linq; namespace Avalonia.Media.Fonts { /// - /// Represents an idetifier for a + /// Represents an identifier for a /// - internal class FontFamilyKey + public class FontFamilyKey { /// /// Creates a new instance of and extracts and from given @@ -21,10 +21,10 @@ namespace Avalonia.Media.Fonts if (source.AbsolutePath.Contains(".ttf")) { - var filePathWithoutExtension = source.AbsolutePath.Replace(".ttf", ""); + var filePathWithoutExtension = source.AbsolutePath.Replace(".ttf", string.Empty); var fileNameWithoutExtension = filePathWithoutExtension.Split('.').Last(); FileName = fileNameWithoutExtension + ".ttf"; - Location = new Uri(source.OriginalString.Replace("." + FileName, ""), UriKind.RelativeOrAbsolute); + Location = new Uri(source.OriginalString.Replace("." + FileName, string.Empty), UriKind.RelativeOrAbsolute); } else { @@ -33,12 +33,12 @@ namespace Avalonia.Media.Fonts } /// - /// Location of stored that belong to a + /// Location of stored font asset that belongs to a /// public Uri Location { get; } /// - /// Optional filename for that belong to a + /// Optional filename for a font asset that belongs to a /// public string FileName { get; } diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs b/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs index ffe6c10acb..803d15d49f 100644 --- a/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs +++ b/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs @@ -9,7 +9,7 @@ using Avalonia.Platform; namespace Avalonia.Media.Fonts { - internal static class FontFamilyLoader + public static class FontFamilyLoader { private static readonly IAssetLoader s_assetLoader; @@ -18,7 +18,7 @@ namespace Avalonia.Media.Fonts s_assetLoader = AvaloniaLocator.Current.GetService(); } - public static IEnumerable LoadFontAssets(FontFamilyKey fontFamilyKey) + public static IEnumerable LoadFontAssets(FontFamilyKey fontFamilyKey) { return fontFamilyKey.FileName != null ? GetFontAssetsByFileName(fontFamilyKey.Location, fontFamilyKey.FileName) @@ -30,38 +30,33 @@ namespace Avalonia.Media.Fonts /// /// /// - private static IEnumerable GetFontAssetsByLocation(Uri location) + private static IEnumerable GetFontAssetsByLocation(Uri location) { var availableAssets = s_assetLoader.GetAssets(location); var locationPath = location.AbsolutePath; - var mathchingAssets = availableAssets.Where(x => x.AbsolutePath.Contains(locationPath) && x.AbsolutePath.EndsWith(".ttf")); + var matchingAssets = availableAssets.Where(x => x.absolutePath.Contains(locationPath) && x.absolutePath.EndsWith(".ttf")); - return mathchingAssets.Select(x => CreateFontAsset(GetAssetUri(x.AbsolutePath, x.Assembly))); + return matchingAssets.Select(x => GetAssetUri(x.absolutePath, x.assembly)); } /// /// 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" + /// File names can target multiple files with * wildcard. For example "FontFile*.ttf" /// /// /// /// - private static IEnumerable GetFontAssetsByFileName(Uri location, string fileName) + private static IEnumerable GetFontAssetsByFileName(Uri location, string fileName) { var availableResources = s_assetLoader.GetAssets(location); var compareTo = location.AbsolutePath + "." + fileName.Split('*').First(); - var matchingResources = availableResources.Where(x => x.AbsolutePath.Contains(compareTo)); + var matchingResources = availableResources.Where(x => x.absolutePath.Contains(compareTo)); - return matchingResources.Select(x => CreateFontAsset(GetAssetUri(x.AbsolutePath, x.Assembly))); - } - - private static FontAsset CreateFontAsset(Uri source) - { - return new FontAsset(source); + return matchingResources.Select(x => GetAssetUri(x.absolutePath, x.assembly)); } /// diff --git a/src/Avalonia.Visuals/Media/IFontFamily.cs b/src/Avalonia.Visuals/Media/IFontFamily.cs new file mode 100644 index 0000000000..b6a5201bf5 --- /dev/null +++ b/src/Avalonia.Visuals/Media/IFontFamily.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Avalonia.Media +{ + using Avalonia.Media.Fonts; + + public interface IFontFamily + { + /// + /// Gets the name of the font family. + /// + /// + /// The name of the font family. + /// + string Name { get; } + + /// + /// Gets the family names. + /// + /// + /// The family familyNames. + /// + FamilyNameCollection FamilyNames { get; } + + /// + /// Gets the key for associated assets. + /// + /// + /// The family familyNames. + /// + FontFamilyKey Key { get; } + } +} diff --git a/src/Avalonia.Visuals/Media/Typeface.cs b/src/Avalonia.Visuals/Media/Typeface.cs index ead15fe0f3..1c619f5f5c 100644 --- a/src/Avalonia.Visuals/Media/Typeface.cs +++ b/src/Avalonia.Visuals/Media/Typeface.cs @@ -14,8 +14,11 @@ namespace Avalonia.Media /// The font size, in DIPs. /// The font style. /// The font weight. - public Typeface(FontFamily fontFamily, double fontSize = 12, FontStyle style = FontStyle.Normal, - FontWeight weight = FontWeight.Normal) + public Typeface( + IFontFamily fontFamily, + double fontSize = 12, + FontStyle style = FontStyle.Normal, + FontWeight weight = FontWeight.Normal) { if (fontSize <= 0) { @@ -44,12 +47,15 @@ namespace Avalonia.Media string fontFamilyName, double fontSize = 12, FontStyle style = FontStyle.Normal, - FontWeight weight = FontWeight.Normal) : this(new FontFamily(fontFamilyName), fontSize, style, weight) { } + FontWeight weight = FontWeight.Normal) + : this(new FontFamily(fontFamilyName), fontSize, style, weight) + { + } /// /// Gets the font family. /// - public FontFamily FontFamily { get; } + public IFontFamily FontFamily { get; } /// /// Gets the size of the font in DIPs. diff --git a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs b/src/Avalonia.Visuals/Properties/AssemblyInfo.cs index a923407ca5..05c3d7e62a 100644 --- a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Visuals/Properties/AssemblyInfo.cs @@ -10,7 +10,5 @@ using Avalonia.Metadata; [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] -[assembly: InternalsVisibleTo("Avalonia.Direct2D1")] [assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests")] -[assembly: InternalsVisibleTo("Avalonia.Skia")] [assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests")] \ 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 a6321f1452..f3fac3faf7 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.Stream) + using (var stream = asset.stream) { try { - return Load(stream, asset.Assembly, 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 2e331daeda..e869cad588 100644 --- a/src/Shared/PlatformSupport/AssetLoader.cs +++ b/src/Shared/PlatformSupport/AssetLoader.cs @@ -67,7 +67,7 @@ namespace Avalonia.Shared.PlatformSupport /// /// The resource was not found. /// - public Stream Open(Uri uri, Uri baseUri = null) => OpenAndGetAssembly(uri, baseUri).Stream; + 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 @@ -83,7 +83,7 @@ namespace Avalonia.Shared.PlatformSupport /// /// The resource was not found. /// - public (Stream Stream, Assembly Assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null) + public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null) { var asset = GetAsset(uri, baseUri); @@ -95,7 +95,7 @@ namespace Avalonia.Shared.PlatformSupport return (asset.GetStream(), asset.Assembly); } - public IEnumerable<(string AbsolutePath, Assembly Assembly)> GetAssets(Uri location) + public IEnumerable<(string absolutePath, Assembly assembly)> GetAssets(Uri location) { var assembly = GetAssembly(location); diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs index 00e5cf62b3..261e6667c3 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs @@ -6,7 +6,39 @@ namespace Avalonia.Skia { internal class SKTypefaceCollection { - struct FontKey + + + private readonly ConcurrentDictionary _cachedTypefaces = + new ConcurrentDictionary(); + + public void AddTypeFace(SKTypeface typeface) + { + var key = new FontKey(typeface.FamilyName, (SKFontStyleWeight)typeface.FontWeight, typeface.FontSlant); + + _cachedTypefaces.TryAdd(key, typeface); + } + + public SKTypeface GetTypeFace(Typeface typeface) + { + SKFontStyleSlant skStyle = SKFontStyleSlant.Upright; + + switch (typeface.Style) + { + case FontStyle.Italic: + skStyle = SKFontStyleSlant.Italic; + break; + + case FontStyle.Oblique: + skStyle = SKFontStyleSlant.Oblique; + break; + } + + var key = new FontKey(typeface.FontFamily.Name, (SKFontStyleWeight)typeface.Weight, skStyle); + + return _cachedTypefaces.TryGetValue(key, out var skTypeface) ? skTypeface : TypefaceCache.Default; + } + + private struct FontKey { public readonly string Name; public readonly SKFontStyleSlant Slant; @@ -34,43 +66,11 @@ namespace Avalonia.Skia return other is FontKey ? Equals((FontKey)other) : false; } - public bool Equals(FontKey other) + private bool Equals(FontKey other) { return Name == other.Name && Slant == other.Slant && Weight == other.Weight; } - - // Equals and GetHashCode ommitted - } - - private readonly ConcurrentDictionary _cachedTypefaces = - new ConcurrentDictionary(); - - public void AddTypeFace(SKTypeface typeface) - { - var key = new FontKey(typeface.FamilyName, (SKFontStyleWeight)typeface.FontWeight, typeface.FontSlant); - - _cachedTypefaces.TryAdd(key, typeface); - } - - public SKTypeface GetTypeFace(Typeface typeface) - { - SKFontStyleSlant skStyle = SKFontStyleSlant.Upright; - - switch (typeface.Style) - { - case FontStyle.Italic: - skStyle = SKFontStyleSlant.Italic; - break; - - case FontStyle.Oblique: - skStyle = SKFontStyleSlant.Oblique; - break; - } - - var key = new FontKey(typeface.FontFamily.Name, (SKFontStyleWeight)typeface.Weight, skStyle); - - return _cachedTypefaces.TryGetValue(key, out var skTypeface) ? skTypeface : TypefaceCache.Default; } } } \ No newline at end of file diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs index 13cce3f3da..f8ee62f1f2 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs @@ -15,22 +15,32 @@ namespace Avalonia.Skia s_cachedCollections = new ConcurrentDictionary(); } - public static SKTypefaceCollection GetOrAddTypefaceCollection(FontFamily fontFamily) + /// + /// Gets the or add typeface collection. + /// + /// The font family. + /// + public static SKTypefaceCollection GetOrAddTypefaceCollection(IFontFamily fontFamily) { return s_cachedCollections.GetOrAdd(fontFamily.Key, x => CreateCustomFontCollection(fontFamily)); } - private static SKTypefaceCollection CreateCustomFontCollection(FontFamily fontFamily) + /// + /// Creates the custom font collection. + /// + /// The font family. + /// + private static SKTypefaceCollection CreateCustomFontCollection(IFontFamily fontFamily) { - var assets = FontFamilyLoader.LoadFontAssets(fontFamily.Key); + var fontAssets = FontFamilyLoader.LoadFontAssets(fontFamily.Key); var typeFaceCollection = new SKTypefaceCollection(); var assetLoader = AvaloniaLocator.Current.GetService(); - foreach (var fontAsset in assets) + foreach (var asset in fontAssets) { - var assetStream = assetLoader.Open(fontAsset.Source); + var assetStream = assetLoader.Open(asset); var typeface = SKTypeface.FromStream(assetStream); diff --git a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs index 37f283c162..1b1cb85495 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs @@ -40,7 +40,6 @@ namespace Avalonia.Direct2D1.Media _stream.Position = fileOffset; fragmentStart = _stream.PositionPointer; - } } diff --git a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs index ba8e48a02c..065da8d3e8 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs @@ -6,6 +6,8 @@ using SharpDX.DirectWrite; namespace Avalonia.Direct2D1.Media { + using System; + internal class DWriteResourceFontLoader : CallbackBase, FontCollectionLoader, FontFileLoader { private readonly List _fontStreams = new List(); @@ -17,15 +19,15 @@ namespace Avalonia.Direct2D1.Media /// /// The factory. /// - public DWriteResourceFontLoader(Factory factory, IEnumerable fontAssets) + public DWriteResourceFontLoader(Factory factory, IEnumerable fontAssets) { var factory1 = factory; var assetLoader = AvaloniaLocator.Current.GetService(); - foreach (var font in fontAssets) + foreach (var asset in fontAssets) { - var assetStream = assetLoader.Open(font.Source); + var assetStream = assetLoader.Open(asset); var dataStream = new DataStream((int)assetStream.Length, true, true); diff --git a/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs b/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs index 4e80cd3ec1..0c69d9932e 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs @@ -13,31 +13,24 @@ namespace Avalonia.Direct2D1.Media static Direct2D1FontCollectionCache() { s_cachedCollections = new ConcurrentDictionary(); + s_factory = AvaloniaLocator.Current.GetService(); + s_installedFontCollection = s_factory.GetSystemFontCollection(false); } - public static SharpDX.DirectWrite.FontCollection GetOrAddFontCollection(FontFamily fontFamily) + public static SharpDX.DirectWrite.FontCollection GetOrAddFontCollection(IFontFamily fontFamily) { return fontFamily.Key == null ? s_installedFontCollection : s_cachedCollections.GetOrAdd(fontFamily.Key, CreateFontCollection); } - private static SharpDX.DirectWrite.FontCollection CreateFontCollection(FontFamilyKey key) - { - var assets = FontFamilyLoader.LoadFontAssets(key); - - var fontLoader = new DWriteResourceFontLoader(s_factory, assets); - - return new SharpDX.DirectWrite.FontCollection(s_factory, fontLoader, fontLoader.Key); - } - public static SharpDX.DirectWrite.TextFormat GetTextFormat(Typeface typeface) { var fontFamily = typeface.FontFamily; var fontCollection = GetOrAddFontCollection(fontFamily); var fontFamilyName = FontFamily.Default.Name; - //Should this be cached? + // Should this be cached? foreach (var familyName in fontFamily.FamilyNames) { if (!fontCollection.FindFamilyName(familyName, out _)) continue; @@ -45,8 +38,23 @@ namespace Avalonia.Direct2D1.Media break; } - return new SharpDX.DirectWrite.TextFormat(s_factory, fontFamilyName, fontCollection, (SharpDX.DirectWrite.FontWeight)typeface.Weight, - (SharpDX.DirectWrite.FontStyle)typeface.Style, SharpDX.DirectWrite.FontStretch.Normal, (float)typeface.FontSize); + return new SharpDX.DirectWrite.TextFormat( + s_factory, + fontFamilyName, + fontCollection, + (SharpDX.DirectWrite.FontWeight)typeface.Weight, + (SharpDX.DirectWrite.FontStyle)typeface.Style, + SharpDX.DirectWrite.FontStretch.Normal, + (float)typeface.FontSize); + } + + private static SharpDX.DirectWrite.FontCollection CreateFontCollection(FontFamilyKey key) + { + var assets = FontFamilyLoader.LoadFontAssets(key); + + var fontLoader = new DWriteResourceFontLoader(s_factory, assets); + + return new SharpDX.DirectWrite.FontCollection(s_factory, fontLoader, fontLoader.Key); } } } \ No newline at end of file diff --git a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs index 61ed73d414..0f09a1827b 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs @@ -28,8 +28,12 @@ namespace Avalonia.Direct2D1.Media textFormat.WordWrapping = wrapping == TextWrapping.Wrap ? DWrite.WordWrapping.Wrap : DWrite.WordWrapping.NoWrap; - TextLayout = new DWrite.TextLayout(factory, Text ?? string.Empty, textFormat, (float)constraint.Width, - (float)constraint.Height) + TextLayout = new DWrite.TextLayout( + factory, + Text ?? string.Empty, + textFormat, + (float)constraint.Width, + (float)constraint.Height) { TextAlignment = textAlignment.ToDirect2D() }; diff --git a/tests/Avalonia.UnitTests/MockAssetLoader.cs b/tests/Avalonia.UnitTests/MockAssetLoader.cs index ca1135acbd..cebd88b6a0 100644 --- a/tests/Avalonia.UnitTests/MockAssetLoader.cs +++ b/tests/Avalonia.UnitTests/MockAssetLoader.cs @@ -27,12 +27,12 @@ namespace Avalonia.UnitTests return new MemoryStream(Encoding.UTF8.GetBytes(_assets[uri])); } - public (Stream Stream, Assembly Assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null) + public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null) { return (Open(uri, baseUri), (Assembly)null); } - public IEnumerable<(string AbsolutePath, Assembly Assembly)> GetAssets(Uri location) + public IEnumerable<(string absolutePath, Assembly assembly)> GetAssets(Uri location) { return _assets.Keys.Select(x => (x.AbsolutePath, Assembly.GetEntryAssembly())); } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs index c88a57ec9d..4c7a89cbda 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs @@ -25,11 +25,19 @@ namespace Avalonia.Visuals.UnitTests.Media } [Fact] - public void ShouldImplicitlyConvertToString() + public void Should_Implicitly_Convert_String_To_FontFamily() { - var fontFamily = new FontFamily("Arial"); + FontFamily fontFamily = "Arial"; - Assert.Equal("Arial", fontFamily); + Assert.Equal(new FontFamily("Arial"), fontFamily); + } + + [Fact] + public void Should_Be_Equal() + { + var fontFamily = new FontFamily("Arial"); + + Assert.Equal(new FontFamily("Arial"), fontFamily); } [Fact] diff --git a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs index fef86ac4fc..14daf5bd8b 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs @@ -21,5 +21,13 @@ namespace Avalonia.Visuals.UnitTests.Media.Fonts { Assert.Throws(() => new FamilyNameCollection(Enumerable.Empty())); } + + [Fact] + public void Should_Be_Equal() + { + var familyNames = new FamilyNameCollection(new[] { "Arial", "Times New Roman" }); + + Assert.Equal(new FamilyNameCollection(new[] { "Arial", "Times New Roman" }), familyNames); + } } }