Browse Source

Improved font matching and exception message. Refactored tests.

MarcinZiabek 3 years ago
parent
commit
a92cc25fb1

+ 87 - 64
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);
         }
     }
 }

+ 18 - 11
QuestPDF/Drawing/FontManager.cs

@@ -10,8 +10,8 @@ namespace QuestPDF.Drawing
     public static class FontManager
     {
         private static ConcurrentDictionary<string, FontStyleSet> StyleSets = new();
-        private static ConcurrentDictionary<string, SKFontMetrics> FontMetrics = new();
-        private static ConcurrentDictionary<string, SKPaint> Paints = new();
+        private static ConcurrentDictionary<object, SKFontMetrics> FontMetrics = new();
+        private static ConcurrentDictionary<object, SKPaint> Paints = new();
         private static ConcurrentDictionary<string, SKPaint> 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);
         }
     }
 }

+ 4 - 2
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)