diff --git a/src/Avalonia.Visuals/Media/FontFamily.cs b/src/Avalonia.Visuals/Media/FontFamily.cs
index 37faa1a068..5660156a1e 100644
--- a/src/Avalonia.Visuals/Media/FontFamily.cs
+++ b/src/Avalonia.Visuals/Media/FontFamily.cs
@@ -3,57 +3,73 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Avalonia.Media.Fonts;
namespace Avalonia.Media
{
public class FontFamily
{
+ internal static FontFamily Default = new FontFamily("Courier New");
+
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- /// The name.
- /// name
- public FontFamily(string name = "Courier New")
+ /// The name of the .
+ /// name
+ public FontFamily(string name)
{
- if (name == null) throw new ArgumentNullException(nameof(name));
- FamilyNames = new FamilyNameList(name);
+ if (name == null) throw new ArgumentNullException();
+ FamilyNames = new FamilyNameCollection(new[] { name });
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The names of the .
+ /// name
public FontFamily(IEnumerable names)
{
- if (names == null) throw new ArgumentNullException(nameof(names));
- FamilyNames = new FamilyNameList(names);
+ if (names == null) throw new ArgumentNullException();
+ FamilyNames = new FamilyNameCollection(names);
}
- ///
///
/// Initializes a new instance of the class.
///
- /// The name.
- /// The source.
+ /// The name of the .
+ /// The source of font resources.
public FontFamily(string name, Uri source) : this(name)
{
Key = new FontFamilyKey(source);
}
///
- /// Gets the name.
+ /// Gets the name of the font family.
///
///
- /// The name.
+ /// The name of the font family.
///
- public string Name => FamilyNames.FirstFamilyName;
+ public string Name => FamilyNames.PrimaryFamilyName;
///
- /// Gets the key.
+ /// Gets the family names.
///
///
- /// The key.
+ /// The family familyNames.
///
- internal FontFamilyKey Key { get; }
+ internal FamilyNameCollection FamilyNames
+ {
+ get;
+ }
- internal FamilyNameList FamilyNames { get; }
+ ///
+ /// Gets the key for associated resources.
+ ///
+ ///
+ /// The family familyNames.
+ ///
+ internal FontFamilyKey Key { get; }
///
/// Returns a that represents this instance.
@@ -63,34 +79,13 @@ namespace Avalonia.Media
///
public override string ToString()
{
- if (Key != null)
- {
- return Key + "#" + Name;
- }
-
return Name;
}
- internal class FamilyNameList : List
- {
- public FamilyNameList(string familyName)
- {
- Add(familyName);
- FirstFamilyName = familyName;
- }
-
- public FamilyNameList(IEnumerable familyNames) : base(familyNames)
- {
- FirstFamilyName = this[0];
- }
-
- public string FirstFamilyName { get; }
- }
-
///
- /// Parses a string.
+ /// Parses a string.
///
- /// The string.
+ /// The string.
///
///
/// Specified family is not supported.
@@ -99,19 +94,20 @@ namespace Avalonia.Media
{
if (string.IsNullOrEmpty(s)) throw new ArgumentException("Specified family is not supported.");
- var fontFamilyExpression = s.Split('#');
+ var segments = s.Split('#');
- switch (fontFamilyExpression.Length)
+ switch (segments.Length)
{
case 1:
- {
- var familyNames = fontFamilyExpression[0].Split(';');
- return new FontFamily(familyNames);
+ {
+ var names = segments[0].Split(',')
+ .Select(x => x.Trim())
+ .Where(x => !string.IsNullOrWhiteSpace(x));
+ return new FontFamily(names);
}
case 2:
{
- var source = new Uri(fontFamilyExpression[0], UriKind.RelativeOrAbsolute);
- return new FontFamily(fontFamilyExpression[1], source);
+ return new FontFamily(segments[1], new Uri(segments[0], UriKind.RelativeOrAbsolute));
}
default:
{
diff --git a/src/Avalonia.Visuals/Media/Fonts/CachedFontFamily.cs b/src/Avalonia.Visuals/Media/Fonts/CachedFontFamily.cs
deleted file mode 100644
index dc0cb8b305..0000000000
--- a/src/Avalonia.Visuals/Media/Fonts/CachedFontFamily.cs
+++ /dev/null
@@ -1,42 +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.Collections.Generic;
-
-namespace Avalonia.Media.Fonts
-{
- ///
- /// Holds a quantity of that belongs to a specific
- ///
- internal class CachedFontFamily
- {
- private readonly FontResourceCollection _fontResourceCollection;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The key.
- /// The font resource collection.
- public CachedFontFamily(FontFamilyKey key, FontResourceCollection fontResourceCollection)
- {
- Key = key;
- _fontResourceCollection = fontResourceCollection;
- }
-
- ///
- /// Gets the key.
- ///
- ///
- /// The key.
- ///
- public FontFamilyKey Key { get; }
-
- ///
- /// Gets the font resources.
- ///
- ///
- /// The font resources.
- ///
- public IEnumerable FontResources => _fontResourceCollection.FontResources;
- }
-}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs b/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs
new file mode 100644
index 0000000000..6296b947cc
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs
@@ -0,0 +1,52 @@
+// 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;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+
+namespace Avalonia.Media.Fonts
+{
+ internal class FamilyNameCollection : IEnumerable
+ {
+ private readonly ReadOnlyCollection _familyNames;
+
+ public FamilyNameCollection(IEnumerable familyNames)
+ {
+ if (familyNames == null) throw new ArgumentNullException(nameof(familyNames));
+ 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;
+ }
+
+ ///
+ /// Gets the primary family name.
+ ///
+ ///
+ /// The primary family name.
+ ///
+ public string PrimaryFamilyName { get; }
+
+ ///
+ /// Gets a value indicating whether fallbacks are defined.
+ ///
+ ///
+ /// true if fallbacks are defined; otherwise, false.
+ ///
+ public bool HasFallbacks { get; }
+
+ public IEnumerator GetEnumerator()
+ {
+ return _familyNames.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyCache.cs b/src/Avalonia.Visuals/Media/Fonts/FontFamilyCache.cs
deleted file mode 100644
index 4ce140b7e9..0000000000
--- a/src/Avalonia.Visuals/Media/Fonts/FontFamilyCache.cs
+++ /dev/null
@@ -1,35 +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.Collections.Concurrent;
-
-namespace Avalonia.Media.Fonts
-{
- ///
- /// Caches all instances to reduce memory usage and speed up loading times of custom font families
- ///
- internal static class FontFamilyCache
- {
- private static readonly ConcurrentDictionary s_cachedFontFamilies = new ConcurrentDictionary();
-
- ///
- /// Gets the or add cached font family.
- ///
- /// The key.
- ///
- public static CachedFontFamily GetOrAddFontFamily(FontFamilyKey key)
- {
- return s_cachedFontFamilies.GetOrAdd(key, CreateCachedFontFamily);
- }
-
- ///
- /// Creates the cached font family.
- ///
- /// The font family key.
- ///
- private static CachedFontFamily CreateCachedFontFamily(FontFamilyKey fontFamilyKey)
- {
- return new CachedFontFamily(fontFamilyKey, new FontResourceCollection(fontFamilyKey));
- }
- }
-}
\ 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 a6115b30e1..d0e3207c70 100644
--- a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs
+++ b/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs
@@ -7,7 +7,7 @@ using System.Linq;
namespace Avalonia.Media.Fonts
{
///
- /// Unique idetifier for a quantity of that is stored at a given location.
+ /// Represents an idetifier for a
///
internal class FontFamilyKey
{
@@ -16,7 +16,7 @@ namespace Avalonia.Media.Fonts
///
///
public FontFamilyKey(Uri source)
- {
+ {
if (source.AbsolutePath.Contains(".ttf"))
{
var filePathWithoutExtension = source.AbsolutePath.Replace(".ttf", "");
diff --git a/src/Avalonia.Visuals/Media/Fonts/FontResourceLoader.cs b/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs
similarity index 76%
rename from src/Avalonia.Visuals/Media/Fonts/FontResourceLoader.cs
rename to src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs
index df7571ed3b..d972c7571a 100644
--- a/src/Avalonia.Visuals/Media/Fonts/FontResourceLoader.cs
+++ b/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs
@@ -9,40 +9,19 @@ using Avalonia.Platform;
namespace Avalonia.Media.Fonts
{
- ///
- ///
- /// Implementation of
- ///
- internal class FontResourceLoader : IFontResourceLoader
+ internal static class FontFamilyLoader
{
private static readonly Dictionary s_assemblyNameCache
= new Dictionary();
- private readonly AssemblyDescriptor _defaultAssembly;
+ private static readonly AssemblyDescriptor s_defaultAssembly;
- ///
- /// Initializes a new instance of the class.
- ///
- /// The default assembly.
- public FontResourceLoader(Assembly assembly = null)
+ static FontFamilyLoader()
{
- if (assembly == null)
- {
- assembly = Assembly.GetEntryAssembly();
- }
- if (assembly != null)
- {
- _defaultAssembly = new AssemblyDescriptor(assembly);
- }
+ s_defaultAssembly = new AssemblyDescriptor(Assembly.GetEntryAssembly());
}
- ///
- ///
- /// Returns a quanity of that belongs to a given
- ///
- ///
- ///
- public IEnumerable GetFontResources(FontFamilyKey fontFamilyKey)
+ public static IEnumerable GetFontResources(FontFamilyKey fontFamilyKey)
{
return fontFamilyKey.FileName != null
? GetFontResourcesByFileName(fontFamilyKey.Location, fontFamilyKey.FileName)
@@ -54,7 +33,7 @@ namespace Avalonia.Media.Fonts
///
///
///
- private IEnumerable GetFontResourcesByLocation(Uri location)
+ private static IEnumerable GetFontResourcesByLocation(Uri location)
{
var assembly = GetAssembly(location);
@@ -64,7 +43,7 @@ namespace Avalonia.Media.Fonts
var matchingResources = assembly.Resources.Where(x => x.Contains(locationPath));
- return matchingResources.Select(x => new FontResource(GetResourceUri(x, assembly.Name)));
+ return matchingResources.Select(x => CreateResource(GetResourceUri(x, assembly.Name)));
}
///
@@ -74,7 +53,7 @@ namespace Avalonia.Media.Fonts
///
///
///
- private IEnumerable GetFontResourcesByFileName(Uri location, string fileName)
+ private static IEnumerable GetFontResourcesByFileName(Uri location, string fileName)
{
var assembly = GetAssembly(location);
@@ -84,7 +63,12 @@ namespace Avalonia.Media.Fonts
var matchingResources = assembly.Resources.Where(x => x.Contains(compareTo));
- return matchingResources.Select(x => new FontResource(GetResourceUri(x, assembly.Name)));
+ return matchingResources.Select(x => CreateResource(GetResourceUri(x, assembly.Name)));
+ }
+
+ private static FontResource CreateResource(Uri source)
+ {
+ return new FontResource(source);
}
///
@@ -103,13 +87,13 @@ namespace Avalonia.Media.Fonts
///
///
///
- private AssemblyDescriptor GetAssembly(Uri uri)
+ private static AssemblyDescriptor GetAssembly(Uri uri)
{
if (uri == null) return null;
var parameters = ParseParameters(uri);
- return parameters.TryGetValue("assembly", out var assemblyName) ? GetAssembly(assemblyName) : _defaultAssembly;
+ return parameters.TryGetValue("assembly", out var assemblyName) ? GetAssembly(assemblyName) : s_defaultAssembly;
}
///
@@ -120,11 +104,11 @@ namespace Avalonia.Media.Fonts
///
///
///
- private AssemblyDescriptor GetAssembly(string name)
+ private static AssemblyDescriptor GetAssembly(string name)
{
if (name == null)
{
- return _defaultAssembly;
+ return s_defaultAssembly;
}
if (!s_assemblyNameCache.TryGetValue(name, out var rv))
diff --git a/src/Avalonia.Visuals/Media/Fonts/FontResource.cs b/src/Avalonia.Visuals/Media/Fonts/FontResource.cs
index 50df3efefd..84e2153b42 100644
--- a/src/Avalonia.Visuals/Media/Fonts/FontResource.cs
+++ b/src/Avalonia.Visuals/Media/Fonts/FontResource.cs
@@ -5,19 +5,23 @@ using System;
namespace Avalonia.Media.Fonts
{
- ///
- /// Represents a font resource
- ///
internal class FontResource
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The source.
public FontResource(Uri source)
{
Source = source;
}
///
- /// Source of the font resource.
+ /// Gets the source.
///
+ ///
+ /// The source.
+ ///
public Uri Source { get; }
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/Fonts/FontResourceCollection.cs b/src/Avalonia.Visuals/Media/Fonts/FontResourceCollection.cs
deleted file mode 100644
index 427ba25277..0000000000
--- a/src/Avalonia.Visuals/Media/Fonts/FontResourceCollection.cs
+++ /dev/null
@@ -1,59 +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;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Avalonia.Media.Fonts
-{
- ///
- /// Represents a collection of that is identified by a unique
- ///
- internal class FontResourceCollection
- {
- private Dictionary _fontResources;
- private readonly IFontResourceLoader _fontResourceLoader = new FontResourceLoader();
-
- public FontResourceCollection(FontFamilyKey key)
- {
- Key = key;
- }
-
- ///
- /// Gets the key.
- ///
- ///
- /// The key.
- ///
- public FontFamilyKey Key { get; }
-
- ///
- /// Gets the font resources.
- ///
- ///
- /// The font resources.
- ///
- public IEnumerable FontResources
- {
- get
- {
- if (_fontResources == null)
- {
- _fontResources = CreateFontResources();
- }
-
- return _fontResources.Values;
- }
- }
-
- ///
- /// Creates the font resources.
- ///
- ///
- private Dictionary CreateFontResources()
- {
- return _fontResourceLoader.GetFontResources(Key).ToDictionary(x => x.Source);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/Fonts/IFontResourceLoader.cs b/src/Avalonia.Visuals/Media/Fonts/IFontResourceLoader.cs
deleted file mode 100644
index 16d512f743..0000000000
--- a/src/Avalonia.Visuals/Media/Fonts/IFontResourceLoader.cs
+++ /dev/null
@@ -1,20 +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.Collections.Generic;
-
-namespace Avalonia.Media.Fonts
-{
- ///
- /// Loads that can be identified by a given
- ///
- internal interface IFontResourceLoader
- {
- ///
- /// Returns a quanity of that belongs to a given
- ///
- ///
- ///
- IEnumerable GetFontResources(FontFamilyKey fontFamilyKey);
- }
-}
\ No newline at end of file
diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
index ec00eb4148..00f0a48a7b 100644
--- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
+++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
@@ -25,7 +25,7 @@ namespace Avalonia.Skia
// Replace 0 characters with zero-width spaces (200B)
Text = Text.Replace((char)0, (char)0x200B);
- SKTypeface skiaTypeface;
+ SKTypeface skiaTypeface = TypefaceCache.Default;
if (typeface.FontFamily.Key != null)
{
@@ -34,11 +34,25 @@ namespace Avalonia.Skia
}
else
{
- skiaTypeface = TypefaceCache.GetTypeface(
- typeface.FontFamily,
- typeface.Style,
- typeface.Weight);
- }
+ if (typeface.FontFamily.FamilyNames.HasFallbacks)
+ {
+ foreach (var familyName in typeface.FontFamily.FamilyNames)
+ {
+ skiaTypeface = TypefaceCache.GetTypeface(
+ familyName,
+ typeface.Style,
+ typeface.Weight);
+ if (skiaTypeface != TypefaceCache.Default) break;
+ }
+ }
+ else
+ {
+ skiaTypeface = TypefaceCache.GetTypeface(
+ typeface.FontFamily.Name,
+ typeface.Style,
+ typeface.Weight);
+ }
+ }
_paint = new SKPaint();
@@ -46,7 +60,7 @@ namespace Avalonia.Skia
//Paint.TextEncoding = SKTextEncoding.Utf8;
_paint.TextEncoding = SKTextEncoding.Utf16;
_paint.IsStroke = false;
- _paint.IsAntialias = true;
+ _paint.IsAntialias = true;
_paint.LcdRenderText = true;
_paint.SubpixelText = true;
_paint.Typeface = skiaTypeface;
@@ -256,7 +270,7 @@ namespace Avalonia.Skia
subStr = Text.Substring(i, len);
ApplyWrapperTo(ref currentPaint, currentWrapper, ref currd, paint, canUseLcdRendering);
-
+
canvas.DrawText(subStr, currX, origin.Y + line.Top + _lineOffset, paint);
i += len;
diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
new file mode 100644
index 0000000000..00e5cf62b3
--- /dev/null
+++ b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
@@ -0,0 +1,76 @@
+using System.Collections.Concurrent;
+using Avalonia.Media;
+using SkiaSharp;
+
+namespace Avalonia.Skia
+{
+ internal class SKTypefaceCollection
+ {
+ struct FontKey
+ {
+ public readonly string Name;
+ public readonly SKFontStyleSlant Slant;
+ public readonly SKFontStyleWeight Weight;
+
+ public FontKey(string name, SKFontStyleWeight weight, SKFontStyleSlant slant)
+ {
+ Name = name;
+ Slant = slant;
+ Weight = weight;
+ }
+
+ public override int GetHashCode()
+ {
+ int hash = 17;
+ hash = hash * 31 + Name.GetHashCode();
+ hash = hash * 31 + (int)Slant;
+ hash = hash * 31 + (int)Weight;
+
+ return hash;
+ }
+
+ public override bool Equals(object other)
+ {
+ return other is FontKey ? Equals((FontKey)other) : false;
+ }
+
+ public 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
new file mode 100644
index 0000000000..fddca39f3f
--- /dev/null
+++ b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
@@ -0,0 +1,43 @@
+using System.Collections.Concurrent;
+using Avalonia.Media;
+using Avalonia.Media.Fonts;
+using Avalonia.Platform;
+using SkiaSharp;
+
+namespace Avalonia.Skia
+{
+ internal static class SKTypefaceCollectionCache
+ {
+ private static readonly ConcurrentDictionary s_cachedCollections;
+
+ static SKTypefaceCollectionCache()
+ {
+ s_cachedCollections = new ConcurrentDictionary();
+ }
+
+ public static SKTypefaceCollection GetOrAddTypefaceCollection(FontFamily fontFamily)
+ {
+ return s_cachedCollections.GetOrAdd(fontFamily.Key, x => CreateCustomFontCollection(fontFamily));
+ }
+
+ private static SKTypefaceCollection CreateCustomFontCollection(FontFamily fontFamily)
+ {
+ var resources = FontFamilyLoader.GetFontResources(fontFamily.Key);
+
+ var typeFaceCollection = new SKTypefaceCollection();
+
+ var assetLoader = AvaloniaLocator.Current.GetService();
+
+ foreach (var fontResource in resources)
+ {
+ var stream = assetLoader.Open(fontResource.Source);
+
+ var typeface = SKTypeface.FromStream(stream);
+
+ typeFaceCollection.AddTypeFace(typeface);
+ }
+
+ return typeFaceCollection;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Skia/Avalonia.Skia/TypefaceCache.cs b/src/Skia/Avalonia.Skia/TypefaceCache.cs
index c65336320c..24674f3b22 100644
--- a/src/Skia/Avalonia.Skia/TypefaceCache.cs
+++ b/src/Skia/Avalonia.Skia/TypefaceCache.cs
@@ -1,15 +1,13 @@
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Media;
-using Avalonia.Media.Fonts;
-using Avalonia.Platform;
using SkiaSharp;
namespace Avalonia.Skia
{
static class TypefaceCache
{
+ public static SKTypeface Default = SKTypeface.FromFamilyName(FontFamily.Default.Name);
static readonly Dictionary> Cache = new Dictionary>();
struct FontKey
@@ -46,9 +44,9 @@ namespace Avalonia.Skia
// Equals and GetHashCode ommitted
}
- static SKTypeface GetTypeface(FontFamily fontFamily, FontKey key)
+ private static SKTypeface GetTypeface(string name, FontKey key)
{
- var familyKey = fontFamily.Name;
+ var familyKey = name;
if (!Cache.TryGetValue(familyKey, out var entry))
{
@@ -59,9 +57,9 @@ namespace Avalonia.Skia
{
typeface = SKTypeface.FromFamilyName(familyKey, key.Weight, SKFontStyleWidth.Normal, key.Slant);
- if (typeface == null)
+ if (typeface.FamilyName != name)
{
- typeface = SKTypeface.FromFamilyName(null);
+ typeface = Default;
}
entry[key] = typeface;
@@ -70,7 +68,7 @@ namespace Avalonia.Skia
return typeface;
}
- public static SKTypeface GetTypeface(FontFamily fontFamily, FontStyle style, FontWeight weight)
+ public static SKTypeface GetTypeface(string name, FontStyle style, FontWeight weight)
{
SKFontStyleSlant skStyle = SKFontStyleSlant.Upright;
@@ -85,113 +83,8 @@ namespace Avalonia.Skia
break;
}
- return GetTypeface(fontFamily, new FontKey((SKFontStyleWeight)weight, skStyle));
+ return GetTypeface(name, new FontKey((SKFontStyleWeight)weight, skStyle));
}
}
-
- internal class SKTypefaceCollection
- {
- private static readonly SKTypeface s_defaultTypeface = SKTypeface.FromFamilyName(null);
-
- struct FontKey
- {
- public readonly string Name;
- public readonly SKFontStyleSlant Slant;
- public readonly SKFontStyleWeight Weight;
-
- public FontKey(string name, SKFontStyleWeight weight, SKFontStyleSlant slant)
- {
- Name = name;
- Slant = slant;
- Weight = weight;
- }
-
- public override int GetHashCode()
- {
- int hash = 17;
- hash = hash * 31 + Name.GetHashCode();
- hash = hash * 31 + (int)Slant;
- hash = hash * 31 + (int)Weight;
-
- return hash;
- }
-
- public override bool Equals(object other)
- {
- return other is FontKey ? Equals((FontKey)other) : false;
- }
-
- public 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(FontFamily fontFamily, SKTypeface typeface)
- {
- var key = new FontKey(fontFamily.Name, (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 : s_defaultTypeface;
- }
- }
-
- internal static class SKTypefaceCollectionCache
- {
- private static readonly ConcurrentDictionary s_cachedCollections =
- new ConcurrentDictionary();
-
- public static SKTypefaceCollection GetOrAddTypefaceCollection(FontFamily fontFamily)
- {
- return s_cachedCollections.GetOrAdd(fontFamily.Key, x => CreateCustomFontCollection(fontFamily));
- }
-
- private static SKTypefaceCollection CreateCustomFontCollection(FontFamily fontFamily)
- {
- var cachedFontFamily = FontFamilyCache.GetOrAddFontFamily(fontFamily.Key);
-
- var typeFaceCollection = new SKTypefaceCollection();
-
- if (!cachedFontFamily.FontResources.Any()) return typeFaceCollection;
-
- var assetLoader = AvaloniaLocator.Current.GetService();
-
- foreach (var fontResource in cachedFontFamily.FontResources)
- {
- var stream = assetLoader.Open(fontResource.Source);
-
- var typeface = SKTypeface.FromStream(stream);
-
- typeFaceCollection.AddTypeFace(fontFamily, typeface);
- }
-
- return typeFaceCollection;
- }
- }
}
\ No newline at end of file
diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
index 296edcb2d9..0c3b01bbf1 100644
--- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
+++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
@@ -117,7 +117,7 @@ namespace Avalonia.Direct2D1
}
public IFormattedTextImpl CreateFormattedText(
- string text,
+ string text,
Typeface typeface,
TextAlignment textAlignment,
TextWrapping wrapping,
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileEnumerator.cs b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileEnumerator.cs
new file mode 100644
index 0000000000..c144e12aea
--- /dev/null
+++ b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileEnumerator.cs
@@ -0,0 +1,67 @@
+using SharpDX;
+using SharpDX.DirectWrite;
+
+namespace Avalonia.Direct2D1.Media
+{
+ ///
+ /// Resource FontFileEnumerator.
+ ///
+ public class DWriteResourceFontFileEnumerator : CallbackBase, FontFileEnumerator
+ {
+ private readonly Factory _factory;
+ private readonly FontFileLoader _loader;
+ private readonly DataStream _keyStream;
+ private FontFile _currentFontFile;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The factory.
+ /// The loader.
+ /// The key.
+ public DWriteResourceFontFileEnumerator(Factory factory, FontFileLoader loader, DataPointer key)
+ {
+ _factory = factory;
+ _loader = loader;
+ _keyStream = new DataStream(key.Pointer, key.Size, true, false);
+ }
+
+ ///
+ /// Advances to the next font file in the collection. When it is first created, the enumerator is positioned before the first element of the collection and the first call to MoveNext advances to the first file.
+ ///
+ ///
+ /// the value TRUE if the enumerator advances to a file; otherwise, FALSE if the enumerator advances past the last file in the collection.
+ ///
+ /// HRESULT IDWriteFontFileEnumerator::MoveNext([Out] BOOL* hasCurrentFile)
+ bool FontFileEnumerator.MoveNext()
+ {
+ bool moveNext = _keyStream.RemainingLength != 0;
+
+ if (!moveNext) return false;
+
+ _currentFontFile?.Dispose();
+
+ _currentFontFile = new FontFile(_factory, _keyStream.PositionPointer, 4, _loader);
+
+ _keyStream.Position += 4;
+
+ return true;
+ }
+
+ ///
+ /// Gets a reference to the current font file.
+ ///
+ ///
+ /// a reference to the newly created object.
+ /// HRESULT IDWriteFontFileEnumerator::GetCurrentFontFile([Out] IDWriteFontFile** fontFile)
+ FontFile FontFileEnumerator.CurrentFontFile
+ {
+ get
+ {
+ ((IUnknown)_currentFontFile).AddReference();
+
+ return _currentFontFile;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs
new file mode 100644
index 0000000000..37f283c162
--- /dev/null
+++ b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs
@@ -0,0 +1,85 @@
+using System;
+using SharpDX;
+using SharpDX.DirectWrite;
+
+namespace Avalonia.Direct2D1.Media
+{
+ ///
+ /// This FontFileStream implem is reading data from a .
+ ///
+ public class DWriteResourceFontFileStream : CallbackBase, FontFileStream
+ {
+ private readonly DataStream _stream;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The stream.
+ public DWriteResourceFontFileStream(DataStream stream)
+ {
+ _stream = stream;
+ }
+
+ ///
+ /// Reads a fragment from a font file.
+ ///
+ /// When this method returns, contains an address of a reference to the start of the font file fragment. This parameter is passed uninitialized.
+ /// The offset of the fragment, in bytes, from the beginning of the font file.
+ /// The size of the file fragment, in bytes.
+ /// When this method returns, contains the address of
+ ///
+ /// Note that ReadFileFragment implementations must check whether the requested font file fragment is within the file bounds. Otherwise, an error should be returned from ReadFileFragment. {{DirectWrite}} may invoke methods on the same object from multiple threads simultaneously. Therefore, ReadFileFragment implementations that rely on internal mutable state must serialize access to such state across multiple threads. For example, an implementation that uses separate Seek and Read operations to read a file fragment must place the code block containing Seek and Read calls under a lock or a critical section.
+ ///
+ /// HRESULT IDWriteFontFileStream::ReadFileFragment([Out, Buffer] const void** fragmentStart,[None] __int64 fileOffset,[None] __int64 fragmentSize,[Out] void** fragmentContext)
+ void FontFileStream.ReadFileFragment(out IntPtr fragmentStart, long fileOffset, long fragmentSize, out IntPtr fragmentContext)
+ {
+ lock (this)
+ {
+ fragmentContext = IntPtr.Zero;
+
+ _stream.Position = fileOffset;
+
+ fragmentStart = _stream.PositionPointer;
+
+ }
+ }
+
+ ///
+ /// Releases a fragment from a file.
+ ///
+ /// A reference to the client-defined context of a font fragment returned from {{ReadFileFragment}}.
+ /// void IDWriteFontFileStream::ReleaseFileFragment([None] void* fragmentContext)
+ void FontFileStream.ReleaseFileFragment(IntPtr fragmentContext)
+ {
+ // Nothing to release. No context are used
+ }
+
+ ///
+ /// Obtains the total size of a file.
+ ///
+ /// the total size of the file.
+ ///
+ /// Implementing GetFileSize() for asynchronously loaded font files may require downloading the complete file contents. Therefore, this method should be used only for operations that either require a complete font file to be loaded (for example, copying a font file) or that need to make decisions based on the value of the file size (for example, validation against a persisted file size).
+ ///
+ /// HRESULT IDWriteFontFileStream::GetFileSize([Out] __int64* fileSize)
+ long FontFileStream.GetFileSize()
+ {
+ return _stream.Length;
+ }
+
+ ///
+ /// Obtains the last modified time of the file.
+ ///
+ ///
+ /// the last modified time of the file in the format that represents the number of 100-nanosecond intervals since January 1, 1601 (UTC).
+ ///
+ ///
+ /// The "last modified time" is used by DirectWrite font selection algorithms to determine whether one font resource is more up to date than another one.
+ ///
+ /// HRESULT IDWriteFontFileStream::GetLastWriteTime([Out] __int64* lastWriteTime)
+ long FontFileStream.GetLastWriteTime()
+ {
+ return 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs
new file mode 100644
index 0000000000..da2ec8c0a6
--- /dev/null
+++ b/src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontLoader.cs
@@ -0,0 +1,97 @@
+using System.Collections.Generic;
+using Avalonia.Media.Fonts;
+using Avalonia.Platform;
+using SharpDX;
+using SharpDX.DirectWrite;
+
+namespace Avalonia.Direct2D1.Media
+{
+ internal class DWriteResourceFontLoader : CallbackBase, FontCollectionLoader, FontFileLoader
+ {
+ private readonly List _fontStreams = new List();
+ private readonly List _enumerators = new List();
+ private readonly DataStream _keyStream;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The factory.
+ ///
+ public DWriteResourceFontLoader(Factory factory, IEnumerable fontResources)
+ {
+ var factory1 = factory;
+
+ var assetLoader = AvaloniaLocator.Current.GetService();
+
+ foreach (var font in fontResources)
+ {
+ var resourceStream = assetLoader.Open(font.Source);
+
+ var dataStream = new DataStream((int)resourceStream.Length, true, true);
+
+ resourceStream.CopyTo(dataStream);
+
+ dataStream.Position = 0;
+
+ _fontStreams.Add(new DWriteResourceFontFileStream(dataStream));
+ }
+
+ // Build a Key storage that stores the index of the font
+ _keyStream = new DataStream(sizeof(int) * _fontStreams.Count, true, true);
+
+ for (int i = 0; i < _fontStreams.Count; i++)
+ {
+ _keyStream.Write(i);
+ }
+
+ _keyStream.Position = 0;
+
+ // Register the
+ factory1.RegisterFontFileLoader(this);
+ factory1.RegisterFontCollectionLoader(this);
+ }
+
+
+ ///
+ /// Gets the key used to identify the FontCollection as well as storing index for fonts.
+ ///
+ /// The key.
+ public DataStream Key => _keyStream;
+
+ ///
+ /// Creates a font file enumerator object that encapsulates a collection of font files. The font system calls back to this interface to create a font collection.
+ ///
+ /// Pointer to the object that was used to create the current font collection.
+ /// A font collection key that uniquely identifies the collection of font files within the scope of the font collection loader being used. The buffer allocated for this key must be at least the size, in bytes, specified by collectionKeySize.
+ ///
+ /// a reference to the newly created font file enumerator.
+ ///
+ /// HRESULT IDWriteFontCollectionLoader::CreateEnumeratorFromKey([None] IDWriteFactory* factory,[In, Buffer] const void* collectionKey,[None] int collectionKeySize,[Out] IDWriteFontFileEnumerator** fontFileEnumerator)
+ FontFileEnumerator FontCollectionLoader.CreateEnumeratorFromKey(Factory factory, DataPointer collectionKey)
+ {
+ var enumerator = new DWriteResourceFontFileEnumerator(factory, this, collectionKey);
+
+ _enumerators.Add(enumerator);
+
+ return enumerator;
+ }
+
+ ///
+ /// Creates a font file stream object that encapsulates an open file resource.
+ ///
+ /// A reference to a font file reference key that uniquely identifies the font file resource within the scope of the font loader being used. The buffer allocated for this key must at least be the size, in bytes, specified by fontFileReferenceKeySize.
+ ///
+ /// a reference to the newly created object.
+ ///
+ ///
+ /// The resource is closed when the last reference to fontFileStream is released.
+ ///
+ /// HRESULT IDWriteFontFileLoader::CreateStreamFromKey([In, Buffer] const void* fontFileReferenceKey,[None] int fontFileReferenceKeySize,[Out] IDWriteFontFileStream** fontFileStream)
+ FontFileStream FontFileLoader.CreateStreamFromKey(DataPointer fontFileReferenceKey)
+ {
+ var index = SharpDX.Utilities.Read(fontFileReferenceKey.Pointer);
+
+ return _fontStreams[index];
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs b/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs
new file mode 100644
index 0000000000..bbb438b204
--- /dev/null
+++ b/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs
@@ -0,0 +1,52 @@
+using System.Collections.Concurrent;
+using Avalonia.Media;
+using Avalonia.Media.Fonts;
+
+namespace Avalonia.Direct2D1.Media
+{
+ internal static class Direct2D1FontCollectionCache
+ {
+ private static readonly ConcurrentDictionary s_cachedCollections;
+ private static readonly SharpDX.DirectWrite.Factory s_factory;
+ private static readonly SharpDX.DirectWrite.FontCollection s_installedFontCollection;
+
+ 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)
+ {
+ return fontFamily.Key == null ? s_installedFontCollection : s_cachedCollections.GetOrAdd(fontFamily.Key, CreateFontCollection);
+ }
+
+ private static SharpDX.DirectWrite.FontCollection CreateFontCollection(FontFamilyKey key)
+ {
+ var resources = FontFamilyLoader.GetFontResources(key);
+
+ var fontLoader = new DWriteResourceFontLoader(s_factory, resources);
+
+ 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?
+ foreach (var familyName in fontFamily.FamilyNames)
+ {
+ if (!fontCollection.FindFamilyName(familyName, out _)) continue;
+ fontFamilyName = familyName;
+ 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);
+ }
+ }
+}
\ 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 3d2c6e64b9..61ed73d414 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs
@@ -1,14 +1,10 @@
// 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;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Media;
-using Avalonia.Media.Fonts;
using Avalonia.Platform;
-using SharpDX;
using DWrite = SharpDX.DirectWrite;
namespace Avalonia.Direct2D1.Media
@@ -27,31 +23,7 @@ namespace Avalonia.Direct2D1.Media
var factory = AvaloniaLocator.Current.GetService();
- DWrite.TextFormat textFormat;
-
- if (typeface.FontFamily.Key != null)
- {
- var fontCollection = Direct2D1CustomFontCollectionCache.GetOrAddCustomFontCollection(typeface.FontFamily, factory);
-
- textFormat = new DWrite.TextFormat(
- factory,
- typeface.FontFamily.Name,
- fontCollection,
- (DWrite.FontWeight)typeface.Weight,
- (DWrite.FontStyle)typeface.Style,
- DWrite.FontStretch.Normal,
- (float)typeface.FontSize);
- }
- else
- {
- textFormat = new DWrite.TextFormat(
- factory,
- typeface.FontFamily.Name,
- (DWrite.FontWeight)typeface.Weight,
- (DWrite.FontStyle)typeface.Style,
- DWrite.FontStretch.Normal,
- (float)typeface.FontSize);
- }
+ var textFormat = Direct2D1FontCollectionCache.GetTextFormat(typeface);
textFormat.WordWrapping =
wrapping == TextWrapping.Wrap ? DWrite.WordWrapping.Wrap : DWrite.WordWrapping.NoWrap;
@@ -144,256 +116,4 @@ namespace Avalonia.Direct2D1.Media
return new Size(width, TextLayout.Metrics.Height);
}
}
-
- internal static class Direct2D1CustomFontCollectionCache
- {
- private static readonly ConcurrentDictionary s_cachedFonts =
- new ConcurrentDictionary();
-
- public static DWrite.FontCollection GetOrAddCustomFontCollection(FontFamily fontFamily, DWrite.Factory factory)
- {
- return s_cachedFonts.GetOrAdd(fontFamily.Key, x => CreateCustomFontCollection(x, factory));
- }
-
- private static DWrite.FontCollection CreateCustomFontCollection(FontFamilyKey key, DWrite.Factory factory)
- {
- var fontFamily = FontFamilyCache.GetOrAddFontFamily(key);
-
- var fontLoader = new ResourceFontLoader(factory, fontFamily.FontResources);
-
- return new DWrite.FontCollection(factory, fontLoader, fontLoader.Key);
- }
- }
-
- internal class ResourceFontLoader : CallbackBase, DWrite.FontCollectionLoader, DWrite.FontFileLoader
- {
- private readonly List _fontStreams = new List();
- private readonly List _enumerators = new List();
- private readonly DataStream _keyStream;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The factory.
- ///
- public ResourceFontLoader(DWrite.Factory factory, IEnumerable fontResources)
- {
- var factory1 = factory;
-
- var assetLoader = AvaloniaLocator.Current.GetService();
-
- foreach (var font in fontResources)
- {
- var resourceStream = assetLoader.Open(font.Source);
-
- var dataStream = new DataStream((int)resourceStream.Length, true, true);
-
- resourceStream.CopyTo(dataStream);
-
- dataStream.Position = 0;
-
- _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);
-
- for (int i = 0; i < _fontStreams.Count; i++)
- {
- _keyStream.Write(i);
- }
-
- _keyStream.Position = 0;
-
- // Register the
- factory1.RegisterFontFileLoader(this);
- factory1.RegisterFontCollectionLoader(this);
- }
-
-
- ///
- /// Gets the key used to identify the FontCollection as well as storing index for fonts.
- ///
- /// The key.
- public DataStream Key => _keyStream;
-
- ///
- /// Creates a font file enumerator object that encapsulates a collection of font files. The font system calls back to this interface to create a font collection.
- ///
- /// Pointer to the object that was used to create the current font collection.
- /// A font collection key that uniquely identifies the collection of font files within the scope of the font collection loader being used. The buffer allocated for this key must be at least the size, in bytes, specified by collectionKeySize.
- ///
- /// a reference to the newly created font file enumerator.
- ///
- /// HRESULT IDWriteFontCollectionLoader::CreateEnumeratorFromKey([None] IDWriteFactory* factory,[In, Buffer] const void* collectionKey,[None] int collectionKeySize,[Out] IDWriteFontFileEnumerator** fontFileEnumerator)
- DWrite.FontFileEnumerator DWrite.FontCollectionLoader.CreateEnumeratorFromKey(DWrite.Factory factory, DataPointer collectionKey)
- {
- var enumerator = new ResourceFontFileEnumerator(factory, this, collectionKey);
-
- _enumerators.Add(enumerator);
-
- return enumerator;
- }
-
- ///
- /// Creates a font file stream object that encapsulates an open file resource.
- ///
- /// A reference to a font file reference key that uniquely identifies the font file resource within the scope of the font loader being used. The buffer allocated for this key must at least be the size, in bytes, specified by fontFileReferenceKeySize.
- ///
- /// a reference to the newly created object.
- ///
- ///
- /// The resource is closed when the last reference to fontFileStream is released.
- ///
- /// HRESULT IDWriteFontFileLoader::CreateStreamFromKey([In, Buffer] const void* fontFileReferenceKey,[None] int fontFileReferenceKeySize,[Out] IDWriteFontFileStream** fontFileStream)
- DWrite.FontFileStream DWrite.FontFileLoader.CreateStreamFromKey(DataPointer fontFileReferenceKey)
- {
- var index = SharpDX.Utilities.Read(fontFileReferenceKey.Pointer);
-
- return _fontStreams[index];
- }
- }
-
- ///
- /// This FontFileStream implem is reading data from a .
- ///
- public class ResourceFontFileStream : CallbackBase, DWrite.FontFileStream
- {
- private readonly DataStream _stream;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The stream.
- public ResourceFontFileStream(DataStream stream)
- {
- _stream = stream;
- }
-
- ///
- /// Reads a fragment from a font file.
- ///
- /// When this method returns, contains an address of a reference to the start of the font file fragment. This parameter is passed uninitialized.
- /// The offset of the fragment, in bytes, from the beginning of the font file.
- /// The size of the file fragment, in bytes.
- /// When this method returns, contains the address of
- ///
- /// Note that ReadFileFragment implementations must check whether the requested font file fragment is within the file bounds. Otherwise, an error should be returned from ReadFileFragment. {{DirectWrite}} may invoke methods on the same object from multiple threads simultaneously. Therefore, ReadFileFragment implementations that rely on internal mutable state must serialize access to such state across multiple threads. For example, an implementation that uses separate Seek and Read operations to read a file fragment must place the code block containing Seek and Read calls under a lock or a critical section.
- ///
- /// HRESULT IDWriteFontFileStream::ReadFileFragment([Out, Buffer] const void** fragmentStart,[None] __int64 fileOffset,[None] __int64 fragmentSize,[Out] void** fragmentContext)
- void DWrite.FontFileStream.ReadFileFragment(out IntPtr fragmentStart, long fileOffset, long fragmentSize, out IntPtr fragmentContext)
- {
- lock (this)
- {
- fragmentContext = IntPtr.Zero;
-
- _stream.Position = fileOffset;
-
- fragmentStart = _stream.PositionPointer;
-
- }
- }
-
- ///
- /// Releases a fragment from a file.
- ///
- /// A reference to the client-defined context of a font fragment returned from {{ReadFileFragment}}.
- /// void IDWriteFontFileStream::ReleaseFileFragment([None] void* fragmentContext)
- void DWrite.FontFileStream.ReleaseFileFragment(IntPtr fragmentContext)
- {
- // Nothing to release. No context are used
- }
-
- ///
- /// Obtains the total size of a file.
- ///
- /// the total size of the file.
- ///
- /// Implementing GetFileSize() for asynchronously loaded font files may require downloading the complete file contents. Therefore, this method should be used only for operations that either require a complete font file to be loaded (for example, copying a font file) or that need to make decisions based on the value of the file size (for example, validation against a persisted file size).
- ///
- /// HRESULT IDWriteFontFileStream::GetFileSize([Out] __int64* fileSize)
- long DWrite.FontFileStream.GetFileSize()
- {
- return _stream.Length;
- }
-
- ///
- /// Obtains the last modified time of the file.
- ///
- ///
- /// the last modified time of the file in the format that represents the number of 100-nanosecond intervals since January 1, 1601 (UTC).
- ///
- ///
- /// The "last modified time" is used by DirectWrite font selection algorithms to determine whether one font resource is more up to date than another one.
- ///
- /// HRESULT IDWriteFontFileStream::GetLastWriteTime([Out] __int64* lastWriteTime)
- long DWrite.FontFileStream.GetLastWriteTime()
- {
- return 0;
- }
- }
-
- ///
- /// Resource FontFileEnumerator.
- ///
- public class ResourceFontFileEnumerator : CallbackBase, DWrite.FontFileEnumerator
- {
- private DWrite.Factory _factory;
- private DWrite.FontFileLoader _loader;
- private DataStream keyStream;
- private DWrite.FontFile _currentFontFile;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The factory.
- /// The loader.
- /// The key.
- public ResourceFontFileEnumerator(DWrite.Factory factory, DWrite.FontFileLoader loader, DataPointer key)
- {
- _factory = factory;
-
- _loader = loader;
-
- keyStream = new DataStream(key.Pointer, key.Size, true, false);
- }
-
- ///
- /// Advances to the next font file in the collection. When it is first created, the enumerator is positioned before the first element of the collection and the first call to MoveNext advances to the first file.
- ///
- ///
- /// the value TRUE if the enumerator advances to a file; otherwise, FALSE if the enumerator advances past the last file in the collection.
- ///
- /// HRESULT IDWriteFontFileEnumerator::MoveNext([Out] BOOL* hasCurrentFile)
- bool DWrite.FontFileEnumerator.MoveNext()
- {
- bool moveNext = keyStream.RemainingLength != 0;
-
- if (!moveNext) return false;
-
- _currentFontFile?.Dispose();
-
- _currentFontFile = new DWrite.FontFile(_factory, keyStream.PositionPointer, 4, _loader);
-
- keyStream.Position += 4;
-
- return true;
- }
-
- ///
- /// Gets a reference to the current font file.
- ///
- ///
- /// a reference to the newly created object.
- /// HRESULT IDWriteFontFileEnumerator::GetCurrentFontFile([Out] IDWriteFontFile** fontFile)
- DWrite.FontFile DWrite.FontFileEnumerator.CurrentFontFile
- {
- get
- {
- ((IUnknown)_currentFontFile).AddReference();
-
- return _currentFontFile;
- }
- }
- }
}