Improved RTL, fixed line breaking algorithm

This commit is contained in:
MarcinZiabek
2022-10-02 19:50:50 +02:00
parent 5d4c9a5dd2
commit 350abc2838
4 changed files with 80 additions and 37 deletions
+7 -9
View File
@@ -604,7 +604,7 @@ namespace QuestPDF.Examples
{
RenderingTest
.Create()
.PageSize(500, 100)
.PageSize(250, 100)
.ProduceImages()
.ShowResults()
@@ -614,9 +614,9 @@ namespace QuestPDF.Examples
.Padding(25)
.MinimalBox()
.Background(Colors.Grey.Lighten2)
.Text("ينا الألم. في بعض الأحيان ونظراً للالتزامات التي يفرضها علينا")
.Text("خوارزمية ترتيب")
.FontFamily(Fonts.Calibri)
.FontSize(20);
.FontSize(30);
});
}
@@ -663,14 +663,12 @@ namespace QuestPDF.Examples
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.PageSize(new PageSize(1000, 500))
.ProducePdf()
.ShowResults()
.Render(container =>
{
var text = "بسم الله الرحمن الرحيم" +
"\n" +
"الحمد لله رب العالمين (1) مالك يوم الدين";
var text = "في المعلوماتية أو الرياضيات، خوارزمية الترتيب هي خوارزمية تمكن من تنظيم مجموعة عناصر حسب ترتيب محدد.";
container
.Padding(25)
@@ -679,7 +677,7 @@ namespace QuestPDF.Examples
{
column.Spacing(20);
foreach (var size in new[] { 14, 12, 11, 10, 8, 7 })
foreach (var size in new[] { 36, 34, 32, 30, 15 })
{
column
.Item()
@@ -690,7 +688,7 @@ namespace QuestPDF.Examples
.Background(Colors.Grey.Lighten2)
.Text(text)
.FontSize(20)
.FontFamily("Segoe UI").DirectionFromRightToLeft();
.FontFamily("Segoe UI");
}
});
});
+66 -21
View File
@@ -62,7 +62,7 @@ namespace QuestPDF.Drawing
yOffset += glyphPositions[i].YAdvance * scaleY;
}
return new TextShapingResult(glyphs);
return new TextShapingResult(buffer.Direction, glyphs);
}
void PopulateBufferWithText(Buffer buffer, string text)
@@ -98,29 +98,65 @@ namespace QuestPDF.Drawing
internal class TextShapingResult
{
public ShapedGlyph[] Glyphs { get; }
private Direction Direction { get; }
private ShapedGlyph[] Glyphs { get; }
public TextShapingResult(ShapedGlyph[] glyphs)
public int Length => Glyphs.Length;
public ShapedGlyph this[int index] =>
Direction == Direction.LeftToRight
? Glyphs[index]
: Glyphs[Glyphs.Length - 1 - index];
public TextShapingResult(Direction direction, ShapedGlyph[] glyphs)
{
Direction = direction;
Glyphs = glyphs;
}
public int BreakText(int startIndex, float maxWidth)
{
var index = startIndex;
maxWidth += Glyphs[startIndex].Position.X;
while (index < Glyphs.Length)
return Direction switch
{
var glyph = Glyphs[index];
if (glyph.Position.X + glyph.Width > maxWidth + Size.Epsilon)
break;
index++;
}
Direction.LeftToRight => BreakTextLeftToRight(),
Direction.RightToLeft => BreakTextRightToLeft(),
_ => throw new ArgumentOutOfRangeException()
};
return index - 1;
int BreakTextLeftToRight()
{
var index = startIndex;
maxWidth += Glyphs[startIndex].Position.X;
while (index < Glyphs.Length)
{
var glyph = Glyphs[index];
if (glyph.Position.X + glyph.Width > maxWidth + Size.Epsilon)
break;
index++;
}
return index - 1;
}
int BreakTextRightToLeft()
{
var index = startIndex;
var startOffset = this[startIndex].Position.X + this[startIndex].Width;
while (index < Glyphs.Length)
{
if (startOffset - this[index].Position.X > maxWidth + Size.Epsilon)
break;
index++;
}
return index - 1;
}
}
public float MeasureWidth(int startIndex, int endIndex)
@@ -128,10 +164,15 @@ namespace QuestPDF.Drawing
if (Glyphs.Length == 0)
return 0;
var start = Glyphs[startIndex];
var end = Glyphs[endIndex];
var start = this[startIndex];
var end = this[endIndex];
return end.Position.X - start.Position.X + end.Width;
return Direction switch
{
Direction.LeftToRight => end.Position.X - start.Position.X + end.Width,
Direction.RightToLeft => start.Position.X - end.Position.X + start.Width,
_ => throw new NotSupportedException()
};
}
public DrawTextCommand? PositionText(int startIndex, int endIndex, TextStyle textStyle)
@@ -152,14 +193,18 @@ namespace QuestPDF.Drawing
{
var runIndex = sourceIndex - startIndex;
glyphSpan[runIndex] = Glyphs[sourceIndex].Codepoint;
positionSpan[runIndex] = Glyphs[sourceIndex].Position;
glyphSpan[runIndex] = this[sourceIndex].Codepoint;
positionSpan[runIndex] = this[sourceIndex].Position;
}
var firstVisualCharacterIndex = Direction == Direction.LeftToRight
? startIndex
: endIndex;
return new DrawTextCommand
{
SkTextBlob = skTextBlobBuilder.Build(),
TextOffsetX = -Glyphs[startIndex].Position.X
TextOffsetX = -this[firstVisualCharacterIndex].Position.X
};
}
}
@@ -47,11 +47,11 @@ namespace QuestPDF.Elements.Text.Items
// ignore leading spaces
if (!request.IsFirstElementInBlock && request.IsFirstElementInLine)
{
while (startIndex < TextShapingResult.Glyphs.Length && Text[startIndex] == spaceCodepoint)
while (startIndex < TextShapingResult.Length && Text[startIndex] == spaceCodepoint)
startIndex++;
}
if (TextShapingResult.Glyphs.Length == 0 || startIndex == TextShapingResult.Glyphs.Length)
if (TextShapingResult.Length == 0 || startIndex == TextShapingResult.Length)
{
return new TextMeasurementResult
{
@@ -90,7 +90,7 @@ namespace QuestPDF.Elements.Text.Items
StartIndex = startIndex,
EndIndex = wrappedText.Value.endIndex,
NextIndex = wrappedText.Value.nextIndex,
TotalIndex = TextShapingResult.Glyphs.Length - 1
TotalIndex = TextShapingResult.Length - 1
};
}
@@ -102,7 +102,7 @@ namespace QuestPDF.Elements.Text.Items
// 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 == TextShapingResult.Glyphs.Length - 1)
if (endIndex == TextShapingResult.Length - 1)
return (endIndex, endIndex);
// breaking anywhere
@@ -110,7 +110,7 @@ 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].Codepoint != spaceCodepoint && TextShapingResult.Glyphs[endIndex + 1].Codepoint == spaceCodepoint)
if (TextShapingResult[endIndex].Codepoint != spaceCodepoint && TextShapingResult[endIndex + 1].Codepoint == spaceCodepoint)
return (endIndex, endIndex + 2);
// find last space within the available text to wrap
@@ -118,7 +118,7 @@ namespace QuestPDF.Elements.Text.Items
while (lastSpaceIndex >= startIndex)
{
if (TextShapingResult.Glyphs[lastSpaceIndex].Codepoint == spaceCodepoint)
if (TextShapingResult[lastSpaceIndex].Codepoint == spaceCodepoint)
break;
lastSpaceIndex--;
+1 -1
View File
@@ -7,7 +7,7 @@
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
<LangVersion>9</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageIcon>Logo.png</PackageIcon>
<PackageIconUrl>https://www.questpdf.com/images/package-logo.png</PackageIconUrl>
<PackageProjectUrl>https://www.questpdf.com/</PackageProjectUrl>