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

[Android] Ensure custom fonts also get weight/slant #1011

Merged
merged 7 commits into from
May 13, 2021
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public void Font()
Assert.Inconclusive("needs testing");
#else
var font = remote.GetProperty<Font>(Button.FontProperty);
Assert.True(font.Weight.HasFlag(FontWeight.Bold));
Assert.AreEqual (FontWeight.Bold, font.Weight);
#endif
}

Expand Down
61 changes: 25 additions & 36 deletions src/Core/src/Fonts/FontManager.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ public FontManager(IFontRegistrar fontRegistrar, ILogger<FontManager>? logger =
}

public float GetFontSize(Font font, float defaultFontSize = 0) =>
font.FontSize > 0 ? (float)font.FontSize : (defaultFontSize > 0 ? defaultFontSize : 14f);
font.FontSize <= 0
? (defaultFontSize > 0 ? defaultFontSize : 14f)
: (float)font.FontSize;


(bool success, Typeface? typeface) TryGetFromAssets(string fontName)
Expand Down Expand Up @@ -79,27 +81,29 @@ public float GetFontSize(Font font, float defaultFontSize = 0) =>
foreach (var folder in folders)
{
formated = $"{folder}{fontFile.FileNameWithExtension()}#{fontFile.PostScriptName}";
var result = LoadTypefaceFromAsset(formated);
var result = LoadTypefaceFromAsset(formated, false);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warnings at each step of the search are not needed.

if (result.success)
return result;
}

}
}

return (false, null);
}

(bool success, Typeface? typeface) LoadTypefaceFromAsset(string fontfamily)
(bool success, Typeface? typeface) LoadTypefaceFromAsset(string fontfamily, bool warning = true)
{
try
{
var result = Typeface.CreateFromAsset(AApplication.Context.Assets, FontNameToFontFile(fontfamily));

return (true, result);
}
catch (Exception ex)
{
_logger?.LogWarning(ex, "Unable to load font '{Font}' from assets.", fontfamily);
if (warning)
_logger?.LogWarning(ex, "Unable to load font '{Font}' from assets.", fontfamily);

return (false, null);
}
}
Expand All @@ -113,54 +117,38 @@ bool IsAssetFontFamily(string name)
{
var (fontFamily, weight, italic) = fontData;
fontFamily ??= string.Empty;
var style = ToTypefaceStyle(weight, italic);

Typeface? result;
var result = Typeface.Default;

if (string.IsNullOrWhiteSpace(fontFamily))
{
if (NativeVersion.IsAtLeast(28))
{
result = Typeface.Create(Typeface.Default, (int)weight, italic);
}
else
{
var style = ToTypefaceStyle(weight, italic);
result = Typeface.Create(Typeface.Default, style);
}
}
else if (IsAssetFontFamily(fontFamily))
if (!string.IsNullOrWhiteSpace(fontFamily))
{
result = Typeface.CreateFromAsset(AApplication.Context.Assets, FontNameToFontFile(fontFamily));
}
else
{
fontFamily ??= string.Empty;
var (success, typeface) = TryGetFromAssets(fontFamily);
if (success)
if (IsAssetFontFamily(fontFamily))
{
return typeface;
result = Typeface.CreateFromAsset(AApplication.Context.Assets, FontNameToFontFile(fontFamily));
}
else
{
if (NativeVersion.IsAtLeast(28))
{
return Typeface.Create(Typeface.Default, (int)weight, italic);
}
var (success, typeface) = TryGetFromAssets(fontFamily);
if (success)
result = typeface;
else
{
var style = ToTypefaceStyle(weight, italic);
return Typeface.Create(Typeface.Default, style);
}
result = Typeface.Create(fontFamily, style);
}
}

if (NativeVersion.IsAtLeast(28))
result = Typeface.Create(result, (int)weight, italic);
else
result = Typeface.Create(result, style);

return result;
}

TypefaceStyle ToTypefaceStyle(FontWeight weight, bool italic)
{
var style = TypefaceStyle.Normal;
var bold = weight > FontWeight.Bold;
var bold = weight >= FontWeight.Bold;
if (bold && italic)
style = TypefaceStyle.BoldItalic;
else if (bold)
Expand All @@ -169,6 +157,7 @@ TypefaceStyle ToTypefaceStyle(FontWeight weight, bool italic)
style = TypefaceStyle.Italic;
return style;
}

string FontNameToFontFile(string fontFamily)
{
fontFamily ??= string.Empty;
Expand Down
5 changes: 4 additions & 1 deletion src/Core/src/Fonts/FontManager.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ public FontFamily GetFontFamily(Font font)
return _fonts.GetOrAdd(font.FontFamily, CreateFontFamily);
}

