Bladeren bron

Ability to provide multiple font fallbacks

Marcin Ziąbek 1 jaar geleden
bovenliggende
commit
a9f8d1448c

+ 1 - 1
Source/QuestPDF.UnitTests/TextStyleTests.cs

@@ -40,7 +40,7 @@ namespace QuestPDF.UnitTests
             {
                 Id = targetStyle.Id, // expect to break when adding new TextStyle properties, so use the real one
                 Size = 20, 
-                FontFamily = "Times New Roman",
+                FontFamilies = new[] { "Times New Roman" },
                 FontWeight = FontWeight.Bold,
                 BackgroundColor = Colors.Red.Lighten2,
                 HasStrikethrough = true

+ 17 - 5
Source/QuestPDF/Drawing/FontManager.cs

@@ -1,5 +1,6 @@
 using System;
 using System.IO;
+using System.Linq;
 using System.Reflection;
 using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
@@ -17,8 +18,8 @@ namespace QuestPDF.Drawing
     {
         internal static SkTypefaceProvider TypefaceProvider { get; } = new();
         
-        private static SkFontCollection LocalFontCollection { get; } = SkFontCollection.Create(FontManager.TypefaceProvider, false, true);
-        private static SkFontCollection GlobalFontCollection { get; } = SkFontCollection.Create(FontManager.TypefaceProvider, true, true);
+        private static SkFontCollection LocalFontCollection { get; } = SkFontCollection.Create(TypefaceProvider, SkFontManager.Empty);
+        private static SkFontCollection GlobalFontCollection { get; } = SkFontCollection.Create(TypefaceProvider, SkFontManager.Global);
         
         internal static SkFontCollection FontCollection => Settings.UseEnvironmentFonts ? GlobalFontCollection : LocalFontCollection;
         
@@ -73,13 +74,24 @@ namespace QuestPDF.Drawing
         
         private static void RegisterLibraryDefaultFonts()
         {
+            var supportedFontExtensions = new[] { ".ttf", ".otf", ".ttc", ".pfb", ".woff", ".woff2" };
+            
             var executionPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
-            var fontFilePaths = Directory.GetFiles(executionPath, "*.ttf", SearchOption.AllDirectories);
+            
+            var fontFilePaths = supportedFontExtensions
+                .SelectMany(extension => Directory.GetFiles(executionPath, extension, SearchOption.AllDirectories));
             
             foreach (var fileName in fontFilePaths)
             {
-                using var fontFileStream = File.OpenRead(fileName);
-                RegisterFont(fontFileStream);
+                try
+                {
+                    using var fontFileStream = File.OpenRead(fileName);
+                    RegisterFont(fontFileStream);
+                }
+                catch
+                {
+                    
+                }
             }
         }
     }

+ 2 - 9
Source/QuestPDF/Fluent/TextSpanDescriptorExtensions.cs

@@ -46,16 +46,9 @@ namespace QuestPDF.Fluent
         }
         
         /// <include file='../Resources/Documentation.xml' path='documentation/doc[@for="text.fontFamily"]/*' />
-        public static T FontFamily<T>(this T descriptor, string value) where T : TextSpanDescriptor
+        public static T FontFamily<T>(this T descriptor, params string[] values) where T : TextSpanDescriptor
         {
-            descriptor.MutateTextStyle(TextStyleExtensions.FontFamily, value);
-            return descriptor;
-        }
-        
-        /// <include file='../Resources/Documentation.xml' path='documentation/doc[@for="text.fontFallback"]/*' />
-        public static T FontFamilyFallback<T>(this T descriptor, string value) where T : TextSpanDescriptor
-        {
-            descriptor.MutateTextStyle(TextStyleExtensions.FontFamilyFallback, value);
+            descriptor.MutateTextStyle(TextStyleExtensions.FontFamily, values);
             return descriptor;
         }
         

+ 2 - 8
Source/QuestPDF/Fluent/TextStyleExtensions.cs

@@ -34,15 +34,9 @@ namespace QuestPDF.Fluent
         }
         
         /// <include file='../Resources/Documentation.xml' path='documentation/doc[@for="text.fontFamily"]/*' />
