From 5fa32a06cc609b95d61137737a263a293e2b8e87 Mon Sep 17 00:00:00 2001 From: MarcinZiabek Date: Thu, 26 May 2022 00:42:50 +0200 Subject: [PATCH] Text: fixed corner case --- QuestPDF.Examples/TextExamples.cs | 4 +-- QuestPDF/Drawing/TextShaper.cs | 15 ++++++---- QuestPDF/Elements/Text/Items/TextBlockSpan.cs | 30 ++++++++++--------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/QuestPDF.Examples/TextExamples.cs b/QuestPDF.Examples/TextExamples.cs index 10c8b71..c562b10 100644 --- a/QuestPDF.Examples/TextExamples.cs +++ b/QuestPDF.Examples/TextExamples.cs @@ -403,7 +403,7 @@ namespace QuestPDF.Examples .Padding(10) .Text(text => { - text.DefaultTextStyle(TextStyle.Default.FontSize(20)); + text.DefaultTextStyle(TextStyle.Default.FontSize(20).BackgroundColor(Colors.Red.Lighten4)); text.AlignLeft(); text.ParagraphSpacing(10); @@ -422,7 +422,7 @@ namespace QuestPDF.Examples { text.Line($"{i}: {Placeholders.Paragraph()}"); - text.Hyperlink("Please visit QuestPDF website", "https://www.questpdf.com"); + text.Hyperlink("Please visit QuestPDF website. ", "https://www.questpdf.com"); text.Span("This is page number "); text.CurrentPageNumber(); diff --git a/QuestPDF/Drawing/TextShaper.cs b/QuestPDF/Drawing/TextShaper.cs index c0d18c8..8867f80 100644 --- a/QuestPDF/Drawing/TextShaper.cs +++ b/QuestPDF/Drawing/TextShaper.cs @@ -99,13 +99,13 @@ namespace QuestPDF.Drawing public int BreakText(int startIndex, float maxWidth) { var index = startIndex; - var currentWidth = 0f; + maxWidth += Glyphs[startIndex].Position.X; while (index < Glyphs.Length) { - currentWidth += Glyphs[index].Width; - - if (currentWidth > maxWidth) + var glyph = Glyphs[index]; + + if (glyph.Position.X + glyph.Width > maxWidth + Size.Epsilon) break; index++; @@ -114,9 +114,12 @@ namespace QuestPDF.Drawing return index - 1; } - public float MeasureWidth(int startGlyphIndex, int endIndex) + public float MeasureWidth(int startIndex, int endIndex) { - var start = Glyphs[startGlyphIndex]; + if (Glyphs.Length == 0) + return 0; + + var start = Glyphs[startIndex]; var end = Glyphs[endIndex]; return end.Position.X - start.Position.X + end.Width; diff --git a/QuestPDF/Elements/Text/Items/TextBlockSpan.cs b/QuestPDF/Elements/Text/Items/TextBlockSpan.cs index 1091bdd..c9702b3 100644 --- a/QuestPDF/Elements/Text/Items/TextBlockSpan.cs +++ b/QuestPDF/Elements/Text/Items/TextBlockSpan.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using QuestPDF.Drawing; using QuestPDF.Elements.Text.Calculation; +using QuestPDF.Helpers; using QuestPDF.Infrastructure; using SkiaSharp; using SkiaSharp.HarfBuzz; @@ -63,19 +64,19 @@ namespace QuestPDF.Elements.Text.Items } // start breaking text from requested position - var endIndex = TextShapingResult.BreakText(startIndex, request.AvailableWidth + Size.Epsilon); + var endIndex = TextShapingResult.BreakText(startIndex, request.AvailableWidth); if (endIndex < 0) return null; // break text only on spaces - var wrappedText = WrapText(endIndex, request.IsFirstElementInLine); + var wrappedText = WrapText(startIndex, endIndex, request.IsFirstElementInLine); if (wrappedText == null) return null; // measure final text - var width = TextShapingResult.MeasureWidth(startIndex, endIndex); + var width = TextShapingResult.MeasureWidth(startIndex, wrappedText.Value.endIndex); return new TextMeasurementResult { @@ -93,15 +94,15 @@ namespace QuestPDF.Elements.Text.Items }; } - // TODO: consider introduce text wrapping abstraction (basic, english-like, asian-like) - private (int endIndex, int nextIndex)? WrapText(int endIndex, bool isFirstElementInLine) + // TODO: consider introducing text wrapping abstraction (basic, english-like, asian-like) + private (int endIndex, int nextIndex)? WrapText(int startIndex, int endIndex, bool isFirstElementInLine) { var spaceCodepoint = Style.ToPaint().ToFont().Typeface.GetGlyphs(" ")[0]; // textLength - length of the part of the text that fits in available width (creating a line) // entire text fits, no need to wrap - if (endIndex == 0 || endIndex == TextShapingResult.Glyphs.Length - 1) + if (endIndex == TextShapingResult.Glyphs.Length - 1) return (endIndex, endIndex); // breaking anywhere @@ -109,13 +110,13 @@ namespace QuestPDF.Elements.Text.Items return (endIndex, endIndex + 1); // current line ends at word, next character is space, perfect place to wrap - if (TextShapingResult.Glyphs[endIndex - 1].Codepoint != spaceCodepoint && TextShapingResult.Glyphs[endIndex].Codepoint == spaceCodepoint) - return (endIndex, endIndex + 1); + if (TextShapingResult.Glyphs[endIndex].Codepoint != spaceCodepoint && TextShapingResult.Glyphs[endIndex + 1].Codepoint == spaceCodepoint) + return (endIndex, endIndex + 2); // find last space within the available text to wrap var lastSpaceIndex = endIndex; - while (lastSpaceIndex > 0) + while (lastSpaceIndex >= startIndex) { if (TextShapingResult.Glyphs[lastSpaceIndex].Codepoint == spaceCodepoint) break; @@ -124,13 +125,13 @@ namespace QuestPDF.Elements.Text.Items } // text contains space that can be used to wrap - if (lastSpaceIndex > 0) - return (lastSpaceIndex, lastSpaceIndex + 1); + if (lastSpaceIndex >= startIndex) + return (lastSpaceIndex - 1, 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 ? (endIndex, endIndex) : null; + return isFirstElementInLine ? (endIndex, endIndex + 1) : null; } public virtual void Draw(TextDrawingRequest request) @@ -140,8 +141,9 @@ namespace QuestPDF.Elements.Text.Items var glyphOffsetY = GetGlyphOffset(); var textDrawingCommand = TextShapingResult.PositionText(request.StartIndex, request.EndIndex, Style); - - request.Canvas.DrawRectangle(new Position(0, request.TotalAscent), new Size(request.TextSize.Width, request.TextSize.Height), Style.BackgroundColor); + + if (Style.BackgroundColor != Colors.Transparent) + request.Canvas.DrawRectangle(new Position(0, request.TotalAscent), new Size(request.TextSize.Width, request.TextSize.Height), Style.BackgroundColor); if (textDrawingCommand.HasValue) request.Canvas.DrawText(textDrawingCommand.Value.SkTextBlob, new Position(textDrawingCommand.Value.TextOffsetX, glyphOffsetY), Style);