Przeglądaj źródła

Added checking if all text glyphs are available in specified font

MarcinZiabek 3 lat temu
rodzic
commit
e17867d1f3

+ 27 - 7
QuestPDF/Drawing/TextShaper.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Linq;
 using HarfBuzzSharp;
 using QuestPDF.Infrastructure;
 using SkiaSharp;
@@ -9,14 +10,16 @@ namespace QuestPDF.Drawing
     internal class TextShaper
     {
         public const int FontShapingScale = 512;
-     
-        private Font Font { get; }
-        private SKPaint Paint { get; }
         
-        public TextShaper(TextStyle style)
+        private TextStyle TextStyle { get; }
+
+        private SKFont Font => TextStyle.ToFont();
+        private Font ShaperFont => TextStyle.ToShaperFont();
+        private SKPaint Paint => TextStyle.ToPaint();
+
+        public TextShaper(TextStyle textStyle)
         {
-            Font = style.ToShaperFont();
-            Paint = style.ToPaint();
+            TextStyle = textStyle;
         }
 
         public TextShapingResult Shape(string text)
@@ -26,7 +29,7 @@ namespace QuestPDF.Drawing
             PopulateBufferWithText(buffer, text);
             buffer.GuessSegmentProperties();
 
-            Font.Shape(buffer);
+            ShaperFont.Shape(buffer);
             
             var length = buffer.Length;
             var glyphInfos = buffer.GlyphInfos;
@@ -52,6 +55,9 @@ namespace QuestPDF.Drawing
                 xOffset += glyphPositions[i].XAdvance * scaleX;
                 yOffset += glyphPositions[i].YAdvance * scaleY;
             }
+
+            if (Settings.CheckIfAllTextGlyphsAreAvailableInSpecifiedFont)
+                CheckIfAllGlyphsAreAvailable(glyphs, text);
             
             return new TextShapingResult(glyphs);
         }
@@ -72,6 +78,20 @@ namespace QuestPDF.Drawing
             else
                 throw new NotSupportedException("TextEncoding of type GlyphId is not supported.");
         }
+        
+        void CheckIfAllGlyphsAreAvailable(ShapedGlyph[] glyphs, string originalText)
+        {
+            var containsMissingGlyphs = glyphs.Any(x => x.Codepoint == default);
+            
+            if (!containsMissingGlyphs)
+                return;
+            
+            throw new ArgumentException(
+                $"Detected missing font glyphs while rendering text. " +
+                $"This means that the document contains text with characters not present in the assigned font. " +
+                $"Such characters are replaced by placeholders, usually visible as empty rectangles. " +
+                $"Font family used: {TextStyle.FontFamily}. Issue detected in text: '{originalText}'");
+        }
     }
     
     internal struct ShapedGlyph

+ 0 - 10
QuestPDF/Infrastructure/Settings.cs

@@ -1,10 +0,0 @@
-namespace QuestPDF.Infrastructure
-{
-    public static class Settings
-    {
-        public static int DocumentLayoutExceptionThreshold { get; set; } = 250;
-        
-        public static bool EnableCaching { get; set; } = !System.Diagnostics.Debugger.IsAttached;
-        public static bool EnableDebugging { get; set; } = System.Diagnostics.Debugger.IsAttached;
-    }
-}

+ 40 - 0
QuestPDF/Settings.cs

@@ -0,0 +1,40 @@
+namespace QuestPDF
+{
+    public static class Settings
+    {
+        /// <summary>
+        /// This value represents the maximum lenght of the document that the library produces.
+        /// This is useful when layout constraints are too strong, e.g. one element does not fit in another.
+        /// In such cases, the library would produce document of infinite length, consuming all available resources.
+        /// To break the algorithm and save the environment, the library breaks the rendering process after reaching specified length of document.
+        /// If your content requires generating longer documents, please assign the most reasonable value.
+        /// </summary>
+        public static int DocumentLayoutExceptionThreshold { get; set; } = 250;
+        
+        /// <summary>
+        /// This flag generates additional document elements to cache layout calculation results.
+        /// In the vast majority of cases, this significantly improves performance, while slightly increasing memory consumption.
+        /// </summary>
+        /// <remarks>By default, this flag is enabled only when the debugger is NOT attached.</remarks>
+        public static bool EnableCaching { get; set; } = !System.Diagnostics.Debugger.IsAttached;
+        
+        /// <summary>
+        /// This flag generates additional document elements to improve layout debugging experience.
+        /// When the DocumentLayoutException is thrown, the library is able to provide additional execution context.
+        /// It includes layout calculation results and path to the problematic area.
+        /// </summary>
+        /// <remarks>By default, this flag is enabled only when the debugger IS attached.</remarks>
+        public static bool EnableDebugging { get; set; } = System.Diagnostics.Debugger.IsAttached;
+        
+        /// <summary>
+        /// This flag enables checking the font glyph availability.
+        /// If your text contains glyphs that are not present in the specified font,
+        /// 1) when this flag is enabled: the DocumentDrawingException is thrown. OR 
+        /// 2) when this flag is disabled: placeholder characters are visible in the produced PDF file. 
+        /// Enabling this flag may slightly decrease document generation performance.
+        /// However, it provides hints that used fonts are not sufficient to produce correct results.
+        /// </summary>
+        /// <remarks>By default, this flag is enabled only when the debugger IS attached.</remarks>
+        public static bool CheckIfAllTextGlyphsAreAvailableInSpecifiedFont { get; set; } = System.Diagnostics.Debugger.IsAttached;
+    }
+}