Introduced new line breaking algorithm: break anywhere

This commit is contained in:
MarcinZiabek
2022-03-26 03:34:31 +01:00
parent 0711bca1e1
commit 11ff53946d
7 changed files with 52 additions and 28 deletions
+4 -2
View File
@@ -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();
});
});
});
@@ -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
View File
@@ -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
View File
@@ -187,7 +187,7 @@ namespace QuestPDF.Elements.Text
});
currentWidth += measurementResponse.Width;
currentItemIndex = measurementResponse.EndIndex + 1;
currentItemIndex = measurementResponse.NextIndex;
if (!measurementResponse.IsLast)
break;
@@ -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
View File
@@ -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
View File
@@ -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()