Browse Source

Text element: improved the default sizing behavior for the Text element. Now, when the text is empty, it works more consistently by taking up zero width while still reserving vertical space based on the font size.

Marcin Ziąbek 1 year ago
parent
commit
32e2b1e98e

+ 36 - 1
Source/QuestPDF/Elements/Text/TextBlock.cs

@@ -4,7 +4,6 @@ using System.Linq;
 using QuestPDF.Drawing;
 using QuestPDF.Drawing;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Elements.Text.Items;
 using QuestPDF.Elements.Text.Items;
-using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
 using QuestPDF.Skia;
 using QuestPDF.Skia;
 using QuestPDF.Skia.Text;
 using QuestPDF.Skia.Text;
@@ -37,6 +36,7 @@ namespace QuestPDF.Elements.Text
         private float MaximumWidth { get; set; }
         private float MaximumWidth { get; set; }
         
         
         private bool IsRendered { get; set; }
         private bool IsRendered { get; set; }
+        private bool? ContainsOnlyWhiteSpace { get; set; }
         private int CurrentLineIndex { get; set; }
         private int CurrentLineIndex { get; set; }
         private float CurrentTopOffset { get; set; }
         private float CurrentTopOffset { get; set; }
         
         
@@ -61,6 +61,13 @@ namespace QuestPDF.Elements.Text
             
             
             if (IsRendered)
             if (IsRendered)
                 return SpacePlan.Empty();
                 return SpacePlan.Empty();
+
+            // if the text block does not contain any items, or all items are null, return SpacePlan.Empty
+            // but if the text block contains only whitespace, return SpacePlan.FullRender with zero width and font-based height
+            ContainsOnlyWhiteSpace ??= Items.All(x => x is TextBlockSpan textBlockSpan && string.IsNullOrWhiteSpace(textBlockSpan.Text));
+            
+            if (ContainsOnlyWhiteSpace == true)
+                return SpacePlan.FullRender(0, MeasureHeightOfParagraphContainingOnlyWhiteSpace());
             
             
             Initialize();
             Initialize();
             
             
@@ -108,6 +115,9 @@ namespace QuestPDF.Elements.Text
             if (IsRendered)
             if (IsRendered)
                 return;
                 return;
             
             
+            if (ContainsOnlyWhiteSpace == true)
+                return;
+            
             CalculateParagraphMetrics(availableSpace);
             CalculateParagraphMetrics(availableSpace);
 
 
             if (MaximumWidth == 0)
             if (MaximumWidth == 0)
@@ -501,5 +511,30 @@ namespace QuestPDF.Elements.Text
                 $"You can disable this check by setting the 'Settings.CheckIfAllTextGlyphsAreAvailable' option to 'false'. \n" +
                 $"You can disable this check by setting the 'Settings.CheckIfAllTextGlyphsAreAvailable' option to 'false'. \n" +
                 $"However, this may result with text glyphs being incorrectly rendered without any warning.");
                 $"However, this may result with text glyphs being incorrectly rendered without any warning.");
         }
         }
+
+        private float MeasureHeightOfParagraphContainingOnlyWhiteSpace()
+        {
+            var paragraphStyle = new ParagraphStyleConfiguration
+            {
+                Alignment = ParagraphStyleConfiguration.TextAlign.Start,
+                Direction = ParagraphStyleConfiguration.TextDirection.Ltr
+            };
+            
+            var builder = SkParagraphBuilderPoolManager.Get(paragraphStyle);
+
+            try
+            {
+                foreach (var textBlockSpan in Items.OfType<TextBlockSpan>())
+                    builder.AddText("\u00A0", textBlockSpan.Style.GetSkTextStyle()); // non-breaking space
+
+                var paragraph = builder.CreateParagraph();
+                paragraph.PlanLayout(1000);
+                return paragraph.GetLineMetrics().First().Height;
+            }
+            finally
+            {
+                SkParagraphBuilderPoolManager.Return(builder);
+            }
+        }
     }
     }
 }
 }

