Compare commits

..

1 Commits

Author SHA1 Message Date
MarcinZiabek 09e642295f Repeat content experiment 2022-10-28 23:22:11 +02:00
54 changed files with 350 additions and 1044 deletions
@@ -1,411 +0,0 @@
using System;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Drawing.Exceptions;
using QuestPDF.Elements;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class ContentDirectionExamples
{
private Action<IContainer> ContentDirectionTemplate(Action<IContainer> content)
{
return container =>
{
container
.Padding(20)
.MinimalBox()
.Border(1)
.ExtendHorizontal()
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
});
table.Header(header =>
{
header.Cell().ContentFromLeftToRight().Element(HeaderCell("Left-to-right"));
header.Cell().ContentFromRightToLeft().Element(HeaderCell("Right-to-left"));
static Action<IContainer> HeaderCell(string label)
{
return container => container
.Border(1)
.BorderColor(Colors.Grey.Medium)
.Background(Colors.Grey.Lighten3)
.PaddingHorizontal(10)
.PaddingVertical(5)
.Text(label)
.FontSize(18)
.SemiBold();
}
});
table.Cell().Element(TestCell).ContentFromLeftToRight().Element(content);
table.Cell()
.Element(TestCell)
.ContentFromRightToLeft()
.Element(content);
static IContainer TestCell(IContainer container)
{
return container.Border(1).BorderColor(Colors.Grey.Medium).Padding(10);
}
});
};
}
[Test]
public void Page()
{
RenderingTest
.Create()
.ProduceImages()
.ShowResults()
.EnableDebugging()
.RenderDocument(document =>
{
document.Page(page =>
{
page.Size(PageSizes.A5);
page.Margin(20);
page.PageColor(Colors.White);
//page.ContentFromRightToLeft();
page.Content().Column(column =>
{
column.Spacing(20);
column.Item().Row(row =>
{
row.Spacing(10);
row.AutoItem().AlignMiddle().Width(20).Height(20).Image(Placeholders.Image);
row.RelativeItem()
.Text("Document title")
.FontSize(24).FontColor(Colors.Blue.Accent1).SemiBold();
});
column.Item().Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
foreach (var i in Enumerable.Range(0, 9))
{
var width = (i % 4 == 0) ? 2 : 1;
table
.Cell()
.ColumnSpan((uint)width)
.Background(i % 4 == 0 ? Colors.Grey.Lighten1 : Colors.Grey.Lighten2)
.Padding(5)
.AlignCenter()
.Text(i)
.FontSize(20);
}
});
});
});
});
}
[Test]
public void Column()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.Render(ContentDirectionTemplate(Content));
void Content(IContainer container)
{
container.Column(column =>
{
column.Spacing(5);
column.Item().Height(50).Width(50).Background(Colors.Red.Lighten1);
column.Item().Height(50).Width(100).Background(Colors.Green.Lighten1);
column.Item().Height(50).Width(150).Background(Colors.Blue.Lighten1);
});
}
}
[Test]
public void Row()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.Render(ContentDirectionTemplate(Content));
void Content(IContainer container)
{
container.Row(row =>
{
row.Spacing(5);
row.AutoItem().Height(50).Width(50).Background(Colors.Red.Lighten1);
row.AutoItem().Height(50).Width(50).Background(Colors.Green.Lighten1);
row.AutoItem().Height(50).Width(75).Background(Colors.Blue.Lighten1);
});
}
}
[Test]
public void Table()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.Render(ContentDirectionTemplate(Content));
void Content(IContainer container)
{
container.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
table.Cell().Height(50).Background(Colors.Red.Lighten1);
table.Cell().Height(50).Background(Colors.Green.Lighten1);
table.Cell().Height(50).Background(Colors.Blue.Lighten1);
table.Cell().ColumnSpan(2).Height(50).Background(Colors.Orange.Lighten1);
});
}
}
[Test]
public void Constrained()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.Render(ContentDirectionTemplate(Content));
void Content(IContainer container)
{
container.Width(50).Height(50).Background(Colors.Red.Lighten1);
}
}
[Test]
public void Unconstrained()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.Render(ContentDirectionTemplate(Content));
void Content(IContainer container)
{
container
.Width(100)
.Height(100)
.Background(Colors.Grey.Lighten3)
.AlignCenter()
.AlignMiddle()
.Unconstrained()
.Width(50)
.Height(50)
.Background(Colors.Red.Lighten1);
}
}
[Test]
public void Inlined()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.Render(ContentDirectionTemplate(Content));
void Content(IContainer container)
{
container.Column(column =>
{
column.Spacing(10);
column.Item().Background(Colors.Grey.Lighten3).Text("Default alignment").FontSize(13);
column.Item().Element(ContentWithAlignment(null));
column.Item().Background(Colors.Grey.Lighten3).Text("Left alignment").FontSize(14);
column.Item().Element(ContentWithAlignment(InlinedAlignment.Left));
column.Item().Background(Colors.Grey.Lighten3).Text("Center alignment").FontSize(14);
column.Item().Element(ContentWithAlignment(InlinedAlignment.Center));
column.Item().Background(Colors.Grey.Lighten3).Text("Right alignment").FontSize(14);
column.Item().Element(ContentWithAlignment(InlinedAlignment.Right));
});
static Action<IContainer> ContentWithAlignment(InlinedAlignment? alignment)
{
return container =>
{
container.Inlined(inlined =>
{
inlined.Spacing(5);
inlined.Alignment(alignment);
inlined.Item().Height(50).Width(50).Background(Colors.Red.Lighten1);
inlined.Item().Height(50).Width(75).Background(Colors.Green.Lighten1);
inlined.Item().Height(50).Width(100).Background(Colors.Blue.Lighten1);
inlined.Item().Height(50).Width(125).Background(Colors.Orange.Lighten1);
});
};
}
}
}
[Test]
public void Text()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.Render(ContentDirectionTemplate(Content));
void Content(IContainer container)
{
container.Column(column =>
{
column.Spacing(10);
column.Item().Background(Colors.Grey.Lighten3).Text("Default alignment").FontSize(13);
column.Item().Element(ContentWithAlignment(null));
column.Item().Background(Colors.Grey.Lighten3).Text("Left alignment").FontSize(14);
column.Item().Element(ContentWithAlignment(HorizontalAlignment.Left));
column.Item().Background(Colors.Grey.Lighten3).Text("Center alignment").FontSize(14);
column.Item().Element(ContentWithAlignment(HorizontalAlignment.Center));
column.Item().Background(Colors.Grey.Lighten3).Text("Right alignment").FontSize(14);
column.Item().Element(ContentWithAlignment(HorizontalAlignment.Right));
});
static Action<IContainer> ContentWithAlignment(HorizontalAlignment? alignment)
{
return container =>
{
container.Text(text =>
{
text.Alignment = alignment; // internal API
text.Span("Lorem ipsum").Bold().FontColor(Colors.Red.Medium);
text.Element().Width(5);
text.Span(Placeholders.LoremIpsum());
});
};
}
}
}
[Test]
public void Decoration()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.Render(ContentDirectionTemplate(Content));
void Content(IContainer container)
{
container.Decoration(decoration =>
{
decoration.Before().Background(Colors.Green.Lighten1).Padding(5).Text("Before").FontSize(16);
decoration.Content().Background(Colors.Green.Lighten2).Padding(5).Text("Content").FontSize(16);
decoration.After().Background(Colors.Green.Lighten3).Padding(5).Text("After").FontSize(16);
});
}
}
[Test]
public void Dynamic()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.Render(ContentDirectionTemplate(Content));
void Content(IContainer container)
{
container.Dynamic(new SimpleDynamic());
}
}
class SimpleDynamic : IDynamicComponent<int>
{
public int State { get; set; }
public DynamicComponentComposeResult Compose(DynamicContext context)
{
var content = context.CreateElement(container =>
{
container.Row(row =>
{
row.ConstantItem(50).Background(Colors.Red.Lighten2).Height(50);
row.ConstantItem(75).Background(Colors.Green.Lighten2).Height(50);
row.ConstantItem(100).Background(Colors.Blue.Lighten2).Height(50);
});
});
return new DynamicComponentComposeResult
{
Content = content,
HasMoreContent = false
};
}
}
}
}
@@ -0,0 +1,38 @@
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
namespace QuestPDF.Examples
{
public class RepeatContentExamples
{
[Test]
public void ItemTypes()
{
RenderingTest
.Create()
.ProducePdf()
.PageSize(PageSizes.A4)
.ShowResults()
.Render(container =>
{
container
.Padding(25)
.Decoration(decoration =>
{
decoration.Before().Text("Test").FontSize(22);
decoration.Content().Column(column =>
{
column.Spacing(20);
foreach (var _ in Enumerable.Range(0, 10))
column.Item().Background(Colors.Grey.Medium).ExtendHorizontal().Height(80);
});
});
});
}
}
}
+4 -40
View File
@@ -1,4 +1,4 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using NUnit.Framework; using NUnit.Framework;
@@ -604,7 +604,7 @@ namespace QuestPDF.Examples
{ {
RenderingTest RenderingTest
.Create() .Create()
.PageSize(250, 100) .PageSize(500, 100)
.ProduceImages() .ProduceImages()
.ShowResults() .ShowResults()
@@ -614,9 +614,9 @@ namespace QuestPDF.Examples
.Padding(25) .Padding(25)
.MinimalBox() .MinimalBox()
.Background(Colors.Grey.Lighten2) .Background(Colors.Grey.Lighten2)
.Text("خوارزمية ترتيب") .Text("ينا الألم. في بعض الأحيان ونظراً للالتزامات التي يفرضها علينا")
.FontFamily(Fonts.Calibri) .FontFamily(Fonts.Calibri)
.FontSize(30); .FontSize(20);
}); });
} }
@@ -689,41 +689,5 @@ namespace QuestPDF.Examples
}); });
}); });
} }
[Test]
public void TextDirectionality()
{
RenderingTest
.Create()
.PageSize(new PageSize(1000, 500))
.ProducePdf()
.ShowResults()
.Render(container =>
{
var text = "في المعلوماتية أو الرياضيات، خوارزمية الترتيب هي خوارزمية تمكن من تنظيم مجموعة عناصر حسب ترتيب محدد.";
container
.Padding(25)
.ContentFromRightToLeft()
.Column(column =>
{
column.Spacing(20);
foreach (var size in new[] { 36, 34, 32, 30, 15 })
{
column
.Item()
.ShowEntire()
.MaxWidth(size * 25)
.Background(Colors.Grey.Lighten3)
.MinimalBox()
.Background(Colors.Grey.Lighten2)
.Text(text)
.FontSize(20)
.FontFamily("Segoe UI");
}
});
});
}
} }
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<Authors>MarcinZiabek</Authors> <Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company> <Company>CodeFlint</Company>
<PackageId>QuestPDF.Previewer</PackageId> <PackageId>QuestPDF.Previewer</PackageId>
<Version>2022.11.0</Version> <Version>2022.9.1</Version>
<PackAsTool>true</PackAsTool> <PackAsTool>true</PackAsTool>
<ToolCommandName>questpdf-previewer</ToolCommandName> <ToolCommandName>questpdf-previewer</ToolCommandName>
<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> <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>
@@ -68,11 +68,9 @@ namespace QuestPDF.ReportSample.Layouts
var lengthStyle = TextStyle.Default.FontColor(Colors.Grey.Medium); var lengthStyle = TextStyle.Default.FontColor(Colors.Grey.Medium);
text.TotalPagesWithinSection(locationName).Style(lengthStyle).Format(x => text.Span(" (").Style(lengthStyle);
{ text.TotalPagesWithinSection(locationName).Style(lengthStyle).Format(x => x == 1 ? "1 page long" : $"{x} pages long");
var formatted = x == 1 ? "1 page long" : $"{x} pages long"; text.Span(")").Style(lengthStyle);
return $" ({formatted})";
});
}); });
}); });
} }
-40
View File
@@ -153,9 +153,7 @@ namespace QuestPDF.UnitTests
Ratio = 2f Ratio = 2f
}) })
.DrawElement(new Size(500, 200)) .DrawElement(new Size(500, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw(new Size(400, 200)) .ExpectChildDraw(new Size(400, 200))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult(); .CheckDrawResult();
} }
@@ -170,45 +168,7 @@ namespace QuestPDF.UnitTests
Ratio = 2f Ratio = 2f
}) })
.DrawElement(new Size(400, 300)) .DrawElement(new Size(400, 300))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw(new Size(400, 200)) .ExpectChildDraw(new Size(400, 200))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult();
}
[Test]
public void DrawChild_PerWidth_RightToLeft()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f,
ContentDirection = ContentDirection.RightToLeft
})
.DrawElement(new Size(500, 200))
.ExpectCanvasTranslate(100, 0)
.ExpectChildDraw(new Size(400, 200))
.ExpectCanvasTranslate(-100, 0)
.CheckDrawResult();
}
[Test]
public void DrawChild_PerHeight_RightToLeft()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f,
ContentDirection = ContentDirection.RightToLeft
})
.DrawElement(new Size(400, 300))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw(new Size(400, 200))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult(); .CheckDrawResult();
} }
} }
-38
View File
@@ -35,9 +35,7 @@ namespace QuestPDF.UnitTests
}) })
.MeasureElement(new Size(400, 300)) .MeasureElement(new Size(400, 300))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.PartialRender(200, 100)) .ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.PartialRender(200, 100))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw(new Size(200, 100)) .ExpectChildDraw(new Size(200, 100))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult(); .CheckDrawResult();
} }
@@ -51,43 +49,7 @@ namespace QuestPDF.UnitTests
}) })
.MeasureElement(new Size(500, 400)) .MeasureElement(new Size(500, 400))
.ExpectChildMeasure(expectedInput: new Size(500, 400), returns: SpacePlan.FullRender(300, 200)) .ExpectChildMeasure(expectedInput: new Size(500, 400), returns: SpacePlan.FullRender(300, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw(new Size(300, 200)) .ExpectChildDraw(new Size(300, 200))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult();
}
[Test]
public void Measure_PartialRender_RightToLeft()
{
TestPlan
.For(x => new MinimalBox
{
Child = x.CreateChild(),
ContentDirection = ContentDirection.RightToLeft
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.PartialRender(200, 100))
.ExpectCanvasTranslate(200, 0)
.ExpectChildDraw(new Size(200, 100))
.ExpectCanvasTranslate(-200, 0)
.CheckDrawResult();
}
[Test]
public void Measure_FullRender_RightToLeft()
{
TestPlan
.For(x => new MinimalBox
{
Child = x.CreateChild(),
ContentDirection = ContentDirection.RightToLeft
})
.MeasureElement(new Size(500, 400))
.ExpectChildMeasure(expectedInput: new Size(500, 400), returns: SpacePlan.FullRender(350, 200))
.ExpectCanvasTranslate(150, 0)
.ExpectChildDraw(new Size(350, 200))
.ExpectCanvasTranslate(-150, 0)
.CheckDrawResult(); .CheckDrawResult();
} }
} }
+6 -6
View File
@@ -195,7 +195,7 @@ namespace QuestPDF.UnitTests.TestEngine
public TestPlan CheckMeasureResult(SpacePlan expected) public TestPlan CheckMeasureResult(SpacePlan expected)
{ {
Element.InjectDependencies(null, Canvas); Element.VisitChildren(x => x?.Initialize(null, Canvas));
var actual = Element.Measure(OperationInput); var actual = Element.Measure(OperationInput);
@@ -210,7 +210,7 @@ namespace QuestPDF.UnitTests.TestEngine
public TestPlan CheckDrawResult() public TestPlan CheckDrawResult()
{ {
Element.InjectDependencies(null, Canvas); Element.VisitChildren(x => x?.Initialize(null, Canvas));
Element.Draw(OperationInput); Element.Draw(OperationInput);
return this; return this;
} }
@@ -253,10 +253,10 @@ namespace QuestPDF.UnitTests.TestEngine
availableSpace ??= new Size(400, 300); availableSpace ??= new Size(400, 300);
var canvas = new FreeCanvas(); var canvas = new FreeCanvas();
value.InjectDependencies(null, canvas); value.VisitChildren(x => x.Initialize(null, canvas));
var valueMeasure = value.Measure(availableSpace.Value); var valueMeasure = value.Measure(availableSpace.Value);
expected.InjectDependencies(null, canvas); expected.VisitChildren(x => x.Initialize(null, canvas));
var expectedMeasure = expected.Measure(availableSpace.Value); var expectedMeasure = expected.Measure(availableSpace.Value);
valueMeasure.Should().BeEquivalentTo(expectedMeasure); valueMeasure.Should().BeEquivalentTo(expectedMeasure);
@@ -267,11 +267,11 @@ namespace QuestPDF.UnitTests.TestEngine
availableSpace ??= new Size(400, 300); availableSpace ??= new Size(400, 300);
var valueCanvas = new OperationRecordingCanvas(); var valueCanvas = new OperationRecordingCanvas();
value.InjectDependencies(null, valueCanvas); value.VisitChildren(x => x.Initialize(null, valueCanvas));
value.Draw(availableSpace.Value); value.Draw(availableSpace.Value);
var expectedCanvas = new OperationRecordingCanvas(); var expectedCanvas = new OperationRecordingCanvas();
expected.InjectDependencies(null, expectedCanvas); expected.VisitChildren(x => x.Initialize(null, expectedCanvas));
expected.Draw(availableSpace.Value); expected.Draw(availableSpace.Value);
valueCanvas.Operations.Should().BeEquivalentTo(expectedCanvas.Operations); valueCanvas.Operations.Should().BeEquivalentTo(expectedCanvas.Operations);
-38
View File
@@ -77,9 +77,7 @@ namespace QuestPDF.UnitTests
}) })
.DrawElement(new Size(900, 800)) .DrawElement(new Size(900, 800))
.ExpectChildMeasure(Size.Max, SpacePlan.PartialRender(1200, 1600)) .ExpectChildMeasure(Size.Max, SpacePlan.PartialRender(1200, 1600))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw(new Size(1200, 1600)) .ExpectChildDraw(new Size(1200, 1600))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult(); .CheckDrawResult();
} }
@@ -93,43 +91,7 @@ namespace QuestPDF.UnitTests
}) })
.DrawElement(new Size(900, 800)) .DrawElement(new Size(900, 800))
.ExpectChildMeasure(Size.Max, SpacePlan.FullRender(1600, 1000)) .ExpectChildMeasure(Size.Max, SpacePlan.FullRender(1600, 1000))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw(new Size(1600, 1000)) .ExpectChildDraw(new Size(1600, 1000))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult();
}
[Test]
public void Draw_WhenChildPartiallyRenders_RightToLeft()
{
TestPlan
.For(x => new Unconstrained
{
Child = x.CreateChild(),
ContentDirection = ContentDirection.RightToLeft
})
.DrawElement(new Size(900, 800))
.ExpectChildMeasure(Size.Max, SpacePlan.PartialRender(1200, 1600))
.ExpectCanvasTranslate(-1200, 0)
.ExpectChildDraw(new Size(1200, 1600))
.ExpectCanvasTranslate(1200, 0)
.CheckDrawResult();
}
[Test]
public void Draw_WhenChildFullyRenders_RightToLeft()
{
TestPlan
.For(x => new Unconstrained
{
Child = x.CreateChild(),
ContentDirection = ContentDirection.RightToLeft
})
.DrawElement(new Size(900, 800))
.ExpectChildMeasure(Size.Max, SpacePlan.FullRender(1600, 1000))
.ExpectCanvasTranslate(-1600, 0)
.ExpectChildDraw(new Size(1600, 1000))
.ExpectCanvasTranslate(1600, 0)
.CheckDrawResult(); .CheckDrawResult();
} }
+36 -20
View File
@@ -65,7 +65,7 @@ namespace QuestPDF.Drawing
document.Compose(container); document.Compose(container);
var content = container.Compose(); var content = container.Compose();
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault); ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
ApplyContentDirection(content, ContentDirection.LeftToRight); ApplyContentRepeatState(content, false);
var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null; var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null;
@@ -80,9 +80,11 @@ namespace QuestPDF.Drawing
internal static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DebuggingState? debuggingState) internal static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DebuggingState? debuggingState)
where TCanvas : ICanvas, IRenderingCanvas where TCanvas : ICanvas, IRenderingCanvas
{ {
InjectDependencies(content, pageContext, canvas); content.VisitChildren(x => x?.Initialize(pageContext, canvas));
content.VisitChildren(x => (x as IStateResettable)?.ResetState()); content.VisitChildren(x => (x as IStateResettable)?.ResetState());
ResetIsRenderedState(content);
canvas.BeginDocument(); canvas.BeginDocument();
var currentPage = 1; var currentPage = 1;
@@ -141,18 +143,6 @@ namespace QuestPDF.Drawing
} }
} }
internal static void InjectDependencies(this Element content, IPageContext pageContext, ICanvas canvas)
{
content.VisitChildren(x =>
{
if (x == null)
return;
x.PageContext = pageContext;
x.Canvas = canvas;
});
}
private static void ApplyCaching(Container content) private static void ApplyCaching(Container content)
{ {
content.VisitChildren(x => content.VisitChildren(x =>
@@ -174,22 +164,48 @@ namespace QuestPDF.Drawing
return debuggingState; return debuggingState;
} }
internal static void ApplyContentDirection(this Element? content, ContentDirection direction) private static void ApplyContentRepeatState(Element? content, bool repeatContent)
{ {
if (content == null) if (content == null)
return; return;
if (content is ContentDirectionSetter contentDirectionSetter) if (content is IVisual visual)
visual.RepeatContent = repeatContent;
if (content is TextBlock textBlock)
{ {
ApplyContentDirection(contentDirectionSetter.Child, contentDirectionSetter.ContentDirection); foreach (var textBlockItem in textBlock.Items)
{
if (textBlockItem is TextBlockElement textElement)
{
ApplyContentRepeatState(textElement.Element, true);
}
}
return; return;
} }
if (content is IContentDirectionAware contentDirectionAware) // TODO: apply RepeatState in dynamic content
contentDirectionAware.ContentDirection = direction; //if (content is DynamicHost dynamicHost)
// dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
if (content is RepeatContentSetter repeatContentSetter)
repeatContent = repeatContentSetter.RepeatContent;
foreach (var child in content.GetChildren()) foreach (var child in content.GetChildren())
ApplyContentDirection(child, direction); ApplyContentRepeatState(child, repeatContent);
}
private static void ResetIsRenderedState(Element? content)
{
if (content == null)
return;
if (content is IVisual visual)
visual.IsRendered = false;
foreach (var child in content.GetChildren())
ResetIsRenderedState(child);
} }
internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle) internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
+18 -69
View File
@@ -29,12 +29,6 @@ namespace QuestPDF.Drawing
PopulateBufferWithText(buffer, text); PopulateBufferWithText(buffer, text);
buffer.GuessSegmentProperties(); buffer.GuessSegmentProperties();
if (TextStyle.Direction == TextDirection.LeftToRight)
buffer.Direction = Direction.LeftToRight;
if (TextStyle.Direction == TextDirection.RightToLeft)
buffer.Direction = Direction.RightToLeft;
ShaperFont.Shape(buffer); ShaperFont.Shape(buffer);
var length = buffer.Length; var length = buffer.Length;
@@ -62,7 +56,7 @@ namespace QuestPDF.Drawing
yOffset += glyphPositions[i].YAdvance * scaleY; yOffset += glyphPositions[i].YAdvance * scaleY;
} }
return new TextShapingResult(buffer.Direction, glyphs); return new TextShapingResult(glyphs);
} }
void PopulateBufferWithText(Buffer buffer, string text) void PopulateBufferWithText(Buffer buffer, string text)
@@ -98,65 +92,29 @@ namespace QuestPDF.Drawing
internal class TextShapingResult internal class TextShapingResult
{ {
private Direction Direction { get; } public ShapedGlyph[] Glyphs { get; }
private ShapedGlyph[] Glyphs { get; }
public int Length => Glyphs.Length; public TextShapingResult(ShapedGlyph[] glyphs)
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; Glyphs = glyphs;
} }
public int BreakText(int startIndex, float maxWidth) public int BreakText(int startIndex, float maxWidth)
{ {
return Direction switch var index = startIndex;
maxWidth += Glyphs[startIndex].Position.X;
while (index < Glyphs.Length)
{ {
Direction.LeftToRight => BreakTextLeftToRight(), var glyph = Glyphs[index];
Direction.RightToLeft => BreakTextRightToLeft(),
_ => throw new ArgumentOutOfRangeException()
};
int BreakTextLeftToRight() if (glyph.Position.X + glyph.Width > maxWidth + Size.Epsilon)
{ break;
var index = startIndex;
maxWidth += Glyphs[startIndex].Position.X;
while (index < Glyphs.Length) index++;
{
var glyph = Glyphs[index];
if (glyph.Position.X + glyph.Width > maxWidth + Size.Epsilon)
break;
index++;
}
return index - 1;
} }
int BreakTextRightToLeft() return index - 1;
{
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) public float MeasureWidth(int startIndex, int endIndex)
@@ -164,15 +122,10 @@ namespace QuestPDF.Drawing
if (Glyphs.Length == 0) if (Glyphs.Length == 0)
return 0; return 0;
var start = this[startIndex]; var start = Glyphs[startIndex];
var end = this[endIndex]; var end = Glyphs[endIndex];
return Direction switch return end.Position.X - start.Position.X + end.Width;
{
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) public DrawTextCommand? PositionText(int startIndex, int endIndex, TextStyle textStyle)
@@ -193,18 +146,14 @@ namespace QuestPDF.Drawing
{ {
var runIndex = sourceIndex - startIndex; var runIndex = sourceIndex - startIndex;
glyphSpan[runIndex] = this[sourceIndex].Codepoint; glyphSpan[runIndex] = Glyphs[sourceIndex].Codepoint;
positionSpan[runIndex] = this[sourceIndex].Position; positionSpan[runIndex] = Glyphs[sourceIndex].Position;
} }
var firstVisualCharacterIndex = Direction == Direction.LeftToRight
? startIndex
: endIndex;
return new DrawTextCommand return new DrawTextCommand
{ {
SkTextBlob = skTextBlobBuilder.Build(), SkTextBlob = skTextBlobBuilder.Build(),
TextOffsetX = -this[firstVisualCharacterIndex].Position.X TextOffsetX = -Glyphs[startIndex].Position.X
}; };
} }
} }
+1 -10
View File
@@ -4,10 +4,8 @@ using QuestPDF.Infrastructure;
namespace QuestPDF.Elements namespace QuestPDF.Elements
{ {
internal class AspectRatio : ContainerElement, ICacheable, IContentDirectionAware internal class AspectRatio : ContainerElement, ICacheable
{ {
public ContentDirection ContentDirection { get; set; }
public float Ratio { get; set; } = 1; public float Ratio { get; set; } = 1;
public AspectRatioOption Option { get; set; } = AspectRatioOption.FitWidth; public AspectRatioOption Option { get; set; } = AspectRatioOption.FitWidth;
@@ -44,14 +42,7 @@ namespace QuestPDF.Elements
return; return;
var size = GetTargetSize(availableSpace); var size = GetTargetSize(availableSpace);
var offset = ContentDirection == ContentDirection.LeftToRight
? Position.Zero
: new Position(availableSpace.Width - size.Width, 0);
Canvas.Translate(offset);
base.Draw(size); base.Draw(size);
Canvas.Translate(offset.Reverse());
} }
private Size GetTargetSize(Size availableSpace) private Size GetTargetSize(Size availableSpace)
+13 -4
View File
@@ -7,19 +7,28 @@ namespace QuestPDF.Elements
{ {
public delegate void DrawOnCanvas(SKCanvas canvas, Size availableSpace); public delegate void DrawOnCanvas(SKCanvas canvas, Size availableSpace);
internal class Canvas : Element, ICacheable internal class Canvas : Element, IVisual, ICacheable
{ {
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public DrawOnCanvas Handler { get; set; } public DrawOnCanvas Handler { get; set; }
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
return availableSpace.IsNegative() if (availableSpace.IsNegative())
? SpacePlan.Wrap() return SpacePlan.Wrap();
: SpacePlan.FullRender(availableSpace);
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return SpacePlan.FullRender(availableSpace);
} }
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
IsRendered = true;
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas; var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
if (Handler == null || skiaCanvas == null) if (Handler == null || skiaCanvas == null)
+3 -11
View File
@@ -5,10 +5,8 @@ using QuestPDF.Infrastructure;
namespace QuestPDF.Elements namespace QuestPDF.Elements
{ {
internal class Constrained : ContainerElement, ICacheable, IContentDirectionAware internal class Constrained : ContainerElement, ICacheable
{ {
public ContentDirection ContentDirection { get; set; }
public float? MinWidth { get; set; } public float? MinWidth { get; set; }
public float? MaxWidth { get; set; } public float? MaxWidth { get; set; }
@@ -47,17 +45,11 @@ namespace QuestPDF.Elements
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
var size = new Size( var available = new Size(
Min(MaxWidth, availableSpace.Width), Min(MaxWidth, availableSpace.Width),
Min(MaxHeight, availableSpace.Height)); Min(MaxHeight, availableSpace.Height));
var offset = ContentDirection == ContentDirection.LeftToRight Child?.Draw(available);
? Position.Zero
: new Position(availableSpace.Width - size.Width, 0);
Canvas.Translate(offset);
base.Draw(size);
Canvas.Translate(offset.Reverse());
} }
private static float Min(float? x, float y) private static float Min(float? x, float y)
@@ -1,9 +0,0 @@
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class ContentDirectionSetter : ContainerElement
{
public ContentDirection ContentDirection { get; set; }
}
}
+3 -9
View File
@@ -14,10 +14,8 @@ namespace QuestPDF.Elements
public Position Offset { get; set; } public Position Offset { get; set; }
} }
internal class Decoration : Element, ICacheable, IContentDirectionAware internal class Decoration : Element, ICacheable
{ {
public ContentDirection ContentDirection { get; set; }
internal Element Before { get; set; } = new Empty(); internal Element Before { get; set; } = new Empty();
internal Element Content { get; set; } = new Empty(); internal Element Content { get; set; } = new Empty();
internal Element After { get; set; } = new Empty(); internal Element After { get; set; } = new Empty();
@@ -66,13 +64,9 @@ namespace QuestPDF.Elements
{ {
var elementSize = new Size(width, command.Measurement.Height); var elementSize = new Size(width, command.Measurement.Height);
var offset = ContentDirection == ContentDirection.LeftToRight Canvas.Translate(command.Offset);
? command.Offset
: new Position(availableSpace.Width - width, command.Offset.Y);
Canvas.Translate(offset);
command.Element.Draw(elementSize); command.Element.Draw(elementSize);
Canvas.Translate(offset.Reverse()); Canvas.Translate(command.Offset.Reverse());
} }
} }
+6 -13
View File
@@ -6,13 +6,12 @@ using QuestPDF.Infrastructure;
namespace QuestPDF.Elements namespace QuestPDF.Elements
{ {
internal class DynamicHost : Element, IStateResettable, IContentDirectionAware internal class DynamicHost : Element, IStateResettable
{ {
private DynamicComponentProxy Child { get; } private DynamicComponentProxy Child { get; }
private object InitialComponentState { get; set; } private object InitialComponentState { get; set; }
internal TextStyle TextStyle { get; set; } = TextStyle.Default; internal TextStyle TextStyle { get; set; } = TextStyle.Default;
public ContentDirection ContentDirection { get; set; }
public DynamicHost(DynamicComponentProxy child) public DynamicHost(DynamicComponentProxy child)
{ {
@@ -52,14 +51,12 @@ namespace QuestPDF.Elements
var context = new DynamicContext var context = new DynamicContext
{ {
PageContext = PageContext,
Canvas = Canvas,
TextStyle = TextStyle,
ContentDirection = ContentDirection,
PageNumber = PageContext.CurrentPage, PageNumber = PageContext.CurrentPage,
TotalPages = PageContext.GetLocation(Infrastructure.PageContext.DocumentLocation).PageEnd, TotalPages = PageContext.GetLocation(Infrastructure.PageContext.DocumentLocation).PageEnd,
PageContext = PageContext,
Canvas = Canvas,
TextStyle = TextStyle,
AvailableSize = availableSize AvailableSize = availableSize
}; };
@@ -76,9 +73,7 @@ namespace QuestPDF.Elements
{ {
internal IPageContext PageContext { get; set; } internal IPageContext PageContext { get; set; }
internal ICanvas Canvas { get; set; } internal ICanvas Canvas { get; set; }
internal TextStyle TextStyle { get; set; } internal TextStyle TextStyle { get; set; }
internal ContentDirection ContentDirection { get; set; }
public int PageNumber { get; internal set; } public int PageNumber { get; internal set; }
public int TotalPages { get; internal set; } public int TotalPages { get; internal set; }
@@ -90,9 +85,7 @@ namespace QuestPDF.Elements
content(container); content(container);
container.ApplyDefaultTextStyle(TextStyle); container.ApplyDefaultTextStyle(TextStyle);
container.ApplyContentDirection(ContentDirection); container.VisitChildren(x => x?.Initialize(PageContext, Canvas));
container.InjectDependencies(PageContext, Canvas);
container.VisitChildren(x => (x as IStateResettable)?.ResetState()); container.VisitChildren(x => (x as IStateResettable)?.ResetState());
container.Size = container.Measure(Size.Max); container.Size = container.Measure(Size.Max);
+16 -4
View File
@@ -6,19 +6,31 @@ using SkiaSharp;
namespace QuestPDF.Elements namespace QuestPDF.Elements
{ {
internal class DynamicImage : Element internal class DynamicImage : Element, IVisual
{ {
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public Func<Size, byte[]>? Source { get; set; } public Func<Size, byte[]>? Source { get; set; }
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
return availableSpace.IsNegative() if (availableSpace.IsNegative())
? SpacePlan.Wrap() return SpacePlan.Wrap();
: SpacePlan.FullRender(availableSpace);
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return SpacePlan.FullRender(availableSpace);
} }
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
IsRendered = true;
if (availableSpace.Width < Size.Epsilon || availableSpace.Height < Size.Epsilon)
return;
var imageData = Source?.Invoke(availableSpace); var imageData = Source?.Invoke(availableSpace);
if (imageData == null) if (imageData == null)
+12 -4
View File
@@ -5,8 +5,11 @@ using SkiaSharp;
namespace QuestPDF.Elements namespace QuestPDF.Elements
{ {
internal class Image : Element, ICacheable internal class Image : Element, IVisual, ICacheable
{ {
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public SKImage? InternalImage { get; set; } public SKImage? InternalImage { get; set; }
~Image() ~Image()
@@ -16,9 +19,13 @@ namespace QuestPDF.Elements
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
return availableSpace.IsNegative() if (availableSpace.IsNegative())
? SpacePlan.Wrap() return SpacePlan.Wrap();
: SpacePlan.FullRender(availableSpace);
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return SpacePlan.FullRender(availableSpace);
} }
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
@@ -26,6 +33,7 @@ namespace QuestPDF.Elements
if (InternalImage == null) if (InternalImage == null)
return; return;
IsRendered = true;
Canvas.DrawImage(InternalImage, Position.Zero, availableSpace); Canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
} }
} }
+12 -28
View File
@@ -26,17 +26,15 @@ namespace QuestPDF.Elements
public SpacePlan Size { get; set; } public SpacePlan Size { get; set; }
} }
internal class Inlined : Element, IStateResettable, IContentDirectionAware internal class Inlined : Element, IStateResettable
{ {
public ContentDirection ContentDirection { get; set; }
public List<InlinedElement> Elements { get; internal set; } = new List<InlinedElement>(); public List<InlinedElement> Elements { get; internal set; } = new List<InlinedElement>();
private Queue<InlinedElement> ChildrenQueue { get; set; } private Queue<InlinedElement> ChildrenQueue { get; set; }
internal float VerticalSpacing { get; set; } internal float VerticalSpacing { get; set; }
internal float HorizontalSpacing { get; set; } internal float HorizontalSpacing { get; set; }
internal InlinedAlignment? ElementsAlignment { get; set; } internal InlinedAlignment ElementsAlignment { get; set; }
internal VerticalAlignment BaselineAlignment { get; set; } internal VerticalAlignment BaselineAlignment { get; set; }
public void ResetState() public void ResetState()
@@ -51,8 +49,6 @@ namespace QuestPDF.Elements
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
SetDefaultAlignment();
if (!ChildrenQueue.Any()) if (!ChildrenQueue.Any())
return SpacePlan.FullRender(Size.Zero); return SpacePlan.FullRender(Size.Zero);
@@ -85,8 +81,6 @@ namespace QuestPDF.Elements
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
SetDefaultAlignment();
var lines = Compose(availableSpace); var lines = Compose(availableSpace);
var topOffset = 0f; var topOffset = 0f;
@@ -109,6 +103,7 @@ namespace QuestPDF.Elements
var elementOffset = ElementOffset(); var elementOffset = ElementOffset();
var leftOffset = AlignOffset(); var leftOffset = AlignOffset();
Canvas.Translate(new Position(leftOffset, 0));
foreach (var measurement in lineMeasurements) foreach (var measurement in lineMeasurements)
{ {
@@ -118,17 +113,16 @@ namespace QuestPDF.Elements
if (size.Height == 0) if (size.Height == 0)
size = new Size(size.Width, lineSize.Height); size = new Size(size.Width, lineSize.Height);
var offset = ContentDirection == ContentDirection.LeftToRight Canvas.Translate(new Position(0, baselineOffset));
? new Position(leftOffset, baselineOffset)
: new Position(availableSpace.Width - size.Width - leftOffset, baselineOffset);
Canvas.Translate(offset);
measurement.Element.Draw(size); measurement.Element.Draw(size);
Canvas.Translate(offset.Reverse()); Canvas.Translate(new Position(0, -baselineOffset));
leftOffset += size.Width + elementOffset; leftOffset += size.Width + elementOffset;
Canvas.Translate(new Position(size.Width + elementOffset, 0));
} }
Canvas.Translate(new Position(-leftOffset, 0));
float ElementOffset() float ElementOffset()
{ {
var difference = availableSpace.Width - lineSize.Width; var difference = availableSpace.Width - lineSize.Width;
@@ -146,15 +140,15 @@ namespace QuestPDF.Elements
float AlignOffset() float AlignOffset()
{ {
var emptySpace = availableSpace.Width - lineSize.Width - (lineMeasurements.Count - 1) * HorizontalSpacing; var difference = availableSpace.Width - lineSize.Width - (lineMeasurements.Count - 1) * HorizontalSpacing;
return ElementsAlignment switch return ElementsAlignment switch
{ {
InlinedAlignment.Left => ContentDirection == ContentDirection.LeftToRight ? 0 : emptySpace, InlinedAlignment.Left => 0,
InlinedAlignment.Justify => 0, InlinedAlignment.Justify => 0,
InlinedAlignment.SpaceAround => elementOffset, InlinedAlignment.SpaceAround => elementOffset,
InlinedAlignment.Center => emptySpace / 2, InlinedAlignment.Center => difference / 2,
InlinedAlignment.Right => ContentDirection == ContentDirection.LeftToRight ? emptySpace : 0, InlinedAlignment.Right => difference,
_ => 0 _ => 0
}; };
} }
@@ -173,16 +167,6 @@ namespace QuestPDF.Elements
} }
} }
void SetDefaultAlignment()
{
if (ElementsAlignment.HasValue)
return;
ElementsAlignment = ContentDirection == ContentDirection.LeftToRight
? InlinedAlignment.Left
: InlinedAlignment.Right;
}
Size GetLineSize(ICollection<InlinedMeasurement> measurements) Size GetLineSize(ICollection<InlinedMeasurement> measurements)
{ {
var width = measurements.Sum(x => x.Size.Width); var width = measurements.Sum(x => x.Size.Width);
+14 -6
View File
@@ -15,34 +15,42 @@ namespace QuestPDF.Elements
Horizontal Horizontal
} }
internal class Line : Element, ILine, ICacheable internal class Line : Element, ILine, IVisual, ICacheable
{ {
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public LineType Type { get; set; } = LineType.Vertical; public LineType Type { get; set; } = LineType.Vertical;
public string Color { get; set; } = Colors.Black; public string Color { get; set; } = Colors.Black;
public float Size { get; set; } = 1; public float Thickness { get; set; } = 1;
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
if (availableSpace.IsNegative()) if (availableSpace.IsNegative())
return SpacePlan.Wrap(); return SpacePlan.Wrap();
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return Type switch return Type switch
{ {
LineType.Vertical when availableSpace.Width + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(Size, 0), LineType.Vertical when availableSpace.Width + Infrastructure.Size.Epsilon >= Thickness => SpacePlan.FullRender(Thickness, 0),
LineType.Horizontal when availableSpace.Height + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(0, Size), LineType.Horizontal when availableSpace.Height + Infrastructure.Size.Epsilon >= Thickness => SpacePlan.FullRender(0, Thickness),
_ => SpacePlan.Wrap() _ => SpacePlan.Wrap()
}; };
} }
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
IsRendered = true;
if (Type == LineType.Vertical) if (Type == LineType.Vertical)
{ {
Canvas.DrawRectangle(new Position(-Size/2, 0), new Size(Size, availableSpace.Height), Color); Canvas.DrawRectangle(new Position(-Thickness/2, 0), new Size(Thickness, availableSpace.Height), Color);
} }
else if (Type == LineType.Horizontal) else if (Type == LineType.Horizontal)
{ {
Canvas.DrawRectangle(new Position(0, -Size/2), new Size(availableSpace.Width, Size), Color); Canvas.DrawRectangle(new Position(0, -Thickness/2), new Size(availableSpace.Width, Thickness), Color);
} }
} }
} }
+1 -9
View File
@@ -3,10 +3,8 @@ using QuestPDF.Infrastructure;
namespace QuestPDF.Elements namespace QuestPDF.Elements
{ {
internal class MinimalBox : ContainerElement, IContentDirectionAware internal class MinimalBox : ContainerElement
{ {
public ContentDirection ContentDirection { get; set; }
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
var targetSize = base.Measure(availableSpace); var targetSize = base.Measure(availableSpace);
@@ -14,13 +12,7 @@ namespace QuestPDF.Elements
if (targetSize.Type == SpacePlanType.Wrap) if (targetSize.Type == SpacePlanType.Wrap)
return; return;
var translate = ContentDirection == ContentDirection.RightToLeft
? new Position(availableSpace.Width - targetSize.Width, 0)
: Position.Zero;
Canvas.Translate(translate);
base.Draw(targetSize); base.Draw(targetSize);
Canvas.Translate(translate.Reverse());
} }
} }
} }
+1 -3
View File
@@ -8,8 +8,7 @@ namespace QuestPDF.Elements
{ {
internal class Page : IComponent internal class Page : IComponent
{ {
public ContentDirection ContentDirection { get; set; } public TextStyle DefaultTextStyle { get; set; } = new TextStyle();
public TextStyle DefaultTextStyle { get; set; } = TextStyle.Default;
public Size MinSize { get; set; } = PageSizes.A4; public Size MinSize { get; set; } = PageSizes.A4;
public Size MaxSize { get; set; } = PageSizes.A4; public Size MaxSize { get; set; } = PageSizes.A4;
@@ -31,7 +30,6 @@ namespace QuestPDF.Elements
public void Compose(IContainer container) public void Compose(IContainer container)
{ {
container container
.ContentDirection(ContentDirection)
.Background(BackgroundColor) .Background(BackgroundColor)
.Layers(layers => .Layers(layers =>
{ {
-1
View File
@@ -25,7 +25,6 @@ namespace QuestPDF.Elements
{ {
if (string.IsNullOrWhiteSpace(Text)) if (string.IsNullOrWhiteSpace(Text))
x.MaxHeight(32).Image(ImageData, ImageScaling.FitArea); x.MaxHeight(32).Image(ImageData, ImageScaling.FitArea);
else else
x.Text(Text).FontSize(14); x.Text(Text).FontSize(14);
}); });
+9
View File
@@ -0,0 +1,9 @@
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class RepeatContentSetter : ContainerElement
{
public bool RepeatContent { get; set; }
}
}
+3 -9
View File
@@ -31,10 +31,8 @@ namespace QuestPDF.Elements
public Position Offset { get; set; } public Position Offset { get; set; }
} }
internal class Row : Element, ICacheable, IStateResettable, IContentDirectionAware internal class Row : Element, ICacheable, IStateResettable
{ {
public ContentDirection ContentDirection { get; set; }
internal List<RowItem> Items { get; } = new(); internal List<RowItem> Items { get; } = new();
internal float Spacing { get; set; } internal float Spacing { get; set; }
@@ -93,13 +91,9 @@ namespace QuestPDF.Elements
if (command.Measurement.Type == SpacePlanType.Wrap) if (command.Measurement.Type == SpacePlanType.Wrap)
continue; continue;
var offset = ContentDirection == ContentDirection.LeftToRight Canvas.Translate(command.Offset);
? command.Offset
: new Position(availableSpace.Width - command.Offset.X - command.Size.Width, 0);
Canvas.Translate(offset);
command.RowItem.Draw(command.Size); command.RowItem.Draw(command.Size);
Canvas.Translate(offset.Reverse()); Canvas.Translate(command.Offset.Reverse());
} }
if (Items.All(x => x.IsRendered)) if (Items.All(x => x.IsRendered))
+15 -27
View File
@@ -9,15 +9,12 @@ using QuestPDF.Infrastructure;
namespace QuestPDF.Elements.Table namespace QuestPDF.Elements.Table
{ {
internal class Table : Element, IStateResettable, IContentDirectionAware internal class Table : Element, IStateResettable
{ {
public ContentDirection ContentDirection { get; set; } public List<TableColumnDefinition> Columns { get; set; } = new List<TableColumnDefinition>();
public List<TableCell> Cells { get; set; } = new List<TableCell>();
public List<TableColumnDefinition> Columns { get; set; } = new();
public List<TableCell> Cells { get; set; } = new();
public bool ExtendLastCellsToTableBottom { get; set; } public bool ExtendLastCellsToTableBottom { get; set; }
private bool CacheInitialized { get; set; }
private int StartingRowsCount { get; set; } private int StartingRowsCount { get; set; }
private int RowsCount { get; set; } private int RowsCount { get; set; }
private int CurrentRow { get; set; } private int CurrentRow { get; set; }
@@ -29,6 +26,16 @@ namespace QuestPDF.Elements.Table
private int MaxRow { get; set; } private int MaxRow { get; set; }
private int MaxRowSpan { get; set; } private int MaxRowSpan { get; set; }
internal override void Initialize(IPageContext pageContext, ICanvas canvas)
{
StartingRowsCount = Cells.Select(x => x.Row).DefaultIfEmpty(0).Max();
RowsCount = Cells.Select(x => x.Row + x.RowSpan - 1).DefaultIfEmpty(0).Max();
Cells = Cells.OrderBy(x => x.Row).ThenBy(x => x.Column).ToList();
BuildCache();
base.Initialize(pageContext, canvas);
}
internal override IEnumerable<Element?> GetChildren() internal override IEnumerable<Element?> GetChildren()
{ {
return Cells; return Cells;
@@ -36,27 +43,12 @@ namespace QuestPDF.Elements.Table
public void ResetState() public void ResetState()
{ {
Initialize();
foreach (var x in Cells) foreach (var x in Cells)
x.IsRendered = false; x.IsRendered = false;
CurrentRow = 1; CurrentRow = 1;
} }
private void Initialize()
{
if (CacheInitialized)
return;
StartingRowsCount = Cells.Select(x => x.Row).DefaultIfEmpty(0).Max();
RowsCount = Cells.Select(x => x.Row + x.RowSpan - 1).DefaultIfEmpty(0).Max();
Cells = Cells.OrderBy(x => x.Row).ThenBy(x => x.Column).ToList();
BuildCache();
CacheInitialized = true;
}
private void BuildCache() private void BuildCache()
{ {
if (CellsCache != null) if (CellsCache != null)
@@ -120,13 +112,9 @@ namespace QuestPDF.Elements.Table
if (command.Measurement.Type == SpacePlanType.Wrap) if (command.Measurement.Type == SpacePlanType.Wrap)
continue; continue;
var offset = ContentDirection == ContentDirection.LeftToRight Canvas.Translate(command.Offset);
? command.Offset
: new Position(availableSpace.Width - command.Offset.X - command.Size.Width, command.Offset.Y);
Canvas.Translate(offset);
command.Cell.Draw(command.Size); command.Cell.Draw(command.Size);
Canvas.Translate(offset.Reverse()); Canvas.Translate(command.Offset.Reverse());
} }
CurrentRow = FindLastRenderedRow(renderingCommands) + 1; CurrentRow = FindLastRenderedRow(renderingCommands) + 1;
@@ -12,7 +12,7 @@ namespace QuestPDF.Elements.Text.Items
public TextMeasurementResult? Measure(TextMeasurementRequest request) public TextMeasurementResult? Measure(TextMeasurementRequest request)
{ {
Element.VisitChildren(x => (x as IStateResettable)?.ResetState()); Element.VisitChildren(x => (x as IStateResettable)?.ResetState());
Element.InjectDependencies(request.PageContext, request.Canvas); Element.VisitChildren(x => x.Initialize(request.PageContext, request.Canvas));
var measurement = Element.Measure(new Size(request.AvailableWidth, Size.Max.Height)); var measurement = Element.Measure(new Size(request.AvailableWidth, Size.Max.Height));
@@ -37,7 +37,7 @@ namespace QuestPDF.Elements.Text.Items
public void Draw(TextDrawingRequest request) public void Draw(TextDrawingRequest request)
{ {
Element.VisitChildren(x => (x as IStateResettable)?.ResetState()); Element.VisitChildren(x => (x as IStateResettable)?.ResetState());
Element.InjectDependencies(request.PageContext, request.Canvas); Element.VisitChildren(x => x.Initialize(request.PageContext, request.Canvas));
request.Canvas.Translate(new Position(0, request.TotalAscent)); request.Canvas.Translate(new Position(0, request.TotalAscent));
Element.Draw(new Size(request.TextSize.Width, -request.TotalAscent)); Element.Draw(new Size(request.TextSize.Width, -request.TotalAscent));
@@ -47,11 +47,11 @@ namespace QuestPDF.Elements.Text.Items
// ignore leading spaces // ignore leading spaces
if (!request.IsFirstElementInBlock && request.IsFirstElementInLine) if (!request.IsFirstElementInBlock && request.IsFirstElementInLine)
{ {
while (startIndex < TextShapingResult.Length && Text[startIndex] == spaceCodepoint) while (startIndex < TextShapingResult.Glyphs.Length && Text[startIndex] == spaceCodepoint)
startIndex++; startIndex++;
} }
if (TextShapingResult.Length == 0 || startIndex == TextShapingResult.Length) if (TextShapingResult.Glyphs.Length == 0 || startIndex == TextShapingResult.Glyphs.Length)
{ {
return new TextMeasurementResult return new TextMeasurementResult
{ {
@@ -90,7 +90,7 @@ namespace QuestPDF.Elements.Text.Items
StartIndex = startIndex, StartIndex = startIndex,
EndIndex = wrappedText.Value.endIndex, EndIndex = wrappedText.Value.endIndex,
NextIndex = wrappedText.Value.nextIndex, NextIndex = wrappedText.Value.nextIndex,
TotalIndex = TextShapingResult.Length - 1 TotalIndex = TextShapingResult.Glyphs.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) // textLength - length of the part of the text that fits in available width (creating a line)
// entire text fits, no need to wrap // entire text fits, no need to wrap
if (endIndex == TextShapingResult.Length - 1) if (endIndex == TextShapingResult.Glyphs.Length - 1)
return (endIndex, endIndex); return (endIndex, endIndex);
// breaking anywhere // breaking anywhere
@@ -110,7 +110,7 @@ namespace QuestPDF.Elements.Text.Items
return (endIndex, endIndex + 1); return (endIndex, endIndex + 1);
// current line ends at word, next character is space, perfect place to wrap // current line ends at word, next character is space, perfect place to wrap
if (TextShapingResult[endIndex].Codepoint != spaceCodepoint && TextShapingResult[endIndex + 1].Codepoint == spaceCodepoint) if (TextShapingResult.Glyphs[endIndex].Codepoint != spaceCodepoint && TextShapingResult.Glyphs[endIndex + 1].Codepoint == spaceCodepoint)
return (endIndex, endIndex + 2); return (endIndex, endIndex + 2);
// find last space within the available text to wrap // find last space within the available text to wrap
@@ -118,7 +118,7 @@ namespace QuestPDF.Elements.Text.Items
while (lastSpaceIndex >= startIndex) while (lastSpaceIndex >= startIndex)
{ {
if (TextShapingResult[lastSpaceIndex].Codepoint == spaceCodepoint) if (TextShapingResult.Glyphs[lastSpaceIndex].Codepoint == spaceCodepoint)
break; break;
lastSpaceIndex--; lastSpaceIndex--;
+42 -32
View File
@@ -4,15 +4,17 @@ using System.Linq;
using QuestPDF.Drawing; using QuestPDF.Drawing;
using QuestPDF.Elements.Text.Calculation; using QuestPDF.Elements.Text.Calculation;
using QuestPDF.Elements.Text.Items; using QuestPDF.Elements.Text.Items;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure; using QuestPDF.Infrastructure;
namespace QuestPDF.Elements.Text namespace QuestPDF.Elements.Text
{ {
internal class TextBlock : Element, IStateResettable, IContentDirectionAware internal class TextBlock : Element, IVisual, IStateResettable
{ {
public ContentDirection ContentDirection { get; set; } public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public HorizontalAlignment? Alignment { get; set; } public HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
public List<ITextBlockItem> Items { get; set; } = new List<ITextBlockItem>(); public List<ITextBlockItem> Items { get; set; } = new List<ITextBlockItem>();
public string Text => string.Join(" ", Items.Where(x => x is TextBlockSpan).Cast<TextBlockSpan>().Select(x => x.Text)); public string Text => string.Join(" ", Items.Where(x => x is TextBlockSpan).Cast<TextBlockSpan>().Select(x => x.Text));
@@ -53,19 +55,13 @@ namespace QuestPDF.Elements.Text
} }
} }
void SetDefaultAlignment()
{
if (Alignment.HasValue)
return;
Alignment = ContentDirection == ContentDirection.LeftToRight
? HorizontalAlignment.Left
: HorizontalAlignment.Right;
}
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
SetDefaultAlignment(); if (availableSpace.IsNegative())
return SpacePlan.Wrap();
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
if (!RenderingQueue.Any()) if (!RenderingQueue.Any())
return SpacePlan.FullRender(Size.Zero); return SpacePlan.FullRender(Size.Zero);
@@ -94,18 +90,25 @@ namespace QuestPDF.Elements.Text
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
SetDefaultAlignment(); if (IsRendered && !RepeatContent)
return;
var lines = DivideTextItemsIntoLines(availableSpace.Width, availableSpace.Height).ToList(); var lines = DivideTextItemsIntoLines(availableSpace.Width, availableSpace.Height).ToList();
if (!lines.Any()) if (!lines.Any())
return; return;
var topOffset = 0f; var heightOffset = 0f;
var widthOffset = 0f;
foreach (var line in lines) foreach (var line in lines)
{ {
var leftOffset = GetAlignmentOffset(line.Width); widthOffset = 0f;
var alignmentOffset = GetAlignmentOffset(line.Width);
Canvas.Translate(new Position(alignmentOffset, 0));
Canvas.Translate(new Position(0, -line.Ascent));
foreach (var item in line.Elements) foreach (var item in line.Elements)
{ {
@@ -121,20 +124,21 @@ namespace QuestPDF.Elements.Text
TotalAscent = line.Ascent TotalAscent = line.Ascent
}; };
var canvasOffset = ContentDirection == ContentDirection.LeftToRight
? new Position(leftOffset, topOffset - line.Ascent)
: new Position(availableSpace.Width - leftOffset - item.Measurement.Width, topOffset - line.Ascent);
Canvas.Translate(canvasOffset);
item.Item.Draw(textDrawingRequest); item.Item.Draw(textDrawingRequest);
Canvas.Translate(canvasOffset.Reverse());
leftOffset += item.Measurement.Width; Canvas.Translate(new Position(item.Measurement.Width, 0));
widthOffset += item.Measurement.Width;
} }
topOffset += line.LineHeight; Canvas.Translate(new Position(-alignmentOffset, 0));
Canvas.Translate(new Position(-line.Width, line.Ascent));
Canvas.Translate(new Position(0, line.LineHeight));
heightOffset += line.LineHeight;
} }
Canvas.Translate(new Position(0, -heightOffset));
lines lines
.SelectMany(x => x.Elements) .SelectMany(x => x.Elements)
.GroupBy(x => x.Item) .GroupBy(x => x.Item)
@@ -147,19 +151,25 @@ namespace QuestPDF.Elements.Text
CurrentElementIndex = lastElementMeasurement.IsLast ? 0 : lastElementMeasurement.NextIndex; CurrentElementIndex = lastElementMeasurement.IsLast ? 0 : lastElementMeasurement.NextIndex;
if (!RenderingQueue.Any()) if (!RenderingQueue.Any())
{
ResetState(); ResetState();
IsRendered = true;
}
float GetAlignmentOffset(float lineWidth) float GetAlignmentOffset(float lineWidth)
{ {
if (Alignment == HorizontalAlignment.Left)
return 0;
var emptySpace = availableSpace.Width - lineWidth; var emptySpace = availableSpace.Width - lineWidth;
return Alignment switch if (Alignment == HorizontalAlignment.Right)
{ return emptySpace;
HorizontalAlignment.Left => ContentDirection == ContentDirection.LeftToRight ? 0 : emptySpace,
HorizontalAlignment.Center => emptySpace / 2, if (Alignment == HorizontalAlignment.Center)
HorizontalAlignment.Right => ContentDirection == ContentDirection.LeftToRight ? emptySpace : 0, return emptySpace / 2;
_ => 0
}; throw new ArgumentException();
} }
} }
+1 -9
View File
@@ -3,10 +3,8 @@ using QuestPDF.Infrastructure;
namespace QuestPDF.Elements namespace QuestPDF.Elements
{ {
internal class Unconstrained : ContainerElement, IContentDirectionAware, ICacheable internal class Unconstrained : ContainerElement, ICacheable
{ {
public ContentDirection ContentDirection { get; set; }
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
var childSize = base.Measure(Size.Max); var childSize = base.Measure(Size.Max);
@@ -27,13 +25,7 @@ namespace QuestPDF.Elements
if (measurement.Type == SpacePlanType.Wrap) if (measurement.Type == SpacePlanType.Wrap)
return; return;
var translate = ContentDirection == ContentDirection.RightToLeft
? new Position(-measurement.Width, 0)
: Position.Zero;
Canvas.Translate(translate);
base.Draw(measurement); base.Draw(measurement);
Canvas.Translate(translate.Reverse());
} }
} }
} }
@@ -1,26 +0,0 @@
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent
{
public static class ContentDirectionExtensions
{
internal static IContainer ContentDirection(this IContainer element, ContentDirection direction)
{
return element.Element(new ContentDirectionSetter
{
ContentDirection = direction
});
}
public static IContainer ContentFromLeftToRight(this IContainer element)
{
return element.ContentDirection(Infrastructure.ContentDirection.LeftToRight);
}
public static IContainer ContentFromRightToLeft(this IContainer element)
{
return element.ContentDirection(Infrastructure.ContentDirection.RightToLeft);
}
}
}
+4 -8
View File
@@ -12,7 +12,7 @@ namespace QuestPDF.Fluent
{ {
var container = new Container(); var container = new Container();
Decoration.Before = container; Decoration.Before = container;
return container; return container.RepeatContentWhenPaging();
} }
public void Before(Action<IContainer> handler) public void Before(Action<IContainer> handler)
@@ -36,7 +36,7 @@ namespace QuestPDF.Fluent
{ {
var container = new Container(); var container = new Container();
Decoration.After = container; Decoration.After = container;
return container; return container.RepeatContentWhenPaging();
} }
public void After(Action<IContainer> handler) public void After(Action<IContainer> handler)
@@ -49,9 +49,7 @@ namespace QuestPDF.Fluent
[Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")] [Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
public IContainer Header() public IContainer Header()
{ {
var container = new Container(); return Before();
Decoration.Before = container;
return container;
} }
[Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")] [Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
@@ -63,9 +61,7 @@ namespace QuestPDF.Fluent
[Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")] [Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
public IContainer Footer() public IContainer Footer()
{ {
var container = new Container(); return After();
Decoration.After = container;
return container;
} }
[Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")] [Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
-1
View File
@@ -55,7 +55,6 @@ namespace QuestPDF.Fluent
public static class GridExtensions public static class GridExtensions
{ {
[Obsolete("This element has been deprecated since version 2022.11. Please use the Table element, or the combination of the Row and Column elements.")]
public static void Grid(this IContainer element, Action<GridDescriptor> handler) public static void Grid(this IContainer element, Action<GridDescriptor> handler)
{ {
var descriptor = new GridDescriptor(); var descriptor = new GridDescriptor();
-1
View File
@@ -30,7 +30,6 @@ namespace QuestPDF.Fluent
public void BaselineMiddle() => Inlined.BaselineAlignment = VerticalAlignment.Middle; public void BaselineMiddle() => Inlined.BaselineAlignment = VerticalAlignment.Middle;
public void BaselineBottom() => Inlined.BaselineAlignment = VerticalAlignment.Bottom; public void BaselineBottom() => Inlined.BaselineAlignment = VerticalAlignment.Bottom;
internal void Alignment(InlinedAlignment? alignment) => Inlined.ElementsAlignment = alignment;
public void AlignLeft() => Inlined.ElementsAlignment = InlinedAlignment.Left; public void AlignLeft() => Inlined.ElementsAlignment = InlinedAlignment.Left;
public void AlignCenter() => Inlined.ElementsAlignment = InlinedAlignment.Center; public void AlignCenter() => Inlined.ElementsAlignment = InlinedAlignment.Center;
public void AlignRight() => Inlined.ElementsAlignment = InlinedAlignment.Right; public void AlignRight() => Inlined.ElementsAlignment = InlinedAlignment.Right;
+1 -1
View File
@@ -24,7 +24,7 @@ namespace QuestPDF.Fluent
return container; return container;
} }
public IContainer Layer() => Layer(false); public IContainer Layer() => Layer(false).RepeatContentWhenPaging();
public IContainer PrimaryLayer() => Layer(true); public IContainer PrimaryLayer() => Layer(true);
internal void Validate() internal void Validate()
+6 -6
View File
@@ -6,11 +6,11 @@ namespace QuestPDF.Fluent
{ {
public static class LineExtensions public static class LineExtensions
{ {
private static ILine Line(this IContainer element, LineType type, float size) private static ILine Line(this IContainer element, LineType type, float thickness)
{ {
var line = new Line var line = new Line
{ {
Size = size, Thickness = thickness,
Type = type Type = type
}; };
@@ -18,14 +18,14 @@ namespace QuestPDF.Fluent
return line; return line;
} }
public static ILine LineVertical(this IContainer element, float size, Unit unit = Unit.Point) public static ILine LineVertical(this IContainer element, float thickness, Unit unit = Unit.Point)
{ {
return element.Line(LineType.Vertical, size.ToPoints(unit)); return element.Line(LineType.Vertical, thickness.ToPoints(unit));
} }
public static ILine LineHorizontal(this IContainer element, float size, Unit unit = Unit.Point) public static ILine LineHorizontal(this IContainer element, float thickness, Unit unit = Unit.Point)
{ {
return element.Line(LineType.Horizontal, size.ToPoints(unit)); return element.Line(LineType.Horizontal, thickness.ToPoints(unit));
} }
public static void LineColor(this ILine descriptor, string value) public static void LineColor(this ILine descriptor, string value)
+1 -27
View File
@@ -10,9 +10,7 @@ namespace QuestPDF.Fluent
{ {
internal Page Page { get; } = new Page(); internal Page Page { get; } = new Page();
#region Size public void Size(float width, float height, Unit unit = Unit.Inch)
public void Size(float width, float height, Unit unit = Unit.Point)
{ {
var pageSize = new PageSize(width, height, unit); var pageSize = new PageSize(width, height, unit);
@@ -42,10 +40,6 @@ namespace QuestPDF.Fluent
Page.MaxSize = pageSize; Page.MaxSize = pageSize;
} }
#endregion
#region Margin
public void MarginLeft(float value, Unit unit = Unit.Point) public void MarginLeft(float value, Unit unit = Unit.Point)
{ {
Page.MarginLeft = value.ToPoints(unit); Page.MarginLeft = value.ToPoints(unit);
@@ -84,10 +78,6 @@ namespace QuestPDF.Fluent
MarginHorizontal(value, unit); MarginHorizontal(value, unit);
} }
#endregion
#region Properties
public void DefaultTextStyle(TextStyle textStyle) public void DefaultTextStyle(TextStyle textStyle)
{ {
Page.DefaultTextStyle = textStyle; Page.DefaultTextStyle = textStyle;
@@ -98,16 +88,6 @@ namespace QuestPDF.Fluent
DefaultTextStyle(handler(TextStyle.Default)); DefaultTextStyle(handler(TextStyle.Default));
} }
public void ContentFromLeftToRight()
{
Page.ContentDirection = ContentDirection.LeftToRight;
}
public void ContentFromRightToLeft()
{
Page.ContentDirection = ContentDirection.RightToLeft;
}
public void PageColor(string color) public void PageColor(string color)
{ {
Page.BackgroundColor = color; Page.BackgroundColor = color;
@@ -119,10 +99,6 @@ namespace QuestPDF.Fluent
PageColor(color); PageColor(color);
} }
#endregion
#region Slots
public IContainer Background() public IContainer Background()
{ {
var container = new Container(); var container = new Container();
@@ -157,8 +133,6 @@ namespace QuestPDF.Fluent
Page.Footer = container; Page.Footer = container;
return container; return container;
} }
#endregion
} }
public static class PageExtensions public static class PageExtensions
@@ -0,0 +1,27 @@
using System;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent
{
public static class RepeatContentExtensions
{
private static IContainer RepeatContent(this IContainer element, Action<RepeatContentSetter> handler)
{
var repeatContentSetter = element as RepeatContentSetter ?? new RepeatContentSetter();
handler(repeatContentSetter);
return element.Element(repeatContentSetter);
}
public static IContainer RepeatContentWhenPaging(this IContainer element)
{
return element.RepeatContent(x => x.RepeatContent = true);
}
public static IContainer DoNotRepeatContentWhenPaging(this IContainer element)
{
return element.RepeatContent(x => x.RepeatContent = false);
}
}
}
+2 -2
View File
@@ -50,7 +50,7 @@ namespace QuestPDF.Fluent
{ {
private ICollection<TextBlock> TextBlocks { get; } = new List<TextBlock>(); private ICollection<TextBlock> TextBlocks { get; } = new List<TextBlock>();
private TextStyle? DefaultStyle { get; set; } private TextStyle? DefaultStyle { get; set; }
internal HorizontalAlignment? Alignment { get; set; } internal HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
private float Spacing { get; set; } = 0f; private float Spacing { get; set; } = 0f;
public void DefaultTextStyle(TextStyle style) public void DefaultTextStyle(TextStyle style)
@@ -242,7 +242,7 @@ namespace QuestPDF.Fluent
internal void Compose(IContainer container) internal void Compose(IContainer container)
{ {
TextBlocks.ToList().ForEach(x => x.Alignment ??= Alignment); TextBlocks.ToList().ForEach(x => x.Alignment = Alignment);
if (DefaultStyle != null) if (DefaultStyle != null)
container = container.DefaultTextStyle(DefaultStyle); container = container.DefaultTextStyle(DefaultStyle);
@@ -164,27 +164,5 @@ namespace QuestPDF.Fluent
} }
#endregion #endregion
#region Direction
public static T DirectionAuto<T>(this T descriptor) where T : TextSpanDescriptor
{
descriptor.MutateTextStyle(x => x.DirectionAuto());
return descriptor;
}
public static T DirectionFromLeftToRight<T>(this T descriptor) where T : TextSpanDescriptor
{
descriptor.MutateTextStyle(x => x.DirectionFromLeftToRight());
return descriptor;
}
public static T DirectionFromRightToLeft<T>(this T descriptor) where T : TextSpanDescriptor
{
descriptor.MutateTextStyle(x => x.DirectionFromRightToLeft());
return descriptor;
}
#endregion
} }
} }
-24
View File
@@ -165,29 +165,5 @@ namespace QuestPDF.Fluent
} }
#endregion #endregion
#region Direction
private static TextStyle TextDirection(this TextStyle style, TextDirection textDirection)
{
return style.Mutate(TextStyleProperty.Direction, textDirection);
}
public static TextStyle DirectionAuto(this TextStyle style)
{
return style.TextDirection(Infrastructure.TextDirection.Auto);
}
public static TextStyle DirectionFromLeftToRight(this TextStyle style)
{
return style.TextDirection(Infrastructure.TextDirection.LeftToRight);
}
public static TextStyle DirectionFromRightToLeft(this TextStyle style)
{
return style.TextDirection(Infrastructure.TextDirection.RightToLeft);
}
#endregion
} }
} }
@@ -1,8 +0,0 @@
namespace QuestPDF.Infrastructure
{
internal enum ContentDirection
{
LeftToRight,
RightToLeft
}
}
+6
View File
@@ -15,6 +15,12 @@ namespace QuestPDF.Infrastructure
yield break; yield break;
} }
internal virtual void Initialize(IPageContext pageContext, ICanvas canvas)
{
PageContext = pageContext;
Canvas = canvas;
}
internal virtual void CreateProxy(Func<Element?, Element?> create) internal virtual void CreateProxy(Func<Element?, Element?> create)
{ {
@@ -1,7 +0,0 @@
namespace QuestPDF.Infrastructure
{
internal interface IContentDirectionAware
{
public ContentDirection ContentDirection { get; set; }
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace QuestPDF.Infrastructure
{
internal interface IVisual
{
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
}
}
-9
View File
@@ -1,9 +0,0 @@
namespace QuestPDF.Infrastructure
{
internal enum TextDirection
{
Auto,
LeftToRight,
RightToLeft
}
}
-2
View File
@@ -16,7 +16,6 @@ namespace QuestPDF.Infrastructure
internal bool? HasStrikethrough { get; set; } internal bool? HasStrikethrough { get; set; }
internal bool? HasUnderline { get; set; } internal bool? HasUnderline { get; set; }
internal bool? WrapAnywhere { get; set; } internal bool? WrapAnywhere { get; set; }
internal TextDirection? Direction { get; set; }
internal TextStyle? Fallback { get; set; } internal TextStyle? Fallback { get; set; }
@@ -33,7 +32,6 @@ namespace QuestPDF.Infrastructure
HasStrikethrough = false, HasStrikethrough = false,
HasUnderline = false, HasUnderline = false,
WrapAnywhere = false, WrapAnywhere = false,
Direction = TextDirection.Auto,
Fallback = null Fallback = null
}; };
+1 -16
View File
@@ -17,8 +17,7 @@ namespace QuestPDF.Infrastructure
HasStrikethrough, HasStrikethrough,
HasUnderline, HasUnderline,
WrapAnywhere, WrapAnywhere,
Fallback, Fallback
Direction
} }
internal static class TextStyleManager internal static class TextStyleManager
@@ -194,19 +193,6 @@ namespace QuestPDF.Infrastructure
return origin with { Fallback = castedValue }; return origin with { Fallback = castedValue };
} }
if (property == TextStyleProperty.Direction)
{
if (!overrideValue && origin.Direction != null)
return origin;
var castedValue = (TextDirection?)value;
if (origin.Direction == castedValue)
return origin;
return origin with { Direction = castedValue };
}
throw new ArgumentOutOfRangeException(nameof(property), property, "Expected to mutate the TextStyle object. Provided property type is not supported."); throw new ArgumentOutOfRangeException(nameof(property), property, "Expected to mutate the TextStyle object. Provided property type is not supported.");
} }
@@ -252,7 +238,6 @@ namespace QuestPDF.Infrastructure
result = MutateStyle(result, TextStyleProperty.HasStrikethrough, parent.HasStrikethrough, overrideStyle); result = MutateStyle(result, TextStyleProperty.HasStrikethrough, parent.HasStrikethrough, overrideStyle);
result = MutateStyle(result, TextStyleProperty.HasUnderline, parent.HasUnderline, overrideStyle); result = MutateStyle(result, TextStyleProperty.HasUnderline, parent.HasUnderline, overrideStyle);
result = MutateStyle(result, TextStyleProperty.WrapAnywhere, parent.WrapAnywhere, overrideStyle); result = MutateStyle(result, TextStyleProperty.WrapAnywhere, parent.WrapAnywhere, overrideStyle);
result = MutateStyle(result, TextStyleProperty.Direction, parent.Direction, overrideStyle);
if (applyFallback) if (applyFallback)
result = MutateStyle(result, TextStyleProperty.Fallback, parent.Fallback, overrideStyle); result = MutateStyle(result, TextStyleProperty.Fallback, parent.Fallback, overrideStyle);
+1 -1
View File
@@ -19,7 +19,7 @@ namespace QuestPDF.Previewer
public event Action? OnPreviewerStopped; public event Action? OnPreviewerStopped;
private const int RequiredPreviewerVersionMajor = 2022; private const int RequiredPreviewerVersionMajor = 2022;
private const int RequiredPreviewerVersionMinor = 11; private const int RequiredPreviewerVersionMinor = 9;
public PreviewerService(int port) public PreviewerService(int port)
{ {
+1 -1
View File
@@ -3,7 +3,7 @@
<Authors>MarcinZiabek</Authors> <Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company> <Company>CodeFlint</Company>
<PackageId>QuestPDF</PackageId> <PackageId>QuestPDF</PackageId>
<Version>2022.11.0-alpha1</Version> <Version>2022.9.1</Version>
<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> <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> <PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
<LangVersion>9</LangVersion> <LangVersion>9</LangVersion>
+1 -1
View File
@@ -21,7 +21,7 @@ Install-Package QuestPDF
dotnet add package QuestPDF dotnet add package QuestPDF
// Package reference in .csproj file // Package reference in .csproj file
<PackageReference Include="QuestPDF" Version="2022.11.0" /> <PackageReference Include="QuestPDF" Version="2022.3.0" />
``` ```
## Documentation ## Documentation
+11 -3
View File
@@ -1,3 +1,11 @@
Added support for the right-to-left content direction. 2022.9.0
Fixed: word-wrapping algorithm does not work correctly with right-to-left languages. - Implemented font-fallback algorithm,
Fixed: Page.Size() API incorrectly uses Unit.Inch as default unit. Replaced with Unit.Point for consistency. - Introduced new Settings API,
- Significantly reduced memory allocation cost for TextStyle objects,
- Implemented optional checking if all font glyphs are available,
- Minor text-rendering optimizations.
2022.9.1
- Fixed: text hyperlinks do not work when the CheckIfAllTextGlyphsAreAvailable option or text fallback are used,
- Fixed: cells with RowSpan (greater than 1) are not always displayed properly,
- Improved predictability of the Inlined element when measuring its children.