Переглянути джерело

Introduced new line breaking algorithm: break anywhere

MarcinZiabek 3 роки тому
батько
коміт
11ff53946d

+ 4 - 2
QuestPDF.Examples/TextExamples.cs

@@ -394,8 +394,10 @@ namespace QuestPDF.Examples
 
                             column.Item().Text(text =>
                             {
-                                text.DefaultTextStyle(x => x.BackgroundColor(Colors.Red.Lighten3));
-                                text.Span("       " + Placeholders.LoremIpsum() + " 0123456789012345678901234567890123456789012345678901234567890123456789         ").FontSize(24);
+                                text.DefaultTextStyle(x => x.BackgroundColor(Colors.Red.Lighten3).FontSize(24));
+                                
+                                text.Span("       " + Placeholders.LoremIpsum());
+                                text.Span(" 0123456789012345678901234567890123456789012345678901234567890123456789         ").BreakAnywhere();
                             });
                         });
                     });

+ 1 - 0
QuestPDF/Elements/Text/Calculation/TextMeasurementResult.cs

@@ -14,6 +14,7 @@ namespace QuestPDF.Elements.Text.Calculation
         
         public int StartIndex { get; set; }
         public int EndIndex { get; set; }
+        public int NextIndex { get; set; }
         public int TotalIndex { get; set; }
 
         public bool IsLast => EndIndex == TotalIndex;

+ 30 - 24
QuestPDF/Elements/Text/Items/TextBlockSpan.cs

@@ -9,6 +9,8 @@ namespace QuestPDF.Elements.Text.Items
 {
     internal class TextBlockSpan : ITextBlockItem
     {
+        private const char Space = ' ';
+        
         public string Text { get; set; }
         public TextStyle Style { get; set; } = new TextStyle();
 
@@ -26,8 +28,6 @@ namespace QuestPDF.Elements.Text.Items
         
         internal TextMeasurementResult? MeasureWithoutCache(TextMeasurementRequest request)
         {
-            const char space = ' ';
-            
             var paint = Style.ToPaint();
             var fontMetrics = Style.ToFontMetrics();
 
@@ -37,7 +37,7 @@ namespace QuestPDF.Elements.Text.Items
             // ignore leading spaces
             if (!request.IsFirstElementInBlock && request.IsFirstElementInLine)
             {
-                while (startIndex < Text.Length && Text[startIndex] == space)
+                while (startIndex < Text.Length && Text[startIndex] == Space)
                     startIndex++;
             }
             
@@ -67,7 +67,7 @@ namespace QuestPDF.Elements.Text.Items
             if (wrappedTextLength == null)
                 return null;
 
-            textLength = wrappedTextLength.Value;
+            textLength = wrappedTextLength.Value.fragmentLength;
 
             text = text.Slice(0, textLength);
 
@@ -87,33 +87,39 @@ namespace QuestPDF.Elements.Text.Items
                 
                 StartIndex = startIndex,
                 EndIndex = endIndex,
+                NextIndex = startIndex + wrappedTextLength.Value.nextIndex,
                 TotalIndex = Text.Length
             };
-
-            static int? WrapText(ReadOnlySpan<char> text, int textLength, bool isFirstElementInLine)
-            {
-                // textLength - length of the part of the text that fits in available width (creating a line)
+        }
+        
+        // TODO: consider introduce text wrapping abstraction (basic, english-like, asian-like)
+        private (int fragmentLength, int nextIndex)? WrapText(ReadOnlySpan<char> text, int textLength, bool isFirstElementInLine)
+        {
+            // textLength - length of the part of the text that fits in available width (creating a line)
                 
-                // entire text fits, no need to wrap
-                if (textLength == text.Length)
-                    return textLength;
+            // entire text fits, no need to wrap
+            if (textLength == text.Length)
+                return (textLength, textLength + 1);
 
-                // current line ends at word, next character is space, perfect place to wrap
-                if (text[textLength - 1] != space && text[textLength] == space)
-                    return textLength;
+            // breaking anywhere
+            if (Style.BreakAnywhere ?? false)
+                return (textLength, textLength);
                 
-                // find last space within the available text to wrap
-                var lastSpaceIndex = text.Slice(0, textLength).LastIndexOf(space);
+            // current line ends at word, next character is space, perfect place to wrap
+            if (text[textLength - 1] != Space && text[textLength] == Space)
+                return (textLength, textLength + 1);
+                
+            // find last space within the available text to wrap
+            var lastSpaceIndex = text.Slice(0, textLength).LastIndexOf(Space);
 
-                // text contains space that can be used to wrap
-                if (lastSpaceIndex > 0)
-                    return lastSpaceIndex;
+            // text contains space that can be used to wrap
+            if (lastSpaceIndex > 0)
+                return (lastSpaceIndex, lastSpaceIndex + 1);
                 
-                // there is no available space to wrap text
-                // if the item is first within the line, perform safe mode and chop the word
-                // otherwise, move the item into the next line
-                return isFirstElementInLine ? textLength : null;
-            }
+            // there is no available space to wrap text
+            // if the item is first within the line, perform safe mode and chop the word
+            // otherwise, move the item into the next line
+            return isFirstElementInLine ? (textLength, textLength + 1) : null;
         }
         
         public virtual void Draw(TextDrawingRequest request)

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

@@ -187,7 +187,7 @@ namespace QuestPDF.Elements.Text
                     });
 
                     currentWidth += measurementResponse.Width;
