Skip to content

Commit

Permalink
feat(android): Proper variable font support
Browse files Browse the repository at this point in the history
  • Loading branch information
Youssef1313 committed Jun 26, 2024
1 parent e43bbeb commit 2a53446
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 24 deletions.
4 changes: 1 addition & 3 deletions src/Uno.UI/Controls/PaintPool.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,11 @@ private static TextPaint InnerBuildPaint(FontWeight fontWeight, FontStyle fontSt
{
var paintSpecs = BuildPaintValueSpecs(fontSize, characterSpacing);

var typefaceStyle = TypefaceStyleHelper.GetTypefaceStyle(fontStyle, fontWeight);

return TextPaintPoolNative.BuildPaint(
paintSpecs.density,
paintSpecs.textSize,
paintSpecs.letterSpacing,
FontHelper.FontFamilyToTypeFace(fontFamily, fontWeight, typefaceStyle),
FontHelper.FontFamilyToTypeFace(fontFamily, fontWeight, fontStyle),
(int)((Android.Graphics.Color)foreground),
(textDecorations & TextDecorations.Underline) == TextDecorations.Underline,
(textDecorations & TextDecorations.Strikethrough) == TextDecorations.Strikethrough,
Expand Down
4 changes: 2 additions & 2 deletions src/Uno.UI/TypefaceStyleHelper.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Uno.UI
{
internal static class TypefaceStyleHelper
{
internal static TypefaceStyle GetTypefaceStyle(FontStyle fontStyle, FontWeight fontWeight)
internal static TypefaceStyle GetTypefaceStyle(bool italic, FontWeight fontWeight)
{
var style = TypefaceStyle.Normal;

Expand All @@ -18,7 +18,7 @@ internal static TypefaceStyle GetTypefaceStyle(FontStyle fontStyle, FontWeight f
style |= TypefaceStyle.Bold;
}

if (fontStyle == FontStyle.Italic)
if (italic)
{
style |= TypefaceStyle.Italic;
}
Expand Down
6 changes: 2 additions & 4 deletions src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,8 @@ partial void UpdateFontPartial()
{
if (Parent != null && _textBoxView != null)
{
var style = TypefaceStyleHelper.GetTypefaceStyle(FontStyle, FontWeight);
var typeface = FontHelper.FontFamilyToTypeFace(FontFamily, FontWeight);

_textBoxView.SetTypeface(typeface, style);
var typeface = FontHelper.FontFamilyToTypeFace(FontFamily, FontWeight, FontStyle);
_textBoxView.Typeface = typeface;
_textBoxView.SetTextSize(ComplexUnitType.Px, (float)Math.Round(ViewHelper.LogicalToPhysicalFontPixels((float)FontSize)));
}
}
Expand Down
48 changes: 38 additions & 10 deletions src/Uno.UI/UI/Xaml/Extensions/FontHelper.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,21 @@ internal partial class FontHelper
private static bool _assetsListed;
private static readonly string DefaultFontFamilyName = "sans-serif";

internal static Typeface? FontFamilyToTypeFace(FontFamily? fontFamily, FontWeight fontWeight, TypefaceStyle style = TypefaceStyle.Normal)
internal static Typeface? FontFamilyToTypeFace(FontFamily? fontFamily, FontWeight fontWeight, FontStyle fontStyle)
{
var entry = new FontFamilyToTypeFaceDictionary.Entry(fontFamily?.Source, fontWeight, style);
var italic = fontStyle is FontStyle.Italic or FontStyle.Oblique;
var entry = new FontFamilyToTypeFaceDictionary.Entry(fontFamily?.Source, fontWeight, italic);

if (!_fontFamilyToTypeFaceDictionary.TryGetValue(entry, out var typeFace))
{
typeFace = InternalFontFamilyToTypeFace(fontFamily, fontWeight, style);
typeFace = InternalFontFamilyToTypeFace(fontFamily, fontWeight, italic);
_fontFamilyToTypeFaceDictionary.Add(entry, typeFace);
}

return typeFace;
}

internal static Typeface? InternalFontFamilyToTypeFace(FontFamily? fontFamily, FontWeight fontWeight, TypefaceStyle style)
internal static Typeface? InternalFontFamilyToTypeFace(FontFamily? fontFamily, FontWeight fontWeight, bool italic)
{
if (fontFamily?.Source == null || fontFamily.Equals(FontFamily.Default))
{
Expand All @@ -58,22 +59,27 @@ internal partial class FontHelper

source = source.TrimStart("ms-appx://", ignoreCase: true);

if (!TryLoadFromPath(style, source, out typeface))
if (!TryLoadFromPath(fontWeight.Weight, italic, source, out typeface))
{
// The lookup used to be performed without the assets folder, even if its required to specify it
// with UWP. Keep this behavior for backward compatibility.
var legacySource = source.TrimStart("/assets/", ignoreCase: true);

// The path for AndroidAssets is not encoded, unlike assets processed by the RetargetAssets tool.
if (!TryLoadFromPath(style, legacySource, out typeface, encodePath: false))
if (!TryLoadFromPath(fontWeight.Weight, italic, legacySource, out typeface, encodePath: false))
{
throw new InvalidOperationException($"Unable to find [{fontFamily.Source}] from the application's assets.");
}
}
}
else
{
var style = TypefaceStyleHelper.GetTypefaceStyle(italic, fontWeight);
typeface = Android.Graphics.Typeface.Create(fontFamily.Source, style);
if (typeface is not null && typeface.Weight != fontWeight.Weight)
{
typeface = Android.Graphics.Typeface.Create(typeface, fontWeight.Weight, italic);
}
}

return typeface;
Expand All @@ -100,7 +106,24 @@ internal partial class FontHelper
}
}

private static bool TryLoadFromPath(TypefaceStyle style, string source, out Typeface? typeface, bool encodePath = true)
private static string FontStretchToPercentage(FontStretch fontStretch)
{
return fontStretch switch
{
FontStretch.UltraCondensed => "50",
FontStretch.ExtraCondensed => "62.5",
FontStretch.Condensed => "75",
FontStretch.SemiCondensed => "87.5",
FontStretch.Normal => "100",
FontStretch.SemiExpanded => "112.5",
FontStretch.Expanded => "125",
FontStretch.ExtraExpanded => "150",
FontStretch.UltraExpanded => "200",
_ => "100",
};
}

private static bool TryLoadFromPath(int weight, bool italic, string source, out Typeface? typeface, bool encodePath = true)
{
source = FontFamilyHelper.RemoveHashFamilyName(source);

Expand All @@ -118,13 +141,18 @@ private static bool TryLoadFromPath(TypefaceStyle style, string source, out Type
string actualAsset = AssetsHelper.FindAssetFile(encodedSource);
if (actualAsset != null)
{
typeface = Android.Graphics.Typeface.CreateFromAsset(Android.App.Application.Context.Assets, actualAsset);
var builder = new Android.Graphics.Typeface.Builder(Android.App.Application.Context.Assets!, actualAsset);
// TODO: Use the actual FontStretch instead of Normal when we support FontStretch.
// NOTE: We are unable to use 'ital' axis here. If that axis doesn't exist in the
// font file, italic will break badly. However, if it exists, we will render "reasonable" (but not ideal) italic text.
builder.SetFontVariationSettings($"'wght' {weight}, 'wdth' {FontStretchToPercentage(FontStretch.Normal)}");
typeface = builder.Build();

if (typeface is not null)
{
if (style != typeface.Style)
if (typeface.Weight != weight || typeface.IsItalic != italic)
{
typeface = Typeface.Create(typeface, style);
typeface = Typeface.Create(typeface, weight, italic);
}

return true;
Expand Down
10 changes: 5 additions & 5 deletions src/Uno.UI/UI/Xaml/Extensions/FontHelper.Lookup.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,25 @@ public class Entry
{
private readonly int _hashCode;

public Entry(string? fontFamily, FontWeight fontWeight, TypefaceStyle style)
public Entry(string? fontFamily, FontWeight fontWeight, bool italic)
{
FontFamily = fontFamily;
FontWeight = fontWeight;
Style = style;
_hashCode = (FontFamily?.GetHashCode() ?? 0) ^ FontWeight.GetHashCode() ^ Style.GetHashCode();
Italic = italic;
_hashCode = (FontFamily?.GetHashCode() ?? 0) ^ FontWeight.GetHashCode() ^ Italic.GetHashCode();
}

public string? FontFamily { get; }
public FontWeight FontWeight { get; }
public TypefaceStyle Style { get; }
public bool Italic { get; }

public override bool Equals(object? other)
{
if (other is Entry otherEntry)
{
return FontFamily == otherEntry.FontFamily
&& FontWeight == otherEntry.FontWeight
&& Style == otherEntry.Style;
&& Italic == otherEntry.Italic;
}

return false;
Expand Down

0 comments on commit 2a53446

Please sign in to comment.