Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FontManager adjustments #15819

Merged
merged 3 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions src/Avalonia.Base/Media/FontManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,16 @@ public bool TryGetGlyphTypeface(Typeface typeface, [NotNullWhen(true)] out IGlyp

var fontFamily = typeface.FontFamily;

typeface = FontCollectionBase.GetImplicitTypeface(typeface);

if (typeface.FontFamily.Name == FontFamily.DefaultFontFamilyName)
{
return TryGetGlyphTypeface(new Typeface(DefaultFontFamily, typeface.Style, typeface.Weight, typeface.Stretch), out glyphTypeface);
}

if (fontFamily.Key is FontFamilyKey)
if (fontFamily.Key != null)
{
if (fontFamily.Key is CompositeFontFamilyKey compositeKey)
{
for (int i = 0; i < compositeKey.Keys.Count; i++)
for (var i = 0; i < compositeKey.Keys.Count; i++)
{
var key = compositeKey.Keys[i];

Expand All @@ -119,9 +117,8 @@ public bool TryGetGlyphTypeface(Typeface typeface, [NotNullWhen(true)] out IGlyp
}
else
{
//Replace known typographic names
var familyName = FontCollectionBase.NormalizeFamilyName(fontFamily.FamilyNames.PrimaryFamilyName);

var familyName = fontFamily.FamilyNames.PrimaryFamilyName;

if (TryGetGlyphTypefaceByKeyAndName(typeface, fontFamily.Key, familyName, out glyphTypeface))
{
return true;
Expand All @@ -132,8 +129,7 @@ public bool TryGetGlyphTypeface(Typeface typeface, [NotNullWhen(true)] out IGlyp
}
else
{
//Replace known typographic names
var familyName = FontCollectionBase.NormalizeFamilyName(fontFamily.FamilyNames.PrimaryFamilyName);
var familyName = fontFamily.FamilyNames.PrimaryFamilyName;

if (SystemFonts.TryGetGlyphTypeface(familyName, typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface))
{
Expand Down
11 changes: 8 additions & 3 deletions src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ public override void Initialize(IFontManagerImpl fontManager)
public override bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface)
{
var typeface = GetImplicitTypeface(new Typeface(familyName, style, weight, stretch), out familyName);

style = typeface.Style;

weight = typeface.Weight;

stretch = typeface.Stretch;

var key = new FontCollectionKey(style, weight, stretch);

if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))
Expand Down Expand Up @@ -113,9 +121,6 @@ public override bool TryGetGlyphTypeface(string familyName, FontStyle style, Fon
}
}

//Replace known typographic names
familyName = NormalizeFamilyName(familyName);

//Try to find a partially matching font
for (var i = 0; i < Count; i++)
{
Expand Down
78 changes: 44 additions & 34 deletions src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.RegularExpressions;
using Avalonia.Platform;
using Avalonia.Utilities;

Expand Down Expand Up @@ -258,30 +257,12 @@ internal static bool TryFindWeightFallback(
return false;
}

private static readonly List<string> s_knownNames = ["Solid", "Regular", "Bold", "Black", "Normal", "Thin", "Italic"];

internal static string NormalizeFamilyName(string familyName)
{
//Return early if no separator is present.
if (!familyName.Contains(' '))
{
return familyName;
}

foreach (var name in s_knownNames)
{
familyName = Regex.Replace(familyName, name, "", RegexOptions.IgnoreCase);
}

return familyName.Trim();
}

internal static Typeface GetImplicitTypeface(Typeface typeface)
internal static Typeface GetImplicitTypeface(Typeface typeface, out string normalizedFamilyName)
{
var familyName = typeface.FontFamily.FamilyNames.PrimaryFamilyName;
normalizedFamilyName = typeface.FontFamily.FamilyNames.PrimaryFamilyName;

//Return early if no separator is present.
if (!familyName.Contains(' '))
if (!normalizedFamilyName.Contains(' '))
{
return typeface;
}
Expand All @@ -290,26 +271,27 @@ internal static Typeface GetImplicitTypeface(Typeface typeface)
var weight = typeface.Weight;
var stretch = typeface.Stretch;

if(TryGetStyle(familyName, out var foundStyle))
if(TryGetStyle(ref normalizedFamilyName, out var foundStyle))
{
style = foundStyle;
}

if(TryGetWeight(familyName, out var foundWeight))
if(TryGetWeight(ref normalizedFamilyName, out var foundWeight))
{
weight = foundWeight;
}

if(TryGetStretch(familyName, out var foundStretch))
if(TryGetStretch(ref normalizedFamilyName, out var foundStretch))
{
stretch = foundStretch;
}

//Preserve old font source
return new Typeface(typeface.FontFamily, style, weight, stretch);

}

internal static bool TryGetWeight(string familyName, out FontWeight weight)
internal static bool TryGetWeight(ref string familyName, out FontWeight weight)
{
weight = FontWeight.Normal;

Expand All @@ -319,16 +301,25 @@ internal static bool TryGetWeight(string familyName, out FontWeight weight)

while (tokenizer.TryReadString(out var weightString))
{
if (Enum.TryParse(weightString, true, out weight))
if (new StringTokenizer(weightString).TryReadInt32(out _))
{
return true;
continue;
}

if (!Enum.TryParse(weightString, true, out weight))
{
continue;
}

familyName = familyName.Replace(" " + weightString, "").TrimEnd();

return true;
}

return false;
}

internal static bool TryGetStyle(string familyName, out FontStyle style)
internal static bool TryGetStyle(ref string familyName, out FontStyle style)
{
style = FontStyle.Normal;

Expand All @@ -338,16 +329,26 @@ internal static bool TryGetStyle(string familyName, out FontStyle style)

while (tokenizer.TryReadString(out var styleString))
{
if (Enum.TryParse(styleString, true, out style))
//Do not try to parse an integer
if (new StringTokenizer(styleString).TryReadInt32(out _))
{
return true;
continue;
}

if (!Enum.TryParse(styleString, true, out style))
{
continue;
}

familyName = familyName.Replace(" " + styleString, "").TrimEnd();

return true;
}

return false;
}

internal static bool TryGetStretch(string familyName, out FontStretch stretch)
internal static bool TryGetStretch(ref string familyName, out FontStretch stretch)
{
stretch = FontStretch.Normal;

Expand All @@ -357,10 +358,19 @@ internal static bool TryGetStretch(string familyName, out FontStretch stretch)

while (tokenizer.TryReadString(out var stretchString))
{
if (Enum.TryParse(stretchString, true, out stretch))
if (new StringTokenizer(stretchString).TryReadInt32(out _))
{
return true;
continue;
}

if (!Enum.TryParse(stretchString, true, out stretch))
{
continue;
}

familyName = familyName.Replace(" " + stretchString, "").TrimEnd();

return true;
}

return false;
Expand Down
53 changes: 31 additions & 22 deletions src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ public override bool TryGetGlyphTypeface(string familyName, FontStyle style, Fon
{
glyphTypeface = null;

var typeface = GetImplicitTypeface(new Typeface(familyName, style, weight, stretch), out familyName);

style = typeface.Style;

weight = typeface.Weight;

stretch = typeface.Stretch;

var key = new FontCollectionKey(style, weight, stretch);

if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))
Expand All @@ -58,38 +66,39 @@ public override bool TryGetGlyphTypeface(string familyName, FontStyle style, Fon
glyphTypefaces ??= _glyphTypefaceCache.GetOrAdd(familyName,
(_) => new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>());

//Try top create the font via system font manager
if (_fontManager.PlatformImpl.TryCreateGlyphTypeface(familyName, style, weight, stretch, out glyphTypeface))
//Try to create the glyph typeface via system font manager
if (!_fontManager.PlatformImpl.TryCreateGlyphTypeface(familyName, style, weight, stretch,
out glyphTypeface))
{
glyphTypefaces.TryAdd(key, glyphTypeface);
glyphTypefaces.TryAdd(key, null);

return true;
return false;
}

//Try to find nearest match if possible
if (!TryGetNearestMatch(glyphTypefaces, key, out glyphTypeface))
var createdKey =
new FontCollectionKey(glyphTypeface.Style, glyphTypeface.Weight, glyphTypeface.Stretch);

//No exact match
if (createdKey != key)
{
if (TryGetGlyphTypeface(_fontManager.DefaultFontFamily.Name, style, weight, stretch, out glyphTypeface))
//Try to find nearest match if possible
if (!TryGetNearestMatch(glyphTypefaces, key, out var nearestMatch))
{
glyphTypefaces.TryAdd(key, glyphTypeface);
glyphTypeface = nearestMatch;
}
else
{
//Try to create a synthetic glyph typeface
if (TryCreateSyntheticGlyphTypeface(glyphTypeface, style, weight, out var syntheticGlyphTypeface))
{
glyphTypeface = syntheticGlyphTypeface;
}
}

return glyphTypeface != null;
}

if (TryCreateSyntheticGlyphTypeface(glyphTypeface, style, weight, out var syntheticGlyphTypeface))
{
glyphTypefaces.TryAdd(key, syntheticGlyphTypeface);

glyphTypeface = syntheticGlyphTypeface;
}
else
{
glyphTypefaces.TryAdd(key, glyphTypeface);
}

return true;
glyphTypefaces.TryAdd(key, glyphTypeface);

return glyphTypeface != null;
}

private bool TryCreateSyntheticGlyphTypeface(IGlyphTypeface glyphTypeface, FontStyle style, FontWeight weight,
Expand Down
24 changes: 16 additions & 8 deletions src/Headless/Avalonia.Headless/HeadlessPlatformStubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ public void Dispose() { }

internal class HeadlessGlyphTypefaceImpl : IGlyphTypeface
{
public HeadlessGlyphTypefaceImpl(string familyName, FontStyle style, FontWeight weight, FontStretch stretch)
{
FamilyName = familyName;
Style = style;
Weight = weight;
Stretch = stretch;
}

public FontMetrics Metrics => new FontMetrics
{
DesignEmHeight = 10,
Expand All @@ -100,13 +108,13 @@ internal class HeadlessGlyphTypefaceImpl : IGlyphTypeface

public FontSimulations FontSimulations => FontSimulations.None;

public string FamilyName => "$Default";
public string FamilyName { get; }

public FontWeight Weight => FontWeight.Normal;
public FontWeight Weight { get; }

public FontStyle Style => FontStyle.Normal;
public FontStyle Style { get; }

public FontStretch Stretch => FontStretch.Normal;
public FontStretch Stretch { get; }

public void Dispose()
{
Expand Down Expand Up @@ -237,14 +245,14 @@ public virtual bool TryCreateGlyphTypeface(string familyName, FontStyle style, F
return false;
}

glyphTypeface = new HeadlessGlyphTypefaceImpl();
glyphTypeface = new HeadlessGlyphTypefaceImpl(familyName, style, weight, stretch);

return true;
}

public virtual bool TryCreateGlyphTypeface(Stream stream, FontSimulations fontSimulations, out IGlyphTypeface glyphTypeface)
{
glyphTypeface = new HeadlessGlyphTypefaceImpl();
glyphTypeface = new HeadlessGlyphTypefaceImpl(FontFamily.DefaultFontFamilyName, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal);

TryCreateGlyphTypefaceCount++;

Expand Down Expand Up @@ -298,14 +306,14 @@ public virtual bool TryCreateGlyphTypeface(string familyName, FontStyle style, F
return false;
}

glyphTypeface = new HeadlessGlyphTypefaceImpl();
glyphTypeface = new HeadlessGlyphTypefaceImpl(familyName, style, weight, stretch);

return true;
}

public virtual bool TryCreateGlyphTypeface(Stream stream, FontSimulations fontSimulations, out IGlyphTypeface glyphTypeface)
{
glyphTypeface = new HeadlessGlyphTypefaceImpl();
glyphTypeface = new HeadlessGlyphTypefaceImpl(FontFamily.DefaultFontFamilyName, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal);

return true;
}
Expand Down
4 changes: 3 additions & 1 deletion tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ private static GlyphRun CreateGlyphRun(double[] glyphAdvances, int[] glyphCluste
glyphInfos[i] = new GlyphInfo(0, glyphClusters[i], glyphAdvances[i]);
}

return new GlyphRun(new HeadlessGlyphTypefaceImpl(), 10, new string('a', count).AsMemory(), glyphInfos, biDiLevel: bidiLevel);
return new GlyphRun(
new HeadlessGlyphTypefaceImpl(FontFamily.DefaultFontFamilyName, FontStyle.Normal, FontWeight.Normal,
FontStretch.Normal), 10, new string('a', count).AsMemory(), glyphInfos, biDiLevel: bidiLevel);
}

private static IDisposable Start()
Expand Down
Loading
Loading