-        public static TextStyle FontFamily(this TextStyle style, string value)
+        public static TextStyle FontFamily(this TextStyle style, params string[] values)
         {
-            return style.Mutate(TextStyleProperty.FontFamily, value);
-        }
-        
-        /// <include file='../Resources/Documentation.xml' path='documentation/doc[@for="text.fontFallback"]/*' />
-        public static TextStyle FontFamilyFallback(this TextStyle style, string value)
-        {
-            return style.Mutate(TextStyleProperty.FontFamilyFallback, value);
+            return style.Mutate(TextStyleProperty.FontFamilies, values);
         }
         
         [Obsolete("This element has been renamed since version 2022.3. Please use the FontSize method.")]

+ 19 - 10
Source/QuestPDF/Infrastructure/TextStyle.cs

@@ -1,4 +1,6 @@
-using System.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
 using QuestPDF.Helpers;
 using QuestPDF.Skia;
 using QuestPDF.Skia.Text;
@@ -12,8 +14,7 @@ namespace QuestPDF.Infrastructure
         internal Color? Color { get; set; }
         internal Color? BackgroundColor { get; set; }
         internal Color? DecorationColor { get; set; }
-        internal string? FontFamily { get; set; }
-        internal string? FontFamilyFallback { get; set; }
+        internal string[]? FontFamilies { get; set; }
         internal float? Size { get; set; }
         internal float? LineHeight { get; set; }
         internal float? LetterSpacing { get; set; }
@@ -39,8 +40,7 @@ namespace QuestPDF.Infrastructure
             Color = Colors.Black,
             BackgroundColor = Colors.Transparent,
             DecorationColor = Colors.Black,
-            FontFamily = Fonts.Lato,
-            FontFamilyFallback = null,
+            FontFamilies = new[] { Fonts.Lato },
             Size = 12,
             LineHeight = 1.2f,
             LetterSpacing = 0,
@@ -63,17 +63,15 @@ namespace QuestPDF.Infrastructure
             if (SkTextStyleCache != null)
                 return SkTextStyleCache;
             
-            using var fontFamily = new SkText(FontFamily);
-            using var fontFamilyFallback = new SkText(FontFamilyFallback);
-            
+            var fontFamilyTexts = FontFamilies.Select(x => new SkText(x)).ToList();
+
             SkTextStyleCache = new SkTextStyle(new TextStyleConfiguration
             {
                 FontSize = CalculateTargetFontSize(),
                 FontWeight = (TextStyleConfiguration.FontWeights?)FontWeight ?? TextStyleConfiguration.FontWeights.Normal,
                 
                 IsItalic = IsItalic ?? false,
-                FontFamily = fontFamily,
-                FontFamilyFallback = fontFamilyFallback,
+                FontFamilies = GetFontFamilyPointers(fontFamilyTexts),
                 ForegroundColor = Color ?? Colors.Black,
                 BackgroundColor = BackgroundColor ?? Colors.Transparent,
                 DecorationColor = DecorationColor ?? Colors.Black,
@@ -89,8 +87,19 @@ namespace QuestPDF.Infrastructure
                 BaselineOffset = CalculateBaselineOffset(),
             });
             
+            fontFamilyTexts.ForEach(x => x.Dispose());
             return SkTextStyleCache;
 