public double GetFontSize(Font font, double defaultFontSize = 0) => font.FontSize > 0 ? font.FontSize : (defaultFontSize > 0 ? defaultFontSize : DefaultFontSize);
public double GetFontSize(Font font, double defaultFontSize = 0) =>
font.FontSize <= 0
? (defaultFontSize > 0 ? defaultFontSize : DefaultFontSize)
: font.FontSize;

FontFamily CreateFontFamily(string fontFamily)
{
Expand Down
50 changes: 28 additions & 22 deletions src/Core/src/Fonts/FontManager.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ namespace Microsoft.Maui
{
public class FontManager : IFontManager
{
// UIFontWeight[Constant] is internal in Xamarin.iOS but the convertion from
// the public (int-based) enum is not helpful in this case.
// -1.0 (Thin / 100) to 1.0 (Black / 900) with 0 being Regular (400)
// which is not quite the center, not are the constant values linear
static readonly (float value, FontWeight weight)[] FontWeightMap = new (float, FontWeight)[] {
(-0.80f, FontWeight.Ultralight),
(-0.60f, FontWeight.Thin),
(-0.40f, FontWeight.Light),
(0.0f, FontWeight.Regular),
(0.23f, FontWeight.Medium),
(0.30f, FontWeight.Semibold),
(0.40f, FontWeight.Bold),
(0.56f, FontWeight.Heavy),
(0.62f, FontWeight.Black)
};

readonly ConcurrentDictionary<Font, UIFont> _fonts = new();
readonly IFontRegistrar _fontRegistrar;
readonly ILogger<FontManager>? _logger;
Expand All @@ -23,25 +39,13 @@ public FontManager(IFontRegistrar fontRegistrar, ILogger<FontManager>? logger =
public UIFont DefaultFont =>
_defaultFont ??= UIFont.SystemFontOfSize(UIFont.SystemFontSize);

public UIFont GetFont(Font font, double defaultFontSize = 0) => GetFont(font, defaultFontSize, CreateFont);

public double GetFontSize(Font font, double defaultFontSize = 0) => font.FontSize <= 0 ? (defaultFontSize > 0 ? (float)defaultFontSize : DefaultFont.PointSize) : (nfloat)font.FontSize;
public UIFont GetFont(Font font, double defaultFontSize = 0) =>
GetFont(font, defaultFontSize, CreateFont);

// UIFontWeight[Constant] is internal in Xamarin.iOS but the convertion from
// the public (int-based) enum is not helpful in this case.
// -1.0 (Thin / 100) to 1.0 (Black / 900) with 0 being Regular (400)
// which is not quite the center, not are the constant values linear
static readonly (float value, FontWeight weight)[] FontWeightMap = new (float, FontWeight)[] {
(-0.80f, FontWeight.Ultralight),
(-0.60f, FontWeight.Thin),
(-0.40f, FontWeight.Light),
(0.0f, FontWeight.Regular),
(0.23f, FontWeight.Medium),
(0.30f, FontWeight.Semibold),
(0.40f, FontWeight.Bold),
(0.56f, FontWeight.Heavy),
(0.62f, FontWeight.Black)
};
public double GetFontSize(Font font, double defaultFontSize = 0) =>
font.FontSize <= 0
? (defaultFontSize > 0 ? (float)defaultFontSize : DefaultFont.PointSize)
: (nfloat)font.FontSize;

static float GetWeightConstant(FontWeight self)
{
Expand All @@ -60,6 +64,7 @@ UIFont GetFont(Font font, double defaultFont, Func<Font, UIFont> factory)
font = font.WithSize(size);
return _fonts.GetOrAdd(font, factory);
}

static UIFontAttributes GetFontAttributes(Font font)
{
var a = new UIFontAttributes
Expand Down Expand Up @@ -92,21 +97,21 @@ UIFont CreateFont(Font font)
var family = font.FontFamily;
var size = (nfloat)font.FontSize;

bool hasAttributes = font.Weight != FontWeight.Regular || font.FontSlant != FontSlant.Default;

var hasAttributes =
font.Weight != FontWeight.Regular ||
font.FontSlant != FontSlant.Default;

if (family != null && family != DefaultFont.FamilyName)
{
try
{
UIFont? result = null;

if (UIFont.FamilyNames.Contains(family))
{
var descriptor = new UIFontDescriptor().CreateWithFamily(family);
if (hasAttributes)
{
descriptor = descriptor.CreateWithAttributes(GetFontAttributes(font));
}

result = UIFont.FromDescriptor(descriptor, size);
if (result != null)
Expand All @@ -128,6 +133,7 @@ UIFont CreateFont(Font font)
result = UIFont.SystemFontOfSize(size, UIFontWeight.Regular);
return result;
}

if (result == null)
result = UIFont.FromName(family, size);
if (result != null)
Expand Down
5 changes: 5 additions & 0 deletions src/Core/tests/DeviceTests/AssertionExtensions.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,5 +205,10 @@ public static TextUtils.TruncateAt ToNative(this LineBreakMode mode) =>
LineBreakMode.MiddleTruncation => TextUtils.TruncateAt.Middle,
_ => throw new ArgumentOutOfRangeException(nameof(mode))
};

public static FontWeight GetFontWeight(this Typeface typeface) =>
NativeVersion.IsAtLeast(28)
? (FontWeight)typeface.Weight
: typeface.IsBold ? FontWeight.Bold : FontWeight.Regular;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Threading.Tasks;
using AndroidX.AppCompat.Widget;
using Java.IO;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.DeviceTests.Stubs;
using Microsoft.Maui.Graphics;
Expand Down Expand Up @@ -135,7 +134,7 @@ double GetNativeUnscaledFontSize(ButtonHandler buttonHandler)
}

bool GetNativeIsBold(ButtonHandler buttonHandler) =>
GetNativeButton(buttonHandler).Typeface.Weight == (int)FontWeight.Bold || GetNativeButton(buttonHandler).Typeface.IsBold;
GetNativeButton(buttonHandler).Typeface.GetFontWeight() == FontWeight.Bold;

bool GetNativeIsItalic(ButtonHandler buttonHandler) =>
GetNativeButton(buttonHandler).Typeface.IsItalic;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ public async Task FontAttributesInitializeCorrectly(FontWeight weight, bool isBo
Font = Font.OfSize("Arial", 10, weight, isItalic ? FontSlant.Italic : FontSlant.Default)
};

await ValidatePropertyInitValue(button, () => button.Font.Weight.HasFlag(FontWeight.Bold), GetNativeIsBold, isBold);
await ValidatePropertyInitValue(button, () => button.Font.FontSlant.HasFlag(FontSlant.Italic), GetNativeIsItalic, isItalic);
await ValidatePropertyInitValue(button, () => button.Font.Weight == FontWeight.Bold, GetNativeIsBold, isBold);
await ValidatePropertyInitValue(button, () => button.Font.FontSlant == FontSlant.Italic, GetNativeIsItalic, isItalic);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ double GetNativeUnscaledFontSize(EntryHandler entryHandler)
}

bool GetNativeIsBold(EntryHandler entryHandler) =>
GetNativeEntry(entryHandler).Typeface.IsBold;
GetNativeEntry(entryHandler).Typeface.GetFontWeight() == FontWeight.Bold;

bool GetNativeIsItalic(EntryHandler entryHandler) =>
GetNativeEntry(entryHandler).Typeface.IsItalic;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ public async Task FontAttributesInitializeCorrectly(FontWeight weight, bool isBo
Font = Font.OfSize("Arial", 10, weight, isItalic ? FontSlant.Italic : FontSlant.Default)
};

await ValidatePropertyInitValue(entry, () => entry.Font.Weight.HasFlag(FontWeight.Bold), GetNativeIsBold, isBold);
await ValidatePropertyInitValue(entry, () => entry.Font.FontSlant.HasFlag(FontSlant.Italic), GetNativeIsItalic, isItalic);
await ValidatePropertyInitValue(entry, () => entry.Font.Weight == FontWeight.Bold, GetNativeIsBold, isBold);
await ValidatePropertyInitValue(entry, () => entry.Font.FontSlant == FontSlant.Italic, GetNativeIsItalic, isItalic);
}

[Theory(DisplayName = "Validates clear button visibility.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ double GetNativeUnscaledFontSize(LabelHandler labelHandler)
}

bool GetNativeIsBold(LabelHandler labelHandler) =>
GetNativeLabel(labelHandler).Typeface.IsBold;
GetNativeLabel(labelHandler).Typeface.GetFontWeight() == FontWeight.Bold;

bool GetNativeIsItalic(LabelHandler labelHandler) =>
GetNativeLabel(labelHandler).Typeface.IsItalic;
Expand Down
38 changes: 35 additions & 3 deletions src/Core/tests/DeviceTests/Handlers/Label/LabelHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Threading.Tasks;
using Microsoft.Maui.DeviceTests.Stubs;
using Microsoft.Maui.Graphics;
Expand Down Expand Up @@ -74,8 +73,8 @@ public async Task FontAttributesInitializeCorrectly(FontWeight weight, bool isBo
Font = Font.OfSize("Arial", 10, weight, isItalic ? FontSlant.Italic : FontSlant.Default)
};

await ValidatePropertyInitValue(label, () => label.Font.Weight.HasFlag(FontWeight.Bold), GetNativeIsBold, isBold);
await ValidatePropertyInitValue(label, () => label.Font.FontSlant.HasFlag(FontSlant.Italic), GetNativeIsItalic, isItalic);
await ValidatePropertyInitValue(label, () => label.Font.Weight == FontWeight.Bold, GetNativeIsBold, isBold);
await ValidatePropertyInitValue(label, () => label.Font.FontSlant == FontSlant.Italic, GetNativeIsItalic, isItalic);
}

[Fact(DisplayName = "CharacterSpacing Initializes Correctly")]
Expand Down Expand Up @@ -168,6 +167,39 @@ await ValidateUnrelatedPropertyUnaffected(
() => label.Font = Font.SystemFontOfSize(newSize));
}

[Theory(DisplayName = "Font Family and Weight Initializes Correctly")]
[InlineData(null, FontWeight.Regular, FontSlant.Default)]
[InlineData(null, FontWeight.Regular, FontSlant.Italic)]
[InlineData(null, FontWeight.Bold, FontSlant.Default)]
[InlineData(null, FontWeight.Bold, FontSlant.Italic)]
[InlineData("Dokdo", FontWeight.Regular, FontSlant.Default)]
[InlineData("Dokdo", FontWeight.Regular, FontSlant.Italic)]
[InlineData("Dokdo", FontWeight.Bold, FontSlant.Default)]
[InlineData("Dokdo", FontWeight.Bold, FontSlant.Italic)]
#if __ANDROID__
// "monospace" is a special font name
[InlineData("monospace", FontWeight.Regular, FontSlant.Default)]
[InlineData("monospace", FontWeight.Regular, FontSlant.Italic)]
[InlineData("monospace", FontWeight.Bold, FontSlant.Default)]
[InlineData("monospace", FontWeight.Bold, FontSlant.Italic)]
#endif
public async Task FontFamilyAndAttributesInitializesCorrectly(string family, FontWeight weight, FontSlant slant)
{
var label = new LabelStub
{
Text = "Test",
Font = Font.OfSize(family, 30, weight, slant)
};

var handler = await CreateHandlerAsync(label);

var isBold = GetNativeIsBold(handler);
var isItalic = GetNativeIsItalic(handler);

Assert.Equal(weight == FontWeight.Bold, isBold);
Assert.Equal(slant == FontSlant.Italic, isItalic);
}

[Theory(DisplayName = "Updating Text Does Not Affect HorizontalTextAlignment")]
[InlineData("Short", "Longer Text")]
[InlineData("Long thext here", "Short")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ double GetNativeUnscaledFontSize(PickerHandler pickerHandler)
}

bool GetNativeIsBold(PickerHandler pickerHandler) =>
GetNativePicker(pickerHandler).Typeface.IsBold;
GetNativePicker(pickerHandler).Typeface.GetFontWeight() == FontWeight.Bold;

bool GetNativeIsItalic(PickerHandler pickerHandler) =>
GetNativePicker(pickerHandler).Typeface.IsItalic;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ public async Task FontAttributesInitializeCorrectly(FontWeight weight, bool isBo
picker.ItemsSource = items;
picker.SelectedIndex = 0;

await ValidatePropertyInitValue(picker, () => picker.Font.Weight.HasFlag(FontWeight.Bold), GetNativeIsBold, isBold);
await ValidatePropertyInitValue(picker, () => picker.Font.FontSlant.HasFlag(FontSlant.Italic), GetNativeIsItalic, isItalic);
await ValidatePropertyInitValue(picker, () => picker.Font.Weight == FontWeight.Bold, GetNativeIsBold, isBold);
await ValidatePropertyInitValue(picker, () => picker.Font.FontSlant == FontSlant.Italic, GetNativeIsItalic, isItalic);
}

[Theory(DisplayName = "Updating Font Does Not Affect HorizontalTextAlignment")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ bool GetNativeIsBold(SearchBarHandler searchBarHandler)
if (editText == null)
return false;

return editText.Typeface.IsBold;
return editText.Typeface.GetFontWeight() == FontWeight.Bold;
}

bool GetNativeIsItalic(SearchBarHandler searchBarHandler)
Expand Down
Loading