+ 4 - 4
Source/QuestPDF/Fluent/TextExtensions.cs

@@ -237,7 +237,7 @@ namespace QuestPDF.Fluent
         /// <include file='../Resources/Documentation.xml' path='documentation/doc[@for="text.returns.spanDescriptor"]/*' />
         /// <include file='../Resources/Documentation.xml' path='documentation/doc[@for="text.returns.spanDescriptor"]/*' />
         public TextSpanDescriptor Span(string? text)
         public TextSpanDescriptor Span(string? text)
         {
         {
-            if (IsNullOrEmpty(text))
+            if (text == null)
                 return new TextSpanDescriptor(new TextBlockSpan());
                 return new TextSpanDescriptor(new TextBlockSpan());
 
 
             var textSpan = new TextBlockSpan() { Text = text };
             var textSpan = new TextBlockSpan() { Text = text };
@@ -352,7 +352,7 @@ namespace QuestPDF.Fluent
             if (IsNullOrEmpty(sectionName))
             if (IsNullOrEmpty(sectionName))
                 throw new ArgumentException("Section name cannot be null or empty", nameof(sectionName));
                 throw new ArgumentException("Section name cannot be null or empty", nameof(sectionName));
 
 
-            if (IsNullOrEmpty(text))
+            if (text == null)
                 return new TextSpanDescriptor(new TextBlockSpan());
                 return new TextSpanDescriptor(new TextBlockSpan());
 
 
             var textBlockItem = new TextBlockSectionLink
             var textBlockItem = new TextBlockSectionLink
@@ -381,7 +381,7 @@ namespace QuestPDF.Fluent
             if (IsNullOrEmpty(url))
             if (IsNullOrEmpty(url))
                 throw new ArgumentException("Url cannot be null or empty", nameof(url));
                 throw new ArgumentException("Url cannot be null or empty", nameof(url));
 
 
-            if (IsNullOrEmpty(text))
+            if (text == null)
                 return new TextSpanDescriptor(new TextBlockSpan());
                 return new TextSpanDescriptor(new TextBlockSpan());
             
             
             var textBlockItem = new TextBlockHyperlink
             var textBlockItem = new TextBlockHyperlink
@@ -478,7 +478,7 @@ namespace QuestPDF.Fluent
         /// <include file='../Resources/Documentation.xml' path='documentation/doc[@for="text.returns.spanDescriptor"]/*' />
         /// <include file='../Resources/Documentation.xml' path='documentation/doc[@for="text.returns.spanDescriptor"]/*' />
         public static TextBlockDescriptor Text(this IContainer container, string? text)
         public static TextBlockDescriptor Text(this IContainer container, string? text)
         {
         {
-            if (IsNullOrEmpty(text))
+            if (text == null)
                 return new TextBlockDescriptor(new TextBlock(), new TextBlockSpan());
                 return new TextBlockDescriptor(new TextBlock(), new TextBlockSpan());
             
             
             var textBlock = new TextBlock();
             var textBlock = new TextBlock();

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

@@ -20,3 +20,7 @@ Bug Fixes:
 - Column Element: Resolved instability issues in nested layouts with spacing and zero-sized elements.
 - Column Element: Resolved instability issues in nested layouts with spacing and zero-sized elements.
 - JPEG Quality: Disabled JPEG image downsampling/downscaling to maintain the highest quality levels.
 - JPEG Quality: Disabled JPEG image downsampling/downscaling to maintain the highest quality levels.
 - Image Compression: Disabled additional image compression performed by Skia.
 - Image Compression: Disabled additional image compression performed by Skia.
+
+
+Version 2024.6.1
+- Text element: improved the default sizing behavior for the Text element. Now, when the text is empty, it works more consistently by taking up zero width while still reserving vertical space based on the font size.