-                    currentItemIndex = measurementResponse.EndIndex + 1;
+                    currentItemIndex = measurementResponse.NextIndex;
                     
                     if (!measurementResponse.IsLast)
                         break;

+ 6 - 0
QuestPDF/Fluent/TextSpanDescriptorExtensions.cs

@@ -63,6 +63,12 @@ namespace QuestPDF.Fluent
             return descriptor;
         }
 
+        public static T BreakAnywhere<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
+        {
+            descriptor.TextStyle.BreakAnywhere = value;
+            return descriptor;
+        }
+
         #region Weight
         
         public static T Weight<T>(this T descriptor, FontWeight weight) where T : TextSpanDescriptor

+ 5 - 0
QuestPDF/Fluent/TextStyleExtensions.cs

@@ -71,6 +71,11 @@ namespace QuestPDF.Fluent
         {
             return style.Mutate(x => x.HasUnderline = value);
         }
+        
+        public static TextStyle BreakAnywhere(this TextStyle style, bool value = true)
+        {
+            return style.Mutate(x => x.BreakAnywhere = value);
+        }
 
         #region Weight
         

+ 5 - 1
QuestPDF/Infrastructure/TextStyle.cs

@@ -16,6 +16,7 @@ namespace QuestPDF.Infrastructure
         internal bool? IsItalic { get; set; }
         internal bool? HasStrikethrough { get; set; }
         internal bool? HasUnderline { get; set; }
+        internal bool? BreakAnywhere { get; set; }
 
         internal object PaintKey { get; private set; }
         internal object FontMetricsKey { get; private set; }
@@ -30,7 +31,8 @@ namespace QuestPDF.Infrastructure
             FontWeight = Infrastructure.FontWeight.Normal,
             IsItalic = false,
             HasStrikethrough = false,
-            HasUnderline = false
+            HasUnderline = false,
+            BreakAnywhere = false
         };
 
         public static TextStyle Default => new TextStyle();
@@ -58,6 +60,7 @@ namespace QuestPDF.Infrastructure
             IsItalic ??= parentStyle.IsItalic;
             HasStrikethrough ??= parentStyle.HasStrikethrough;
             HasUnderline ??= parentStyle.HasUnderline;
+            BreakAnywhere ??= parentStyle.BreakAnywhere;
         }
 
         internal void OverrideStyle(TextStyle parentStyle)
@@ -71,6 +74,7 @@ namespace QuestPDF.Infrastructure
             IsItalic = parentStyle.IsItalic ?? IsItalic;
             HasStrikethrough = parentStyle.HasStrikethrough ?? HasStrikethrough;
             HasUnderline = parentStyle.HasUnderline ?? HasUnderline;
+            BreakAnywhere = parentStyle.BreakAnywhere ?? BreakAnywhere;
         }
         
         internal TextStyle Clone()