From c71db148591ba6ed8fd59bf1fa9c5b5d4781919e Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 2 Dec 2025 15:26:51 +0100 Subject: [PATCH] Only use IGlyphTypeface --- api/Avalonia.nupkg.xml | 24 ++++++++++ samples/RenderDemo/Pages/GlyphRunPage.xaml.cs | 4 +- samples/TextTestApp/MainWindow.axaml.cs | 4 +- src/Avalonia.Base/Media/FontManager.cs | 4 +- .../Media/Fonts/FontCollectionBase.cs | 46 ++++++++++--------- .../Media/Fonts/IFontCollection.cs | 16 +++++-- .../Media/Fonts/SystemFontCollection.cs | 31 +++++++++++-- src/Avalonia.Base/Media/GlyphRun.cs | 8 ++-- src/Avalonia.Base/Media/IPlatformTypeface.cs | 5 ++ .../Media/TextFormatting/ShapedBuffer.cs | 6 +-- .../Media/TextFormatting/TextCharacters.cs | 4 +- .../Media/TextFormatting/TextMetrics.cs | 2 +- .../Media/TextFormatting/TextRunProperties.cs | 4 +- .../Media/TextFormatting/TextShaperOptions.cs | 6 +-- src/Avalonia.Base/Media/Typeface.cs | 2 +- .../Server/DiagnosticTextRenderer.cs | 2 +- .../HeadlessPlatformStubs.cs | 4 +- .../Media/GlyphTypefaceTests.cs | 2 + .../Media/EmbeddedFontCollectionTests.cs | 6 +-- .../Media/FontCollectionTests.cs | 6 +-- .../Media/FontManagerTests.cs | 13 ++++-- 21 files changed, 134 insertions(+), 65 deletions(-) diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index 16bb0e1583..cf44429ab7 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -703,6 +703,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0006 + M:Avalonia.Media.Fonts.IFontCollection.TryCreateSyntheticGlyphTypeface(Avalonia.Media.IGlyphTypeface,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0006 M:Avalonia.Media.Fonts.IFontCollection.TryGetFamilyTypefaces(System.String,System.Collections.Generic.IReadOnlyList{Avalonia.Media.Typeface}@) @@ -721,6 +727,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0006 + M:Avalonia.Media.Fonts.IFontCollection.TryGetNearestMatch(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0006 M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvance(System.UInt16) @@ -961,6 +973,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0006 + M:Avalonia.Media.Fonts.IFontCollection.TryCreateSyntheticGlyphTypeface(Avalonia.Media.IGlyphTypeface,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0006 M:Avalonia.Media.Fonts.IFontCollection.TryGetFamilyTypefaces(System.String,System.Collections.Generic.IReadOnlyList{Avalonia.Media.Typeface}@) @@ -979,6 +997,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0006 + M:Avalonia.Media.Fonts.IFontCollection.TryGetNearestMatch(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0006 M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvance(System.UInt16) diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs index addd6d1dbd..a94ec5e262 100644 --- a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs +++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs @@ -22,7 +22,7 @@ namespace RenderDemo.Pages public class GlyphRunControl : Control { - private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; + private IGlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; private readonly Random _rand = new Random(); private ushort[] _glyphIndices = new ushort[1]; private char[] _characters = new char[1]; @@ -81,7 +81,7 @@ namespace RenderDemo.Pages public class GlyphRunGeometryControl : Control { - private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; + private IGlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; private readonly Random _rand = new Random(); private ushort[] _glyphIndices = new ushort[1]; private char[] _characters = new char[1]; diff --git a/samples/TextTestApp/MainWindow.axaml.cs b/samples/TextTestApp/MainWindow.axaml.cs index f32a5b707e..493bc3e9d4 100644 --- a/samples/TextTestApp/MainWindow.axaml.cs +++ b/samples/TextTestApp/MainWindow.axaml.cs @@ -223,12 +223,12 @@ namespace TextTestApp } } - private IImage CreateGlyphDrawing(GlyphTypeface glyphTypeface, double emSize, GlyphInfo info) + private IImage CreateGlyphDrawing(IGlyphTypeface glyphTypeface, double emSize, GlyphInfo info) { return new DrawingImage { Drawing = new GeometryDrawing { Brush = Brushes.Black, Geometry = GetGlyphOutline(glyphTypeface, emSize, info) } }; } - private Geometry GetGlyphOutline(GlyphTypeface typeface, double emSize, GlyphInfo info) + private Geometry GetGlyphOutline(IGlyphTypeface typeface, double emSize, GlyphInfo info) { // substitute for GlyphTypeface.GetGlyphOutline return new GlyphRun(typeface, emSize, new[] { '\0' }, [info]).BuildGeometry(); diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs index 0c9f1c870a..075e674ece 100644 --- a/src/Avalonia.Base/Media/FontManager.cs +++ b/src/Avalonia.Base/Media/FontManager.cs @@ -98,7 +98,7 @@ namespace Avalonia.Media /// /// True, if the could create the glyph typeface, False otherwise. /// - public bool TryGetGlyphTypeface(Typeface typeface, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface) + public bool TryGetGlyphTypeface(Typeface typeface, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { glyphTypeface = null; @@ -187,7 +187,7 @@ namespace Avalonia.Media } } - private bool TryGetGlyphTypefaceByKeyAndName(Typeface typeface, FontFamilyKey key, string familyName, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface) + private bool TryGetGlyphTypefaceByKeyAndName(Typeface typeface, FontFamilyKey key, string familyName, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { var source = key.Source.EnsureAbsolute(key.BaseUri); diff --git a/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs b/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs index 4dd5e91dfa..72608a5133 100644 --- a/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs +++ b/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs @@ -15,7 +15,7 @@ namespace Avalonia.Media.Fonts Comparer.Create((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); // Make this internal for testing purposes - internal readonly ConcurrentDictionary> _glyphTypefaceCache = new(); + internal readonly ConcurrentDictionary> _glyphTypefaceCache = new(); private readonly object _fontFamiliesLock = new(); private volatile FontFamily[] _fontFamilies = Array.Empty(); @@ -87,11 +87,11 @@ namespace Avalonia.Media.Fonts } public virtual bool TryCreateSyntheticGlyphTypeface( - GlyphTypeface glyphTypeface, + IGlyphTypeface glyphTypeface, FontStyle style, FontWeight weight, FontStretch stretch, - [NotNullWhen(true)] out GlyphTypeface? syntheticGlyphTypeface) + [NotNullWhen(true)] out IGlyphTypeface? syntheticGlyphTypeface) { syntheticGlyphTypeface = null; @@ -154,7 +154,7 @@ namespace Avalonia.Media.Fonts public IEnumerator GetEnumerator() => ((IEnumerable)_fontFamilies).GetEnumerator(); public virtual bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight, - FontStretch stretch, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface) + FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { var typeface = new Typeface(familyName, style, weight, stretch).Normalize(out familyName); @@ -189,7 +189,7 @@ namespace Avalonia.Media.Fonts return false; } - public bool TryGetNearestMatch(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface) + public bool TryGetNearestMatch(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { if (!_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces)) { @@ -206,15 +206,15 @@ namespace Avalonia.Media.Fonts /// /// Attempts to add the specified to the font collection. /// - /// This method checks the and, if applicable, - /// the typographic family name and other family names provided by the interface. + /// This method checks the and, if applicable, + /// the typographic family name and other family names provided by the interface. /// If any of these names can be associated with the glyph typeface, the typeface is added to the collection. /// The method ensures that duplicate entries are not added. /// The glyph typeface to add. Must not be and must have a non-empty . /// if the glyph typeface was successfully added to the collection; otherwise, . - public bool TryAddGlyphTypeface(GlyphTypeface glyphTypeface) + public bool TryAddGlyphTypeface(IGlyphTypeface glyphTypeface) { if (glyphTypeface == null || string.IsNullOrEmpty(glyphTypeface.FamilyName)) { @@ -263,7 +263,7 @@ namespace Avalonia.Media.Fonts /// succeeds; otherwise, . /// if the glyph typeface was successfully created and added; otherwise, . - public bool TryAddGlyphTypeface(Stream stream, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface) + public bool TryAddGlyphTypeface(Stream stream, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { glyphTypeface = null; @@ -272,7 +272,7 @@ namespace Avalonia.Media.Fonts return false; } - glyphTypeface = new GlyphTypeface(platformTypeface, FontSimulations.None); + glyphTypeface = new GlyphTypeface(platformTypeface); return TryAddGlyphTypeface(glyphTypeface); } @@ -313,7 +313,7 @@ namespace Avalonia.Media.Fonts continue; } - var glyphTypeface = new GlyphTypeface(platformTypeface, FontSimulations.None); + var glyphTypeface = new GlyphTypeface(platformTypeface); var key = glyphTypeface.ToFontCollectionKey(); @@ -348,7 +348,7 @@ namespace Avalonia.Media.Fonts if (_fontManagerImpl.TryCreateGlyphTypeface(stream, FontSimulations.None, out var platformTypeface)) { - var glyphTypeface = new GlyphTypeface(platformTypeface, FontSimulations.None); + var glyphTypeface = new GlyphTypeface(platformTypeface); if (TryAddGlyphTypeface(glyphTypeface)) { @@ -372,7 +372,7 @@ namespace Avalonia.Media.Fonts if (_fontManagerImpl.TryCreateGlyphTypeface(stream, FontSimulations.None, out var platformTypeface)) { - var glyphTypeface = new GlyphTypeface(platformTypeface, FontSimulations.None); + var glyphTypeface = new GlyphTypeface(platformTypeface); if (TryAddGlyphTypeface(glyphTypeface)) { @@ -452,7 +452,7 @@ namespace Avalonia.Media.Fonts /// When this method returns, contains the matching if a match is found; otherwise, /// . /// if a matching glyph typeface is found; otherwise, . - protected bool TryGetGlyphTypeface(string familyName, FontCollectionKey key, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface) + protected bool TryGetGlyphTypeface(string familyName, FontCollectionKey key, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { glyphTypeface = null; @@ -558,7 +558,8 @@ namespace Avalonia.Media.Fonts /// provided collection of glyph typefaces. /// /// This method attempts to find the best match for the specified font key by considering - /// various fallback strategies, such as normalizing the font style, stretch, and weight. If no suitable match is found, the method will return the first available non-null from the + /// various fallback strategies, such as normalizing the font style, stretch, and weight. + /// If no suitable match is found, the method will return the first available non-null from the /// collection, if any. /// A collection of glyph typefaces, indexed by . /// The representing the desired font attributes. @@ -566,7 +567,8 @@ namespace Avalonia.Media.Fonts /// key, if a match is found; otherwise, . /// if a matching is found; otherwise, . - protected bool TryGetNearestMatch(IDictionary glyphTypefaces, FontCollectionKey key, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface) + protected bool TryGetNearestMatch(IDictionary glyphTypefaces, + FontCollectionKey key, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { if (glyphTypefaces.TryGetValue(key, out glyphTypeface) && glyphTypeface != null) { @@ -631,7 +633,7 @@ namespace Avalonia.Media.Fonts /// The glyph typeface to add to the cache. Can be null. /// if the glyph typeface was successfully added to the cache; otherwise, . - protected bool TryAddGlyphTypeface(string familyName, FontCollectionKey key, GlyphTypeface? glyphTypeface) + protected bool TryAddGlyphTypeface(string familyName, FontCollectionKey key, IGlyphTypeface? glyphTypeface) { if (string.IsNullOrEmpty(familyName)) { @@ -655,7 +657,7 @@ namespace Avalonia.Media.Fonts } // Family doesn't exist yet. Create a new dictionary instance and try to install it. - var newDict = new ConcurrentDictionary(); + var newDict = new ConcurrentDictionary(); // GetOrAdd will return the instance that ended up in the dictionary. If it's our // newDict instance then we won the race to add the family and should publish it. @@ -697,9 +699,9 @@ namespace Avalonia.Media.Fonts /// null. /// true if a suitable fallback glyph typeface is found; otherwise, false. private static bool TryFindStretchFallback( - IDictionary glyphTypefaces, + IDictionary glyphTypefaces, FontCollectionKey key, - [NotNullWhen(true)] out GlyphTypeface? glyphTypeface) + [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { glyphTypeface = null; @@ -744,9 +746,9 @@ namespace Avalonia.Media.Fonts /// null. /// true if a fallback glyph typeface matching the requested weight is found; otherwise, false. private static bool TryFindWeightFallback( - IDictionary glyphTypefaces, + IDictionary glyphTypefaces, FontCollectionKey key, - [NotNullWhen(true)] out GlyphTypeface? glyphTypeface) + [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { glyphTypeface = null; var weight = (int)key.Weight; diff --git a/src/Avalonia.Base/Media/Fonts/IFontCollection.cs b/src/Avalonia.Base/Media/Fonts/IFontCollection.cs index b074dd5ce4..0bed5eadd3 100644 --- a/src/Avalonia.Base/Media/Fonts/IFontCollection.cs +++ b/src/Avalonia.Base/Media/Fonts/IFontCollection.cs @@ -5,6 +5,12 @@ using System.Globalization; namespace Avalonia.Media.Fonts { + /// + /// Represents a collection of font families and provides methods for querying and managing font typefaces + /// within the collection. + /// + /// Implementations of this interface allow applications to retrieve font families, match + /// characters to typefaces, and obtain glyph typefaces based on specific font properties. public interface IFontCollection : IReadOnlyList, IDisposable { /// @@ -22,7 +28,7 @@ namespace Avalonia.Media.Fonts /// The glyph typeface. /// Returns true if a glyph typface can be found; otherwise, false bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight, - FontStretch stretch, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface); + FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface); /// /// Tries to match a specified character to a that supports specified font properties. @@ -59,7 +65,8 @@ namespace Avalonia.Media.Fonts /// The font stretch. /// /// Returns true if a synthetic glyph typface can be created; otherwise, false - bool TryCreateSyntheticGlyphTypeface(GlyphTypeface glyphTypeface, FontStyle style, FontWeight weight, FontStretch stretch, [NotNullWhen(true)] out GlyphTypeface? syntheticGlyphTypeface); + bool TryCreateSyntheticGlyphTypeface(IGlyphTypeface glyphTypeface, FontStyle style, FontWeight weight, FontStretch stretch, + [NotNullWhen(true)] out IGlyphTypeface? syntheticGlyphTypeface); /// /// Attempts to retrieve the glyph typeface that most closely matches the specified font family name, style, @@ -74,9 +81,10 @@ namespace Avalonia.Media.Fonts /// The desired font style. /// The desired font weight. /// The desired font stretch. - /// When this method returns, contains the that most closely matches the specified + /// When this method returns, contains the that most closely matches the specified /// parameters, if a match is found; otherwise, . This parameter is passed uninitialized. /// if a matching glyph typeface is found; otherwise, . - bool TryGetNearestMatch(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface); + bool TryGetNearestMatch(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, + [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface); } } diff --git a/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs b/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs index 4f86cbad88..3b8929cdb5 100644 --- a/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs +++ b/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs @@ -26,7 +26,7 @@ namespace Avalonia.Media.Fonts public override Uri Key => FontManager.SystemFontsKey; public override bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight, - FontStretch stretch, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface) + FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) { var typeface = new Typeface(familyName, style, weight, stretch).Normalize(out familyName); @@ -86,13 +86,34 @@ namespace Avalonia.Media.Fonts if (_platformImpl.TryMatchCharacter(codepoint, style, weight, stretch, familyName, culture, out var platformTypeface)) { - var glyphTypeface = new GlyphTypeface(platformTypeface); - + // Construct the resulting Typeface match = new Typeface(platformTypeface.FamilyName, platformTypeface.Style, platformTypeface.Weight, platformTypeface.Stretch); - // Add to cache if not already present - return TryAddGlyphTypeface(glyphTypeface); + // Compute the key for cache lookup this can be different from the requested key + var key = match.ToFontCollectionKey(); + + // Check cache first: if an entry exists and is non-null, match succeeded and we can return true. + if (_glyphTypefaceCache.TryGetValue(platformTypeface.FamilyName, out var glyphTypefaces) && glyphTypefaces.TryGetValue(key, out var existing)) + { + return existing != null; + } + + // Not in cache yet: create glyph typeface and try to add it. + var glyphTypeface = new GlyphTypeface(platformTypeface); + + if (TryAddGlyphTypeface(platformTypeface.FamilyName, key, glyphTypeface)) + { + return true; + } + + // TryAddGlyphTypeface failed: another thread may have added an entry. Re-check the cache. + if (_glyphTypefaceCache.TryGetValue(platformTypeface.FamilyName, out glyphTypefaces) && glyphTypefaces.TryGetValue(key, out existing)) + { + return existing != null; + } + + return false; } return false; diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index a54ea7363b..8742fb0807 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -39,7 +39,7 @@ namespace Avalonia.Media /// The baseline origin of the run. /// The bidi level. public GlyphRun( - GlyphTypeface glyphTypeface, + IGlyphTypeface glyphTypeface, double fontRenderingEmSize, ReadOnlyMemory characters, IReadOnlyList glyphIndices, @@ -61,7 +61,7 @@ namespace Avalonia.Media /// The baseline origin of the run. /// The bidi level. public GlyphRun( - GlyphTypeface glyphTypeface, + IGlyphTypeface glyphTypeface, double fontRenderingEmSize, ReadOnlyMemory characters, IReadOnlyList glyphInfos, @@ -90,7 +90,7 @@ namespace Avalonia.Media } private static IReadOnlyList CreateGlyphInfos(IReadOnlyList glyphIndices, - double fontRenderingEmSize, GlyphTypeface glyphTypeface) + double fontRenderingEmSize, IGlyphTypeface glyphTypeface) { var glyphIndexSpan = ListToSpan(glyphIndices); @@ -142,7 +142,7 @@ namespace Avalonia.Media /// /// Gets the for the . /// - public GlyphTypeface GlyphTypeface { get; } + public IGlyphTypeface GlyphTypeface { get; } /// /// Gets or sets the em size used for rendering the . diff --git a/src/Avalonia.Base/Media/IPlatformTypeface.cs b/src/Avalonia.Base/Media/IPlatformTypeface.cs index 4365354adc..21c95412f4 100644 --- a/src/Avalonia.Base/Media/IPlatformTypeface.cs +++ b/src/Avalonia.Base/Media/IPlatformTypeface.cs @@ -31,6 +31,11 @@ namespace Avalonia.Media /// FontStretch Stretch { get; } + /// + /// Gets the algorithmic style simulations applied to object. + /// + FontSimulations FontSimulations { get; } + /// /// Returns the font file stream represented by the . /// diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs index 71640fac08..5ab7770192 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs @@ -13,7 +13,7 @@ namespace Avalonia.Media.TextFormatting private GlyphInfo[]? _rentedBuffer; private ArraySlice _glyphInfos; - public ShapedBuffer(ReadOnlyMemory text, int bufferLength, GlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) + public ShapedBuffer(ReadOnlyMemory text, int bufferLength, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) { Text = text; _rentedBuffer = ArrayPool.Shared.Rent(bufferLength); @@ -23,7 +23,7 @@ namespace Avalonia.Media.TextFormatting BidiLevel = bidiLevel; } - internal ShapedBuffer(ReadOnlyMemory text, ArraySlice glyphInfos, GlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) + internal ShapedBuffer(ReadOnlyMemory text, ArraySlice glyphInfos, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) { Text = text; _glyphInfos = glyphInfos; @@ -40,7 +40,7 @@ namespace Avalonia.Media.TextFormatting /// /// The buffer's glyph typeface. /// - public GlyphTypeface GlyphTypeface { get; } + public IGlyphTypeface GlyphTypeface { get; } /// /// The buffers font rendering em size. diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs index cd8b0a3175..b5bd1f4da5 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs @@ -167,8 +167,8 @@ namespace Avalonia.Media.TextFormatting /// internal static bool TryGetShapeableLength( ReadOnlySpan text, - GlyphTypeface glyphTypeface, - GlyphTypeface? defaultGlyphTypeface, + IGlyphTypeface glyphTypeface, + IGlyphTypeface? defaultGlyphTypeface, out int length) { length = 0; diff --git a/src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs b/src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs index c0412d1efc..db59f92661 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs @@ -5,7 +5,7 @@ /// public readonly record struct TextMetrics { - public TextMetrics(GlyphTypeface glyphTypeface, double fontRenderingEmSize) + public TextMetrics(IGlyphTypeface glyphTypeface, double fontRenderingEmSize) { var fontMetrics = glyphTypeface.Metrics; diff --git a/src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs index cf2f74ee4d..11db49aaf1 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs @@ -12,7 +12,7 @@ namespace Avalonia.Media.TextFormatting /// public abstract class TextRunProperties : IEquatable { - private GlyphTypeface? _cachedGlyphTypeFace; + private IGlyphTypeface? _cachedGlyphTypeFace; /// /// Run typeface @@ -54,7 +54,7 @@ namespace Avalonia.Media.TextFormatting /// public virtual BaselineAlignment BaselineAlignment => BaselineAlignment.Baseline; - internal GlyphTypeface CachedGlyphTypeface + internal IGlyphTypeface CachedGlyphTypeface => _cachedGlyphTypeFace ??= Typeface.GlyphTypeface; public bool Equals(TextRunProperties? other) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs index 9ae20a3eaa..48dbaedad4 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs @@ -10,7 +10,7 @@ namespace Avalonia.Media.TextFormatting { // TODO12: Remove in 12.0.0 and make fontFeatures parameter in main ctor optional public TextShaperOptions( - GlyphTypeface typeface, + IGlyphTypeface typeface, double fontRenderingEmSize = 12, sbyte bidiLevel = 0, CultureInfo? culture = null, @@ -22,7 +22,7 @@ namespace Avalonia.Media.TextFormatting // TODO12:Change signature in 12.0.0 public TextShaperOptions( - GlyphTypeface typeface, + IGlyphTypeface typeface, IReadOnlyList? fontFeatures, double fontRenderingEmSize = 12, sbyte bidiLevel = 0, @@ -42,7 +42,7 @@ namespace Avalonia.Media.TextFormatting /// /// Get the typeface. /// - public GlyphTypeface GlyphTypeface { get; } + public IGlyphTypeface GlyphTypeface { get; } /// /// Get the font rendering em size. /// diff --git a/src/Avalonia.Base/Media/Typeface.cs b/src/Avalonia.Base/Media/Typeface.cs index 1adcac5b75..c9ad271568 100644 --- a/src/Avalonia.Base/Media/Typeface.cs +++ b/src/Avalonia.Base/Media/Typeface.cs @@ -83,7 +83,7 @@ namespace Avalonia.Media /// /// The glyph typeface. /// - public GlyphTypeface GlyphTypeface + public IGlyphTypeface GlyphTypeface { get { diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DiagnosticTextRenderer.cs b/src/Avalonia.Base/Rendering/Composition/Server/DiagnosticTextRenderer.cs index 2c2e3e886b..087bce5a14 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/DiagnosticTextRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/DiagnosticTextRenderer.cs @@ -29,7 +29,7 @@ namespace Avalonia.Rendering.Composition.Server return maxHeight; } - public DiagnosticTextRenderer(GlyphTypeface glyphTypeface, double fontRenderingEmSize) + public DiagnosticTextRenderer(IGlyphTypeface glyphTypeface, double fontRenderingEmSize) { var chars = new char[LastChar - FirstChar + 1]; for (var c = FirstChar; c <= LastChar; c++) diff --git a/src/Headless/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Headless/Avalonia.Headless/HeadlessPlatformStubs.cs index a015beff81..7dd73b1556 100644 --- a/src/Headless/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Headless/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -76,7 +76,7 @@ namespace Avalonia.Headless { _fontMemory = UnmanagedFontMemory.LoadFromStream(stream); - var dummy = new GlyphTypeface(this, FontSimulations.None); + var dummy = new GlyphTypeface(this); FamilyName = familyName; Weight = dummy.Weight; @@ -92,6 +92,8 @@ namespace Avalonia.Headless public FontStretch Stretch { get; } + public FontSimulations FontSimulations => FontSimulations.None; + public void Dispose() { _fontMemory.Dispose(); diff --git a/tests/Avalonia.Base.UnitTests/Media/GlyphTypefaceTests.cs b/tests/Avalonia.Base.UnitTests/Media/GlyphTypefaceTests.cs index b8f697517b..28f7280b3a 100644 --- a/tests/Avalonia.Base.UnitTests/Media/GlyphTypefaceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/GlyphTypefaceTests.cs @@ -90,6 +90,8 @@ namespace Avalonia.Base.UnitTests.Media public string FamilyName { get; } + public FontSimulations FontSimulations => FontSimulations.None; + public void Dispose() { _fontMemory.Dispose(); diff --git a/tests/Avalonia.Skia.UnitTests/Media/EmbeddedFontCollectionTests.cs b/tests/Avalonia.Skia.UnitTests/Media/EmbeddedFontCollectionTests.cs index 87a2b867d2..a927fd8024 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/EmbeddedFontCollectionTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/EmbeddedFontCollectionTests.cs @@ -139,14 +139,14 @@ namespace Avalonia.Skia.UnitTests.Media _createSyntheticTypefaces = createSyntheticTypefaces; } - public IDictionary> GlyphTypefaceCache => _glyphTypefaceCache; + public IDictionary> GlyphTypefaceCache => _glyphTypefaceCache; public override bool TryCreateSyntheticGlyphTypeface( - GlyphTypeface glyphTypeface, + IGlyphTypeface glyphTypeface, FontStyle style, FontWeight weight, FontStretch stretch, - [NotNullWhen(true)] out GlyphTypeface? syntheticGlyphTypeface) + [NotNullWhen(true)] out IGlyphTypeface? syntheticGlyphTypeface) { if (!_createSyntheticTypefaces) { diff --git a/tests/Avalonia.Skia.UnitTests/Media/FontCollectionTests.cs b/tests/Avalonia.Skia.UnitTests/Media/FontCollectionTests.cs index 494fa4462d..7a97435a7c 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/FontCollectionTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/FontCollectionTests.cs @@ -45,7 +45,7 @@ namespace Avalonia.Skia.UnitTests.Media { } - public IDictionary> GlyphTypefaceCache => _glyphTypefaceCache; + public IDictionary> GlyphTypefaceCache => _glyphTypefaceCache; } [Fact] @@ -126,11 +126,11 @@ namespace Avalonia.Skia.UnitTests.Media } public override bool TryCreateSyntheticGlyphTypeface( - GlyphTypeface glyphTypeface, + IGlyphTypeface glyphTypeface, FontStyle style, FontWeight weight, FontStretch stretch, - [NotNullWhen(true)] out GlyphTypeface? syntheticGlyphTypeface) + [NotNullWhen(true)] out IGlyphTypeface? syntheticGlyphTypeface) { syntheticGlyphTypeface = null; diff --git a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs index bc375ed8e3..bf50870eb4 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs @@ -313,13 +313,18 @@ namespace Avalonia.Skia.UnitTests.Media new Uri(s_fontUri, UriKind.Absolute))); Assert.True(FontManager.Current.TryGetGlyphTypeface(new Typeface("Noto Mono", FontStyle.Italic, FontWeight.Bold), - out var glyphTypeface)); + out var italicBoldTypeface)); - Assert.Equal("Noto Mono", glyphTypeface.FamilyName); + Assert.Equal("Noto Mono", italicBoldTypeface.FamilyName); - Assert.Equal(FontWeight.Bold, glyphTypeface.Weight); + Assert.True(italicBoldTypeface.PlatformTypeface.FontSimulations.HasFlag(FontSimulations.Bold)); - Assert.Equal(FontStyle.Italic, glyphTypeface.Style); + Assert.True(italicBoldTypeface.PlatformTypeface.FontSimulations.HasFlag(FontSimulations.Oblique)); + + Assert.True(FontManager.Current.TryGetGlyphTypeface(new Typeface("Noto Mono", FontStyle.Normal, FontWeight.Normal), + out var regularTypeface)); + + Assert.NotEqual(((SkiaTypeface)regularTypeface.PlatformTypeface).SKTypeface, ((SkiaTypeface)italicBoldTypeface.PlatformTypeface).SKTypeface); } } }