diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs index 477348b59a..eb1833193c 100644 --- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs +++ b/src/Skia/Avalonia.Skia/FontManagerImpl.cs @@ -50,7 +50,7 @@ namespace Avalonia.Skia skFontStyle = SKFontStyle.BoldItalic; break; default: - skFontStyle = new SKFontStyle((SKFontStyleWeight)fontWeight, (SKFontStyleWidth)fontStretch, (SKFontStyleSlant)fontStyle); + skFontStyle = new SKFontStyle((SKFontStyleWeight)fontWeight, (SKFontStyleWidth)fontStretch, fontStyle.ToSkia()); break; } @@ -63,7 +63,12 @@ namespace Avalonia.Skia if (skTypeface != null) { - fontKey = new Typeface(skTypeface.FamilyName, (FontStyle)skTypeface.FontStyle.Slant, (FontWeight)skTypeface.FontStyle.Weight, (FontStretch)skTypeface.FontStyle.Width); + // ToDo: create glyph typeface here to get the correct style/weight/stretch + fontKey = new Typeface( + skTypeface.FamilyName, + skTypeface.FontStyle.Slant.ToAvalonia(), + (FontWeight)skTypeface.FontStyle.Weight, + (FontStretch)skTypeface.FontStyle.Width); return true; } @@ -78,8 +83,7 @@ namespace Avalonia.Skia { glyphTypeface = null; - var fontStyle = new SKFontStyle((SKFontStyleWeight)weight, (SKFontStyleWidth)stretch, - (SKFontStyleSlant)style); + var fontStyle = new SKFontStyle((SKFontStyleWeight)weight, (SKFontStyleWidth)stretch, style.ToSkia()); var skTypeface = _skFontManager.MatchFamily(familyName, fontStyle); @@ -127,7 +131,7 @@ namespace Avalonia.Skia var set = _skFontManager.GetFontStyles(familyName); - if(set.Count == 0) + if (set.Count == 0) { return false; } diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs index 2def64c18d..703496a834 100644 --- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs @@ -16,7 +16,6 @@ namespace Avalonia.Skia internal class GlyphTypefaceImpl : IGlyphTypeface2 { private bool _isDisposed; - private readonly SKTypeface _typeface; private readonly NameTable? _nameTable; private readonly OS2Table? _os2Table; private readonly HorizontalHeadTable? _hhTable; @@ -24,7 +23,7 @@ namespace Avalonia.Skia public GlyphTypefaceImpl(SKTypeface typeface, FontSimulations fontSimulations) { - _typeface = typeface ?? throw new ArgumentNullException(nameof(typeface)); + SKTypeface = typeface ?? throw new ArgumentNullException(nameof(typeface)); Face = new Face(GetTable) { UnitsPerEm = typeface.UnitsPerEm }; @@ -96,6 +95,11 @@ namespace Avalonia.Skia var style = _os2Table != null ? GetFontStyle(_os2Table.FontStyle) : FontStyle.Normal; + if (typeface.FontStyle.Slant == SKFontStyleSlant.Oblique) + { + style = FontStyle.Oblique; + } + Style = (fontSimulations & FontSimulations.Oblique) != 0 ? FontStyle.Italic : style; var stretch = _os2Table != null ? (FontStretch)_os2Table.WidthClass : FontStretch.Normal; @@ -205,6 +209,8 @@ namespace Avalonia.Skia } } + public SKTypeface SKTypeface { get; } + public Face Face { get; } public Font Font { get; } @@ -300,12 +306,12 @@ namespace Avalonia.Skia private static FontStyle GetFontStyle(OS2Table.FontStyleSelection styleSelection) { - if((styleSelection & OS2Table.FontStyleSelection.ITALIC) != 0) + if ((styleSelection & OS2Table.FontStyleSelection.ITALIC) != 0) { return FontStyle.Italic; } - if((styleSelection & OS2Table.FontStyleSelection.OBLIQUE) != 0) + if ((styleSelection & OS2Table.FontStyleSelection.OBLIQUE) != 0) { return FontStyle.Oblique; } @@ -315,18 +321,18 @@ namespace Avalonia.Skia private Blob? GetTable(Face face, Tag tag) { - var size = _typeface.GetTableSize(tag); + var size = SKTypeface.GetTableSize(tag); var data = Marshal.AllocCoTaskMem(size); var releaseDelegate = new ReleaseDelegate(() => Marshal.FreeCoTaskMem(data)); - return _typeface.TryGetTableData(tag, 0, size, data) ? + return SKTypeface.TryGetTableData(tag, 0, size, data) ? new Blob(data, size, MemoryMode.ReadOnly, releaseDelegate) : null; } public SKFont CreateSKFont(float size) - => new(_typeface, size, skewX: (FontSimulations & FontSimulations.Oblique) != 0 ? -0.3f : 0.0f) + => new(SKTypeface, size, skewX: (FontSimulations & FontSimulations.Oblique) != 0 ? -0.3f : 0.0f) { LinearMetrics = true, Embolden = (FontSimulations & FontSimulations.Bold) != 0 @@ -348,7 +354,7 @@ namespace Avalonia.Skia Font.Dispose(); Face.Dispose(); - _typeface.Dispose(); + SKTypeface.Dispose(); } public void Dispose() @@ -359,14 +365,14 @@ namespace Avalonia.Skia public bool TryGetTable(uint tag, out byte[] table) { - return _typeface.TryGetTableData(tag, out table); + return SKTypeface.TryGetTableData(tag, out table); } public bool TryGetStream([NotNullWhen(true)] out Stream? stream) { try { - var asset = _typeface.OpenStream(); + var asset = SKTypeface.OpenStream(); var size = asset.Length; var buffer = new byte[size]; diff --git a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs index add72caa30..d94cc4aaf7 100644 --- a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs +++ b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs @@ -1,8 +1,8 @@ using System; using System.Diagnostics.CodeAnalysis; using Avalonia.Media; -using Avalonia.Platform; using Avalonia.Media.Imaging; +using Avalonia.Platform; using SkiaSharp; namespace Avalonia.Skia @@ -67,7 +67,7 @@ namespace Avalonia.Skia { return new SKPoint((float)p.X, (float)p.Y); } - + public static SKPoint ToSKPoint(this Vector p) { return new SKPoint((float)p.X, (float)p.Y); @@ -77,17 +77,17 @@ namespace Avalonia.Skia { return new SKRect((float)r.X, (float)r.Y, (float)r.Right, (float)r.Bottom); } - + internal static SKRect ToSKRect(this LtrbRect r) { return new SKRect((float)r.Left, (float)r.Right, (float)r.Right, (float)r.Bottom); } - + public static SKRectI ToSKRectI(this PixelRect r) { return new SKRectI(r.X, r.Y, r.Right, r.Bottom); } - + internal static SKRectI ToSKRectI(this LtrbPixelRect r) { return new SKRectI(r.Left, r.Top, r.Right, r.Bottom); @@ -103,7 +103,7 @@ namespace Avalonia.Skia { r.RadiiTopLeft.ToSKPoint(), r.RadiiTopRight.ToSKPoint(), r.RadiiBottomRight.ToSKPoint(), r.RadiiBottomLeft.ToSKPoint(), - }); + }); return result; } @@ -112,17 +112,17 @@ namespace Avalonia.Skia { return new Rect(r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top); } - + internal static LtrbRect ToAvaloniaLtrbRect(this SKRect r) { return new LtrbRect(r.Left, r.Top, r.Right, r.Bottom); } - + public static PixelRect ToAvaloniaPixelRect(this SKRectI r) { return new PixelRect(r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top); } - + internal static LtrbPixelRect ToAvaloniaLtrbPixelRect(this SKRectI r) { return new LtrbPixelRect(r.Left, r.Top, r.Right, r.Bottom); @@ -220,9 +220,12 @@ namespace Avalonia.Skia switch (m) { default: - case GradientSpreadMethod.Pad: return SKShaderTileMode.Clamp; - case GradientSpreadMethod.Reflect: return SKShaderTileMode.Mirror; - case GradientSpreadMethod.Repeat: return SKShaderTileMode.Repeat; + case GradientSpreadMethod.Pad: + return SKShaderTileMode.Clamp; + case GradientSpreadMethod.Reflect: + return SKShaderTileMode.Mirror; + case GradientSpreadMethod.Repeat: + return SKShaderTileMode.Repeat; } } @@ -231,9 +234,12 @@ namespace Avalonia.Skia switch (a) { default: - case TextAlignment.Left: return SKTextAlign.Left; - case TextAlignment.Center: return SKTextAlign.Center; - case TextAlignment.Right: return SKTextAlign.Right; + case TextAlignment.Left: + return SKTextAlign.Left; + case TextAlignment.Center: + return SKTextAlign.Center; + case TextAlignment.Right: + return SKTextAlign.Right; } } @@ -262,9 +268,12 @@ namespace Avalonia.Skia switch (a) { default: - case SKTextAlign.Left: return TextAlignment.Left; - case SKTextAlign.Center: return TextAlignment.Center; - case SKTextAlign.Right: return TextAlignment.Right; + case SKTextAlign.Left: + return TextAlignment.Left; + case SKTextAlign.Center: + return TextAlignment.Center; + case SKTextAlign.Right: + return TextAlignment.Right; } } @@ -275,7 +284,18 @@ namespace Avalonia.Skia SKFontStyleSlant.Upright => FontStyle.Normal, SKFontStyleSlant.Italic => FontStyle.Italic, SKFontStyleSlant.Oblique => FontStyle.Oblique, - _ => throw new ArgumentOutOfRangeException(nameof (slant), slant, null) + _ => throw new ArgumentOutOfRangeException(nameof(slant), slant, null) + }; + } + + public static SKFontStyleSlant ToSkia(this FontStyle style) + { + return style switch + { + FontStyle.Normal => SKFontStyleSlant.Upright, + FontStyle.Italic => SKFontStyleSlant.Italic, + FontStyle.Oblique => SKFontStyleSlant.Oblique, + _ => throw new ArgumentOutOfRangeException(nameof(style), style, null) }; } diff --git a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs index 6259547a48..2713e7133b 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; using Avalonia.Fonts.Inter; using Avalonia.Headless; using Avalonia.Media; @@ -103,7 +101,7 @@ namespace Avalonia.Skia.UnitTests.Media { Assert.True(FontManager.Current.TryGetGlyphTypeface(Typeface.Default, out _)); - for (int i = 0;i < 10; i++) + for (int i = 0; i < 10; i++) { FontManager.Current.TryGetGlyphTypeface(new Typeface("Unknown"), out _); } @@ -313,7 +311,7 @@ namespace Avalonia.Skia.UnitTests.Media { Assert.True(FontManager.Current.TryGetGlyphTypeface(new Typeface("微軟正黑體"), out var glyphTypeface)); - Assert.Equal("Microsoft JhengHei",glyphTypeface.FamilyName); + Assert.Equal("Microsoft JhengHei", glyphTypeface.FamilyName); } } } @@ -325,7 +323,7 @@ namespace Avalonia.Skia.UnitTests.Media { using (AvaloniaLocator.EnterScope()) { - FontManager.Current.AddFontCollection(new InterFontCollection()); + FontManager.Current.AddFontCollection(new InterFontCollection()); Assert.True(FontManager.Current.TryGetGlyphTypeface(new Typeface("fonts:Inter#Inter"), out var glyphTypeface)); @@ -346,12 +344,12 @@ namespace Avalonia.Skia.UnitTests.Media { using (AvaloniaLocator.EnterScope()) { - AvaloniaLocator.CurrentMutable.BindToSelf(new FontManagerOptions - { - DefaultFamilyName = s_fontUri, - FontFamilyMappings = new Dictionary - { - { "Segoe UI", new FontFamily("fonts:Inter#Inter") } + AvaloniaLocator.CurrentMutable.BindToSelf(new FontManagerOptions + { + DefaultFamilyName = s_fontUri, + FontFamilyMappings = new Dictionary + { + { "Segoe UI", new FontFamily("fonts:Inter#Inter") } } }); @@ -428,6 +426,33 @@ namespace Avalonia.Skia.UnitTests.Media } } + + [Win32Fact("Windows specific font")] + public void Should_Get_Regular_Font_After_Matching_Italic_Font() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new FontManagerImpl()))) + { + using (AvaloniaLocator.EnterScope()) + { + Assert.True(FontManager.Current.TryMatchCharacter('こ', FontStyle.Italic, FontWeight.Normal, FontStretch.Normal, null, null, out var italicTypeface)); + + Assert.Equal(FontSimulations.None, italicTypeface.GlyphTypeface.FontSimulations); + + Assert.Equal("Yu Gothic UI", italicTypeface.GlyphTypeface.FamilyName); + + Assert.NotEqual(FontStyle.Normal, italicTypeface.Style); + + Assert.True(FontManager.Current.TryMatchCharacter('こ', FontStyle.Normal, FontWeight.Normal, FontStretch.Normal, null, null, out var regularTypeface)); + + Assert.Equal("Yu Gothic UI", regularTypeface.GlyphTypeface.FamilyName); + + Assert.Equal(FontStyle.Normal, regularTypeface.Style); + + Assert.NotEqual(((GlyphTypefaceImpl)italicTypeface.GlyphTypeface).SKTypeface, ((GlyphTypefaceImpl)regularTypeface.GlyphTypeface).SKTypeface); + } + } + } + [Fact] public void Should_Fallback_When_Font_Family_Is_Empty() {