+            IntPtr[] GetFontFamilyPointers(IList<SkText> texts)
+            {
+                var result = new IntPtr[TextStyleConfiguration.FONT_FAMILIES_LENGTH];
+                
+                for (var i = 0; i < Math.Min(result.Length, texts.Count); i++)
+                    result[i] = texts[i].Instance;
+                
+                return result;
+            }
+            
             TextStyleConfiguration.TextDecorationMode DecorationMode()
             {
                 if (DecorationStyle == TextStyleConfiguration.TextDecorationStyle.Solid)

+ 30 - 2
Source/QuestPDF/Infrastructure/TextStyleManager.cs

@@ -12,8 +12,7 @@ namespace QuestPDF.Infrastructure
         Color,
         BackgroundColor,
         DecorationColor,
-        FontFamily,
-        FontFamilyFallback,
+        FontFamilies,
         Size,
         LineHeight,
         LetterSpacing,
@@ -52,6 +51,9 @@ namespace QuestPDF.Infrastructure
 
         private static TextStyle MutateStyle(this TextStyle origin, TextStyleProperty targetProperty, object? newValue, bool overrideValue)
         {
+            if (targetProperty == TextStyleProperty.FontFamilies)
+                return MutateFontFamily(origin, newValue as string[], overrideValue);
+            
             lock (MutationLock)
             {
                 if (overrideValue && newValue is null)
@@ -78,6 +80,32 @@ namespace QuestPDF.Infrastructure
             }
         }
         
+        private static TextStyle MutateFontFamily(this TextStyle origin, string[]? newValue, bool overrideValue)
+        {
+            lock (MutationLock)
+            {
+                if (overrideValue && newValue is null)
+                    return origin;
+                
+                var newIndex = TextStyles.Count;
+                var newTextStyle = origin with { Id = newIndex };
+                newTextStyle.Id = newIndex;
+                
+                newValue ??= Array.Empty<string>();
+                var oldValue = origin.FontFamilies ?? Array.Empty<string>();
+                
+                if (origin.FontFamilies?.SequenceEqual(newValue) == true)
+                    return origin;
+                
+                newTextStyle.FontFamilies = overrideValue 
+                    ? newValue 
+                    : newValue.Concat(oldValue).Distinct().ToArray();
+
+                TextStyles.Add(newTextStyle);
+                return newTextStyle;
+            }
+        }
+        
         internal static TextStyle ApplyInheritedStyle(this TextStyle style, TextStyle parent)
         {
             return TextStyleApplyInheritedCache.GetOrAdd((style.Id, parent.Id), key => ApplyStyleProperties(key.originId, key.parentId, overrideStyle: false));

+ 1 - 1
Source/QuestPDF/QuestPDF.csproj

@@ -3,7 +3,7 @@
         <Authors>MarcinZiabek</Authors>
         <Company>CodeFlint</Company>
         <PackageId>QuestPDF</PackageId>
-        <Version>2024.3.0-beta</Version>
+        <Version>2024.3.0-beta1</Version>
         <PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API. Easily generate PDF reports, invoices, exports, etc.</PackageDescription>
         <PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
         <LangVersion>10</LangVersion>

+ 1 - 8
Source/QuestPDF/Resources/Documentation.xml

@@ -441,14 +441,7 @@
             This API reduces the garbage collector pressure and offers the best performance.
         </remarks>
     </doc>
-
-    <doc for="text.fontFallback">
-        <summary>
-            Specifies an alternative font family to be used when the primary font family does not support certain character glyphs.
-            <a href="https://www.questpdf.com/api-reference/text.html#font-fallback">Learn more</a>
-        </summary>
-    </doc>
-
+    
     <!-- PREVIEWER -->
 
     <doc for="previewer.support">

+ 6 - 0
Source/QuestPDF/Resources/ReleaseNotes.txt

@@ -29,3 +29,9 @@ Version 2024.3.0-beta
 - Improved native memory management and fixed memory leaks,
 - Include licenses of the third party dependencies in the NuGet package,
 - Performed code cleanup and made minor adjustments.
+
+
+
+Version 2024.3.0-beta1
+- Fixed: font fallback does not work when the Settings.UseEnvironmentFonts setting is set to false,
+- Ability to provide multiple font fallbacks.

+ 2 - 10
Source/QuestPDF/Skia/Text/SkFontCollection.cs

@@ -17,22 +17,14 @@ internal sealed class SkFontCollection : IDisposable
     {
         public IntPtr FontManager;
         public IntPtr TypefaceProvider;
-        [MarshalAs(UnmanagedType.I1)] public bool EnableFontFallback;
     }
 
-    public static SkFontCollection Create(SkTypefaceProvider? typefaceProvider = null, bool useGlobalFonts = false, bool enableFontFallback = false)
+    public static SkFontCollection Create(SkTypefaceProvider typefaceProvider, SkFontManager fontManager)
     {
-        typefaceProvider ??= new SkTypefaceProvider();
-
-        var fontManager = useGlobalFonts
-            ? SkFontManager.Global
-            : SkFontManager.Empty;
-        
         var command = new CreateCommand
         {
             FontManager = fontManager.Instance,
-            TypefaceProvider = typefaceProvider.Instance,
-            EnableFontFallback = enableFontFallback
+            TypefaceProvider = typefaceProvider.Instance
         };
         
         var instance = API.font_collection_create(command);

+ 2 - 2
Source/QuestPDF/Skia/Text/SkTextStyle.cs

@@ -10,8 +10,8 @@ internal struct TextStyleConfiguration
     public FontWeights FontWeight;
     public bool IsItalic;
 
-    public IntPtr FontFamily;
-    public IntPtr FontFamilyFallback;
+    public const int FONT_FAMILIES_LENGTH = 16;
+    [MarshalAs(UnmanagedType.ByValArray, SizeConst = FONT_FAMILIES_LENGTH)] public IntPtr[] FontFamilies;
     
     public uint ForegroundColor;
     public uint BackgroundColor;