diff --git a/QuestPDF.UnitTests/FontStyleSetTests.cs b/QuestPDF.UnitTests/FontStyleSetTests.cs index 5f5a47d..9743888 100644 --- a/QuestPDF.UnitTests/FontStyleSetTests.cs +++ b/QuestPDF.UnitTests/FontStyleSetTests.cs @@ -1,123 +1,146 @@ -using NUnit.Framework; +using FluentAssertions; +using NUnit.Framework; using QuestPDF.Drawing; using SkiaSharp; +using static SkiaSharp.SKFontStyleSlant; namespace QuestPDF.UnitTests { [TestFixture] public class FontStyleSetTests { - private void ExpectComparisonOrder(SKFontStyle target, params SKFontStyle[] styles) + private void ExpectComparisonOrder(SKFontStyle target, SKFontStyle[] styles) { - for (int i = 0; i < styles.Length - 1; i++) + for (var i = 0; i < styles.Length - 1; i++) { - Assert.True(FontStyleSet.IsBetterMatch(target, styles[i], styles[i + 1])); - Assert.False(FontStyleSet.IsBetterMatch(target, styles[i + 1], styles[i])); + var currentStyle = styles[i]; + var nextStyle = styles[i + 1]; + + FontStyleSet.IsBetterMatch(target, currentStyle, nextStyle).Should().BeTrue(); + FontStyleSet.IsBetterMatch(target, nextStyle, currentStyle).Should().BeFalse(); } } [Test] public void FontStyleSet_IsBetterMatch_CondensedWidth() { - ExpectComparisonOrder( - new SKFontStyle(500, 5, SKFontStyleSlant.Upright), - new SKFontStyle(500, 5, SKFontStyleSlant.Upright), - new SKFontStyle(500, 4, SKFontStyleSlant.Upright), - new SKFontStyle(500, 3, SKFontStyleSlant.Upright), - new SKFontStyle(500, 6, SKFontStyleSlant.Upright) - ); + var styles = new[] + { + new SKFontStyle(500, 5, Upright), + new SKFontStyle(500, 4, Upright), + new SKFontStyle(500, 3, Upright), + new SKFontStyle(500, 6, Upright) + }; + + ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles); } [Test] public void FontStyleSet_IsBetterMatch_ExpandedWidth() { - ExpectComparisonOrder( - new SKFontStyle(500, 6, SKFontStyleSlant.Upright), - new SKFontStyle(500, 6, SKFontStyleSlant.Upright), - new SKFontStyle(500, 7, SKFontStyleSlant.Upright), - new SKFontStyle(500, 8, SKFontStyleSlant.Upright), - new SKFontStyle(500, 5, SKFontStyleSlant.Upright) - ); + var styles = new[] + { + new SKFontStyle(500, 6, Upright), + new SKFontStyle(500, 7, Upright), + new SKFontStyle(500, 8, Upright), + new SKFontStyle(500, 5, Upright) + }; + + ExpectComparisonOrder(new SKFontStyle(500, 6, Upright), styles); } [Test] public void FontStyleSet_IsBetterMatch_ItalicSlant() { - ExpectComparisonOrder( - new SKFontStyle(500, 5, SKFontStyleSlant.Italic), - new SKFontStyle(500, 5, SKFontStyleSlant.Italic), - new SKFontStyle(500, 5, SKFontStyleSlant.Oblique), - new SKFontStyle(500, 5, SKFontStyleSlant.Upright) - ); + var styles = new[] + { + new SKFontStyle(500, 5, Italic), + new SKFontStyle(500, 5, Oblique), + new SKFontStyle(500, 5, Upright) + }; + + ExpectComparisonOrder(new SKFontStyle(500, 5, Italic), styles); } [Test] public void FontStyleSet_IsBetterMatch_ObliqueSlant() { - ExpectComparisonOrder( - new SKFontStyle(500, 5, SKFontStyleSlant.Oblique), - new SKFontStyle(500, 5, SKFontStyleSlant.Oblique), - new SKFontStyle(500, 5, SKFontStyleSlant.Italic), - new SKFontStyle(500, 5, SKFontStyleSlant.Upright) - ); + var styles = new[] + { + new SKFontStyle(500, 5, Oblique), + new SKFontStyle(500, 5, Italic), + new SKFontStyle(500, 5, Upright) + }; + + ExpectComparisonOrder(new SKFontStyle(500, 5, Oblique), styles); } [Test] public void FontStyleSet_IsBetterMatch_UprightSlant() { - ExpectComparisonOrder( - new SKFontStyle(500, 5, SKFontStyleSlant.Upright), - new SKFontStyle(500, 5, SKFontStyleSlant.Upright), - new SKFontStyle(500, 5, SKFontStyleSlant.Oblique), - new SKFontStyle(500, 5, SKFontStyleSlant.Italic) - ); + var styles = new[] + { + new SKFontStyle(500, 5, Upright), + new SKFontStyle(500, 5, Oblique), + new SKFontStyle(500, 5, Italic) + }; + + ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles); } [Test] public void FontStyleSet_IsBetterMatch_ThinWeight() { - ExpectComparisonOrder( - new SKFontStyle(300, 5, SKFontStyleSlant.Upright), - new SKFontStyle(300, 5, SKFontStyleSlant.Upright), - new SKFontStyle(200, 5, SKFontStyleSlant.Upright), - new SKFontStyle(100, 5, SKFontStyleSlant.Upright), - new SKFontStyle(400, 5, SKFontStyleSlant.Upright) - ); + var styles = new[] + { + new SKFontStyle(300, 5, Upright), + new SKFontStyle(200, 5, Upright), + new SKFontStyle(100, 5, Upright), + new SKFontStyle(400, 5, Upright) + }; + + ExpectComparisonOrder(new SKFontStyle(300, 5, Upright), styles); } [Test] public void FontStyleSet_IsBetterMatch_RegularWeight() { - ExpectComparisonOrder( - new SKFontStyle(400, 5, SKFontStyleSlant.Upright), - new SKFontStyle(500, 5, SKFontStyleSlant.Upright), - new SKFontStyle(300, 5, SKFontStyleSlant.Upright), - new SKFontStyle(100, 5, SKFontStyleSlant.Upright), - new SKFontStyle(600, 5, SKFontStyleSlant.Upright) - ); + var styles = new[] + { + new SKFontStyle(500, 5, Upright), + new SKFontStyle(300, 5, Upright), + new SKFontStyle(100, 5, Upright), + new SKFontStyle(600, 5, Upright) + }; + + ExpectComparisonOrder(new SKFontStyle(400, 5, Upright), styles); } [Test] public void FontStyleSet_IsBetterMatch_BoldWeight() { - ExpectComparisonOrder( - new SKFontStyle(600, 5, SKFontStyleSlant.Upright), - new SKFontStyle(600, 5, SKFontStyleSlant.Upright), - new SKFontStyle(700, 5, SKFontStyleSlant.Upright), - new SKFontStyle(800, 5, SKFontStyleSlant.Upright), - new SKFontStyle(500, 5, SKFontStyleSlant.Upright) - ); + var styles = new[] + { + new SKFontStyle(600, 5, Upright), + new SKFontStyle(700, 5, Upright), + new SKFontStyle(800, 5, Upright), + new SKFontStyle(500, 5, Upright) + }; + + ExpectComparisonOrder(new SKFontStyle(600, 5, Upright), styles); } [Test] public void FontStyleSet_RespectsPriority() { - ExpectComparisonOrder( - new SKFontStyle(500, 5, SKFontStyleSlant.Upright), - new SKFontStyle(600, 5, SKFontStyleSlant.Italic), - new SKFontStyle(600, 6, SKFontStyleSlant.Upright), - new SKFontStyle(500, 6, SKFontStyleSlant.Italic) - ); + var styles = new[] + { + new SKFontStyle(600, 5, Italic), + new SKFontStyle(600, 6, Upright), + new SKFontStyle(500, 6, Italic) + }; + + ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles); } } } \ No newline at end of file diff --git a/QuestPDF/Drawing/FontManager.cs b/QuestPDF/Drawing/FontManager.cs index fd5cac5..1a51bcf 100644 --- a/QuestPDF/Drawing/FontManager.cs +++ b/QuestPDF/Drawing/FontManager.cs @@ -10,8 +10,8 @@ namespace QuestPDF.Drawing public static class FontManager { private static ConcurrentDictionary StyleSets = new(); - private static ConcurrentDictionary FontMetrics = new(); - private static ConcurrentDictionary Paints = new(); + private static ConcurrentDictionary FontMetrics = new(); + private static ConcurrentDictionary Paints = new(); private static ConcurrentDictionary ColorPaint = new(); private static void RegisterFontType(SKData fontData, string? customName = null) @@ -59,7 +59,7 @@ namespace QuestPDF.Drawing internal static SKPaint ToPaint(this TextStyle style) { - return Paints.GetOrAdd(style.Key, key => Convert(style)); + return Paints.GetOrAdd(style.PaintKey, key => Convert(style)); static SKPaint Convert(TextStyle style) { @@ -67,8 +67,7 @@ namespace QuestPDF.Drawing { Color = SKColor.Parse(style.Color), Typeface = GetTypeface(style), - TextSize = style.Size ?? 12, - TextEncoding = SKTextEncoding.Utf32 + TextSize = style.Size ?? 12 }; } @@ -77,19 +76,27 @@ namespace QuestPDF.Drawing var weight = (SKFontStyleWeight)(style.FontWeight ?? FontWeight.Normal); var slant = (style.IsItalic ?? false) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright; - var skFontStyle = new SKFontStyle(weight, SKFontStyleWidth.Normal, slant); + var fontStyle = new SKFontStyle(weight, SKFontStyleWidth.Normal, slant); - if (StyleSets.TryGetValue(style.FontType, out var set)) - return set.Match(skFontStyle); + if (StyleSets.TryGetValue(style.FontType, out var fontStyleSet)) + return fontStyleSet.Match(fontStyle); - return SKTypeface.FromFamilyName(style.FontType, skFontStyle) - ?? throw new ArgumentException($"The typeface {style.FontType} could not be found. Please consider installing the font file on your system or loading it from a file using the FontManager.RegisterFontType() static method."); + var fontFromDefaultSource = SKFontManager.Default.MatchFamily(style.FontType, fontStyle); + + if (fontFromDefaultSource != null) + return fontFromDefaultSource; + + throw new ArgumentException( + $"The typeface '{style.FontType}' could not be found. " + + $"Please consider the following options: " + + $"1) install the font on your operating system or execution environment. " + + $"2) load a font file specifically for QuestPDF usage via the QuestPDF.Drawing.FontManager.RegisterFontType(Stream fileContentStream) static method."); } } internal static SKFontMetrics ToFontMetrics(this TextStyle style) { - return FontMetrics.GetOrAdd(style.Key, key => style.ToPaint().FontMetrics); + return FontMetrics.GetOrAdd(style.FontMetricsKey, key => style.ToPaint().FontMetrics); } } } \ No newline at end of file diff --git a/QuestPDF/Infrastructure/TextStyle.cs b/QuestPDF/Infrastructure/TextStyle.cs index 640d9d2..552f54f 100644 --- a/QuestPDF/Infrastructure/TextStyle.cs +++ b/QuestPDF/Infrastructure/TextStyle.cs @@ -17,7 +17,8 @@ namespace QuestPDF.Infrastructure internal bool? HasStrikethrough { get; set; } internal bool? HasUnderline { get; set; } - internal string? Key { get; private set; } + internal object PaintKey { get; private set; } + internal object FontMetricsKey { get; private set; } internal static TextStyle LibraryDefault => new TextStyle { @@ -42,7 +43,8 @@ namespace QuestPDF.Infrastructure HasGlobalStyleApplied = true; ApplyParentStyle(globalStyle); - Key ??= $"{Color}|{BackgroundColor}|{FontType}|{Size}|{LineHeight}|{FontWeight}|{IsItalic}|{HasStrikethrough}|{HasUnderline}"; + PaintKey ??= (FontType, Size, FontWeight, IsItalic, Color); + FontMetricsKey ??= (FontType, Size, FontWeight, IsItalic); } internal void ApplyParentStyle(TextStyle parentStyle)