Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2772b5b4a4 | |||
| 3dfe7a59e0 | |||
| df5c5524ee |
@@ -1,33 +0,0 @@
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class BarCode
|
||||
{
|
||||
[Test]
|
||||
public void Example()
|
||||
{
|
||||
FontManager.RegisterFontType("LibreBarcode39", File.OpenRead("LibreBarcode39-Regular.ttf"));
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 100)
|
||||
.FileName()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background(Colors.White)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("*QuestPDF*", TextStyle.Default.FontType("LibreBarcode39").Size(64));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ContinuousPageDocument : IDocument
|
||||
{
|
||||
public DocumentMetadata GetMetadata() => DocumentMetadata.Default;
|
||||
|
||||
public void Compose(IDocumentContainer container)
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Margin(20);
|
||||
page.ContinuousSize(150);
|
||||
|
||||
page.Header().Text("Header");
|
||||
|
||||
page.Content().PaddingVertical(10).Border(1).Padding(10).Stack(stack =>
|
||||
{
|
||||
foreach (var index in Enumerable.Range(1, 100))
|
||||
stack.Item().Text($"Line {index}", TextStyle.Default.Color(Placeholders.Color()));
|
||||
});
|
||||
|
||||
page.Footer().Text("Footer");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class ContinuousPageExamples
|
||||
{
|
||||
[Test]
|
||||
public void ContinuousPage()
|
||||
{
|
||||
var path = "example.pdf";
|
||||
new ContinuousPageDocument().GeneratePdf(path);
|
||||
Process.Start("explorer", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -581,7 +581,7 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(650, 450)
|
||||
.PageSize(350, 350)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
@@ -595,8 +595,8 @@ namespace QuestPDF.Examples
|
||||
foreach (var turns in Enumerable.Range(0, 4))
|
||||
{
|
||||
grid.Item()
|
||||
.Width(300)
|
||||
.Height(200)
|
||||
.Width(150)
|
||||
.Height(150)
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(10)
|
||||
.Element(element =>
|
||||
@@ -778,83 +778,5 @@ namespace QuestPDF.Examples
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComplexLayout()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 225)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Stack(stack =>
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn().LabelCell("Label 1");
|
||||
|
||||
row.RelativeColumn(3).Grid(grid =>
|
||||
{
|
||||
grid.Columns(3);
|
||||
|
||||
grid.Item(2).LabelCell("Label 2");
|
||||
grid.Item().LabelCell("Label 3");
|
||||
|
||||
grid.Item(2).ValueCell().Text("Value 2");
|
||||
grid.Item().ValueCell().Text("Value 3");
|
||||
});
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn().ValueCell().Text("Value 1");
|
||||
|
||||
row.RelativeColumn(3).Grid(grid =>
|
||||
{
|
||||
grid.Columns(3);
|
||||
|
||||
grid.Item().LabelCell("Label 4");
|
||||
grid.Item(2).LabelCell("Label 5");
|
||||
|
||||
grid.Item().ValueCell().Text("Value 4");
|
||||
grid.Item(2).ValueCell().Text("Value 5");
|
||||
});
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn().LabelCell("Label 6");
|
||||
row.RelativeColumn().ValueCell().Text("Value 6");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DomainSpecificLanguage()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(600, 310)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Grid(grid =>
|
||||
{
|
||||
grid.Columns(10);
|
||||
|
||||
for(var i=1; i<=4; i++)
|
||||
{
|
||||
grid.Item(2).LabelCell(Placeholders.Label());
|
||||
grid.Item(3).ValueCell().Image(Placeholders.Image(200, 150));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,24 +12,23 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
return container
|
||||
.Border(1)
|
||||
.Background(dark ? Colors.Grey.Lighten2 : Colors.White)
|
||||
.Background(dark ? "#EEE" : "#FFF")
|
||||
.Padding(10);
|
||||
}
|
||||
|
||||
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text, TextStyle.Default.Medium());
|
||||
public static IContainer LabelCell(this IContainer container) => container.Cell(true);
|
||||
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
|
||||
}
|
||||
|
||||
public class FrameExample
|
||||
{
|
||||
[Test]
|
||||
public void Frame()
|
||||
public void Frame(IContainer container)
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(550, 400)
|
||||
.FileName()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -41,7 +40,7 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(2).LabelCell(Placeholders.Label());
|
||||
row.RelativeColumn(2).LabelCell().Text(Placeholders.Label());
|
||||
row.RelativeColumn(3).ValueCell().Text(Placeholders.Paragraph());
|
||||
});
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -12,9 +12,8 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 300)
|
||||
.PageSize(200, 150)
|
||||
.FileName()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Include="nunit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -20,9 +20,6 @@
|
||||
<None Update="quo-vadis.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="LibreBarcode39-Regular.ttf">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace QuestPDF.Examples
|
||||
text.DefaultTextStyle(normalStyle);
|
||||
|
||||
text.Span("Ten dokument został wygenerowany na podstawie książki w formacie TXT opublikowanej w serwisie ");
|
||||
text.ExternalLocation("wolnelektury.pl", "https://wolnelektury.pl/", normalStyle.Color(Colors.Blue.Medium).Underline());
|
||||
text.ExternalLocation("wolnelektury.pl", "https://wolnelektury.pl/", normalStyle.Color(Colors.Blue.Medium).Underlined());
|
||||
text.Span(". Dziękuję za wspieranie polskiego czytelnictwa!");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
@@ -11,140 +10,21 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
public class TextExamples
|
||||
{
|
||||
[Test]
|
||||
public void SimpleTextBlock()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 300)
|
||||
.FileName()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
text.Span("This is a normal text, followed by an ");
|
||||
text.Span("underlined red text.", TextStyle.Default.Size(20).Color(Colors.Red.Medium).Underline());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParagraphSpacing()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 300)
|
||||
.FileName()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.ParagraphSpacing(10);
|
||||
|
||||
foreach (var i in Enumerable.Range(1, 3))
|
||||
{
|
||||
text.Span($"Paragraph {i}: ", TextStyle.Default.SemiBold());
|
||||
text.Line(Placeholders.Paragraph());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CustomElement()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 200)
|
||||
.FileName()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
text.Span("This is a random image aligned to the baseline: ");
|
||||
|
||||
text.Element()
|
||||
.PaddingBottom(-6)
|
||||
.Height(24)
|
||||
.Width(48)
|
||||
.Image(Placeholders.Image);
|
||||
|
||||
text.Span(".");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextElements()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.PageSize(500, 400)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default);
|
||||
text.AlignLeft();
|
||||
text.ParagraphSpacing(10);
|
||||
|
||||
text.Line(Placeholders.LoremIpsum());
|
||||
|
||||
text.Span($"This is target text that should show up. {DateTime.UtcNow:T} > This is a short sentence that will be wrapped into second line hopefully, right? <", TextStyle.Default.Underline());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextStack()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
@@ -152,29 +32,78 @@ namespace QuestPDF.Examples
|
||||
text.AlignLeft();
|
||||
text.ParagraphSpacing(10);
|
||||
|
||||
foreach (var i in Enumerable.Range(1, 100))
|
||||
text.Line($"{i}: {Placeholders.Paragraph()}");
|
||||
text.Span(Placeholders.LoremIpsum());
|
||||
|
||||
text.NewLine();
|
||||
|
||||
text.Span("This text is a normal text, ");
|
||||
text.Span("this is a bold text, ", TextStyle.Default.Bold());
|
||||
text.Span("this is a red and underlined text, ", TextStyle.Default.Color(Colors.Red.Medium).Underlined());
|
||||
text.Span("and this is slightly bigger text.", TextStyle.Default.Size(16));
|
||||
|
||||
text.NewLine();
|
||||
|
||||
text.Span("The new text element also supports injecting custom content between words: ");
|
||||
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Span(".");
|
||||
|
||||
text.NewLine();
|
||||
|
||||
text.Span("This is page number ");
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" out of ");
|
||||
text.TotalPages();
|
||||
|
||||
text.NewLine();
|
||||
|
||||
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
|
||||
|
||||
text.NewLine();
|
||||
|
||||
/*
|
||||
text.Span("Let's start with bold text. ", TextStyle.Default.Bold().BackgroundColor(Colors.Grey.Lighten3).Size(16));
|
||||
text.Span("Then something bigger. ", TextStyle.Default.Size(28).Color(Colors.DeepOrange.Darken2).BackgroundColor(Colors.Yellow.Lighten3).Underlined());
|
||||
text.Span("And tiny \r\n teeny-tiny. ", TextStyle.Default.Size(6));
|
||||
text.Span("Stroked text also works fine. ", TextStyle.Default.Size(14).Stroked().BackgroundColor(Colors.Grey.Lighten4));
|
||||
|
||||
text.Span("0123456789-0123456789-0123456789-0123456789-0123456789-0123456789-0123456789", TextStyle.Default.Size(18));
|
||||
|
||||
text.NewLine();
|
||||
text.NewLine();
|
||||
text.NewLine();
|
||||
text.Span("Is it time for lorem ipsum? ", TextStyle.Default.Size(12).Underlined().BackgroundColor(Colors.Grey.Lighten3));
|
||||
text.Span(Placeholders.LoremIpsum(), TextStyle.Default.Size(12));
|
||||
|
||||
text.Span("Before element - ");
|
||||
text.Element().PaddingBottom(-10).Background(Colors.Red.Lighten4).Height(20).PaddingHorizontal(5).AlignMiddle().Text("Text inside text", TextStyle.Default.Size(8));
|
||||
text.Span(" - end of element.");
|
||||
|
||||
text.NewLine();
|
||||
// text.Span("And now some colors: ", TextStyle.Default.Size(16).Color(Colors.Green.Medium));
|
||||
//
|
||||
// foreach (var i in Enumerable.Range(1, 100))
|
||||
// {
|
||||
// text.Span($"{i}: {Placeholders.Sentence()} ", TextStyle.Default.Size(12 + i / 5).LineHeight(2.75f - i / 50f).Color(Placeholders.Color()).BackgroundColor(Placeholders.BackgroundColor()));
|
||||
// }*/
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SpaceIssue()
|
||||
public void PageNumber()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.PageSize(500, 400)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
@@ -182,94 +111,10 @@ namespace QuestPDF.Examples
|
||||
text.AlignLeft();
|
||||
text.ParagraphSpacing(10);
|
||||
|
||||
text.Span(Placeholders.LoremIpsum());
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span("This text is a normal text, ");
|
||||
text.Span("this is a bold text, ", TextStyle.Default.Bold());
|
||||
text.Span("this is a red and underlined text, ", TextStyle.Default.Color(Colors.Red.Medium).Underline());
|
||||
text.Span("and this is slightly bigger text.", TextStyle.Default.Size(16));
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span("The new text element also supports injecting custom content between words: ");
|
||||
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Span(".");
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span("This is page number ");
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" out of ");
|
||||
text.TotalPages();
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span(Placeholders.Paragraphs());
|
||||
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span(Placeholders.Paragraphs(), TextStyle.Default.Italic());
|
||||
|
||||
text.Line("This is target text that does not show up. " + Placeholders.Paragraph());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HugeList()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default);
|
||||
text.AlignLeft();
|
||||
text.ParagraphSpacing(10);
|
||||
|
||||
text.Span("This text is a normal text, ");
|
||||
text.Span("this is a bold text, ", TextStyle.Default.Bold());
|
||||
text.Span("this is a red and underlined text, ", TextStyle.Default.Color(Colors.Red.Medium).Underline());
|
||||
text.Span("and this is slightly bigger text.", TextStyle.Default.Size(16));
|
||||
|
||||
text.Span("The new text element also supports injecting custom content between words: ");
|
||||
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Span(".");
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
foreach (var i in Enumerable.Range(1, 100))
|
||||
{
|
||||
text.Line($"{i}: {Placeholders.Paragraph()}");
|
||||
|
||||
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
|
||||
|
||||
text.Span("This is page number ");
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" out of ");
|
||||
text.TotalPages();
|
||||
|
||||
text.EmptyLine();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Include="DeepCloner" Version="0.10.2" />
|
||||
<PackageReference Include="nunit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -10,7 +10,11 @@ namespace QuestPDF.UnitTests
|
||||
public class AlignmentTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<Alignment>();
|
||||
public void Measure_ShouldHandleNullChild() => new Alignment().MeasureWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Draw_ShouldHandleNullChild() => new Alignment().DrawWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Draw_HorizontalCenter_VerticalCenter()
|
||||
{
|
||||
|
||||
@@ -9,6 +9,12 @@ namespace QuestPDF.UnitTests
|
||||
[TestFixture]
|
||||
public class AspectRatioTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure_ShouldHandleNullChild() => new AspectRatio().MeasureWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Draw_ShouldHandleNullChild() => new AspectRatio().DrawWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Measure_FitWidth_EnoughSpace_FullRender()
|
||||
{
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace QuestPDF.UnitTests
|
||||
public class BackgroundTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<Background>();
|
||||
|
||||
public void Measure_ShouldHandleNullChild() => new Background().MeasureWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Draw_ShouldHandleNullChild()
|
||||
{
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace QuestPDF.UnitTests
|
||||
public class BorderTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<Border>();
|
||||
|
||||
public void Measure_ShouldHandleNullChild() => new Border().MeasureWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void ComponentShouldNotAffectLayout()
|
||||
{
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class BoxTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<Box>();
|
||||
|
||||
[Test]
|
||||
public void Draw_Wrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Box
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: new Wrap())
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_PartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Box
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: new PartialRender(200, 100))
|
||||
.ExpectChildDraw(new Size(200, 100))
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_FullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Box
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(500, 400))
|
||||
.ExpectChildMeasure(expectedInput: new Size(500, 400), returns: new FullRender(300, 200))
|
||||
.ExpectChildDraw(new Size(300, 200))
|
||||
.CheckDrawResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,12 @@ namespace QuestPDF.UnitTests
|
||||
[TestFixture]
|
||||
public class ConstrainedTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure_ShouldHandleNullChild() => new Constrained().MeasureWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Draw_ShouldHandleNullChild() => new Constrained().DrawWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Measure_MinHeight_ExpectWrap()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class DebugTests
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -78,8 +78,5 @@ namespace QuestPDF.UnitTests
|
||||
.ExpectChildMeasure(new Size(400, 300), new FullRender(300, 250))
|
||||
.CheckMeasureResult(new FullRender(300, 250));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw() => SimpleContainerTests.Draw<EnsureSpace>();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,12 @@ namespace QuestPDF.UnitTests
|
||||
[TestFixture]
|
||||
public class ExtendTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure_ShouldHandleNullChild() => new Extend().MeasureWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Draw_ShouldHandleNullChild() => new Extend().DrawWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenChildReturnsWrap()
|
||||
{
|
||||
@@ -81,8 +87,5 @@ namespace QuestPDF.UnitTests
|
||||
.ExpectChildMeasure(new Size(400, 200), new FullRender(100, 100))
|
||||
.CheckMeasureResult(new FullRender(100, 200));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw() => SimpleContainerTests.Draw<Extend>();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ExternalLinkTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<ExternalLink>();
|
||||
|
||||
// TODO: consider tests for the Draw method
|
||||
}
|
||||
}
|
||||
@@ -39,30 +39,30 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Stack(stack =>
|
||||
expected.Container().Stack(stack =>
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(6).Element(childA);
|
||||
row.RelativeColumn(4).Element(childB);
|
||||
row.RelativeColumn(6).Container().Element(childA);
|
||||
row.RelativeColumn(4).Container().Element(childB);
|
||||
row.RelativeColumn(2);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(4).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(4).Container().Element(childC);
|
||||
row.RelativeColumn(2).Container().Element(childD);
|
||||
row.RelativeColumn(6);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(8).Element(childE);
|
||||
row.RelativeColumn(8).Container().Element(childE);
|
||||
row.RelativeColumn(4);
|
||||
});
|
||||
});
|
||||
|
||||
TestPlan.CompareOperations(structure, expected);
|
||||
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -93,33 +93,33 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Stack(stack =>
|
||||
expected.Container().Stack(stack =>
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(1);
|
||||
row.RelativeColumn(6).Element(childA);
|
||||
row.RelativeColumn(4).Element(childB);
|
||||
row.RelativeColumn(6).Container().Element(childA);
|
||||
row.RelativeColumn(4).Container().Element(childB);
|
||||
row.RelativeColumn(1);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeColumn(4).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(4).Container().Element(childC);
|
||||
row.RelativeColumn(2).Container().Element(childD);
|
||||
row.RelativeColumn(3);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeColumn(8).Element(childE);
|
||||
row.RelativeColumn(8).Container().Element(childE);
|
||||
row.RelativeColumn(2);
|
||||
});
|
||||
});
|
||||
|
||||
TestPlan.CompareOperations(structure, expected);
|
||||
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -150,30 +150,30 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Stack(stack =>
|
||||
expected.Container().Stack(stack =>
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeColumn(6).Element(childA);
|
||||
row.RelativeColumn(4).Element(childB);
|
||||
row.RelativeColumn(6).Container().Element(childA);
|
||||
row.RelativeColumn(4).Container().Element(childB);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(6);
|
||||
row.RelativeColumn(4).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(4).Container().Element(childC);
|
||||
row.RelativeColumn(2).Container().Element(childD);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(4);
|
||||
row.RelativeColumn(8).Element(childE);
|
||||
row.RelativeColumn(8).Container().Element(childE);
|
||||
});
|
||||
});
|
||||
|
||||
TestPlan.CompareOperations(structure, expected);
|
||||
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties());
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -210,7 +210,7 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Stack(stack =>
|
||||
expected.Container().Stack(stack =>
|
||||
{
|
||||
stack.Spacing(20);
|
||||
|
||||
@@ -219,8 +219,8 @@ namespace QuestPDF.UnitTests
|
||||
row.Spacing(30);
|
||||
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeColumn(5).Element(childA);
|
||||
row.RelativeColumn(5).Element(childB);
|
||||
row.RelativeColumn(5).Container().Element(childA);
|
||||
row.RelativeColumn(5).Container().Element(childB);
|
||||
row.RelativeColumn(3);
|
||||
});
|
||||
|
||||
@@ -229,7 +229,7 @@ namespace QuestPDF.UnitTests
|
||||
row.Spacing(30);
|
||||
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeColumn(10).Element(childC);
|
||||
row.RelativeColumn(10).Container().Element(childC);
|
||||
row.RelativeColumn(3);
|
||||
});
|
||||
|
||||
@@ -238,12 +238,12 @@ namespace QuestPDF.UnitTests
|
||||
row.Spacing(30);
|
||||
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeColumn(12).Element(childD);
|
||||
row.RelativeColumn(12).Container().Element(childD);
|
||||
row.RelativeColumn(2);
|
||||
});
|
||||
});
|
||||
|
||||
TestPlan.CompareOperations(structure, expected);
|
||||
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties().AllowingInfiniteRecursion());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
public static class Helpers
|
||||
{
|
||||
public static Random Random = new Random();
|
||||
|
||||
public static Size RandomSize => new Size(Random.Next(200, 400), Random.Next(100, 200));
|
||||
|
||||
public static void ShouldEqual(this IEnumerable<OperationBase> commands, IEnumerable<OperationBase> expected)
|
||||
{
|
||||
commands.Should().HaveSameCount(expected);
|
||||
|
||||
commands
|
||||
.Zip(expected)
|
||||
.ToList()
|
||||
.ForEach(x =>
|
||||
{
|
||||
x.First.Should().BeOfType(x.Second.GetType());
|
||||
x.First.Should().BeEquivalentTo(x.Second, y => y.RespectingRuntimeTypes());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,12 @@ namespace QuestPDF.UnitTests
|
||||
[TestFixture]
|
||||
public class ImageTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure_ShouldHandleNullChild() => new AspectRatio().MeasureWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Draw_ShouldHandleNullChild() => new AspectRatio().DrawWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Measure_TakesAvailableSpaceRegardlessOfSize()
|
||||
{
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class InternalLinkTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<InternalLink>();
|
||||
|
||||
// TODO: consider tests for the Draw method
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class InternalLocationTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<InternalLink>();
|
||||
|
||||
// TODO: consider tests for the Draw method
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class LayersTests
|
||||
{
|
||||
private const string BackgroundLayer = "background";
|
||||
private const string MainLayer = "main";
|
||||
private const string ForegroundLayer = "foreground";
|
||||
|
||||
private static Layers GetLayers(TestPlan x)
|
||||
{
|
||||
return new Layers
|
||||
{
|
||||
Children = new List<Layer>
|
||||
{
|
||||
new Layer
|
||||
{
|
||||
Child = x.CreateChild(BackgroundLayer)
|
||||
},
|
||||
new Layer
|
||||
{
|
||||
Child = x.CreateChild(MainLayer),
|
||||
IsPrimary = true
|
||||
},
|
||||
new Layer
|
||||
{
|
||||
Child = x.CreateChild(ForegroundLayer)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#region measure
|
||||
|
||||
[Test]
|
||||
public void Measure_Wrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(GetLayers)
|
||||
.MeasureElement(new Size(800, 600))
|
||||
.ExpectChildMeasure(MainLayer, new Size(800, 600), new Wrap())
|
||||
.CheckMeasureResult(new Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_PartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(GetLayers)
|
||||
.MeasureElement(new Size(800, 600))
|
||||
.ExpectChildMeasure(MainLayer, new Size(800, 600), new PartialRender(700, 500))
|
||||
.CheckMeasureResult(new PartialRender(700, 500));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_FullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(GetLayers)
|
||||
.MeasureElement(new Size(800, 600))
|
||||
.ExpectChildMeasure(MainLayer, new Size(800, 600), new FullRender(500, 400))
|
||||
.CheckMeasureResult(new FullRender(500, 400));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region draw
|
||||
|
||||
[Test]
|
||||
public void Draw_Simple()
|
||||
{
|
||||
TestPlan
|
||||
.For(GetLayers)
|
||||
.MeasureElement(new Size(800, 600))
|
||||
|
||||
.ExpectChildMeasure(BackgroundLayer, new Size(800, 600), new FullRender(100, 200))
|
||||
.ExpectChildMeasure(MainLayer, new Size(800, 600), new PartialRender(200, 300))
|
||||
.ExpectChildMeasure(ForegroundLayer, new Size(800, 600), new FullRender(300, 400))
|
||||
|
||||
|
||||
.ExpectChildDraw(BackgroundLayer, new Size(800, 600))
|
||||
.ExpectChildDraw(MainLayer, new Size(800, 600))
|
||||
.ExpectChildDraw(ForegroundLayer, new Size(800, 600))
|
||||
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_WhenSecondaryLayerReturnsWrap_SkipThatLayer_1()
|
||||
{
|
||||
TestPlan
|
||||
.For(GetLayers)
|
||||
.MeasureElement(new Size(800, 600))
|
||||
|
||||
.ExpectChildMeasure(BackgroundLayer, new Size(800, 600), new PartialRender(100, 200))
|
||||
.ExpectChildMeasure(MainLayer, new Size(800, 600), new PartialRender(200, 300))
|
||||
.ExpectChildMeasure(ForegroundLayer, new Size(800, 600), new Wrap())
|
||||
|
||||
.ExpectChildDraw(BackgroundLayer, new Size(800, 600))
|
||||
.ExpectChildDraw(MainLayer, new Size(800, 600))
|
||||
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_WhenSecondaryLayerReturnsWrap_SkipThatLayer_2()
|
||||
{
|
||||
TestPlan
|
||||
.For(GetLayers)
|
||||
.MeasureElement(new Size(800, 600))
|
||||
|
||||
.ExpectChildMeasure(BackgroundLayer, new Size(800, 600), new Wrap())
|
||||
.ExpectChildMeasure(MainLayer, new Size(800, 600), new PartialRender(200, 300))
|
||||
.ExpectChildMeasure(ForegroundLayer, new Size(800, 600), new PartialRender(300, 400))
|
||||
|
||||
.ExpectChildDraw(MainLayer, new Size(800, 600))
|
||||
.ExpectChildDraw(ForegroundLayer, new Size(800, 600))
|
||||
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,12 @@ namespace QuestPDF.UnitTests
|
||||
[TestFixture]
|
||||
public class PaddingTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure_ShouldHandleNullChild() => new Padding().MeasureWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Draw_ShouldHandleNullChild() => new Padding().DrawWithoutChild();
|
||||
|
||||
private Padding GetPadding(TestPlan plan)
|
||||
{
|
||||
return new Padding()
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class PageNumberTests
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.1.0" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="nunit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class RotateTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<Rotate>();
|
||||
|
||||
[Test]
|
||||
public void Draw()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Rotate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
Angle = 123
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectCanvasRotate(123)
|
||||
.ExpectChildDraw(new Size(400, 300))
|
||||
.ExpectCanvasRotate(-123)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
@@ -110,158 +109,8 @@ namespace QuestPDF.UnitTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region Structure
|
||||
|
||||
[Test]
|
||||
public void Structure_RelativeColumnsHandling()
|
||||
{
|
||||
// arrange
|
||||
var childA = TestPlan.CreateUniqueElement();
|
||||
var childB = TestPlan.CreateUniqueElement();
|
||||
var childC = TestPlan.CreateUniqueElement();
|
||||
var childD = TestPlan.CreateUniqueElement();
|
||||
var childE = TestPlan.CreateUniqueElement();
|
||||
|
||||
const int spacing = 25;
|
||||
var availableSpace = new Size(1100, 400);
|
||||
|
||||
// act
|
||||
var value = new Container();
|
||||
|
||||
value.Row(row =>
|
||||
{
|
||||
row.Spacing(spacing);
|
||||
|
||||
row.ConstantColumn(150).Element(childA);
|
||||
row.ConstantColumn(250).Element(childB);
|
||||
row.RelativeColumn(1).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(3).Element(childE);
|
||||
});
|
||||
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Row(row =>
|
||||
{
|
||||
row.Spacing(spacing);
|
||||
|
||||
row.ConstantColumn(150).Element(childA);
|
||||
row.ConstantColumn(250).Element(childB);
|
||||
row.ConstantColumn(100).Element(childC);
|
||||
row.ConstantColumn(200).Element(childD);
|
||||
row.ConstantColumn(300).Element(childE);
|
||||
});
|
||||
|
||||
TestPlan.CompareOperations(value, expected, availableSpace);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Structure_Tree()
|
||||
{
|
||||
// arrange
|
||||
var childA = TestPlan.CreateUniqueElement();
|
||||
var childB = TestPlan.CreateUniqueElement();
|
||||
var childC = TestPlan.CreateUniqueElement();
|
||||
var childD = TestPlan.CreateUniqueElement();
|
||||
var childE = TestPlan.CreateUniqueElement();
|
||||
|
||||
const int spacing = 25;
|
||||
var availableSpace = new Size(1200, 400);
|
||||
|
||||
// act
|
||||
var value = new Container();
|
||||
|
||||
value.Row(row =>
|
||||
{
|
||||
row.Spacing(spacing);
|
||||
|
||||
row.ConstantColumn(150).Element(childA);
|
||||
row.ConstantColumn(200).Element(childB);
|
||||
row.ConstantColumn(250).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(3).Element(childE);
|
||||
});
|
||||
|
||||
// assert
|
||||
var expected = new SimpleRow
|
||||
{
|
||||
Left = new SimpleRow
|
||||
{
|
||||
Left = new SimpleRow
|
||||
{
|
||||
Left = new Constrained
|
||||
{
|
||||
MinWidth = 150,
|
||||
MaxWidth = 150,
|
||||
Child = childA
|
||||
},
|
||||
Right = new Constrained
|
||||
{
|
||||
MinWidth = 25,
|
||||
MaxWidth = 25
|
||||
}
|
||||
},
|
||||
Right = new SimpleRow
|
||||
{
|
||||
Left = new Constrained
|
||||
{
|
||||
MinWidth = 200,
|
||||
MaxWidth = 200,
|
||||
Child = childB
|
||||
},
|
||||
Right = new Constrained
|
||||
{
|
||||
MinWidth = 25,
|
||||
MaxWidth = 25
|
||||
}
|
||||
}
|
||||
},
|
||||
Right = new SimpleRow
|
||||
{
|
||||
Left = new SimpleRow
|
||||
{
|
||||
Left = new Constrained
|
||||
{
|
||||
MinWidth = 250,
|
||||
MaxWidth = 250,
|
||||
Child = childC
|
||||
},
|
||||
Right = new Constrained
|
||||
{
|
||||
MinWidth = 25,
|
||||
MaxWidth = 25
|
||||
}
|
||||
},
|
||||
Right = new SimpleRow
|
||||
{
|
||||
Left = new Constrained
|
||||
{
|
||||
MinWidth = 200,
|
||||
MaxWidth = 200,
|
||||
Child = childD
|
||||
},
|
||||
Right = new SimpleRow
|
||||
{
|
||||
Left = new Constrained
|
||||
{
|
||||
MinWidth = 25,
|
||||
MaxWidth = 25
|
||||
},
|
||||
Right = new Constrained
|
||||
{
|
||||
MinWidth = 300,
|
||||
MaxWidth = 300,
|
||||
Child = childE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TestPlan.CompareOperations(value, expected, availableSpace);
|
||||
}
|
||||
|
||||
#endregion
|
||||
// TODO: add tests for the spacing property
|
||||
// TODO: add tests for the tree builder method
|
||||
// TODO: add tests for relative column
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ScaleTests
|
||||
{
|
||||
#region measure
|
||||
|
||||
[Test]
|
||||
public void Measure_Wrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Scale
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ScaleX = 3,
|
||||
ScaleY = 2
|
||||
})
|
||||
.MeasureElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(new Size(300, 400), new Wrap())
|
||||
.CheckMeasureResult(new Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_PartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Scale
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ScaleX = 3,
|
||||
ScaleY = 2
|
||||
})
|
||||
.MeasureElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(new Size(300, 400), new PartialRender(200, 350))
|
||||
.CheckMeasureResult(new PartialRender(600, 700));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_FullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Scale
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ScaleX = 3,
|
||||
ScaleY = 2
|
||||
})
|
||||
.MeasureElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(new Size(300, 400), new FullRender(250, 300))
|
||||
.CheckMeasureResult(new FullRender(750, 600));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_NegativeScaleX()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Scale
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ScaleX = -2,
|
||||
ScaleY = 1
|
||||
})
|
||||
.MeasureElement(new Size(800, 600))
|
||||
.ExpectChildMeasure(new Size(400, 600), new FullRender(300, 500))
|
||||
.CheckMeasureResult(new FullRender(600, 500));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_NegativeScaleY()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Scale
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ScaleX = 1,
|
||||
ScaleY = -3
|
||||
})
|
||||
.MeasureElement(new Size(800, 600))
|
||||
.ExpectChildMeasure(new Size(800, 200), new FullRender(800, 100))
|
||||
.CheckMeasureResult(new FullRender(800, 300));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region draw
|
||||
|
||||
[Test]
|
||||
public void Draw_Simple()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Scale
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ScaleX = 3,
|
||||
ScaleY = 2
|
||||
})
|
||||
.DrawElement(new Size(900, 800))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectCanvasScale(3, 2)
|
||||
.ExpectChildDraw(new Size(300, 400))
|
||||
.ExpectCanvasScale(1/3f, 1/2f)
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_NegativeScaleX()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Scale
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ScaleX = -3,
|
||||
ScaleY = 2
|
||||
})
|
||||
.DrawElement(new Size(900, 800))
|
||||
.ExpectCanvasTranslate(900, 0)
|
||||
.ExpectCanvasScale(-3, 2)
|
||||
.ExpectChildDraw(new Size(300, 400))
|
||||
.ExpectCanvasScale(-1/3f, 1/2f)
|
||||
.ExpectCanvasTranslate(-900, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_NegativeScaleY()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Scale
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ScaleX = 3,
|
||||
ScaleY = -2
|
||||
})
|
||||
.DrawElement(new Size(900, 800))
|
||||
.ExpectCanvasTranslate(0, 800)
|
||||
.ExpectCanvasScale(3, -2)
|
||||
.ExpectChildDraw(new Size(300, 400))
|
||||
.ExpectCanvasScale(1/3f, -1/2f)
|
||||
.ExpectCanvasTranslate(0, -800)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -47,8 +47,5 @@ namespace QuestPDF.UnitTests
|
||||
.ExpectChildMeasure(new Size(400, 300), new FullRender(300, 200))
|
||||
.CheckMeasureResult(new FullRender(300, 200));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw() => SimpleContainerTests.Draw<ShowEntire>();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using NUnit.Framework;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
@@ -10,7 +11,33 @@ namespace QuestPDF.UnitTests
|
||||
public class ShowOnceTest
|
||||
{
|
||||
[Test]
|
||||
public void Draw()
|
||||
public void Measure_ShouldHandleNullChild() => new ShowOnce().MeasureWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void Draw_ShouldHandleNullChild() => new ShowOnce().DrawWithoutChild();
|
||||
|
||||
[Test]
|
||||
public void ShouldRenderOnce_WhenRenderingCalledMultipleTimes()
|
||||
{
|
||||
var child = new Mock<Element>();
|
||||
|
||||
child
|
||||
.Setup(x => x.Measure(It.IsAny<Size>()))
|
||||
.Returns(() => new FullRender(Size.Zero));
|
||||
|
||||
var element = new ShowOnce()
|
||||
{
|
||||
Child = child.Object
|
||||
};
|
||||
|
||||
element.Draw(Size.Zero);
|
||||
element.Draw(Size.Zero);
|
||||
|
||||
child.Verify(x => x.Draw(It.IsAny<Size>()), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_HorizontalRight_VerticalTop()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new ShowOnce()
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class SimpleRotateTests
|
||||
{
|
||||
#region measure
|
||||
|
||||
[Test]
|
||||
public void Measure_Wrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new SimpleRotate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
TurnCount = 0
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(new Size(400, 300), new Wrap())
|
||||
.CheckMeasureResult(new Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_PartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new SimpleRotate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
TurnCount = 0
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(new Size(400, 300), new PartialRender(300, 200))
|
||||
.CheckMeasureResult(new PartialRender(300, 200));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_RotateRight()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new SimpleRotate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
TurnCount = 1
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(new Size(300, 400), new FullRender(200, 300))
|
||||
.CheckMeasureResult(new FullRender(300, 200));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_RotateFlip()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new SimpleRotate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
TurnCount = 2
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(new Size(400, 300), new FullRender(200, 100))
|
||||
.CheckMeasureResult(new FullRender(200, 100));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_RotateLeft()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new SimpleRotate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
TurnCount = 3 // or -1
|
||||
})
|
||||
.MeasureElement(new Size(500, 400))
|
||||
.ExpectChildMeasure(new Size(400, 500), new FullRender(300, 350))
|
||||
.CheckMeasureResult(new FullRender(350, 300));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region draw
|
||||
|
||||
[Test]
|
||||
public void Draw_Simple()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new SimpleRotate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
TurnCount = 0
|
||||
})
|
||||
.DrawElement(new Size(640, 480))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectCanvasRotate(0)
|
||||
.ExpectChildDraw(new Size(640, 480))
|
||||
.ExpectCanvasRotate(0)
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_RotateRight()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new SimpleRotate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
TurnCount = 1
|
||||
})
|
||||
.DrawElement(new Size(640, 480))
|
||||
.ExpectCanvasTranslate(640, 0)
|
||||
.ExpectCanvasRotate(90)
|
||||
.ExpectChildDraw(new Size(480, 640))
|
||||
.ExpectCanvasRotate(-90)
|
||||
.ExpectCanvasTranslate(-640, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_RotateFlip()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new SimpleRotate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
TurnCount = 2
|
||||
})
|
||||
.DrawElement(new Size(640, 480))
|
||||
.ExpectCanvasTranslate(640, 480)
|
||||
.ExpectCanvasRotate(180)
|
||||
.ExpectChildDraw(new Size(640, 480))
|
||||
.ExpectCanvasRotate(-180)
|
||||
.ExpectCanvasTranslate(-640, -480)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_RotateLeft()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new SimpleRotate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
TurnCount = 3 // or -1
|
||||
})
|
||||
.DrawElement(new Size(640, 480))
|
||||
.ExpectCanvasTranslate(0, 480)
|
||||
.ExpectCanvasRotate(270)
|
||||
.ExpectChildDraw(new Size(480, 640))
|
||||
.ExpectCanvasRotate(-270)
|
||||
.ExpectCanvasTranslate(0, -480)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
@@ -231,81 +229,7 @@ namespace QuestPDF.UnitTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region Structure
|
||||
|
||||
[Test]
|
||||
public void Structure_Simple()
|
||||
{
|
||||
// arrange
|
||||
var childA = TestPlan.CreateUniqueElement();
|
||||
var childB = TestPlan.CreateUniqueElement();
|
||||
var childC = TestPlan.CreateUniqueElement();
|
||||
var childD = TestPlan.CreateUniqueElement();
|
||||
var childE = TestPlan.CreateUniqueElement();
|
||||
|
||||
const int spacing = 20;
|
||||
|
||||
// act
|
||||
var structure = new Container();
|
||||
|
||||
structure.Stack(stack =>
|
||||
{
|
||||
stack.Spacing(spacing);
|
||||
|
||||
stack.Item().Element(childA);
|
||||
stack.Item().Element(childB);
|
||||
stack.Item().Element(childC);
|
||||
stack.Item().Element(childD);
|
||||
stack.Item().Element(childE);
|
||||
});
|
||||
|
||||
// assert
|
||||
var expected = new Padding
|
||||
{
|
||||
Bottom = -spacing,
|
||||
|
||||
Child = new SimpleStack
|
||||
{
|
||||
First = new SimpleStack
|
||||
{
|
||||
First = new Padding
|
||||
{
|
||||
Bottom = spacing,
|
||||
Child = childA
|
||||
},
|
||||
Second = new Padding
|
||||
{
|
||||
Bottom = spacing,
|
||||
Child = childB
|
||||
}
|
||||
},
|
||||
Second = new SimpleStack
|
||||
{
|
||||
First = new Padding
|
||||
{
|
||||
Bottom = spacing,
|
||||
Child = childC
|
||||
},
|
||||
Second = new SimpleStack
|
||||
{
|
||||
First = new Padding
|
||||
{
|
||||
Bottom = spacing,
|
||||
Child = childD
|
||||
},
|
||||
Second = new Padding
|
||||
{
|
||||
Bottom = spacing,
|
||||
Child = childE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TestPlan.CompareOperations(structure, expected);
|
||||
}
|
||||
|
||||
#endregion
|
||||
// TODO: add tests for the spacing property
|
||||
// TODO: add tests for the tree builder method
|
||||
}
|
||||
}
|
||||
+25
-9
@@ -4,25 +4,41 @@ using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine
|
||||
{
|
||||
internal class MockCanvas : ICanvas
|
||||
internal class CanvasMock : ICanvas
|
||||
{
|
||||
public Action<Position> TranslateFunc { get; set; }
|
||||
public Action<float> RotateFunc { get; set; }
|
||||
public Action<float, float> ScaleFunc { get; set; }
|
||||
public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
|
||||
public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
|
||||
public Action<Position, Size, string> DrawRectFunc { get; set; }
|
||||
|
||||
public void Translate(Position vector) => TranslateFunc(vector);
|
||||
public void Rotate(float angle) => RotateFunc(angle);
|
||||
public void Scale(float scaleX, float scaleY) => ScaleFunc(scaleX, scaleY);
|
||||
|
||||
public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
|
||||
public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
|
||||
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
|
||||
|
||||
public void DrawExternalLink(string url, Size size) => throw new NotImplementedException();
|
||||
public void DrawLocationLink(string locationName, Size size) => throw new NotImplementedException();
|
||||
public void DrawLocation(string locationName) => throw new NotImplementedException();
|
||||
public void DrawExternalLink(string url, Size size)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DrawLocationLink(string locationName, Size size)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DrawLocation(string locationName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DrawLink(string url, Size size)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Size MeasureText(string text, TextStyle style)
|
||||
{
|
||||
return new Size(text.Length * style.Size, style.Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine.Operations;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine
|
||||
{
|
||||
internal class OperationRecordingCanvas : ICanvas
|
||||
{
|
||||
public ICollection<OperationBase> Operations { get; } = new List<OperationBase>();
|
||||
|
||||
public void Translate(Position vector) => Operations.Add(new CanvasTranslateOperation(vector));
|
||||
public void Rotate(float angle) => Operations.Add(new CanvasRotateOperation(angle));
|
||||
public void Scale(float scaleX, float scaleY) => Operations.Add(new CanvasScaleOperation(scaleX, scaleY));
|
||||
|
||||
public void DrawRectangle(Position vector, Size size, string color) => Operations.Add(new CanvasDrawRectangleOperation(vector, size, color));
|
||||
public void DrawText(string text, Position position, TextStyle style) => Operations.Add(new CanvasDrawTextOperation(text, position, style));
|
||||
public void DrawImage(SKImage image, Position position, Size size) => Operations.Add(new CanvasDrawImageOperation(position, size));
|
||||
|
||||
public void DrawExternalLink(string url, Size size) => throw new NotImplementedException();
|
||||
public void DrawLocationLink(string locationName, Size size) => throw new NotImplementedException();
|
||||
public void DrawLocation(string locationName) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class CanvasDrawImageOperation : OperationBase
|
||||
internal class CanvasDrawImageOperationBase : OperationBase
|
||||
{
|
||||
public Position Position { get; }
|
||||
public Size Size { get; }
|
||||
|
||||
public CanvasDrawImageOperation(Position position, Size size)
|
||||
public CanvasDrawImageOperationBase(Position position, Size size)
|
||||
{
|
||||
Position = position;
|
||||
Size = size;
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class CanvasDrawRectangleOperation : OperationBase
|
||||
internal class CanvasDrawRectangleOperationBase : OperationBase
|
||||
{
|
||||
public Position Position { get; }
|
||||
public Size Size { get; }
|
||||
public string Color { get; }
|
||||
|
||||
public CanvasDrawRectangleOperation(Position position, Size size, string color)
|
||||
public CanvasDrawRectangleOperationBase(Position position, Size size, string color)
|
||||
{
|
||||
Position = position;
|
||||
Size = size;
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class CanvasDrawTextOperation : OperationBase
|
||||
internal class CanvasDrawTextOperationBase : OperationBase
|
||||
{
|
||||
public string Text { get; }
|
||||
public Position Position { get; }
|
||||
public TextStyle Style { get; }
|
||||
|
||||
public CanvasDrawTextOperation(string text, Position position, TextStyle style)
|
||||
public CanvasDrawTextOperationBase(string text, Position position, TextStyle style)
|
||||
{
|
||||
Text = text;
|
||||
Position = position;
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
public class CanvasRotateOperation : OperationBase
|
||||
{
|
||||
public float Angle { get; }
|
||||
|
||||
public CanvasRotateOperation(float angle)
|
||||
{
|
||||
Angle = angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
public class CanvasScaleOperation : OperationBase
|
||||
{
|
||||
public float ScaleX { get; }
|
||||
public float ScaleY { get; }
|
||||
|
||||
public CanvasScaleOperation(float scaleX, float scaleY)
|
||||
{
|
||||
ScaleX = scaleX;
|
||||
ScaleY = scaleY;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class CanvasTranslateOperation : OperationBase
|
||||
internal class CanvasTranslateOperationBase : OperationBase
|
||||
{
|
||||
public Position Position { get; }
|
||||
|
||||
public CanvasTranslateOperation(Position position)
|
||||
public CanvasTranslateOperationBase(Position position)
|
||||
{
|
||||
Position = position;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
public class ChildDrawOperation : OperationBase
|
||||
public class ChildDrawOperationBase : OperationBase
|
||||
{
|
||||
public string ChildId { get; }
|
||||
public Size Input { get; }
|
||||
|
||||
public ChildDrawOperation(string childId, Size input)
|
||||
public ChildDrawOperationBase(string childId, Size input)
|
||||
{
|
||||
ChildId = childId;
|
||||
Input = input;
|
||||
|
||||
@@ -3,13 +3,13 @@ using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class ChildMeasureOperation : OperationBase
|
||||
internal class ChildMeasureOperationBase : OperationBase
|
||||
{
|
||||
public string ChildId { get; }
|
||||
public Size Input { get; }
|
||||
public ISpacePlan Output { get; }
|
||||
|
||||
public ChildMeasureOperation(string childId, Size input, ISpacePlan output)
|
||||
public ChildMeasureOperationBase(string childId, Size input, ISpacePlan output)
|
||||
{
|
||||
ChildId = childId;
|
||||
Input = input;
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
public class ElementMeasureOperation : OperationBase
|
||||
public class ElementMeasureOperationBase : OperationBase
|
||||
{
|
||||
public ElementMeasureOperation(Size input)
|
||||
public ElementMeasureOperationBase(Size input)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine
|
||||
{
|
||||
internal static class SimpleContainerTests
|
||||
{
|
||||
#region measure
|
||||
|
||||
public static void Measure<TElement>() where TElement : Element, IContainer, new()
|
||||
{
|
||||
Measure_Wrap<TElement>();
|
||||
Measure_PartialRender<TElement>();
|
||||
Measure_FullRender<TElement>();
|
||||
}
|
||||
|
||||
private static void Measure_Wrap<TElement>() where TElement : Element, IContainer, new()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new TElement
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(new Size(400, 300), new Wrap())
|
||||
.CheckMeasureResult(new Wrap());
|
||||
}
|
||||
|
||||
private static void Measure_PartialRender<TElement>() where TElement : Element, IContainer, new()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new TElement
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(new Size(400, 300), new PartialRender(200, 100))
|
||||
.CheckMeasureResult(new PartialRender(200, 100));
|
||||
}
|
||||
|
||||
private static void Measure_FullRender<TElement>() where TElement : Element, IContainer, new()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new TElement
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(new Size(400, 300), new FullRender(250, 150))
|
||||
.CheckMeasureResult(new FullRender(250, 150));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static void Draw<TElement>() where TElement : Element, IContainer, new()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new TElement
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.DrawElement(new Size(1200, 900))
|
||||
.ExpectChildDraw(new Size(1200, 900))
|
||||
.CheckDrawResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine
|
||||
{
|
||||
public static class SingleChildTests
|
||||
{
|
||||
internal static void MeasureWithoutChild<T>(this T element) where T : ContainerElement
|
||||
{
|
||||
element.Child = null;
|
||||
element.Measure(Size.Zero).Should().BeEquivalentTo(new FullRender(Size.Zero));
|
||||
}
|
||||
|
||||
internal static void DrawWithoutChild<T>(this T element) where T : ContainerElement
|
||||
{
|
||||
// component does not throw an exception when called with null child
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
element.Child = null;
|
||||
|
||||
// component does not perform any canvas operation when called with null child
|
||||
TestPlan
|
||||
.For(x => element)
|
||||
.DrawElement(new Size(200, 100))
|
||||
.CheckDrawResult();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Helpers;
|
||||
@@ -15,8 +13,6 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
internal class TestPlan
|
||||
{
|
||||
private const string DefaultChildName = "child";
|
||||
|
||||
private static Random Random { get; } = new Random();
|
||||
|
||||
private Element Element { get; set; }
|
||||
private ICanvas Canvas { get; }
|
||||
@@ -49,31 +45,20 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
|
||||
private ICanvas CreateCanvas()
|
||||
{
|
||||
return new MockCanvas
|
||||
return new CanvasMock
|
||||
{
|
||||
TranslateFunc = position =>
|
||||
{
|
||||
var expected = GetExpected<CanvasTranslateOperation>();
|
||||
var expected = GetExpected<CanvasTranslateOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.Position.X, position.X, "Translate X");
|
||||
Assert.AreEqual(expected.Position.Y, position.Y, "Translate Y");
|
||||
},
|
||||
RotateFunc = angle =>
|
||||
{
|
||||
var expected = GetExpected<CanvasRotateOperation>();
|
||||
|
||||
Assert.AreEqual(expected.Angle, angle, "Rotate angle");
|
||||
},
|
||||
ScaleFunc = (scaleX, scaleY) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasScaleOperation>();
|
||||
|
||||
Assert.AreEqual(expected.ScaleX, scaleX, "Scale X");
|
||||
Assert.AreEqual(expected.ScaleY, scaleY, "Scale Y");
|
||||
|
||||
//position.Should().BeEquivalentTo(expected.Position);
|
||||
},
|
||||
DrawRectFunc = (position, size, color) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasDrawRectangleOperation>();
|
||||
var expected = GetExpected<CanvasDrawRectangleOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.Position.X, position.X, "Draw rectangle: X");
|
||||
Assert.AreEqual(expected.Position.Y, position.Y, "Draw rectangle: Y");
|
||||
@@ -82,10 +67,14 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
Assert.AreEqual(expected.Size.Height, size.Height, "Draw rectangle: height");
|
||||
|
||||
Assert.AreEqual(expected.Color, color, "Draw rectangle: color");
|
||||
|
||||
/*position.Should().BeEquivalentTo(expected.Position);
|
||||
size.Should().BeEquivalentTo(expected.Size);
|
||||
color.Should().Be(expected.Color);*/
|
||||
},
|
||||
DrawTextFunc = (text, position, style) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasDrawTextOperation>();
|
||||
var expected = GetExpected<CanvasDrawTextOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.Text, text);
|
||||
|
||||
@@ -95,16 +84,23 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
Assert.AreEqual(expected.Style.Color, style.Color, "Draw text: color");
|
||||
Assert.AreEqual(expected.Style.FontType, style.FontType, "Draw text: font");
|
||||
Assert.AreEqual(expected.Style.Size, style.Size, "Draw text: size");
|
||||
|
||||
/*text.Should().Be(expected.Text);
|
||||
position.Should().BeEquivalentTo(expected.Position);
|
||||
style.Should().BeEquivalentTo(expected.Style);*/
|
||||
},
|
||||
DrawImageFunc = (image, position, size) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasDrawImageOperation>();
|
||||
var expected = GetExpected<CanvasDrawImageOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.Position.X, position.X, "Draw image: X");
|
||||
Assert.AreEqual(expected.Position.Y, position.Y, "Draw image: Y");
|
||||
|
||||
Assert.AreEqual(expected.Size.Width, size.Width, "Draw image: width");
|
||||
Assert.AreEqual(expected.Size.Height, size.Height, "Draw image: height");
|
||||
|
||||
/*position.Should().BeEquivalentTo(expected.Position);
|
||||
size.Should().BeEquivalentTo(expected.Size);*/
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -118,23 +114,29 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
Id = id,
|
||||
MeasureFunc = availableSpace =>
|
||||
{
|
||||
var expected = GetExpected<ChildMeasureOperation>();
|
||||
var expected = GetExpected<ChildMeasureOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.ChildId, id);
|
||||
|
||||
Assert.AreEqual(expected.Input.Width, availableSpace.Width, $"Measure: width of child '{expected.ChildId}'");
|
||||
Assert.AreEqual(expected.Input.Height, availableSpace.Height, $"Measure: height of child '{expected.ChildId}'");
|
||||
|
||||
// id.Should().Be(expected.ChildId);
|
||||
// availableSpace.Should().Be(expected.Input);
|
||||
|
||||
return expected.Output;
|
||||
},
|
||||
DrawFunc = availableSpace =>
|
||||
{
|
||||
var expected = GetExpected<ChildDrawOperation>();
|
||||
var expected = GetExpected<ChildDrawOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.ChildId, id);
|
||||
|
||||
Assert.AreEqual(expected.Input.Width, availableSpace.Width, $"Draw: width of child '{expected.ChildId}'");
|
||||
Assert.AreEqual(expected.Input.Height, availableSpace.Height, $"Draw: width of child '{expected.ChildId}'");
|
||||
|
||||
/*id.Should().Be(expected.ChildId);
|
||||
availableSpace.Should().Be(expected.Input);*/
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -164,7 +166,7 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
|
||||
public TestPlan ExpectChildMeasure(string child, Size expectedInput, ISpacePlan returns)
|
||||
{
|
||||
return AddOperation(new ChildMeasureOperation(child, expectedInput, returns));
|
||||
return AddOperation(new ChildMeasureOperationBase(child, expectedInput, returns));
|
||||
}
|
||||
|
||||
public TestPlan ExpectChildDraw(Size expectedInput)
|
||||
@@ -174,42 +176,32 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
|
||||
public TestPlan ExpectChildDraw(string child, Size expectedInput)
|
||||
{
|
||||
return AddOperation(new ChildDrawOperation(child, expectedInput));
|
||||
return AddOperation(new ChildDrawOperationBase(child, expectedInput));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasTranslate(Position position)
|
||||
{
|
||||
return AddOperation(new CanvasTranslateOperation(position));
|
||||
return AddOperation(new CanvasTranslateOperationBase(position));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasTranslate(float left, float top)
|
||||
{
|
||||
return AddOperation(new CanvasTranslateOperation(new Position(left, top)));
|
||||
return AddOperation(new CanvasTranslateOperationBase(new Position(left, top)));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasScale(float scaleX, float scaleY)
|
||||
{
|
||||
return AddOperation(new CanvasScaleOperation(scaleX, scaleY));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasRotate(float angle)
|
||||
{
|
||||
return AddOperation(new CanvasRotateOperation(angle));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawRectangle(Position position, Size size, string color)
|
||||
{
|
||||
return AddOperation(new CanvasDrawRectangleOperation(position, size, color));
|
||||
return AddOperation(new CanvasDrawRectangleOperationBase(position, size, color));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawText(string text, Position position, TextStyle style)
|
||||
{
|
||||
return AddOperation(new CanvasDrawTextOperation(text, position, style));
|
||||
return AddOperation(new CanvasDrawTextOperationBase(text, position, style));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawImage(Position position, Size size)
|
||||
{
|
||||
return AddOperation(new CanvasDrawImageOperation(position, size));
|
||||
return AddOperation(new CanvasDrawImageOperationBase(position, size));
|
||||
}
|
||||
|
||||
public TestPlan CheckMeasureResult(ISpacePlan expected)
|
||||
@@ -254,51 +246,10 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
|
||||
public static Element CreateUniqueElement()
|
||||
{
|
||||
return new Constrained
|
||||
return new DynamicImage
|
||||
{
|
||||
MinWidth = 90,
|
||||
MinHeight = 60,
|
||||
|
||||
Child = new DynamicImage
|
||||
{
|
||||
Source = Placeholders.Image
|
||||
}
|
||||
Source = Placeholders.Image
|
||||
};
|
||||
}
|
||||
|
||||
public static void CompareOperations(Element value, Element expected, Size? availableSpace = null)
|
||||
{
|
||||
CompareMeasureOperations(value, expected, availableSpace);
|
||||
CompareDrawOperations(value, expected, availableSpace);
|
||||
}
|
||||
|
||||
private static void CompareMeasureOperations(Element value, Element expected, Size? availableSpace = null)
|
||||
{
|
||||
availableSpace ??= new Size(400, 300);
|
||||
|
||||
var canvas = new FreeCanvas();
|
||||
value.HandleVisitor(x => x.Initialize(null, canvas));
|
||||
var valueMeasure = value.Measure(availableSpace);
|
||||
|
||||
expected.HandleVisitor(x => x.Initialize(null, canvas));
|
||||
var expectedMeasure = expected.Measure(availableSpace);
|
||||
|
||||
valueMeasure.Should().BeEquivalentTo(expectedMeasure);
|
||||
}
|
||||
|
||||
private static void CompareDrawOperations(Element value, Element expected, Size? availableSpace = null)
|
||||
{
|
||||
availableSpace ??= new Size(400, 300);
|
||||
|
||||
var valueCanvas = new OperationRecordingCanvas();
|
||||
value.HandleVisitor(x => x.Initialize(null, valueCanvas));
|
||||
value.Draw(availableSpace);
|
||||
|
||||
var expectedCanvas = new OperationRecordingCanvas();
|
||||
expected.HandleVisitor(x => x.Initialize(null, expectedCanvas));
|
||||
expected.Draw(availableSpace);
|
||||
|
||||
valueCanvas.Operations.Should().BeEquivalentTo(expectedCanvas.Operations);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[SetUpFixture]
|
||||
public class TestsBase
|
||||
{
|
||||
[OneTimeSetUp]
|
||||
public void RunBeforeAnyTests()
|
||||
{
|
||||
AssertionOptions.AssertEquivalencyUsing(options => options
|
||||
.IncludingNestedObjects()
|
||||
.IncludingInternalProperties()
|
||||
.IncludingInternalFields()
|
||||
.AllowingInfiniteRecursion()
|
||||
.RespectingRuntimeTypes()
|
||||
.WithTracing()
|
||||
.WithStrictOrdering());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TextTests
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TranslateTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<Translate>();
|
||||
|
||||
[Test]
|
||||
public void Draw()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Translate
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
TranslateX = 50,
|
||||
TranslateY = 75
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectCanvasTranslate(50, 75)
|
||||
.ExpectChildDraw(new Size(400, 300))
|
||||
.ExpectCanvasTranslate(-50, -75)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class UnconstrainedTests
|
||||
{
|
||||
#region measure
|
||||
|
||||
[Test]
|
||||
public void Measure_Wrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Unconstrained
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(Size.Max, new Wrap())
|
||||
.CheckMeasureResult(new Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_PartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Unconstrained
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(Size.Max, new PartialRender(1200, 1600))
|
||||
.CheckMeasureResult(new PartialRender(Size.Zero));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_FullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Unconstrained
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(Size.Max, new FullRender(1200, 1600))
|
||||
.CheckMeasureResult(new FullRender(Size.Zero));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region draw
|
||||
|
||||
[Test]
|
||||
public void Draw_SkipWhenChildWraps()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Unconstrained
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.DrawElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(Size.Max, new Wrap())
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_WhenChildPartiallyRenders()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Unconstrained
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.DrawElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(Size.Max, new PartialRender(1200, 1600))
|
||||
.ExpectChildDraw(new Size(1200, 1600))
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_WhenChildFullyRenders()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Unconstrained
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.DrawElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(Size.Max, new FullRender(1600, 1000))
|
||||
.ExpectChildDraw(new Size(1600, 1000))
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using System.Collections.Concurrent;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
{
|
||||
public static class FontManager
|
||||
internal static class CanvasCache
|
||||
{
|
||||
private static ConcurrentDictionary<string, SKTypeface> Typefaces = new ConcurrentDictionary<string, SKTypeface>();
|
||||
internal static ConcurrentDictionary<string, SKPaint> Paints = new ConcurrentDictionary<string, SKPaint>();
|
||||
private static ConcurrentDictionary<string, SKFontMetrics> FontMetrics = new ConcurrentDictionary<string, SKFontMetrics>();
|
||||
private static ConcurrentDictionary<string, SKPaint> Paints = new ConcurrentDictionary<string, SKPaint>();
|
||||
private static ConcurrentDictionary<string, SKPaint> ColorPaint = new ConcurrentDictionary<string, SKPaint>();
|
||||
|
||||
public static void RegisterFontType(string fontName, Stream stream)
|
||||
{
|
||||
Typefaces.TryAdd(fontName, SKTypeface.FromStream(stream));
|
||||
}
|
||||
|
||||
internal static SKPaint ColorToPaint(this string color)
|
||||
{
|
||||
return ColorPaint.GetOrAdd(color, Convert);
|
||||
@@ -38,25 +29,16 @@ namespace QuestPDF.Drawing
|
||||
|
||||
static SKPaint Convert(TextStyle style)
|
||||
{
|
||||
var slant = style.IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
|
||||
|
||||
return new SKPaint
|
||||
{
|
||||
Color = SKColor.Parse(style.Color),
|
||||
Typeface = GetTypeface(style),
|
||||
Typeface = SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant),
|
||||
TextSize = style.Size,
|
||||
TextEncoding = SKTextEncoding.Utf32
|
||||
};
|
||||
}
|
||||
|
||||
static SKTypeface GetTypeface(TextStyle style)
|
||||
{
|
||||
if (Typefaces.TryGetValue(style.FontType, out var result))
|
||||
return result;
|
||||
|
||||
var slant = style.IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
|
||||
|
||||
return SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant)
|
||||
?? throw new ArgumentException($"The typeface {style.FontType} could not be found.");
|
||||
}
|
||||
}
|
||||
|
||||
internal static SKFontMetrics ToFontMetrics(this TextStyle style)
|
||||
@@ -56,10 +56,7 @@ namespace QuestPDF.Drawing
|
||||
var spacePlan = content.Measure(Size.Max) as Size;
|
||||
|
||||
if (spacePlan == null)
|
||||
{
|
||||
canvas.EndDocument();
|
||||
ThrowLayoutException();
|
||||
}
|
||||
break;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -87,11 +84,6 @@ namespace QuestPDF.Drawing
|
||||
}
|
||||
|
||||
canvas.EndDocument();
|
||||
|
||||
void ThrowLayoutException()
|
||||
{
|
||||
throw new DocumentLayoutException("Composed layout generates infinite document.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,16 +66,6 @@ namespace QuestPDF.Drawing
|
||||
|
||||
}
|
||||
|
||||
public void Rotate(float angle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Scale(float scaleX, float scaleY)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -51,15 +51,5 @@ namespace QuestPDF.Drawing
|
||||
{
|
||||
Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
|
||||
}
|
||||
|
||||
public void Rotate(float angle)
|
||||
{
|
||||
Canvas.RotateDegrees(angle);
|
||||
}
|
||||
|
||||
public void Scale(float scaleX, float scaleY)
|
||||
{
|
||||
Canvas.Scale(scaleX, scaleY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace QuestPDF.Elements
|
||||
{
|
||||
public const int DefaultColumnsCount = 12;
|
||||
|
||||
public List<GridElement> Children { get; } = new List<GridElement>();
|
||||
public List<GridElement> Children { get; set; } = new List<GridElement>();
|
||||
public Queue<GridElement> ChildrenQueue { get; set; } = new Queue<GridElement>();
|
||||
public int ColumnsCount { get; set; } = DefaultColumnsCount;
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
@@ -37,20 +36,9 @@ namespace QuestPDF.Elements
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration.Header().Element(Header);
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.Element(x => IsClose(MinSize.Width, MaxSize.Width) ? x.ExtendHorizontal() : x)
|
||||
.Element(x => IsClose(MinSize.Height, MaxSize.Height) ? x.ExtendVertical() : x)
|
||||
.Element(Content);
|
||||
|
||||
decoration.Content().Extend().Element(Content);
|
||||
decoration.Footer().Element(Footer);
|
||||
});
|
||||
|
||||
bool IsClose(float x, float y)
|
||||
{
|
||||
return Math.Abs(x - y) < Size.Epsilon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,16 @@ namespace QuestPDF.Elements
|
||||
|
||||
internal override void Draw(Size availableSpace)
|
||||
{
|
||||
Canvas.Rotate(Angle);
|
||||
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
||||
|
||||
if (skiaCanvas == null)
|
||||
return;
|
||||
|
||||
var currentMatrix = skiaCanvas.TotalMatrix;
|
||||
|
||||
skiaCanvas.RotateDegrees(Angle);
|
||||
Child?.Draw(availableSpace);
|
||||
Canvas.Rotate(-Angle);
|
||||
skiaCanvas.SetMatrix(currentMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
-37
@@ -6,31 +6,29 @@ using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Elements
|
||||
{
|
||||
internal abstract class RowElement : Constrained
|
||||
internal abstract class RowElement : ContainerElement
|
||||
{
|
||||
public float Size { get; protected set; }
|
||||
public float Width { get; set; } = 1;
|
||||
|
||||
public void SetWidth(float width)
|
||||
internal override void Draw(Size availableSpace)
|
||||
{
|
||||
MinWidth = width;
|
||||
MaxWidth = width;
|
||||
Child?.Draw(availableSpace);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ConstantRowElement : RowElement
|
||||
{
|
||||
public ConstantRowElement(float size)
|
||||
public ConstantRowElement(float width)
|
||||
{
|
||||
Size = size;
|
||||
SetWidth(size);
|
||||
Width = width;
|
||||
}
|
||||
}
|
||||
|
||||
internal class RelativeRowElement : RowElement
|
||||
{
|
||||
public RelativeRowElement(float size)
|
||||
public RelativeRowElement(float width)
|
||||
{
|
||||
Size = size;
|
||||
Width = width;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,61 +83,70 @@ namespace QuestPDF.Elements
|
||||
|
||||
internal class Row : Element
|
||||
{
|
||||
public float Spacing { get; set; } = 0;
|
||||
|
||||
public ICollection<RowElement> Children { get; internal set; } = new List<RowElement>();
|
||||
private Element? RootElement { get; set; }
|
||||
|
||||
public float Spacing { get; set; } = 0;
|
||||
|
||||
internal override void HandleVisitor(Action<Element?> visit)
|
||||
{
|
||||
if (RootElement == null)
|
||||
ComposeTree();
|
||||
|
||||
RootElement.HandleVisitor(visit);
|
||||
Children.ToList().ForEach(x => x.HandleVisitor(visit));
|
||||
base.HandleVisitor(visit);
|
||||
}
|
||||
|
||||
internal override ISpacePlan Measure(Size availableSpace)
|
||||
{
|
||||
UpdateElementsWidth(availableSpace.Width);
|
||||
return RootElement.Measure(availableSpace);
|
||||
return Compose(availableSpace.Width).Measure(availableSpace);
|
||||
}
|
||||
|
||||
internal override void Draw(Size availableSpace)
|
||||
{
|
||||
UpdateElementsWidth(availableSpace.Width);
|
||||
RootElement.Draw(availableSpace);
|
||||
Compose(availableSpace.Width).Draw(availableSpace);
|
||||
}
|
||||
|
||||
#region structure
|
||||
|
||||
private void ComposeTree()
|
||||
private Element Compose(float availableWidth)
|
||||
{
|
||||
Children = AddSpacing(Children, Spacing);
|
||||
var elements = AddSpacing(Children, Spacing);
|
||||
var rowElements = ReduceRows(elements, availableWidth);
|
||||
var tree = BuildTree(rowElements.ToArray());
|
||||
tree.HandleVisitor(x => x.Initialize(PageContext, Canvas));
|
||||
|
||||
var elements = Children.Cast<Element>().ToArray();
|
||||
RootElement = BuildTree(elements);
|
||||
return tree;
|
||||
}
|
||||
|
||||
private void UpdateElementsWidth(float availableWidth)
|
||||
|
||||
private static ICollection<Element> ReduceRows(ICollection<RowElement> elements, float availableWidth)
|
||||
{
|
||||
var constantWidth = Children
|
||||
var constantWidth = elements
|
||||
.Where(x => x is ConstantRowElement)
|
||||
.Cast<ConstantRowElement>()
|
||||
.Sum(x => x.Size);
|
||||
.Sum(x => x.Width);
|
||||
|
||||
var relativeWidth = Children
|
||||
var relativeWidth = elements
|
||||
.Where(x => x is RelativeRowElement)
|
||||
.Cast<RelativeRowElement>()
|
||||
.Sum(x => x.Size);
|
||||
.Sum(x => x.Width);
|
||||
|
||||
var widthPerRelativeUnit = (availableWidth - constantWidth) / relativeWidth;
|
||||
|
||||
Children
|
||||
.Where(x => x is RelativeRowElement)
|
||||
.Cast<RelativeRowElement>()
|
||||
.ToList()
|
||||
.ForEach(x => x.SetWidth(x.Size * widthPerRelativeUnit));
|
||||
return elements
|
||||
.Select(x =>
|
||||
{
|
||||
if (x is RelativeRowElement r)
|
||||
return new ConstantRowElement(r.Width * widthPerRelativeUnit)
|
||||
{
|
||||
Child = x.Child
|
||||
};
|
||||
|
||||
return x;
|
||||
})
|
||||
.Select(x => new Constrained
|
||||
{
|
||||
MinWidth = x.Width,
|
||||
MaxWidth = x.Width,
|
||||
Child = x.Child
|
||||
})
|
||||
.Cast<Element>()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static ICollection<RowElement> AddSpacing(ICollection<RowElement> elements, float spacing)
|
||||
|
||||
@@ -35,21 +35,26 @@ namespace QuestPDF.Elements
|
||||
|
||||
internal override void Draw(Size availableSpace)
|
||||
{
|
||||
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
||||
|
||||
if (skiaCanvas == null)
|
||||
return;
|
||||
|
||||
var targetSpace = new Size(
|
||||
Math.Abs(availableSpace.Width / ScaleX),
|
||||
Math.Abs(availableSpace.Height / ScaleY));
|
||||
|
||||
var translate = new Position(
|
||||
ScaleX < 0 ? availableSpace.Width : 0,
|
||||
ScaleY < 0 ? availableSpace.Height : 0);
|
||||
var currentMatrix = skiaCanvas.TotalMatrix;
|
||||
|
||||
Canvas.Translate(translate);
|
||||
Canvas.Scale(ScaleX, ScaleY);
|
||||
if (ScaleX < 0)
|
||||
skiaCanvas.Translate(availableSpace.Width, 0);
|
||||
|
||||
if (ScaleY < 0)
|
||||
skiaCanvas.Translate(0, availableSpace.Height);
|
||||
|
||||
skiaCanvas.Scale(ScaleX, ScaleY);
|
||||
Child?.Draw(targetSpace);
|
||||
|
||||
Canvas.Scale(1/ScaleX, 1/ScaleY);
|
||||
Canvas.Translate(translate.Reverse());
|
||||
skiaCanvas.SetMatrix(currentMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,22 +33,26 @@ namespace QuestPDF.Elements
|
||||
|
||||
internal override void Draw(Size availableSpace)
|
||||
{
|
||||
var translate = new Position(
|
||||
(NormalizedTurnCount == 1 || NormalizedTurnCount == 2) ? availableSpace.Width : 0,
|
||||
(NormalizedTurnCount == 2 || NormalizedTurnCount == 3) ? availableSpace.Height : 0);
|
||||
|
||||
var rotate = NormalizedTurnCount * 90;
|
||||
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
||||
|
||||
Canvas.Translate(translate);
|
||||
Canvas.Rotate(rotate);
|
||||
if (skiaCanvas == null)
|
||||
return;
|
||||
|
||||
var currentMatrix = skiaCanvas.TotalMatrix;
|
||||
|
||||
if (NormalizedTurnCount == 1 || NormalizedTurnCount == 2)
|
||||
skiaCanvas.Translate(availableSpace.Width, 0);
|
||||
|
||||
if (NormalizedTurnCount == 2 || NormalizedTurnCount == 3)
|
||||
skiaCanvas.Translate(0, availableSpace.Height);
|
||||
|
||||
skiaCanvas.RotateDegrees(NormalizedTurnCount * 90);
|
||||
|
||||
if (NormalizedTurnCount == 1 || NormalizedTurnCount == 3)
|
||||
availableSpace = new Size(availableSpace.Height, availableSpace.Width);
|
||||
|
||||
Child?.Draw(availableSpace);
|
||||
|
||||
Canvas.Rotate(-rotate);
|
||||
Canvas.Translate(translate.Reverse());
|
||||
skiaCanvas.SetMatrix(currentMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ namespace QuestPDF.Elements
|
||||
|
||||
internal class Stack : IComponent
|
||||
{
|
||||
public ICollection<Element> Children { get; } = new List<Element>();
|
||||
public ICollection<Element> Children { get; internal set; } = new List<Element>();
|
||||
public float Spacing { get; set; } = 0;
|
||||
|
||||
public void Compose(IContainer container)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuestPDF.Elements.Text.Items;
|
||||
|
||||
@@ -7,41 +6,14 @@ namespace QuestPDF.Elements.Text.Calculation
|
||||
{
|
||||
internal class TextLine
|
||||
{
|
||||
public ICollection<TextLineElement> Elements { get; private set; }
|
||||
public ICollection<TextLineElement> Elements { get; set; }
|
||||
|
||||
public float TextHeight { get; private set; }
|
||||
public float LineHeight { get; private set; }
|
||||
public float TextHeight => Elements.Max(x => x.Measurement.Height);
|
||||
public float LineHeight => Elements.Max(x => x.Measurement.LineHeight * x.Measurement.Height);
|
||||
|
||||
public float Ascent { get; private set; }
|
||||
public float Descent { get; private set; }
|
||||
public float Ascent => Elements.Min(x => x.Measurement.Ascent) - (LineHeight - TextHeight) / 2;
|
||||
public float Descent => Elements.Max(x => x.Measurement.Descent) + (LineHeight - TextHeight) / 2;
|
||||
|
||||
public float Width { get; private set; }
|
||||
|
||||
public static TextLine From(ICollection<TextLineElement> elements)
|
||||
{
|
||||
if (elements.Count == 0)
|
||||
{
|
||||
return new TextLine
|
||||
{
|
||||
Elements = elements
|
||||
};
|
||||
}
|
||||
|
||||
var textHeight = elements.Max(x => x.Measurement.Height);
|
||||
var lineHeight = elements.Max(x => x.Measurement.LineHeight * x.Measurement.Height);
|
||||
|
||||
return new TextLine
|
||||
{
|
||||
Elements = elements,
|
||||
|
||||
TextHeight = textHeight,
|
||||
LineHeight = lineHeight,
|
||||
|
||||
Ascent = elements.Min(x => x.Measurement.Ascent) - (lineHeight - textHeight) / 2,
|
||||
Descent = elements.Max(x => x.Measurement.Descent) + (lineHeight - textHeight) / 2,
|
||||
|
||||
Width = elements.Sum(x => x.Measurement.Width)
|
||||
};
|
||||
}
|
||||
public float Width => Elements.Sum(x => x.Measurement.Width);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using QuestPDF.Elements.Text.Items;
|
||||
using QuestPDF.Elements.Text.Calculation;
|
||||
|
||||
namespace QuestPDF.Elements.Text.Calculation
|
||||
namespace QuestPDF.Elements.Text.Items
|
||||
{
|
||||
internal class TextLineElement
|
||||
{
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace QuestPDF.Elements.Text.Items
|
||||
var fontMetrics = Style.ToFontMetrics();
|
||||
|
||||
var startIndex = request.StartIndex;
|
||||
|
||||
|
||||
if (request.IsFirstLineElement)
|
||||
{
|
||||
while (startIndex + 1 < Text.Length && Text[startIndex] == space)
|
||||
@@ -54,18 +54,15 @@ namespace QuestPDF.Elements.Text.Items
|
||||
// start breaking text from requested position
|
||||
var text = Text.Substring(startIndex);
|
||||
|
||||
var textLength = (int)paint.BreakText(text, request.AvailableWidth);
|
||||
var breakingIndex = (int)paint.BreakText(text, request.AvailableWidth);
|
||||
|
||||
if (textLength <= 0)
|
||||
if (breakingIndex <= 0)
|
||||
return null;
|
||||
|
||||
if (textLength < text.Length && text[textLength] == space)
|
||||
textLength++;
|
||||
|
||||
// break text only on spaces
|
||||
if (textLength < text.Length)
|
||||
if (breakingIndex < text.Length)
|
||||
{
|
||||
var lastSpaceIndex = text.Substring(0, textLength).LastIndexOf(space) - 1;
|
||||
var lastSpaceIndex = text.Substring(0, breakingIndex).LastIndexOf(space) - 1;
|
||||
|
||||
if (lastSpaceIndex <= 0)
|
||||
{
|
||||
@@ -74,13 +71,13 @@ namespace QuestPDF.Elements.Text.Items
|
||||
}
|
||||
else
|
||||
{
|
||||
textLength = lastSpaceIndex + 1;
|
||||
breakingIndex = lastSpaceIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
text = text.Substring(0, textLength);
|
||||
text = text.Substring(0, breakingIndex);
|
||||
|
||||
var endIndex = startIndex + textLength;
|
||||
var endIndex = startIndex + breakingIndex;
|
||||
var nextIndex = endIndex;
|
||||
|
||||
while (nextIndex + 1 < Text.Length && Text[nextIndex] == space)
|
||||
@@ -115,11 +112,11 @@ namespace QuestPDF.Elements.Text.Items
|
||||
request.Canvas.DrawText(text, Position.Zero, Style);
|
||||
|
||||
// draw underline
|
||||
if (Style.HasUnderline && fontMetrics.UnderlinePosition.HasValue)
|
||||
if (Style.IsUnderlined && fontMetrics.UnderlinePosition.HasValue)
|
||||
DrawLine(fontMetrics.UnderlinePosition.Value, fontMetrics.UnderlineThickness.Value);
|
||||
|
||||
// draw stroke
|
||||
if (Style.HasStrikethrough && fontMetrics.StrikeoutPosition.HasValue)
|
||||
if (Style.IsStroked && fontMetrics.StrikeoutPosition.HasValue)
|
||||
DrawLine(fontMetrics.StrikeoutPosition.Value, fontMetrics.StrikeoutThickness.Value);
|
||||
|
||||
void DrawLine(float offset, float thickness)
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace QuestPDF.Elements.Text
|
||||
if (!RenderingQueue.Any())
|
||||
return new FullRender(Size.Zero);
|
||||
|
||||
var lines = DivideTextItemsIntoLines(availableSpace.Width, availableSpace.Height).ToList();
|
||||
var lines = DivideTextItemsIntoLines(availableSpace.Width, availableSpace.Height);
|
||||
|
||||
if (!lines.Any())
|
||||
return new PartialRender(Size.Zero);
|
||||
@@ -64,7 +64,7 @@ namespace QuestPDF.Elements.Text
|
||||
widthOffset = 0f;
|
||||
|
||||
var alignmentOffset = GetAlignmentOffset(line.Width);
|
||||
|
||||
|
||||
Canvas.Translate(new Position(alignmentOffset, 0));
|
||||
Canvas.Translate(new Position(0, -line.Ascent));
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace QuestPDF.Elements.Text
|
||||
|
||||
if (!RenderingQueue.Any())
|
||||
ResetState();
|
||||
|
||||
|
||||
float GetAlignmentOffset(float lineWidth)
|
||||
{
|
||||
if (Alignment == HorizontalAlignment.Left)
|
||||
@@ -192,7 +192,10 @@ namespace QuestPDF.Elements.Text
|
||||
queue.Dequeue();
|
||||
}
|
||||
|
||||
return TextLine.From(currentLineElements);
|
||||
return new TextLine
|
||||
{
|
||||
Elements = currentLineElements
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,14 @@ namespace QuestPDF.Elements
|
||||
|
||||
internal override void Draw(Size availableSpace)
|
||||
{
|
||||
var translate = new Position(TranslateX, TranslateY);
|
||||
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
||||
|
||||
Canvas.Translate(translate);
|
||||
if (skiaCanvas == null)
|
||||
return;
|
||||
|
||||
skiaCanvas.Translate(TranslateX, TranslateY);
|
||||
base.Draw(availableSpace);
|
||||
Canvas.Translate(translate.Reverse());
|
||||
skiaCanvas.Translate(-TranslateX, -TranslateY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,10 @@ namespace QuestPDF.Fluent
|
||||
{
|
||||
if (element?.Child != null && element.Child is Empty == false)
|
||||
{
|
||||
var message = "You should not assign multiple child elements to a single-child container. " +
|
||||
"This may happen when a container variable is used outside of its scope/closure OR the container is used in multiple fluent API chains OR the container is used incorrectly in a loop. " +
|
||||
"This exception is thrown to help you detect that some part of the code is overriding fragments of the document layout with a new content - essentially destroying existing content.";
|
||||
|
||||
var message = $"Container {element.GetType().Name} already contains an {element.Child.GetType().Name} child. " +
|
||||
$"Tried to assign {child?.GetType()?.Name}." +
|
||||
$"You should not assign multiple elements to single-child containers.";
|
||||
|
||||
throw new DocumentComposeException(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,24 +12,14 @@ namespace QuestPDF.Fluent
|
||||
|
||||
public void Size(PageSize pageSize)
|
||||
{
|
||||
MinSize(pageSize);
|
||||
MaxSize(pageSize);
|
||||
}
|
||||
|
||||
public void ContinuousSize(float width)
|
||||
{
|
||||
MinSize(new PageSize(width, 0));
|
||||
MaxSize(new PageSize(width, Infrastructure.Size.Max.Height));
|
||||
Page.MinSize = pageSize;
|
||||
Page.MaxSize = pageSize;
|
||||
}
|
||||
|
||||
public void MinSize(PageSize pageSize)
|
||||
public void ContinuousSize(float width)
|
||||
{
|
||||
Page.MinSize = pageSize;
|
||||
}
|
||||
|
||||
public void MaxSize(PageSize pageSize)
|
||||
{
|
||||
Page.MaxSize = pageSize;
|
||||
Page.MinSize = new PageSize(width, 0);
|
||||
Page.MaxSize = new PageSize(width, Infrastructure.Size.Max.Height);
|
||||
}
|
||||
|
||||
public void MarginLeft(float value)
|
||||
|
||||
@@ -5,7 +5,6 @@ using QuestPDF.Elements;
|
||||
using QuestPDF.Elements.Text;
|
||||
using QuestPDF.Elements.Text.Items;
|
||||
using QuestPDF.Infrastructure;
|
||||
using static System.String;
|
||||
|
||||
namespace QuestPDF.Fluent
|
||||
{
|
||||
@@ -54,8 +53,7 @@ namespace QuestPDF.Fluent
|
||||
style ??= DefaultStyle;
|
||||
|
||||
var items = text
|
||||
.Replace("\r", string.Empty)
|
||||
.Split(new[] { '\n' }, StringSplitOptions.None)
|
||||
.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
|
||||
.Select(x => new TextBlockSpan
|
||||
{
|
||||
Text = x,
|
||||
@@ -75,17 +73,7 @@ namespace QuestPDF.Fluent
|
||||
.ForEach(TextBlocks.Add);
|
||||
}
|
||||
|
||||
public void Line(string text, TextStyle? style = null)
|
||||
{
|
||||
Span(text + Environment.NewLine, style);
|
||||
}
|
||||
|
||||
public void Line(string text)
|
||||
{
|
||||
Span(text + Environment.NewLine);
|
||||
}
|
||||
|
||||
public void EmptyLine()
|
||||
public void NewLine()
|
||||
{
|
||||
Span(Environment.NewLine);
|
||||
}
|
||||
@@ -149,7 +137,7 @@ namespace QuestPDF.Fluent
|
||||
Element = container
|
||||
});
|
||||
|
||||
return container.AlignBottom().Box();
|
||||
return container.Box();
|
||||
}
|
||||
|
||||
internal void Compose(IContainer container)
|
||||
|
||||
@@ -43,14 +43,14 @@ namespace QuestPDF.Fluent
|
||||
return style.Mutate(x => x.IsItalic = value);
|
||||
}
|
||||
|
||||
public static TextStyle Strikethrough(this TextStyle style, bool value = true)
|
||||
public static TextStyle Stroked(this TextStyle style, bool value = true)
|
||||
{
|
||||
return style.Mutate(x => x.HasStrikethrough = value);
|
||||
return style.Mutate(x => x.IsStroked = value);
|
||||
}
|
||||
|
||||
public static TextStyle Underline(this TextStyle style, bool value = true)
|
||||
public static TextStyle Underlined(this TextStyle style, bool value = true)
|
||||
{
|
||||
return style.Mutate(x => x.HasUnderline = value);
|
||||
return style.Mutate(x => x.IsUnderlined = value);
|
||||
}
|
||||
|
||||
#region Weight
|
||||
|
||||
@@ -13,8 +13,5 @@ namespace QuestPDF.Infrastructure
|
||||
void DrawExternalLink(string url, Size size);
|
||||
void DrawLocationLink(string locationName, Size size);
|
||||
void DrawLocation(string locationName);
|
||||
|
||||
void Rotate(float angle);
|
||||
void Scale(float scaleX, float scaleY);
|
||||
}
|
||||
}
|
||||
@@ -30,13 +30,5 @@
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((Size) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Width.GetHashCode() * 397) ^ Height.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ namespace QuestPDF.Infrastructure
|
||||
internal float LineHeight { get; set; } = 1.2f;
|
||||
internal FontWeight FontWeight { get; set; } = FontWeight.Normal;
|
||||
internal bool IsItalic { get; set; } = false;
|
||||
internal bool HasStrikethrough { get; set; } = false;
|
||||
internal bool HasUnderline { get; set; } = false;
|
||||
internal bool IsStroked { get; set; } = false;
|
||||
internal bool IsUnderlined { get; set; } = false;
|
||||
|
||||
public static TextStyle Default => new TextStyle();
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace QuestPDF.Infrastructure
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
KeyCache ??= $"{Color}|{BackgroundColor}|{FontType}|{Size}|{LineHeight}|{FontWeight}|{IsItalic}|{HasStrikethrough}|{HasUnderline}";
|
||||
KeyCache ??= $"{Color}|{BackgroundColor}|{FontType}|{Size}|{LineHeight}|{FontWeight}|{IsItalic}|{IsStroked}|{IsUnderlined}";
|
||||
return KeyCache;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<Authors>MarcinZiabek</Authors>
|
||||
<Company>CodeFlint</Company>
|
||||
<PackageId>QuestPDF</PackageId>
|
||||
<Version>2021.10.1</Version>
|
||||
<Version>2021.8</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>
|
||||
<PackageReleaseNotes>Enhanced text rendering capabilities. Improved rendering performance.</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>New elements: Unconstrained, Scale, ScaleHorizontal, ScaleVertical, FlipOver, FlipHorizontal, FlipVertical, Rotate, RotateLeft, RotateRight, TranslateX, TranslateY. Added support for more placeholders in the PageNumber element (current page, total number of pages, page number of any predefined location). Added support for documents with various page sizes. Various API improvements.</PackageReleaseNotes>
|
||||
<LangVersion>8</LangVersion>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageIcon>Logo.png</PackageIcon>
|
||||
@@ -14,15 +14,15 @@
|
||||
<PackageProjectUrl>https://www.questpdf.com/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/QuestPDF/library.git</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<Copyright>Marcin Ziąbek, QuestPDF contributors</Copyright>
|
||||
<PackageTags>pdf file export generate generation tool create creation render portable document format quest html library converter open source free standard core</PackageTags>
|
||||
<Copyright>QuestPDF contributors</Copyright>
|
||||
<PackageTags>PDF file export generate create render portable document format quest free</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<Nullable>enable</Nullable>
|
||||
<TargetFrameworks>net462;netstandard2.0;netcoreapp2.0;netcoreapp3.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
|
||||
## Overview
|
||||
|
||||
**Rely on solid fundamentals** - This library is created specifically for designing and arranging document layouts, with full paging support. Alternative solutions, such as HTML-based converters, are not designed for this purpose and therefore are often unpredictable and do not produce desired results.
|
||||
1) **Rely on solid fundamentals** - This library is created specifically for designing and arranging document layouts, with full paging support. Alternative solutions, such as HTML-based converters, are not designed for this purpose and therefore are often unpredictable and do not produce desired results.
|
||||
|
||||
**Work with organized self-explanatory code** - The entire process of implementing PDF document, takes place in your code. Free yourself from slow visual designers and strange technological limitations. Follow simple yet highly effective approaches to create maintainable, high-quality code.
|
||||
2) **Work with organized self-explanatory code** - The entire process of implementing PDF document, takes place in your code. Free yourself from slow visual designers and strange technological limitations. Follow simple yet highly effective approaches to create maintainable, high-quality code.
|
||||
|
||||
**Compose simple components into complex documents** - Do you remember the feeling when your code just works? When your ideas are becoming real without any effort? Working with simple, easy to understand, self-explanatory and highly composable layout elements is the key here!
|
||||
3) **Compose simple components into complex documents** - Do you remember the feeling when your code just works? When your ideas are becoming real without any effort? Working with simple, easy to understand, self-explanatory and highly composable layout elements is the key here!
|
||||
|
||||
**Create and reuse components** - Feel no fear of complex documents! Create custom, reusable components and divide the document's layout into easy to maintain pieces. Inject data to customize content and use slots to enhance composability. Decide how complex approaches your solution needs and follow the best path.
|
||||
4) **Create and reuse components** - Feel no fear of complex documents! Create custom, reusable components and divide the document's layout into easy to maintain pieces. Inject data to customize content and use slots to enhance composability. Decide how complex approaches your solution needs and follow the best path.
|
||||
|
||||
**Prototype with ease** - We understand that document generation is often tricky and require multiple iterations. The library offers additional prototyping tools such as random text generator or image placeholder element. By following best practices, you can develop a document without having data.
|
||||
5) **Prototype with ease** - We understand that document generation is often tricky and require multiple iterations. The library offers additional prototyping tools such as random text generator or image placeholder element. By following best practices, you can develop a document without having data.
|
||||
|
||||
**Enjoy fast PDF generation** - QuestPDF is created upon SkiaSharp, a well-known graphical library, and converts your data into PDF documents. It offers a highly optimized layouting engine capable of generating over 1000 PDF files per minute per core. The entire process is thread-safe.
|
||||
6) **Enjoy fast PDF generation** - QuestPDF is created upon SkiaSharp, a well-known graphical library, and converts your data into PDF documents. It offers a highly optimized layouting engine capable of generating over 1000 PDF files per minute per core. The entire process is thread-safe.
|
||||
|
||||
## Support QuestPDF
|
||||
|
||||
@@ -36,10 +36,10 @@ The library is available as a nuget package. You can install it as any other nug
|
||||
|
||||
## Documentation
|
||||
|
||||
**[Release notes and roadmap](https://www.questpdf.com/documentation/releases.html)** - everything that is planned for future library iterations, description of new features and information about potential breaking changes.
|
||||
**[Getting started tutorial](https://www.questpdf.com/documentation/getting-started.html)** - a short and easy to follow tutorial showing how to design an invoice document under 200 lines of code.
|
||||
**[API Reference](https://www.questpdf.com/documentation/api-reference.html)** - a detailed description of behavior of all available components and how to use them with C# Fluent API.
|
||||
**[Patterns and practices](https://www.questpdf.com/documentation/patterns-and-practices.html#document-metadata)** - everything that may help you designing great reports and reusable code that is easy to maintain.
|
||||
1. [Release notes and roadmap](https://www.questpdf.com/documentation/releases.html) - everything that is planned for future library iterations, description of new features and information about potential breaking changes.
|
||||
2. [Getting started tutorial](https://www.questpdf.com/documentation/getting-started.html) - a short and easy to follow tutorial showing how to design an invoice document under 200 lines of code.
|
||||
3. [API Reference](https://www.questpdf.com/documentation/api-reference.html) - a detailed description of behavior of all available components and how to use them with C# Fluent API.
|
||||
4. [Patterns and practices](https://www.questpdf.com/documentation/patterns-and-practices.html#document-metadata) - everything that may help you designing great reports and reusable code that is easy to maintain.
|
||||
|
||||
## Example invoice
|
||||
|
||||
@@ -51,184 +51,39 @@ For tutorial, documentation and API reference, please visit [the QuestPDF docume
|
||||
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/invoice.png" width="595px">
|
||||
</a>
|
||||
|
||||
Here you can find an example code showing how easy is to write and understand the fluent API.
|
||||
|
||||
**General document structure** with header, content and footer:
|
||||
Here you can find an example code showing how easy is to write and understand the fluent API:
|
||||
|
||||
```csharp
|
||||
public void Compose(IDocumentContainer container)
|
||||
{
|
||||
{
|
||||
container
|
||||
.Page(page =>
|
||||
{
|
||||
page.Margin(50);
|
||||
page.MarginVertical(60);
|
||||
page.MarginHorizontal(40);
|
||||
|
||||
page.Size(PageSizes.A4);
|
||||
|
||||
page.Header().Element(ComposeHeader);
|
||||
page.Content().Element(ComposeContent);
|
||||
|
||||
page.Footer().AlignCenter().Text(x =>
|
||||
{
|
||||
x.CurrentPageNumber();
|
||||
x.Span(" / ");
|
||||
x.TotalPages();
|
||||
});
|
||||
page.Footer().AlignCenter().PageNumber();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**The header area** consists of basic invoice information along with a logo placeholder.
|
||||
|
||||
```csharp
|
||||
void ComposeHeader(IContainer container)
|
||||
{
|
||||
var titleTextStyle = TextStyle.Default.Size(20).SemiBold().Color(Colors.Blue.Medium);
|
||||
|
||||
container.Row(row =>
|
||||
{
|
||||
row.RelativeColumn().Stack(stack =>
|
||||
{
|
||||
stack.Item().Text($"Invoice #{Model.InvoiceNumber}", titleStyle);
|
||||
|
||||
stack.Item().Text(text =>
|
||||
{
|
||||
text.Span("Issue date: ", TextStyle.Default.SemiBold());
|
||||
text.Span($"{Model.IssueDate:d}");
|
||||
});
|
||||
|
||||
stack.Item().Text(text =>
|
||||
{
|
||||
text.Span("Due date: ", TextStyle.Default.SemiBold());
|
||||
text.Span($"{Model.DueDate:d}");
|
||||
});
|
||||
stack.Item().Text($"Invoice #{Model.InvoiceNumber}", TextStyle.Default.Size(20).Bold());
|
||||
stack.Item().Text($"Issue date: {Model.IssueDate:d}");
|
||||
stack.Item().Text($"Due date: {Model.DueDate:d}");
|
||||
});
|
||||
|
||||
row.ConstantColumn(100).Height(50).Placeholder();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Implementation of **the content area** that contains seller and customer details, then listing of all bought products, then a comments section.
|
||||
|
||||
```csharp
|
||||
void ComposeContent(IContainer container)
|
||||
{
|
||||
container.PaddingVertical(40).Stack(column =>
|
||||
{
|
||||
column.Spacing(20);
|
||||
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn().Component(new AddressComponent("From", Model.SellerAddress));
|
||||
row.ConstantColumn(50);
|
||||
row.RelativeColumn().Component(new AddressComponent("For", Model.CustomerAddress));
|
||||
});
|
||||
|
||||
column.Item().Element(ComposeTable);
|
||||
|
||||
var totalPrice = Model.Items.Sum(x => x.Price * x.Quantity);
|
||||
|
||||
column
|
||||
.Item()
|
||||
.PaddingRight(5)
|
||||
.AlignRight()
|
||||
.Text($"Grand total: {totalPrice}$", TextStyle.Default.SemiBold());
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Model.Comments))
|
||||
column.Item().PaddingTop(25).Element(ComposeComments);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**The table and comments** codes are extracted into separate methods to increase clarity:
|
||||
|
||||
```csharp
|
||||
void ComposeTable(IContainer container)
|
||||
{
|
||||
var headerStyle = TextStyle.Default.SemiBold();
|
||||
|
||||
container.Decoration(decoration =>
|
||||
{
|
||||
// header
|
||||
decoration.Header().BorderBottom(1).Padding(5).Row(row =>
|
||||
{
|
||||
row.ConstantColumn(25).Text("#", headerStyle);
|
||||
row.RelativeColumn(3).Text("Product", headerStyle);
|
||||
row.RelativeColumn().AlignRight().Text("Unit price", headerStyle);
|
||||
row.RelativeColumn().AlignRight().Text("Quantity", headerStyle);
|
||||
row.RelativeColumn().AlignRight().Text("Total", headerStyle);
|
||||
});
|
||||
|
||||
// content
|
||||
decoration
|
||||
.Content()
|
||||
.Stack(column =>
|
||||
{
|
||||
foreach (var item in Model.Items)
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.ShowEntire()
|
||||
.BorderBottom(1)
|
||||
.BorderColor(Colors.Grey.Lighten2)
|
||||
.Padding(5)
|
||||
.Row(row =>
|
||||
{
|
||||
row.ConstantColumn(25).Text(Model.Items.IndexOf(item) + 1);
|
||||
row.RelativeColumn(3).Text(item.Name);
|
||||
row.RelativeColumn().AlignRight().Text($"{item.Price}$");
|
||||
row.RelativeColumn().AlignRight().Text(item.Quantity);
|
||||
row.RelativeColumn().AlignRight().Text($"{item.Price * item.Quantity}$");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
```csharp
|
||||
void ComposeComments(IContainer container)
|
||||
{
|
||||
container.ShowEntire().Background(Colors.Grey.Lighten3).Padding(10).Stack(message =>
|
||||
{
|
||||
message.Spacing(5);
|
||||
message.Item().Text("Comments", TextStyle.Default.Size(14).SemiBold());
|
||||
message.Item().Text(Model.Comments);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**The address details section** is implemented using components. This way the code can be easily reused for both seller and customer:
|
||||
|
||||
```csharp
|
||||
public class AddressComponent : IComponent
|
||||
{
|
||||
private string Title { get; }
|
||||
private Address Address { get; }
|
||||
|
||||
public AddressComponent(string title, Address address)
|
||||
{
|
||||
Title = title;
|
||||
Address = address;
|
||||
}
|
||||
|
||||
public void Compose(IContainer container)
|
||||
{
|
||||
container.ShowEntire().Stack(column =>
|
||||
{
|
||||
column.Spacing(5);
|
||||
|
||||
column
|
||||
.Item()
|
||||
.BorderBottom(1)
|
||||
.PaddingBottom(5)
|
||||
.Text(Title, TextStyle.Default.SemiBold());
|
||||
|
||||
column.Item().Text(Address.CompanyName);
|
||||
column.Item().Text(Address.Street);
|
||||
column.Item().Text($"{Address.City}, {Address.State}");
|
||||
column.Item().Text(Address.Email);
|
||||
column.Item().Text(Address.Phone);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// code describing content
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user