diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index 05ae4e11f3..98f9568316 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -152,6 +152,15 @@ namespace Avalonia.Controls.Presenters
set => TextBlock.SetFontWeight(this, value);
}
+ ///
+ /// Gets or sets the font stretch.
+ ///
+ public FontStretch FontStretch
+ {
+ get => TextBlock.GetFontStretch(this);
+ set => TextBlock.SetFontStretch(this, value);
+ }
+
///
/// Gets or sets a brush used to paint the text.
///
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index e41328b79a..2dbb10f8e2 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -64,6 +64,15 @@ namespace Avalonia.Controls
inherits: true,
defaultValue: FontWeight.Normal);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty FontStretchProperty =
+ AvaloniaProperty.RegisterAttached(
+ nameof(FontStretch),
+ inherits: true,
+ defaultValue: FontStretch.Normal);
+
///
/// Defines the property.
///
@@ -219,6 +228,15 @@ namespace Avalonia.Controls
get { return GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
+
+ ///
+ /// Gets or sets the font stretch.
+ ///
+ public FontStretch FontStretch
+ {
+ get { return GetValue(FontStretchProperty); }
+ set { SetValue(FontStretchProperty, value); }
+ }
///
/// Gets or sets a brush used to paint the text.
@@ -297,7 +315,7 @@ namespace Avalonia.Controls
/// Gets the value of the attached on a control.
///
/// The control.
- /// The font family.
+ /// The font size.
public static double GetFontSize(Control control)
{
return control.GetValue(FontSizeProperty);
@@ -307,7 +325,7 @@ namespace Avalonia.Controls
/// Gets the value of the attached on a control.
///
/// The control.
- /// The font family.
+ /// The font style.
public static FontStyle GetFontStyle(Control control)
{
return control.GetValue(FontStyleProperty);
@@ -317,11 +335,21 @@ namespace Avalonia.Controls
/// Gets the value of the attached on a control.
///
/// The control.
- /// The font family.
+ /// The font weight.
public static FontWeight GetFontWeight(Control control)
{
return control.GetValue(FontWeightProperty);
}
+
+ ///
+ /// Gets the value of the attached on a control.
+ ///
+ /// The control.
+ /// The font stretch.
+ public static FontStretch GetFontStretch(Control control)
+ {
+ return control.GetValue(FontStretchProperty);
+ }
///
/// Gets the value of the attached on a control.
@@ -338,7 +366,6 @@ namespace Avalonia.Controls
///
/// The control.
/// The property value to set.
- /// The font family.
public static void SetFontFamily(Control control, FontFamily value)
{
control.SetValue(FontFamilyProperty, value);
@@ -349,7 +376,6 @@ namespace Avalonia.Controls
///
/// The control.
/// The property value to set.
- /// The font family.
public static void SetFontSize(Control control, double value)
{
control.SetValue(FontSizeProperty, value);
@@ -360,7 +386,6 @@ namespace Avalonia.Controls
///
/// The control.
/// The property value to set.
- /// The font family.
public static void SetFontStyle(Control control, FontStyle value)
{
control.SetValue(FontStyleProperty, value);
@@ -371,18 +396,26 @@ namespace Avalonia.Controls
///
/// The control.
/// The property value to set.
- /// The font family.
public static void SetFontWeight(Control control, FontWeight value)
{
control.SetValue(FontWeightProperty, value);
}
+
+ ///
+ /// Sets the value of the attached on a control.
+ ///
+ /// The control.
+ /// The property value to set.
+ public static void SetFontStretch(Control control, FontStretch value)
+ {
+ control.SetValue(FontStretchProperty, value);
+ }
///
/// Sets the value of the attached on a control.
///
/// The control.
/// The property value to set.
- /// The font family.
public static void SetForeground(Control control, IBrush? value)
{
control.SetValue(ForegroundProperty, value);
@@ -432,7 +465,7 @@ namespace Avalonia.Controls
{
return new TextLayout(
text ?? string.Empty,
- new Typeface(FontFamily, FontStyle, FontWeight),
+ new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
FontSize,
Foreground ?? Brushes.Transparent,
TextAlignment,
diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs
index b619b9d129..9ac74e5e78 100644
--- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs
+++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs
@@ -157,9 +157,10 @@ namespace Avalonia.Headless
return new List { "Arial" };
}
- public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily, CultureInfo culture, out Typeface typeface)
+ public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch,
+ FontFamily fontFamily, CultureInfo culture, out Typeface typeface)
{
- typeface = new Typeface("Arial", fontStyle, fontWeight);
+ typeface = new Typeface("Arial", fontStyle, fontWeight, fontStretch);
return true;
}
}
diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt
index 2a2f4d13f9..b74b678632 100644
--- a/src/Avalonia.Visuals/ApiCompatBaseline.txt
+++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt
@@ -6,6 +6,7 @@ MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation
InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract.
MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Media.FormattedText)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean Avalonia.Media.FontManager.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor(Avalonia.Platform.IPlatformRenderInterface)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
@@ -49,6 +50,8 @@ MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult..ctor()'
MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsInside.set(System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsTrailing.set(System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.TextPosition.set(System.Int32)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.Typeface..ctor(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.Typeface..ctor(System.String, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract.
TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract.
@@ -121,6 +124,9 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor
MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Platform.IFormattedTextImpl)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.PopBitmapBlendMode()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.PushBitmapBlendMode(Avalonia.Visuals.Media.Imaging.BitmapBlendingMode)' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontStretch, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract.
TypesMustExist : Type 'Avalonia.Platform.IFormattedTextImpl' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Double Avalonia.Platform.IGeometryImpl.ContourLength' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Double Avalonia.Platform.IGeometryImpl.ContourLength.get()' is present in the implementation but not in the contract.
@@ -147,4 +153,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.GlyphR
MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Rendering.RendererBase.RenderFps(Avalonia.Platform.IDrawingContextImpl, Avalonia.Rect, System.Nullable)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Utilities.ReadOnlySlice..ctor(System.ReadOnlyMemory, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 147
+Total Issues: 153
diff --git a/src/Avalonia.Visuals/Media/FontManager.cs b/src/Avalonia.Visuals/Media/FontManager.cs
index 72c1c8dcac..37091b82e3 100644
--- a/src/Avalonia.Visuals/Media/FontManager.cs
+++ b/src/Avalonia.Visuals/Media/FontManager.cs
@@ -118,26 +118,22 @@ namespace Avalonia.Media
/// The codepoint to match against.
/// The font style.
/// The font weight.
+ /// The font stretch.
/// The font family. This is optional and used for fallback lookup.
/// The culture.
/// The matching .
///
/// True, if the could match the character to specified parameters, False otherwise.
///
- public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
- FontWeight fontWeight,
+ public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight,
+ FontStretch fontStretch,
FontFamily? fontFamily, CultureInfo? culture, out Typeface typeface)
{
if(_fontFallbacks != null)
{
foreach (var fallback in _fontFallbacks)
{
- if(fallback is null)
- {
- continue;
- }
-
- typeface = new Typeface(fallback.FontFamily, fontStyle, fontWeight);
+ typeface = new Typeface(fallback.FontFamily, fontStyle, fontWeight, fontStretch);
var glyphTypeface = typeface.GlyphTypeface;
@@ -147,7 +143,7 @@ namespace Avalonia.Media
}
}
- return PlatformImpl.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontFamily, culture, out typeface);
+ return PlatformImpl.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, fontFamily, culture, out typeface);
}
}
}
diff --git a/src/Avalonia.Visuals/Media/FontStretch.cs b/src/Avalonia.Visuals/Media/FontStretch.cs
new file mode 100644
index 0000000000..b110475215
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/FontStretch.cs
@@ -0,0 +1,19 @@
+namespace Avalonia.Media
+{
+ ///
+ /// FontStretch describes relative change from the normal aspect ratio
+ /// as specified by a font designer for the glyphs in a font.
+ ///
+ public enum FontStretch
+ {
+ Normal = 5,
+ UltraCondensed = 1,
+ ExtraCondensed = 2,
+ Condensed = 3,
+ SemiCondensed = 4,
+ SemiExpanded = 6,
+ Expanded = 7,
+ ExtraExpanded = 8,
+ UltraExpanded = 9
+ }
+}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs
index faa73719c8..8d3b7a67c7 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs
@@ -118,7 +118,8 @@ namespace Avalonia.Media.TextFormatting
//ToDo: Fix FontFamily fallback
var matchFound =
FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight,
- defaultTypeface.FontFamily, defaultProperties.CultureInfo, out currentTypeface);
+ defaultTypeface.Stretch, defaultTypeface.FontFamily, defaultProperties.CultureInfo,
+ out currentTypeface);
if (matchFound && TryGetShapeableLength(text, currentTypeface, out count, out _))
{
diff --git a/src/Avalonia.Visuals/Media/Typeface.cs b/src/Avalonia.Visuals/Media/Typeface.cs
index 45540a5812..f0daa841d9 100644
--- a/src/Avalonia.Visuals/Media/Typeface.cs
+++ b/src/Avalonia.Visuals/Media/Typeface.cs
@@ -16,18 +16,26 @@ namespace Avalonia.Media
/// The font family.
/// The font style.
/// The font weight.
+ /// The font stretch.
public Typeface([NotNull] FontFamily fontFamily,
FontStyle style = FontStyle.Normal,
- FontWeight weight = FontWeight.Normal)
+ FontWeight weight = FontWeight.Normal,
+ FontStretch stretch = FontStretch.Normal)
{
if (weight <= 0)
{
throw new ArgumentException("Font weight must be > 0.");
}
+
+ if ((int)stretch < 1)
+ {
+ throw new ArgumentException("Font stretch must be > 1.");
+ }
FontFamily = fontFamily;
Style = style;
Weight = weight;
+ Stretch = stretch;
}
///
@@ -36,10 +44,12 @@ namespace Avalonia.Media
/// The name of the font family.
/// The font style.
/// The font weight.
+ /// The font stretch.
public Typeface(string fontFamilyName,
FontStyle style = FontStyle.Normal,
- FontWeight weight = FontWeight.Normal)
- : this(new FontFamily(fontFamilyName), style, weight)
+ FontWeight weight = FontWeight.Normal,
+ FontStretch stretch = FontStretch.Normal)
+ : this(new FontFamily(fontFamilyName), style, weight, stretch)
{
}
@@ -59,6 +69,11 @@ namespace Avalonia.Media
/// Gets the font weight.
///
public FontWeight Weight { get; }
+
+ ///
+ /// Gets the font stretch.
+ ///
+ public FontStretch Stretch { get; }
///
/// Gets the glyph typeface.
@@ -85,7 +100,8 @@ namespace Avalonia.Media
public bool Equals(Typeface other)
{
- return FontFamily == other.FontFamily && Style == other.Style && Weight == other.Weight;
+ return FontFamily == other.FontFamily && Style == other.Style &&
+ Weight == other.Weight && Stretch == other.Stretch;
}
public override int GetHashCode()
@@ -95,6 +111,7 @@ namespace Avalonia.Media
var hashCode = (FontFamily != null ? FontFamily.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (int)Style;
hashCode = (hashCode * 397) ^ (int)Weight;
+ hashCode = (hashCode * 397) ^ (int)Stretch;
return hashCode;
}
}
diff --git a/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs b/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs
index 113c72d373..0110287afd 100644
--- a/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs
+++ b/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs
@@ -23,6 +23,7 @@ namespace Avalonia.Platform
/// The codepoint to match against.
/// The font style.
/// The font weight.
+ /// The font stretch.
/// The font family. This is optional and used for fallback lookup.
/// The culture.
/// The matching typeface.
@@ -30,7 +31,7 @@ namespace Avalonia.Platform
/// True, if the could match the character to specified parameters, False otherwise.
///
bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
- FontWeight fontWeight,
+ FontWeight fontWeight, FontStretch fontStretch,
FontFamily? fontFamily, CultureInfo? culture, out Typeface typeface);
///
diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs
index 6b560ac739..024bd67532 100644
--- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs
+++ b/src/Skia/Avalonia.Skia/FontManagerImpl.cs
@@ -29,27 +29,27 @@ namespace Avalonia.Skia
[ThreadStatic] private static string[] t_languageTagBuffer;
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
- FontWeight fontWeight,
+ FontWeight fontWeight, FontStretch fontStretch,
FontFamily fontFamily, CultureInfo culture, out Typeface fontKey)
{
SKFontStyle skFontStyle;
switch (fontWeight)
{
- case FontWeight.Normal when fontStyle == FontStyle.Normal:
+ case FontWeight.Normal when fontStyle == FontStyle.Normal && fontStretch == FontStretch.Normal:
skFontStyle = SKFontStyle.Normal;
break;
- case FontWeight.Normal when fontStyle == FontStyle.Italic:
+ case FontWeight.Normal when fontStyle == FontStyle.Italic && fontStretch == FontStretch.Normal:
skFontStyle = SKFontStyle.Italic;
break;
- case FontWeight.Bold when fontStyle == FontStyle.Normal:
+ case FontWeight.Bold when fontStyle == FontStyle.Normal && fontStretch == FontStretch.Normal:
skFontStyle = SKFontStyle.Bold;
break;
- case FontWeight.Bold when fontStyle == FontStyle.Italic:
+ case FontWeight.Bold when fontStyle == FontStyle.Italic && fontStretch == FontStretch.Normal:
skFontStyle = SKFontStyle.BoldItalic;
break;
default:
- skFontStyle = new SKFontStyle((SKFontStyleWeight)fontWeight, SKFontStyleWidth.Normal, (SKFontStyleSlant)fontStyle);
+ skFontStyle = new SKFontStyle((SKFontStyleWeight)fontWeight, (SKFontStyleWidth)fontStretch, (SKFontStyleSlant)fontStyle);
break;
}
@@ -80,7 +80,7 @@ namespace Avalonia.Skia
continue;
}
- fontKey = new Typeface(skTypeface.FamilyName, fontStyle, fontWeight);
+ fontKey = new Typeface(skTypeface.FamilyName, fontStyle, fontWeight, fontStretch);
return true;
}
@@ -91,7 +91,7 @@ namespace Avalonia.Skia
if (skTypeface != null)
{
- fontKey = new Typeface(skTypeface.FamilyName, fontStyle, fontWeight);
+ fontKey = new Typeface(skTypeface.FamilyName, fontStyle, fontWeight, fontStretch);
return true;
}
@@ -109,10 +109,17 @@ namespace Avalonia.Skia
if (typeface.FontFamily.Key == null)
{
var defaultName = SKTypeface.Default.FamilyName;
- var fontStyle = new SKFontStyle((SKFontStyleWeight)typeface.Weight, SKFontStyleWidth.Normal, (SKFontStyleSlant)typeface.Style);
+
+ var fontStyle = new SKFontStyle((SKFontStyleWeight)typeface.Weight, (SKFontStyleWidth)typeface.Stretch,
+ (SKFontStyleSlant)typeface.Style);
foreach (var familyName in typeface.FontFamily.FamilyNames)
{
+ if(familyName == FontFamily.DefaultFontFamilyName)
+ {
+ continue;
+ }
+
skTypeface = _skFontManager.MatchFamily(familyName, fontStyle);
if (skTypeface is null
diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
index 2b9d0b103e..f66df9e6e9 100644
--- a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
+++ b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
@@ -1,6 +1,4 @@
using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
using Avalonia.Media;
using SkiaSharp;
@@ -18,48 +16,168 @@ namespace Avalonia.Skia
public SKTypeface Get(Typeface typeface)
{
- return GetNearestMatch(_typefaces, typeface);
+ return GetNearestMatch(typeface);
}
- private static SKTypeface GetNearestMatch(IDictionary typefaces, Typeface key)
+ private SKTypeface GetNearestMatch(Typeface key)
{
- if (typefaces.TryGetValue(key, out var typeface))
+ if (_typefaces.Count == 0)
{
- return typeface;
+ return null;
}
- var initialWeight = (int)key.Weight;
+ if (_typefaces.TryGetValue(key, out var typeface))
+ {
+ return typeface;
+ }
+
+ if(key.Style != FontStyle.Normal)
+ {
+ key = new Typeface(key.FontFamily, FontStyle.Normal, key.Weight, key.Stretch);
+ }
+
+ if(key.Stretch != FontStretch.Normal)
+ {
+ if(TryFindStretchFallback(key, out typeface))
+ {
+ return typeface;
+ }
+
+ if(key.Weight != FontWeight.Normal)
+ {
+ if (TryFindStretchFallback(new Typeface(key.FontFamily, key.Style, FontWeight.Normal, key.Stretch), out typeface))
+ {
+ return typeface;
+ }
+ }
+
+ key = new Typeface(key.FontFamily, key.Style, key.Weight, FontStretch.Normal);
+ }
+
+ if(TryFindWeightFallback(key, out typeface))
+ {
+ return typeface;
+ }
+
+ //Nothing was found so we try some regular cases.
+ if (_typefaces.TryGetValue(new Typeface(key.FontFamily), out typeface))
+ {
+ return typeface;
+ }
+
+ return _typefaces.TryGetValue(new Typeface(key.FontFamily, FontStyle.Italic), out typeface) ?
+ typeface :
+ null;
+ }
+
+ private bool TryFindStretchFallback(Typeface key, out SKTypeface typeface)
+ {
+ typeface = null;
+ var stretch = (int)key.Stretch;
+
+ if (stretch < 5)
+ {
+ for (var i = 0; stretch + i < 9; i++)
+ {
+ if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, key.Weight, (FontStretch)(stretch + i)), out typeface))
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ for (var i = 0; stretch - i > 1; i++)
+ {
+ if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, key.Weight, (FontStretch)(stretch - i)), out typeface))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ private bool TryFindWeightFallback(Typeface key, out SKTypeface typeface)
+ {
+ typeface = null;
var weight = (int)key.Weight;
- weight -= weight % 50; // make sure we start at a full weight
+ //If the target weight given is between 400 and 500 inclusive
+ if (weight >= 400 && weight <= 500)
+ {
+ //Look for available weights between the target and 500, in ascending order.
+ for (var i = 0; weight + i <= 500; i += 50)
+ {
+ if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight + i), key.Stretch), out typeface))
+ {
+ return true;
+ }
+ }
+
+ //If no match is found, look for available weights less than the target, in descending order.
+ for (var i = 0; weight - i >= 100; i += 50)
+ {
+ if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight - i), key.Stretch), out typeface))
+ {
+ return true;
+ }
+ }
- for (var i = 0; i < 2; i++)
+ //If no match is found, look for available weights greater than 500, in ascending order.
+ for (var i = 0; weight + i <= 900; i += 50)
+ {
+ if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight + i), key.Stretch), out typeface))
+ {
+ return true;
+ }
+ }
+ }
+
+ //If a weight less than 400 is given, look for available weights less than the target, in descending order.
+ if (weight < 400)
{
- for (var j = 0; j < initialWeight; j += 50)
+ for (var i = 0; weight - i >= 100; i += 50)
+ {
+ if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight - i), key.Stretch), out typeface))
+ {
+ return true;
+ }
+ }
+
+ //If no match is found, look for available weights less than the target, in descending order.
+ for (var i = 0; weight + i <= 900; i += 50)
{
- if (weight - j >= 100)
+ if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight + i), key.Stretch), out typeface))
{
- if (typefaces.TryGetValue(new Typeface(key.FontFamily, (FontStyle)i, (FontWeight)(weight - j)), out typeface))
- {
- return typeface;
- }
+ return true;
}
+ }
+ }
- if (weight + j > 900)
+ //If a weight greater than 500 is given, look for available weights greater than the target, in ascending order.
+ if (weight > 500)
+ {
+ for (var i = 0; weight + i <= 900; i += 50)
+ {
+ if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight + i), key.Stretch), out typeface))
{
- continue;
+ return true;
}
+ }
- if (typefaces.TryGetValue(new Typeface(key.FontFamily, (FontStyle)i, (FontWeight)(weight + j)), out typeface))
+ //If no match is found, look for available weights less than the target, in descending order.
+ for (var i = 0; weight - i >= 100; i += 50)
+ {
+ if (_typefaces.TryGetValue(new Typeface(key.FontFamily, key.Style, (FontWeight)(weight - i), key.Stretch), out typeface))
{
- return typeface;
+ return true;
}
}
}
- //Nothing was found so we try to get a regular typeface.
- return typefaces.TryGetValue(new Typeface(key.FontFamily), out typeface) ? typeface : null;
+ return false;
}
}
}
diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
index 2816146806..5ca7f40d17 100644
--- a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
+++ b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
@@ -51,13 +51,13 @@ namespace Avalonia.Skia
if (typeface == null)
throw new InvalidOperationException("Typeface could not be loaded.");
- if (typeface.FamilyName != fontFamily.Name)
+ if (!typeface.FamilyName.Contains(fontFamily.Name))
{
continue;
}
var key = new Typeface(fontFamily, typeface.FontSlant.ToAvalonia(),
- (FontWeight)typeface.FontWeight);
+ (FontWeight)typeface.FontWeight, (FontStretch)typeface.FontWidth);
typeFaceCollection.AddTypeface(key, typeface);
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs b/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs
index 78bf25d607..792bf2d0be 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Direct2D1FontCollectionCache.cs
@@ -5,6 +5,7 @@ using SharpDX.DirectWrite;
using FontFamily = Avalonia.Media.FontFamily;
using FontStyle = SharpDX.DirectWrite.FontStyle;
using FontWeight = SharpDX.DirectWrite.FontWeight;
+using FontStretch = SharpDX.DirectWrite.FontStretch;
namespace Avalonia.Direct2D1.Media
{
@@ -32,7 +33,7 @@ namespace Avalonia.Direct2D1.Media
{
return fontCollection.GetFontFamily(index).GetFirstMatchingFont(
(FontWeight)typeface.Weight,
- FontStretch.Normal,
+ (FontStretch)typeface.Stretch,
(FontStyle)typeface.Style);
}
}
@@ -41,7 +42,7 @@ namespace Avalonia.Direct2D1.Media
return InstalledFontCollection.GetFontFamily(index).GetFirstMatchingFont(
(FontWeight)typeface.Weight,
- FontStretch.Normal,
+ (FontStretch)typeface.Stretch,
(FontStyle)typeface.Style);
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs
index 6d95d759ec..c996337520 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs
@@ -4,6 +4,7 @@ using Avalonia.Media;
using Avalonia.Platform;
using SharpDX.DirectWrite;
using FontFamily = Avalonia.Media.FontFamily;
+using FontStretch = Avalonia.Media.FontStretch;
using FontStyle = Avalonia.Media.FontStyle;
using FontWeight = Avalonia.Media.FontWeight;
@@ -32,7 +33,7 @@ namespace Avalonia.Direct2D1.Media
}
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
- FontWeight fontWeight,
+ FontWeight fontWeight, FontStretch fontStretch,
FontFamily fontFamily, CultureInfo culture, out Typeface typeface)
{
var familyCount = Direct2D1FontCollectionCache.InstalledFontCollection.FontFamilyCount;
@@ -40,7 +41,8 @@ namespace Avalonia.Direct2D1.Media
for (var i = 0; i < familyCount; i++)
{
var font = Direct2D1FontCollectionCache.InstalledFontCollection.GetFontFamily(i)
- .GetMatchingFonts((SharpDX.DirectWrite.FontWeight)fontWeight, FontStretch.Normal,
+ .GetMatchingFonts((SharpDX.DirectWrite.FontWeight)fontWeight,
+ (SharpDX.DirectWrite.FontStretch)fontStretch,
(SharpDX.DirectWrite.FontStyle)fontStyle).GetFont(0);
if (!font.HasCharacter(codepoint))
@@ -50,7 +52,7 @@ namespace Avalonia.Direct2D1.Media
var fontFamilyName = font.FontFamily.FamilyNames.GetString(0);
- typeface = new Typeface(fontFamilyName, fontStyle, fontWeight);
+ typeface = new Typeface(fontFamilyName, fontStyle, fontWeight, fontStretch);
return true;
}
diff --git a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
index cc4b727bd9..04b408a666 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
@@ -16,7 +16,7 @@ namespace Avalonia.Skia.UnitTests.Media
private readonly Typeface _defaultTypeface =
new Typeface("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Mono");
private readonly Typeface _italicTypeface =
- new Typeface("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Sans");
+ new Typeface("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Sans", FontStyle.Italic);
private readonly Typeface _emojiTypeface =
new Typeface("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Twitter Color Emoji");
@@ -38,7 +38,8 @@ namespace Avalonia.Skia.UnitTests.Media
private readonly string[] _bcp47 = { CultureInfo.CurrentCulture.ThreeLetterISOLanguageName, CultureInfo.CurrentCulture.TwoLetterISOLanguageName };
- public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily,
+ public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch,
+ FontFamily fontFamily,
CultureInfo culture, out Typeface typeface)
{
foreach (var customTypeface in _customTypefaces)
@@ -54,7 +55,7 @@ namespace Avalonia.Skia.UnitTests.Media
}
var fallback = SKFontManager.Default.MatchCharacter(fontFamily?.Name, (SKFontStyleWeight)fontWeight,
- SKFontStyleWidth.Normal, (SKFontStyleSlant)fontStyle, _bcp47, codepoint);
+ (SKFontStyleWidth)fontStretch, (SKFontStyleSlant)fontStyle, _bcp47, codepoint);
typeface = new Typeface(fallback?.FamilyName ?? _defaultFamilyName, fontStyle, fontWeight);
diff --git a/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs
index 002da66070..9f958bdcdd 100644
--- a/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs
+++ b/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs
@@ -36,8 +36,8 @@ namespace Avalonia.UnitTests
return _customTypefaces.Select(x => x.FontFamily!.Name);
}
- public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily,
- CultureInfo culture, out Typeface fontKey)
+ public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch,
+ FontFamily fontFamily, CultureInfo culture, out Typeface fontKey)
{
foreach (var customTypeface in _customTypefaces)
{
@@ -62,11 +62,6 @@ namespace Avalonia.UnitTests
{
var fontFamily = typeface.FontFamily;
- if (fontFamily == null)
- {
- return null;
- }
-
if (fontFamily.IsDefault)
{
fontFamily = _defaultTypeface.FontFamily;
diff --git a/tests/Avalonia.UnitTests/MockFontManagerImpl.cs b/tests/Avalonia.UnitTests/MockFontManagerImpl.cs
index ba3b346f1b..e2678394df 100644
--- a/tests/Avalonia.UnitTests/MockFontManagerImpl.cs
+++ b/tests/Avalonia.UnitTests/MockFontManagerImpl.cs
@@ -24,7 +24,8 @@ namespace Avalonia.UnitTests
return new[] { _defaultFamilyName };
}
- public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily,
+ public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight,
+ FontStretch fontStretch, FontFamily fontFamily,
CultureInfo culture, out Typeface fontKey)
{
fontKey = new Typeface(_defaultFamilyName);
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs
index a48639a426..2be0209c85 100644
--- a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs
@@ -64,7 +64,8 @@ namespace Avalonia.Visuals.UnitTests.Media
{
AvaloniaLocator.CurrentMutable.Bind().ToConstant(options);
- FontManager.Current.TryMatchCharacter(1, FontStyle.Normal, FontWeight.Normal, FontFamily.Default, null, out var typeface);
+ FontManager.Current.TryMatchCharacter(1, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal,
+ FontFamily.Default, null, out var typeface);
Assert.Equal("MyFont", typeface.FontFamily.Name);
}