diff --git a/src/Avalonia.Base/Logging/LogArea.cs b/src/Avalonia.Base/Logging/LogArea.cs
index 139129e623..07553a647e 100644
--- a/src/Avalonia.Base/Logging/LogArea.cs
+++ b/src/Avalonia.Base/Logging/LogArea.cs
@@ -20,6 +20,11 @@ namespace Avalonia.Logging
///
public const string Animations = nameof(Animations);
+ ///
+ /// The log event comes from the fonts system.
+ ///
+ public const string Fonts = nameof(Fonts);
+
///
/// The log event comes from the visual system.
///
diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs
index 9d1d0145d0..1f15820b9a 100644
--- a/src/Avalonia.Base/Media/FontManager.cs
+++ b/src/Avalonia.Base/Media/FontManager.cs
@@ -199,7 +199,7 @@ namespace Avalonia.Media
return true;
}
- var logger = Logger.TryGet(LogEventLevel.Debug, "FontManager");
+ var logger = Logger.TryGet(LogEventLevel.Debug, LogArea.Fonts);
logger?.Log(this,
$"Font family '{familyName}' could not be found. Present font families: [{string.Join(",", fontCollection)}]");
diff --git a/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs b/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs
index 4c535bdc0e..a3375652b8 100644
--- a/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs
+++ b/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs
@@ -128,7 +128,9 @@ namespace Avalonia.Media.Fonts
{
if (_fontManagerImpl.TryCreateGlyphTypeface(stream, fontSimulations, out var platformTypeface))
{
- syntheticGlyphTypeface = new GlyphTypeface(platformTypeface, fontSimulations);
+ syntheticGlyphTypeface = GlyphTypeface.TryCreate(platformTypeface, fontSimulations);
+ if (syntheticGlyphTypeface is null)
+ return false;
//Add the TypographicFamilyName to the cache
if (!string.IsNullOrEmpty(glyphTypeface.TypographicFamilyName))
@@ -272,16 +274,14 @@ namespace Avalonia.Media.Fonts
/// langword="false"/>.
public bool TryAddGlyphTypeface(Stream stream, [NotNullWhen(true)] out GlyphTypeface? glyphTypeface)
{
- glyphTypeface = null;
-
if (!_fontManagerImpl.TryCreateGlyphTypeface(stream, FontSimulations.None, out var platformTypeface))
{
+ glyphTypeface = null;
return false;
}
- glyphTypeface = new GlyphTypeface(platformTypeface);
-
- return TryAddGlyphTypeface(glyphTypeface);
+ glyphTypeface = GlyphTypeface.TryCreate(platformTypeface);
+ return glyphTypeface is not null && TryAddGlyphTypeface(glyphTypeface);
}
///
@@ -315,13 +315,12 @@ namespace Avalonia.Media.Fonts
{
var stream = _assetLoader.Open(fontAsset);
- if (!_fontManagerImpl.TryCreateGlyphTypeface(stream, FontSimulations.None, out var platformTypeface))
+ if (!_fontManagerImpl.TryCreateGlyphTypeface(stream, FontSimulations.None, out var platformTypeface) ||
+ GlyphTypeface.TryCreate(platformTypeface) is not { } glyphTypeface)
{
continue;
}
- var glyphTypeface = new GlyphTypeface(platformTypeface);
-
var key = glyphTypeface.ToFontCollectionKey();
//Add TypographicFamilyName to the cache
@@ -353,14 +352,11 @@ namespace Avalonia.Media.Fonts
using var stream = File.OpenRead(source.LocalPath);
- if (_fontManagerImpl.TryCreateGlyphTypeface(stream, FontSimulations.None, out var platformTypeface))
+ if (_fontManagerImpl.TryCreateGlyphTypeface(stream, FontSimulations.None, out var platformTypeface) &&
+ GlyphTypeface.TryCreate(platformTypeface) is { } glyphTypeface &&
+ TryAddGlyphTypeface(glyphTypeface))
{
- var glyphTypeface = new GlyphTypeface(platformTypeface);
-
- if (TryAddGlyphTypeface(glyphTypeface))
- {
- result = true;
- }
+ result = true;
}
}
// If the path is a directory, load all font files from that directory
@@ -377,14 +373,11 @@ namespace Avalonia.Media.Fonts
{
using var stream = File.OpenRead(file);
- if (_fontManagerImpl.TryCreateGlyphTypeface(stream, FontSimulations.None, out var platformTypeface))
+ if (_fontManagerImpl.TryCreateGlyphTypeface(stream, FontSimulations.None, out var platformTypeface) &&
+ GlyphTypeface.TryCreate(platformTypeface) is { } glyphTypeface &&
+ TryAddGlyphTypeface(glyphTypeface))
{
- var glyphTypeface = new GlyphTypeface(platformTypeface);
-
- if (TryAddGlyphTypeface(glyphTypeface))
- {
- result = true;
- }
+ result = true;
}
}
}
diff --git a/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs b/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
index cf055e5d99..3c81e9890f 100644
--- a/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
+++ b/src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
@@ -52,7 +52,11 @@ namespace Avalonia.Media.Fonts
return false;
}
- glyphTypeface = new GlyphTypeface(platformTypeface);
+ glyphTypeface = GlyphTypeface.TryCreate(platformTypeface);
+ if (glyphTypeface is null)
+ {
+ return false;
+ }
//Add to cache with platform typeface family name first
TryAddGlyphTypeface(platformTypeface.FamilyName, key, glyphTypeface);
@@ -112,7 +116,10 @@ namespace Avalonia.Media.Fonts
}
// Not in cache yet: create glyph typeface and try to add it.
- var glyphTypeface = new GlyphTypeface(platformTypeface);
+ if (GlyphTypeface.TryCreate(platformTypeface) is not { } glyphTypeface)
+ {
+ return false;
+ }
// Try adding with the platform typeface family name first.
TryAddGlyphTypeface(platformTypeface.FamilyName, key, glyphTypeface);
diff --git a/src/Avalonia.Base/Media/GlyphTypeface.cs b/src/Avalonia.Base/Media/GlyphTypeface.cs
index ca8e3fec16..3b3875b5de 100644
--- a/src/Avalonia.Base/Media/GlyphTypeface.cs
+++ b/src/Avalonia.Base/Media/GlyphTypeface.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Runtime.CompilerServices;
+using Avalonia.Logging;
using Avalonia.Media.Fonts;
using Avalonia.Media.Fonts.Tables;
using Avalonia.Media.Fonts.Tables.Cmap;
@@ -222,6 +222,25 @@ namespace Avalonia.Media
}
}
+ internal static GlyphTypeface? TryCreate(IPlatformTypeface typeface, FontSimulations fontSimulations = FontSimulations.None)
+ {
+ try
+ {
+ return new GlyphTypeface(typeface, fontSimulations);
+ }
+ catch (Exception ex)
+ {
+ Logger.TryGet(LogEventLevel.Warning, LogArea.Fonts)?.Log(
+ null,
+ "Could not create glyph typeface from platform typeface named {FamilyName} with simulations {Simulations}: {Exception}",
+ typeface.FamilyName,
+ fontSimulations,
+ ex);
+
+ return null;
+ }
+ }
+
///
/// Gets the family name of the font.
///
diff --git a/tests/Avalonia.Skia.UnitTests/Fonts/TestFontNoCmap412.ttf b/tests/Avalonia.Skia.UnitTests/Fonts/TestFontNoCmap412.ttf
new file mode 100644
index 0000000000..03a0fbe537
Binary files /dev/null and b/tests/Avalonia.Skia.UnitTests/Fonts/TestFontNoCmap412.ttf differ
diff --git a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs
index 994edc6095..76d4857e64 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/FontManagerTests.cs
@@ -1,10 +1,11 @@
using System;
using System.Collections.Generic;
using Avalonia.Fonts.Inter;
-using Avalonia.Headless;
+using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Media.Fonts;
using Avalonia.Media.TextFormatting.Unicode;
+using Avalonia.Platform;
using Avalonia.UnitTests;
using SkiaSharp;
using Xunit;
@@ -494,5 +495,53 @@ namespace Avalonia.Skia.UnitTests.Media
}
}
}
+
+ [Fact]
+ public void TryGetGlyphTypeface_Should_Return_False_For_Font_Without_Supported_Cmap()
+ {
+ const string fontUri = "resm:Avalonia.Skia.UnitTests.Fonts.TestFontNoCmap412.ttf?assembly=Avalonia.Skia.UnitTests#TestFontNoCmap412";
+
+ using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new FontManagerImpl()));
+ using var scope = AvaloniaLocator.EnterScope();
+
+ // Skia can load the font
+ AssertCanCreatePlatformTypeface();
+
+ // But Avalonia can't, because it has no supported cmap subtable
+ AssertCannotCreateGlyphTypeface();
+
+ void AssertCanCreatePlatformTypeface()
+ {
+ var fontManagerImpl = AvaloniaLocator.Current.GetRequiredService();
+ var assetLoader = AvaloniaLocator.Current.GetRequiredService();
+
+ using var stream = assetLoader.Open(new Uri(fontUri));
+
+ Assert.True(fontManagerImpl.TryCreateGlyphTypeface(stream, FontSimulations.None, out var platformTypeface));
+ Assert.NotNull(platformTypeface);
+ Assert.Equal("TestFontNoCmap412", platformTypeface.FamilyName);
+ }
+
+ void AssertCannotCreateGlyphTypeface()
+ {
+ string? loggedTemplate = null;
+ object?[] loggedValues = [];
+
+ using var logSinkScope = TestLogSink.Start((level, area, _, template, values) =>
+ {
+ if (level == LogEventLevel.Warning && area == LogArea.Fonts)
+ {
+ loggedTemplate = template;
+ loggedValues = values;
+ }
+ });
+
+ Assert.False(FontManager.Current.TryGetGlyphTypeface(new Typeface(fontUri), out _));
+ Assert.Equal(loggedTemplate, "Could not create glyph typeface from platform typeface named {FamilyName} with simulations {Simulations}: {Exception}");
+ Assert.Equal(3, loggedValues.Length);
+ Assert.Equal("TestFontNoCmap412", Assert.IsType(loggedValues[0]));
+ Assert.Equal("No suitable cmap subtable found.", Assert.IsType(loggedValues[2]).Message);
+ }
+ }
}
}