Browse Source

Code refactorization + added support for multi typeface font types

MarcinZiabek 3 years ago
parent
commit
8db283d559

+ 3 - 3
QuestPDF.Examples/BarcodeExamples.cs

@@ -13,11 +13,11 @@ namespace QuestPDF.Examples
         [Test]
         [Test]
         public void Example()
         public void Example()
         {
         {
-            FontManager.RegisterFontType("LibreBarcode39", File.OpenRead("LibreBarcode39-Regular.ttf"));
+            FontManager.RegisterFontType(File.OpenRead("LibreBarcode39-Regular.ttf"));
             
             
             RenderingTest
             RenderingTest
                 .Create()
                 .Create()
-                .PageSize(400, 100)
+                .PageSize(400, 200)
                 .ShowResults()
                 .ShowResults()
                 .Render(container =>
                 .Render(container =>
                 {
                 {
@@ -25,7 +25,7 @@ namespace QuestPDF.Examples
                         .Background(Colors.White)
                         .Background(Colors.White)
                         .AlignCenter()
                         .AlignCenter()
                         .AlignMiddle()
                         .AlignMiddle()
-                        .Text("*QuestPDF*", TextStyle.Default.FontType("LibreBarcode39").Size(64));
+                        .Text("*QuestPDF*", TextStyle.Default.FontType("Libre Barcode 39").Size(64));
                 });
                 });
         }
         }
     }
     }

+ 32 - 25
QuestPDF/Drawing/FontManager.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.IO;
 using System.IO;
+using System.Linq;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
 using SkiaSharp;
 using SkiaSharp;
 
 
@@ -8,27 +9,39 @@ namespace QuestPDF.Drawing
 {
 {
     public static class FontManager
     public static class FontManager
     {
     {
-        private static ConcurrentDictionary<string, FontStyleSet> StyleSets = new ConcurrentDictionary<string, FontStyleSet>();
-        private static ConcurrentDictionary<string, SKFontMetrics> FontMetrics = new ConcurrentDictionary<string, SKFontMetrics>();
-        private static ConcurrentDictionary<string, SKPaint> Paints = new ConcurrentDictionary<string, SKPaint>();
-        private static ConcurrentDictionary<string, SKPaint> ColorPaint = new ConcurrentDictionary<string, SKPaint>();
+        private static ConcurrentDictionary<string, FontStyleSet> StyleSets = new();
+        private static ConcurrentDictionary<string, SKFontMetrics> FontMetrics = new();
+        private static ConcurrentDictionary<string, SKPaint> Paints = new();
+        private static ConcurrentDictionary<string, SKPaint> ColorPaint = new();
 
 
-        private static void RegisterFontType(string fontName, SKTypeface typeface)
+        private static void RegisterFontType(SKData fontData, string? customName = null)
         {
         {
-            FontStyleSet set = StyleSets.GetOrAdd(fontName, _ => new FontStyleSet());
-            set.Add(typeface);
+            foreach (var index in Enumerable.Range(0, 256))
+            {
+                var typeface = SKTypeface.FromData(fontData, index);
+                
+                if (typeface == null)
+                    break;
+                
+                var typefaceName = customName ?? typeface.FamilyName;
+
+                var fontStyleSet = StyleSets.GetOrAdd(typefaceName, _ => new FontStyleSet());
+                fontStyleSet.Add(typeface);
+            }
         }
         }
 
 
+        [Obsolete("Since version 2022.3, the FontManager class offers better font type matching support. Please use the RegisterFontType(Stream stream) overload.")]
         public static void RegisterFontType(string fontName, Stream stream)
         public static void RegisterFontType(string fontName, Stream stream)
         {
         {
-            SKTypeface typeface = SKTypeface.FromStream(stream);
-            RegisterFontType(fontName, typeface);
+            using var fontData = SKData.Create(stream);
+            RegisterFontType(fontData);
+            RegisterFontType(fontData, customName: fontName);
         }
         }
 
 
         public static void RegisterFontType(Stream stream)
         public static void RegisterFontType(Stream stream)
         {
         {
-            SKTypeface typeface = SKTypeface.FromStream(stream);
-            RegisterFontType(typeface.FamilyName, typeface);
+            using var fontData = SKData.Create(stream);
+            RegisterFontType(fontData);
         }
         }
 
 
         internal static SKPaint ColorToPaint(this string color)
         internal static SKPaint ColorToPaint(this string color)
@@ -54,29 +67,23 @@ namespace QuestPDF.Drawing
                 {
                 {
                     Color = SKColor.Parse(style.Color),
                     Color = SKColor.Parse(style.Color),
                     Typeface = GetTypeface(style),
                     Typeface = GetTypeface(style),
-                    TextSize = (style.Size ?? 12),
+                    TextSize = style.Size ?? 12,
                     TextEncoding = SKTextEncoding.Utf32
                     TextEncoding = SKTextEncoding.Utf32
                 };
                 };
             }
             }
 
 
             static SKTypeface GetTypeface(TextStyle style)
             static SKTypeface GetTypeface(TextStyle style)
             {
             {
-                SKFontStyleWeight weight = (SKFontStyleWeight)(style.FontWeight ?? FontWeight.Normal);
-                SKFontStyleWidth width = SKFontStyleWidth.Normal;
-                SKFontStyleSlant slant = (style.IsItalic ?? false) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
+                var weight = (SKFontStyleWeight)(style.FontWeight ?? FontWeight.Normal);
+                var slant = (style.IsItalic ?? false) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
 
 
-                SKFontStyle skFontStyle = new SKFontStyle(weight, width, slant);
+                var skFontStyle = new SKFontStyle(weight, SKFontStyleWidth.Normal, slant);
 
 
-                FontStyleSet set;
-                if (StyleSets.TryGetValue(style.FontType, out set))
-                {
+                if (StyleSets.TryGetValue(style.FontType, out var set))
                     return set.Match(skFontStyle);
                     return set.Match(skFontStyle);
-                }
-                else
-                {
-                    return SKTypeface.FromFamilyName(style.FontType, skFontStyle)
-                        ?? throw new ArgumentException($"The typeface {style.FontType} could not be found.");
-                }
+
+                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.");
             }
             }
         }
         }
 
 

