diff --git a/src/Avalonia.Base/Media/FontFamily.cs b/src/Avalonia.Base/Media/FontFamily.cs
index 80365aaf4d..e1365bb87d 100644
--- a/src/Avalonia.Base/Media/FontFamily.cs
+++ b/src/Avalonia.Base/Media/FontFamily.cs
@@ -104,6 +104,11 @@ namespace Avalonia.Media
/// Key is only used for custom fonts.
public FontFamilyKey? Key { get; }
+ ///
+ /// Gets the typefaces for this font family.
+ ///
+ public IReadOnlyList FamilyTypefaces => FontManager.Current.GetFamilyTypefaces(this);
+
///
/// Implicit conversion of string to FontFamily
///
diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs
index 28f2e6b234..c51dbbfee3 100644
--- a/src/Avalonia.Base/Media/FontManager.cs
+++ b/src/Avalonia.Base/Media/FontManager.cs
@@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
-using System.Linq;
using Avalonia.Logging;
using Avalonia.Media.Fonts;
using Avalonia.Platform;
@@ -339,6 +338,36 @@ namespace Avalonia.Media
return false;
}
+ internal IReadOnlyList GetFamilyTypefaces(FontFamily fontFamily)
+ {
+ var key = fontFamily.Key;
+
+ if (key == null)
+ {
+ if(SystemFonts is IFontCollection2 fontCollection2)
+ {
+ if (fontCollection2.TryGetFamilyTypefaces(fontFamily.Name, out var familyTypefaces))
+ {
+ return familyTypefaces;
+ }
+ }
+ }
+ else
+ {
+ var source = key.Source.EnsureAbsolute(key.BaseUri);
+
+ if (TryGetFontCollection(source, out var fontCollection) && fontCollection is IFontCollection2 fontCollection2)
+ {
+ if (fontCollection2.TryGetFamilyTypefaces(fontFamily.Name, out var familyTypefaces))
+ {
+ return familyTypefaces;
+ }
+ }
+ }
+
+ return [];
+ }
+
private bool TryGetFontCollection(Uri source, [NotNullWhen(true)] out IFontCollection? fontCollection)
{
Debug.Assert(source.IsAbsoluteUri);
diff --git a/src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs b/src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs
index 789122988f..f06e5d1562 100644
--- a/src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs
+++ b/src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs
@@ -7,7 +7,7 @@ using Avalonia.Platform;
namespace Avalonia.Media.Fonts
{
- public class EmbeddedFontCollection : FontCollectionBase
+ public class EmbeddedFontCollection : FontCollectionBase, IFontCollection2
{
private readonly List _fontFamilies = new List(1);
@@ -142,5 +142,26 @@ namespace Avalonia.Media.Fonts
glyphTypeface);
}
}
+
+ bool IFontCollection2.TryGetFamilyTypefaces(string familyName, [NotNullWhen(true)] out IReadOnlyList? familyTypefaces)
+ {
+ familyTypefaces = null;
+
+ if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))
+ {
+ var typefaces = new List(glyphTypefaces.Count);
+
+ foreach (var key in glyphTypefaces.Keys)
+ {
+ typefaces.Add(new Typeface(new FontFamily(_key, familyName), key.Style, key.Weight, key.Stretch));
+ }
+
+ familyTypefaces = typefaces;
+
+ return true;
+ }
+
+ return false;
+ }
}
}
diff --git a/src/Avalonia.Base/Media/Fonts/IFontCollection.cs b/src/Avalonia.Base/Media/Fonts/IFontCollection.cs
index 1a30f168f1..a2fbdb69b0 100644
--- a/src/Avalonia.Base/Media/Fonts/IFontCollection.cs
+++ b/src/Avalonia.Base/Media/Fonts/IFontCollection.cs
@@ -47,4 +47,17 @@ namespace Avalonia.Media.Fonts
bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight,
FontStretch fontStretch, string? familyName, CultureInfo? culture, out Typeface typeface);
}
+
+ internal interface IFontCollection2 : IFontCollection
+ {
+ ///
+ /// Tries to get a list of typefaces for the specified family name.
+ ///
+ /// The family name.
+ /// The list of typefaces.
+ ///
+ /// True, if the could get the list of typefaces, False otherwise.
+ ///
+ bool TryGetFamilyTypefaces(string familyName, [NotNullWhen(true)] out IReadOnlyList? familyTypefaces);
+ }
}
diff --git a/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs b/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
index be5c2a2588..d59b1d1954 100644
--- a/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
+++ b/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
@@ -7,7 +7,7 @@ using Avalonia.Platform;
namespace Avalonia.Media.Fonts
{
- internal class SystemFontCollection : FontCollectionBase
+ internal class SystemFontCollection : FontCollectionBase, IFontCollection2
{
private readonly FontManager _fontManager;
private readonly List _familyNames;
@@ -158,5 +158,17 @@ namespace Avalonia.Media.Fonts
glyphTypeface);
}
}
+
+ bool IFontCollection2.TryGetFamilyTypefaces(string familyName, [NotNullWhen(true)] out IReadOnlyList? familyTypefaces)
+ {
+ familyTypefaces = null;
+
+ if (_fontManager.PlatformImpl is IFontManagerImpl2 fontManagerImpl2)
+ {
+ return fontManagerImpl2.TryGetFamilyTypefaces(familyName, out familyTypefaces);
+ }
+
+ return false;
+ }
}
}
diff --git a/src/Avalonia.Base/Media/Fonts/Tables/OS2Table.cs b/src/Avalonia.Base/Media/Fonts/Tables/OS2Table.cs
index 73d80edd7d..9dc41ef083 100644
--- a/src/Avalonia.Base/Media/Fonts/Tables/OS2Table.cs
+++ b/src/Avalonia.Base/Media/Fonts/Tables/OS2Table.cs
@@ -11,8 +11,7 @@ namespace Avalonia.Media.Fonts.Tables
{
internal const string TableName = "OS/2";
internal static OpenTypeTag Tag = OpenTypeTag.Parse(TableName);
-
- private readonly ushort styleType;
+
private readonly byte[] panose;
private readonly short capHeight;
private readonly short familyClass;
@@ -31,8 +30,6 @@ namespace Avalonia.Media.Fonts.Tables
private readonly ushort lowerOpticalPointSize;
private readonly ushort maxContext;
private readonly ushort upperOpticalPointSize;
- private readonly ushort weightClass;
- private readonly ushort widthClass;
private readonly short averageCharWidth;
public OS2Table(
@@ -67,9 +64,9 @@ namespace Avalonia.Media.Fonts.Tables
ushort winDescent)
{
this.averageCharWidth = averageCharWidth;
- this.weightClass = weightClass;
- this.widthClass = widthClass;
- this.styleType = styleType;
+ WeightClass = weightClass;
+ WidthClass = widthClass;
+ StyleType = styleType;
SubscriptXSize = subscriptXSize;
SubscriptYSize = subscriptYSize;
SubscriptXOffset = subscriptXOffset;
@@ -108,9 +105,9 @@ namespace Avalonia.Media.Fonts.Tables
ushort maxContext)
: this(
version0Table.averageCharWidth,
- version0Table.weightClass,
- version0Table.widthClass,
- version0Table.styleType,
+ version0Table.WeightClass,
+ version0Table.WidthClass,
+ version0Table.StyleType,
version0Table.SubscriptXSize,
version0Table.SubscriptYSize,
version0Table.SubscriptXOffset,
@@ -249,6 +246,12 @@ namespace Avalonia.Media.Fonts.Tables
public short SuperscriptYSize { get; }
+ public ushort StyleType { get; }
+
+ public ushort WeightClass { get; }
+
+ public ushort WidthClass { get; }
+
public static OS2Table? Load(IGlyphTypeface glyphTypeface)
{
if (!glyphTypeface.TryGetTable(Tag, out var table))
diff --git a/src/Avalonia.Base/Platform/IFontManagerImpl.cs b/src/Avalonia.Base/Platform/IFontManagerImpl.cs
index f868bfaaed..ce9f85a5e2 100644
--- a/src/Avalonia.Base/Platform/IFontManagerImpl.cs
+++ b/src/Avalonia.Base/Platform/IFontManagerImpl.cs
@@ -1,7 +1,9 @@
-using System.Diagnostics.CodeAnalysis;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using Avalonia.Media;
+using Avalonia.Media.Fonts;
using Avalonia.Metadata;
namespace Avalonia.Platform
@@ -60,4 +62,17 @@ namespace Avalonia.Platform
///
bool TryCreateGlyphTypeface(Stream stream, FontSimulations fontSimulations, [NotNullWhen(returnValue: true)] out IGlyphTypeface? glyphTypeface);
}
+
+ internal interface IFontManagerImpl2 : IFontManagerImpl
+ {
+ ///
+ /// Tries to get a list of typefaces for the specified family name.
+ ///
+ /// The family name.
+ /// The list of typefaces.
+ ///
+ /// True, if the could get the list of typefaces, False otherwise.
+ ///
+ bool TryGetFamilyTypefaces(string familyName, [NotNullWhen(true)] out IReadOnlyList? familyTypefaces);
+ }
}
diff --git a/src/Avalonia.Fonts.Inter/Assets/Inter-ExtraLight.ttf b/src/Avalonia.Fonts.Inter/Assets/Inter-ExtraLight.ttf
deleted file mode 100644
index 64aee30a4e..0000000000
Binary files a/src/Avalonia.Fonts.Inter/Assets/Inter-ExtraLight.ttf and /dev/null differ
diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs
index 1865594aa5..477348b59a 100644
--- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs
+++ b/src/Skia/Avalonia.Skia/FontManagerImpl.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
@@ -8,7 +9,7 @@ using SkiaSharp;
namespace Avalonia.Skia
{
- internal class FontManagerImpl : IFontManagerImpl
+ internal class FontManagerImpl : IFontManagerImpl, IFontManagerImpl2
{
private SKFontManager _skFontManager = SKFontManager.Default;
@@ -119,5 +120,28 @@ namespace Avalonia.Skia
return false;
}
+
+ public bool TryGetFamilyTypefaces(string familyName, [NotNullWhen(true)] out IReadOnlyList? familyTypefaces)
+ {
+ familyTypefaces = null;
+
+ var set = _skFontManager.GetFontStyles(familyName);
+
+ if(set.Count == 0)
+ {
+ return false;
+ }
+
+ var typefaces = new List(set.Count);
+
+ foreach (var fontStyle in set)
+ {
+ typefaces.Add(new Typeface(familyName, fontStyle.Slant.ToAvalonia(), (FontWeight)fontStyle.Weight, (FontStretch)fontStyle.Width));
+ }
+
+ familyTypefaces = typefaces;
+
+ return true;
+ }
}
}
diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
index 0b9a48deab..9170393034 100644
--- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
+++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
@@ -90,13 +90,17 @@ namespace Avalonia.Skia
FontSimulations = fontSimulations;
- Weight = (fontSimulations & FontSimulations.Bold) != 0 ? FontWeight.Bold : (FontWeight)typeface.FontWeight;
+ var fontWeight = _os2Table != null ? (FontWeight)_os2Table.WeightClass : FontWeight.Normal;
- Style = (fontSimulations & FontSimulations.Oblique) != 0 ?
- FontStyle.Italic :
- typeface.FontSlant.ToAvalonia();
+ Weight = (fontSimulations & FontSimulations.Bold) != 0 ? FontWeight.Bold : fontWeight;
- Stretch = (FontStretch)typeface.FontStyle.Width;
+ var style = _os2Table != null ? GetFontStyle(_os2Table.FontStyle) : FontStyle.Normal;
+
+ Style = (fontSimulations & FontSimulations.Oblique) != 0 ? FontStyle.Italic : style;
+
+ var stretch = _os2Table != null ? (FontStretch)_os2Table.WidthClass : FontStretch.Normal;
+
+ Stretch = stretch;
_nameTable = NameTable.Load(this);
@@ -265,6 +269,21 @@ namespace Avalonia.Skia
return Font.GetHorizontalGlyphAdvances(glyphIndices);
}
+ private static FontStyle GetFontStyle(OS2Table.FontStyleSelection styleSelection)
+ {
+ if((styleSelection & OS2Table.FontStyleSelection.ITALIC) != 0)
+ {
+ return FontStyle.Italic;
+ }
+
+ if((styleSelection & OS2Table.FontStyleSelection.OBLIQUE) != 0)
+ {
+ return FontStyle.Oblique;
+ }
+
+ return FontStyle.Normal;
+ }
+
private Blob? GetTable(Face face, Tag tag)
{
var size = _typeface.GetTableSize(tag);
diff --git a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs
index 6114b662d9..ac6da9c661 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs
@@ -364,5 +364,21 @@ namespace Avalonia.Skia.UnitTests.Media
}
}
}
+
+ [Fact]
+ public void Should_Get_FamilyTypefaces()
+ {
+ using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new FontManagerImpl())))
+ {
+ using (AvaloniaLocator.EnterScope())
+ {
+ FontManager.Current.AddFontCollection(new InterFontCollection());
+
+ var familyTypefaces = FontManager.Current.GetFamilyTypefaces(new FontFamily("fonts:Inter#Inter"));
+
+ Assert.Equal(6, familyTypefaces.Count);
+ }
+ }
+ }
}
}