+ 54 - 30
QuestPDF/Drawing/FontStyleSet.cs

@@ -7,18 +7,18 @@ namespace QuestPDF.Drawing
 {
 {
     internal class FontStyleSet
     internal class FontStyleSet
     {
     {
-        private ConcurrentDictionary<SKFontStyle, SKTypeface> Styles = new ConcurrentDictionary<SKFontStyle, SKTypeface>();
+        private ConcurrentDictionary<SKFontStyle, SKTypeface> Styles { get; } = new();
 
 
         public void Add(SKTypeface typeface)
         public void Add(SKTypeface typeface)
         {
         {
-            SKFontStyle style = typeface.FontStyle;
-            Styles.AddOrUpdate(style, (_) => typeface, (_, _) => typeface);
+            var style = typeface.FontStyle;
+            Styles.AddOrUpdate(style, _ => typeface, (_, _) => typeface);
         }
         }
 
 
-        public SKTypeface Match(SKFontStyle target)
+        public SKTypeface? Match(SKFontStyle target)
         {
         {
-            SKFontStyle bestStyle = null;
-            SKTypeface bestTypeface = null;
+            SKFontStyle? bestStyle = null;
+            SKTypeface? bestTypeface = null;
 
 
             foreach (var entry in Styles)
             foreach (var entry in Styles)
             {
             {
@@ -40,40 +40,55 @@ namespace QuestPDF.Drawing
         };
         };
 
 
         // Checks whether style a is a better match for the target then style b. Uses the CSS font style matching algorithm
         // Checks whether style a is a better match for the target then style b. Uses the CSS font style matching algorithm
-        internal static bool IsBetterMatch(SKFontStyle target, SKFontStyle a, SKFontStyle b)
+        internal static bool IsBetterMatch(SKFontStyle? target, SKFontStyle? a, SKFontStyle? b)
         {
         {
             // A font is better than no font
             // A font is better than no font
-            if (b == null) return true;
-            if (a == null) return false;
+            if (b == null) 
+                return true;
+            
+            if (a == null) 
+                return false;
 
 
             // First check font width
             // First check font width
             // For normal and condensed widths prefer smaller widths
             // For normal and condensed widths prefer smaller widths
             // For expanded widths prefer larger widths
             // For expanded widths prefer larger widths
             if (target.Width <= (int)SKFontStyleWidth.Normal)
             if (target.Width <= (int)SKFontStyleWidth.Normal)
             {
             {
-                if (a.Width <= target.Width && b.Width > target.Width) return true;
-                if (a.Width > target.Width && b.Width <= target.Width) return false;
+                if (a.Width <= target.Width && b.Width > target.Width) 
+                    return true;
+                
+                if (a.Width > target.Width && b.Width <= target.Width) 
+                    return false;
             }
             }
             else
             else
             {
             {
-                if (a.Width >= target.Width && b.Width < target.Width) return true;
-                if (a.Width < target.Width && b.Width >= target.Width) return false;
+                if (a.Width >= target.Width && b.Width < target.Width) 
+                    return true;
+                
+                if (a.Width < target.Width && b.Width >= target.Width) 
+                    return false;
             }
             }
 
 
             // Prefer closest match
             // Prefer closest match
-            int widthDifferenceA = Math.Abs(a.Width - target.Width);
-            int widthDifferenceB = Math.Abs(b.Width - target.Width);
+            var widthDifferenceA = Math.Abs(a.Width - target.Width);
+            var widthDifferenceB = Math.Abs(b.Width - target.Width);
 
 
-            if (widthDifferenceA < widthDifferenceB) return true;
-            if (widthDifferenceB < widthDifferenceA) return false;
+            if (widthDifferenceA < widthDifferenceB) 
+                return true;
+            
+            if (widthDifferenceB < widthDifferenceA) 
+                return false;
 
 
             // Prefer closest slant based on provided fallback list
             // Prefer closest slant based on provided fallback list
-            List<SKFontStyleSlant> slantFallback = SlantFallbacks[target.Slant];
-            int slantIndexA = slantFallback.IndexOf(a.Slant);
-            int slantIndexB = slantFallback.IndexOf(b.Slant);
+            var slantFallback = SlantFallbacks[target.Slant];
+            var slantIndexA = slantFallback.IndexOf(a.Slant);
+            var slantIndexB = slantFallback.IndexOf(b.Slant);
 
 
-            if (slantIndexA < slantIndexB) return true;
-            if (slantIndexB < slantIndexA) return false;
+            if (slantIndexA < slantIndexB) 
+                return true;
+            
+            if (slantIndexB < slantIndexA) 
+                return false;
 
 
             // Check weight last
             // Check weight last
             // For thin (<400) weights, prefer thinner weights
             // For thin (<400) weights, prefer thinner weights
@@ -83,24 +98,33 @@ namespace QuestPDF.Drawing
 
 
             if (target.Weight >= 400 && target.Weight <= 500)
             if (target.Weight >= 400 && target.Weight <= 500)
             {
             {
-                if ((a.Weight >= 400 && a.Weight <= 500) && !(b.Weight >= 400 && b.Weight <= 500)) return true;
-                if (!(a.Weight >= 400 && a.Weight <= 500) && (b.Weight >= 400 && b.Weight <= 500)) return false;
+                if ((a.Weight >= 400 && a.Weight <= 500) && !(b.Weight >= 400 && b.Weight <= 500)) 
+                    return true;
+                
+                if (!(a.Weight >= 400 && a.Weight <= 500) && (b.Weight >= 400 && b.Weight <= 500)) 
+                    return false;
             }
             }
 
 
             if (target.Weight < 450)
             if (target.Weight < 450)
             {
             {
-                if (a.Weight <= target.Weight && b.Weight > target.Weight) return true;
-                if (a.Weight > target.Weight && b.Weight <= target.Weight) return false;
+                if (a.Weight <= target.Weight && b.Weight > target.Weight) 
+                    return true;
+                
+                if (a.Weight > target.Weight && b.Weight <= target.Weight)
+                    return false;
             }
             }
             else
             else
             {
             {
-                if (a.Weight >= target.Weight && b.Weight < target.Weight) return true;
-                if (a.Weight < target.Weight && b.Weight >= target.Weight) return false;
+                if (a.Weight >= target.Weight && b.Weight < target.Weight) 
+                    return true;
+                
+                if (a.Weight < target.Weight && b.Weight >= target.Weight) 
+                    return false;
             }
             }
 
 
             // Prefer closest weight
             // Prefer closest weight
-            int weightDifferenceA = Math.Abs(a.Weight - target.Weight);
-            int weightDifferenceB = Math.Abs(b.Weight - target.Weight);
+            var weightDifferenceA = Math.Abs(a.Weight - target.Weight);
+            var weightDifferenceB = Math.Abs(b.Weight - target.Weight);
 
 
             return weightDifferenceA < weightDifferenceB;
             return weightDifferenceA < weightDifferenceB;
         }
         }