Compare commits

...

14 Commits

Author SHA1 Message Date
Marcin Ziąbek e18b5170e6 Improved text run calculation 2021-05-05 11:21:53 +02:00
Marcin Ziąbek 5010b61565 Merge branch 'main' into richtext
# Conflicts:
#	QuestPDF/Elements/Stack.cs
2021-05-05 00:16:45 +02:00
Marcin Ziąbek 44d7538578 Examples to the documentation 2021-05-05 00:10:48 +02:00
Marcin Ziąbek f8d270bd2a Font measurement improvements 2021-05-04 15:33:10 +02:00
Marcin Ziąbek b894262f7e Improved developer experience via injecting Element() and Component() content in separate containers 2021-05-03 21:04:23 +02:00
Marcin Ziąbek 4423774648 Fixed Grid spacing. Adjusted performance test 2021-05-03 13:32:46 +02:00
Marcin Ziąbek 5d4f1f530a Merge pull request #11 from QuestPDF/tree_stack
Improved Stack and Row
2021-05-03 11:40:57 +02:00
Marcin Ziąbek 3ba8185871 Merge pull request #10 from QuestPDF/2021.05
2021.05 Update
2021-05-03 11:39:59 +02:00
Marcin Ziąbek 78e5cbd098 Minor refactorization 2021-05-03 00:38:48 +02:00
Marcin Ziąbek f792f6c749 Added tests for stack and row, project cleanup 2021-05-02 21:11:08 +02:00
Marcin Ziąbek 0d86039c0c Implemented experimental, tree-based simple stack and row 2021-05-02 14:40:08 +02:00
Marcin Ziąbek f884547d16 First steps with text shaper 2021-04-29 11:18:16 +02:00
Marcin Ziąbek 4a7f9385c4 Improved text positioning 2021-04-28 23:49:22 +02:00
Marcin Ziąbek f4181de3d2 Simple drawing text 2021-04-28 00:24:26 +02:00
36 changed files with 1294 additions and 759 deletions
+111 -19
View File
@@ -1,5 +1,7 @@
using System.Linq;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
@@ -19,20 +21,20 @@ namespace QuestPDF.Examples
//[ShowResult]
[ImageSize(300, 300)]
public void Section(IContainer container)
public void Decoration(IContainer container)
{
container
.Background("#FFF")
.Padding(25)
.Decoration(section =>
.Decoration(decoration =>
{
section
decoration
.Header()
.Background("#888")
.Padding(10)
.Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
section
decoration
.Content()
.Background("#DDD")
.Padding(10)
@@ -135,18 +137,20 @@ namespace QuestPDF.Examples
});
}
//[ShowResult]
[ImageSize(300, 200)]
[ShowResult]
[ImageSize(210, 210)]
public void Debug(IContainer container)
{
container
.Padding(25)
.Debug()
.Padding(-5)
.Row(row =>
.Debug("Grid example", Colors.Blue.Medium)
.Grid(grid =>
{
row.RelativeColumn().Padding(5).Extend().Placeholder();
row.RelativeColumn().Padding(5).Extend().Placeholder();
grid.Columns(3);
grid.Spacing(5);
foreach (var _ in Enumerable.Range(0, 8))
grid.Item().Height(50).Placeholder();
});
}
@@ -178,19 +182,107 @@ namespace QuestPDF.Examples
.AlignRight()
.Grid(grid =>
{
grid.Spacing(5);
grid.VerticalSpacing(20);
grid.HorizontalSpacing(10);
grid.Columns(12);
grid.Element(8).Background("#DDD").Height(50).Padding(5).Text("This is a short text", textStyle);
grid.Element(4).Background("#BBB").Padding(5).Text("Showing how to...", textStyle);
grid.Element(2).Background("#999").Height(50);
grid.Element(4).Background("#AAA").Border(2).BorderColor("#666").Padding(5).Text("...generate", textStyle);
grid.Element(6).Background("#CCC").Padding(5).Text("simple grids", textStyle.Size(18).Bold());
grid.Element(8).Background("#DDD").Height(50);
grid.Item(8).Background("#DDD").Height(50).Padding(5).Text("This is a short text", textStyle);
grid.Item(4).Background("#BBB").Padding(5).Text("Showing how to...", textStyle);
grid.Item(2).Background("#999").Height(50);
grid.Item(4).Background("#AAA").Border(2).BorderColor("#666").Padding(5).Text("...generate", textStyle);
grid.Item(6).Background("#CCC").Padding(5).Text("simple grids", textStyle.Size(18).Bold());
grid.Item(8).Background("#DDD").Height(50);
});
}
//[ShowResult]
[ImageSize(300, 300)]
public void RandomColorMatrix(IContainer container)
{
container
.Padding(25)
.Grid(grid =>
{
grid.Columns(5);
Enumerable
.Range(0, 25)
.Select(x => Placeholders.BackgroundColor())
.ToList()
.ForEach(x => grid.Item().Height(50).Background(x));
});
}
[ShowResult]
//[ShowResult]
[ImageSize(450, 150)]
public void DefinedColors(IContainer container)
{
var colors = new[]
{
Colors.Green.Darken4,
Colors.Green.Darken3,
Colors.Green.Darken2,
Colors.Green.Darken1,
Colors.Green.Medium,
Colors.Green.Lighten1,
Colors.Green.Lighten2,
Colors.Green.Lighten3,
Colors.Green.Lighten4,
Colors.Green.Lighten5,
Colors.Green.Accent1,
Colors.Green.Accent2,
Colors.Green.Accent3,
Colors.Green.Accent4,
};
container
.Padding(25)
.Height(100)
.Row(row =>
{
foreach (var color in colors)
row.RelativeColumn().Background(color);
});
}
//[ShowResult]
[ImageSize(500, 175)]
public void DefinedFonts(IContainer container)
{
var fonts = new[]
{
Fonts.Calibri,
Fonts.Candara,
Fonts.Arial,
Fonts.TimesNewRoman,
Fonts.Consolas,
Fonts.Tahoma,
Fonts.Impact,
Fonts.Trebuchet,
Fonts.ComicSans
};
container
.Padding(25)
.Grid(grid =>
{
grid.Columns(3);
foreach (var font in fonts)
{
grid.Item()
.Border(1)
.BorderColor(Colors.Grey.Medium)
.Padding(10)
.Text(font, TextStyle.Default.FontType(font).Size(16));
}
});
}
//[ShowResult]
[ImageSize(300, 300)]
public void Layers(IContainer container)
{
+212
View File
@@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.Linq;
using QuestPDF.Elements;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Examples
{
public class TextExample : ExampleTestBase
{
[ShowResult]
[ImageSize(1400, 600)]
public void OldText(IContainer container)
{
var fonts = new[]
{
Fonts.Arial,
Fonts.Calibri,
Fonts.Cambria,
Fonts.Candara,
Fonts.ComicSans,
Fonts.Consolas,
Fonts.Corbel,
Fonts.Courier,
Fonts.CourierNew,
Fonts.Georgia,
Fonts.Impact,
Fonts.LucidaConsole,
Fonts.SegoeSD,
Fonts.SegoeUI,
Fonts.Tahoma,
Fonts.TimesNewRoman,
Fonts.TimesRoman,
Fonts.Trebuchet,
Fonts.Verdana,
};
container
.Padding(50)
.Grid(stack =>
{
stack.Spacing(10);
stack.Columns(2);
foreach (var font in fonts)
{
stack
.Element()
.Box()
.Background(Placeholders.BackgroundColor())
.Text($"Lorem ipsum dolor sit amet {font}", TextStyle.Default.Size(24).FontType(font));
}
});
}
//[ShowResult]
[ImageSize(1400, 800)]
public void Test(IContainer container)
{
List<TextElement> Lorem()
{
return new List<TextElement>
{
new TextElement()
{
Style = TextStyle.Default.Size(32).BackgroundColor(Placeholders.BackgroundColor()),
Text = "Podstawowy łaciński Tabela znaków Unicode"
},
new TextElement()
{
Style = TextStyle.Default.FontType("Segoe UI Emoji").Size(32).BackgroundColor(Placeholders.BackgroundColor()),
Text = "✔"
},
new TextElement()
{
Style = TextStyle.Default.FontType("Segoe UI Emoji").Size(32).BackgroundColor(Placeholders.BackgroundColor()),
Text = "🥛"
},
new TextElement()
{
Style = TextStyle.Default.FontType("Segoe UI Emoji").Size(32).BackgroundColor(Placeholders.BackgroundColor()),
Text = "🧀"
},
new TextElement()
{
Style = TextStyle.Default.FontType("Segoe UI Emoji").Size(32).BackgroundColor(Placeholders.BackgroundColor()),
Text = "❤🚵‍♀️"
},
new TextElement()
{
Style = TextStyle.Default.Size(32).BackgroundColor(Placeholders.BackgroundColor()),
Text = "Lorem ipsum "
},
new TextElement()
{
Style = TextStyle.Default.Size(24).BackgroundColor(Placeholders.BackgroundColor()),
Text = " dolor "
},
new TextElement()
{
Style = TextStyle.Default.Size(64).BackgroundColor(Placeholders.BackgroundColor()),
Text = " sijt "
},
new TextElement()
{
Style = TextStyle.Default.Size(32).BackgroundColor(Placeholders.BackgroundColor()),
Text = " amet"
}
};
}
Func<List<TextElement>> Source = () => Split(RandomText());
container
.Padding(50)
.Box().Border(1).Stack(stack =>
{
stack
.Element()
.Box()
//.Background(Placeholders.BackgroundColor())
.Element(new TextRun()
{
Elements = Lorem()
});
stack
.Element()
.Box()
//.Background(Placeholders.BackgroundColor())
.Element(new TextRun()
{
Elements = Source()
});
stack
.Element()
.Box()
//.Background(Placeholders.BackgroundColor())
.Element(new TextRun()
{
Elements = Source()
});
stack
.Element()
.Box()
//.Background(Placeholders.BackgroundColor())
.Element(new TextRun()
{
Elements = Source()
});
stack
.Element()
.Box()
//.Background(Placeholders.BackgroundColor())
.Element(new TextRun()
{
Elements = Source()
});
stack
.Element()
.Box()
//.Background(Placeholders.BackgroundColor())
.Element(new TextRun()
{
Elements = Source()
});
});
}
List<TextElement> RandomText()
{
var sizes = new[] { 24, 32, 48, 64};
return Placeholders
.Sentence()
.Split(" ")
.Select(x => new TextElement
{
Text = $"{x} ",
Style = TextStyle
.Default
//.Size(sizes[Placeholders.Random.Next(0, 3)])
.Size(24)
.BackgroundColor(Placeholders.BackgroundColor())
//.LineHeight((float)Placeholders.Random.NextDouble() / 2 + 1f)
.LineHeight(1.2f)
})
.ToList();
}
List<TextElement> Split(List<TextElement> elements)
{
return elements
.SelectMany(x => x
.Text
.Select(y => new TextElement
{
Style = x.Style,
Text = y.ToString()
}))
.ToList();
}
}
}
+6 -7
View File
@@ -18,8 +18,8 @@ namespace QuestPDF.ReportSample
HeaderFields = HeaderFields(),
LogoData = Helpers.GetImage("Logo.png"),
Sections = Enumerable.Range(0, 8).Select(x => GenerateSection()).ToList(),
Photos = Enumerable.Range(2, 6).Select(x => GetReportPhotos()).ToList()
Sections = Enumerable.Range(0, 50).Select(x => GenerateSection()).ToList(),
Photos = Enumerable.Range(0, 30).Select(x => GetReportPhotos()).ToList()
};
List<ReportHeaderField> HeaderFields()
@@ -52,8 +52,8 @@ namespace QuestPDF.ReportSample
ReportSection GenerateSection()
{
var sectionLength = Helpers.Random.NextDouble() > 0.75
? Helpers.Random.Next(10, 20)
: Helpers.Random.Next(3, 11);
? Helpers.Random.Next(20, 40)
: Helpers.Random.Next(5, 10);
return new ReportSection
{
@@ -100,8 +100,7 @@ namespace QuestPDF.ReportSample
{
Label = "Photos",
Photos = Enumerable
.Range(0, Helpers.Random.Next(1, 10))
.Select(x => Helpers.Random.Next(0, 128))
.Range(0, Helpers.Random.Next(1, 15))
.Select(x => Placeholders.Image(400, 300))
.ToList()
};
@@ -111,7 +110,7 @@ namespace QuestPDF.ReportSample
{
return new ReportPhoto()
{
PhotoData = Placeholders.Image(400, 300),
PhotoData = Placeholders.Image(800, 600),
Comments = Placeholders.Sentence(),
Date = DateTime.Now - TimeSpan.FromDays(Helpers.Random.NextDouble() * 100),
@@ -20,8 +20,8 @@ namespace QuestPDF.ReportSample.Layouts
.Stack(stack =>
{
stack.Spacing(5);
stack.Item(PhotoWithMaps);
stack.Item(PhotoDetails);
stack.Item().Element(PhotoWithMaps);
stack.Item().Element(PhotoDetails);
});
}
@@ -48,13 +48,13 @@ namespace QuestPDF.ReportSample.Layouts
{
grid.Columns(6);
grid.Element().LabelCell().Text("Date", Typography.Normal);
grid.Element(2).ValueCell().Text(Model.Date?.ToString("g") ?? string.Empty, Typography.Normal);
grid.Element().LabelCell().Text("Location", Typography.Normal);
grid.Element(2).ValueCell().Text(Model.Location.Format(), Typography.Normal);
grid.Item().LabelCell().Text("Date", Typography.Normal);
grid.Item(2).ValueCell().Text(Model.Date?.ToString("g") ?? string.Empty, Typography.Normal);
grid.Item().LabelCell().Text("Location", Typography.Normal);
grid.Item(2).ValueCell().Text(Model.Location.Format(), Typography.Normal);
grid.Element().LabelCell().Text("Comments", Typography.Normal);
grid.Element(5).ValueCell().Text(Model.Comments, Typography.Normal);
grid.Item().LabelCell().Text("Comments", Typography.Normal);
grid.Item(5).ValueCell().Text(Model.Comments, Typography.Normal);
});
}
}
@@ -17,14 +17,14 @@ namespace QuestPDF.ReportSample.Layouts
{
container
.EnsureSpace()
.Decoration(section =>
.Decoration(decoration =>
{
section
decoration
.Header()
.PaddingBottom(5)
.Text(Model.Title, Typography.Headline);
section.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Stack(stack =>
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Stack(stack =>
{
foreach (var part in Model.Parts)
{
@@ -72,12 +72,12 @@ namespace QuestPDF.ReportSample.Layouts
return;
}
container.Debug("Photo gallery").Grid(grid =>
container.Debug("Photos").Grid(grid =>
{
grid.Spacing(5);
grid.Columns(3);
model.Photos.ForEach(x => grid.Element().AspectRatio(4 / 3f).Image(Placeholders.Image));
model.Photos.ForEach(x => grid.Item().AspectRatio(4 / 3f).Image(Placeholders.Image));
});
}
}
@@ -30,8 +30,8 @@ namespace QuestPDF.ReportSample.Layouts
.PaddingHorizontal(50)
.Page(page =>
{
page.Header(ComposeHeader);
page.Content(ComposeContent);
page.Header().Element(ComposeHeader);
page.Content().Element(ComposeContent);
page.Footer().AlignCenter().PageNumber("Page {number}");
});
}
@@ -57,7 +57,7 @@ namespace QuestPDF.ReportSample.Layouts
foreach (var field in Model.HeaderFields)
{
grid.Element().Stack(row =>
grid.Item().Stack(row =>
{
row.Item().AlignLeft().Text(field.Label, Typography.Normal.SemiBold());
row.Item().Text(field.Value, Typography.Normal);
@@ -16,21 +16,21 @@ namespace QuestPDF.ReportSample.Layouts
public void Compose(IContainer container)
{
container
.Decoration(section =>
.Decoration(decoration =>
{
section
decoration
.Header()
.PaddingBottom(5)
.Text("Table of contents", Typography.Headline);
section.Content().Stack(stack =>
decoration.Content().Stack(stack =>
{
stack.Spacing(5);
for (var i = 0; i < Sections.Count; i++)
stack.Item(c => DrawLink(c, i+1, Sections[i].Title));
stack.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
stack.Item(c => DrawLink(c, Sections.Count+1, "Photos"));
stack.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
});
});
}
+3 -1
View File
@@ -25,9 +25,11 @@ namespace QuestPDF.ReportSample
[Test]
public void PerformanceBenchmark()
{
// target document length should be around 100 pages
// test size
const int testSize = 100;
const decimal performanceTarget = 5; // documents per second
const decimal performanceTarget = 1; // documents per second
// create report models
var reports = Enumerable
+46 -3
View File
@@ -7,10 +7,10 @@ using QuestPDF.UnitTests.TestEngine;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class WrapWhenLittleSpaceTests
public class EnsureSpaceTests
{
[Test]
public void Measure_ReturnsWrap_WhenNotEnoughSpace()
public void Measure_ReturnsWrap_WhenChildReturnsWrap()
{
TestPlan
.For(x => new EnsureSpace
@@ -19,11 +19,54 @@ namespace QuestPDF.UnitTests
MinHeight = 200
})
.MeasureElement(new Size(400, 100))
.ExpectChildMeasure(new Size(400, 100), new Wrap())
.CheckMeasureResult(new Wrap());
}
[Test]
public void Measure_Continues_WhenEnoughSpace()
public void Measure_ReturnsWrap_WhenChildReturnsPartialRender_AndNotEnoughSpace()
{
TestPlan
.For(x => new EnsureSpace
{
Child = x.CreateChild(),
MinHeight = 200
})
.MeasureElement(new Size(400, 100))
.ExpectChildMeasure(new Size(400, 100), new PartialRender(300, 50))
.CheckMeasureResult(new Wrap());
}
[Test]
public void Measure_ReturnsPartialRender_WhenChildReturnsPartialRender_AndEnoughSpace()
{
TestPlan
.For(x => new EnsureSpace
{
Child = x.CreateChild(),
MinHeight = 200
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure(new Size(400, 300), new PartialRender(300, 250))
.CheckMeasureResult(new PartialRender(300, 250));
}
[Test]
public void Measure_ReturnsFullRender_WhenChildReturnsFullRender_AndNotEnoughSpace()
{
TestPlan
.For(x => new EnsureSpace
{
Child = x.CreateChild(),
MinHeight = 200
})
.MeasureElement(new Size(400, 100))
.ExpectChildMeasure(new Size(400, 100), new FullRender(300, 50))
.CheckMeasureResult(new FullRender(300, 50));
}
[Test]
public void Measure_ReturnsFullRender_WhenChildReturnsFullRender_AndEnoughSpace()
{
TestPlan
.For(x => new EnsureSpace
+118 -47
View File
@@ -1,4 +1,5 @@
using FluentAssertions;
using FluentAssertions.Equivalency;
using NUnit.Framework;
using QuestPDF.Elements;
using QuestPDF.Fluent;
@@ -29,40 +30,40 @@ namespace QuestPDF.UnitTests
{
grid.AlignLeft();
grid.Element(6).Element(childA);
grid.Element(4).Element(childB);
grid.Element(4).Element(childC);
grid.Element(2).Element(childD);
grid.Element(8).Element(childE);
grid.Item(6).Element(childA);
grid.Item(4).Element(childB);
grid.Item(4).Element(childC);
grid.Item(2).Element(childD);
grid.Item(8).Element(childE);
});
// 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(2).Element(new Empty());
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(6).Element(new Empty());
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(4).Element(new Empty());
row.RelativeColumn(8).Container().Element(childE);
row.RelativeColumn(4);
});
});
structure.Child.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering().IncludingAllRuntimeProperties());
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties());
}
[Test]
@@ -83,43 +84,43 @@ namespace QuestPDF.UnitTests
{
grid.AlignCenter();
grid.Element(6).Element(childA);
grid.Element(4).Element(childB);
grid.Element(4).Element(childC);
grid.Element(2).Element(childD);
grid.Element(8).Element(childE);
grid.Item(6).Element(childA);
grid.Item(4).Element(childB);
grid.Item(4).Element(childC);
grid.Item(2).Element(childD);
grid.Item(8).Element(childE);
});
// assert
var expected = new Container();
expected.Stack(stack =>
expected.Container().Stack(stack =>
{
stack.Item().Row(row =>
{
row.RelativeColumn(1).Element(new Empty());
row.RelativeColumn(6).Element(childA);
row.RelativeColumn(4).Element(childB);
row.RelativeColumn(1).Element(new Empty());
row.RelativeColumn(1);
row.RelativeColumn(6).Container().Element(childA);
row.RelativeColumn(4).Container().Element(childB);
row.RelativeColumn(1);
});
stack.Item().Row(row =>
{
row.RelativeColumn(3).Element(new Empty());
row.RelativeColumn(4).Element(childC);
row.RelativeColumn(2).Element(childD);
row.RelativeColumn(3).Element(new Empty());
row.RelativeColumn(3);
row.RelativeColumn(4).Container().Element(childC);
row.RelativeColumn(2).Container().Element(childD);
row.RelativeColumn(3);
});
stack.Item().Row(row =>
{
row.RelativeColumn(2).Element(new Empty());
row.RelativeColumn(8).Element(childE);
row.RelativeColumn(2).Element(new Empty());
row.RelativeColumn(2);
row.RelativeColumn(8).Container().Element(childE);
row.RelativeColumn(2);
});
});
structure.Child.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering().IncludingAllRuntimeProperties());
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties());
}
[Test]
@@ -140,11 +141,11 @@ namespace QuestPDF.UnitTests
{
grid.AlignRight();
grid.Element(6).Element(childA);
grid.Element(4).Element(childB);
grid.Element(4).Element(childC);
grid.Element(2).Element(childD);
grid.Element(8).Element(childE);
grid.Item(6).Element(childA);
grid.Item(4).Element(childB);
grid.Item(4).Element(childC);
grid.Item(2).Element(childD);
grid.Item(8).Element(childE);
});
// assert
@@ -154,26 +155,96 @@ namespace QuestPDF.UnitTests
{
stack.Item().Row(row =>
{
row.RelativeColumn(2).Element(new Empty());
row.RelativeColumn(6).Element(childA);
row.RelativeColumn(4).Element(childB);
row.RelativeColumn(2);
row.RelativeColumn(6).Container().Element(childA);
row.RelativeColumn(4).Container().Element(childB);
});
stack.Item().Row(row =>
{
row.RelativeColumn(6).Element(new Empty());
row.RelativeColumn(4).Element(childC);
row.RelativeColumn(2).Element(childD);
row.RelativeColumn(6);
row.RelativeColumn(4).Container().Element(childC);
row.RelativeColumn(2).Container().Element(childD);
});
stack.Item().Row(row =>
{
row.RelativeColumn(4).Element(new Empty());
row.RelativeColumn(8).Element(childE);
row.RelativeColumn(4);
row.RelativeColumn(8).Container().Element(childE);
});
});
structure.Child.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering().IncludingAllRuntimeProperties());
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties());
}
#endregion
#region Spacing
[Test]
public void Spacing()
{
// arrange
var structure = new Container();
var childA = TestPlan.CreateUniqueElement();
var childB = TestPlan.CreateUniqueElement();
var childC = TestPlan.CreateUniqueElement();
var childD = TestPlan.CreateUniqueElement();
// act
structure
.Grid(grid =>
{
grid.Columns(16);
grid.AlignCenter();
grid.VerticalSpacing(20);
grid.HorizontalSpacing(30);
grid.Item(5).Element(childA);
grid.Item(5).Element(childB);
grid.Item(10).Element(childC);
grid.Item(12).Element(childD);
});
// assert
var expected = new Container();
expected.Container().Stack(stack =>
{
stack.Spacing(20);
stack.Item().Row(row =>
{
row.Spacing(30);
row.RelativeColumn(3);
row.RelativeColumn(5).Container().Element(childA);
row.RelativeColumn(5).Container().Element(childB);
row.RelativeColumn(3);
});
stack.Item().Row(row =>
{
row.Spacing(30);
row.RelativeColumn(3);
row.RelativeColumn(10).Container().Element(childC);
row.RelativeColumn(3);
});
stack.Item().Row(row =>
{
row.Spacing(30);
row.RelativeColumn(2);
row.RelativeColumn(12).Container().Element(childD);
row.RelativeColumn(2);
});
});
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties().AllowingInfiniteRecursion());
}
#endregion
+93 -89
View File
@@ -1,8 +1,7 @@
using System.Collections.Generic;
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;
namespace QuestPDF.UnitTests
@@ -10,103 +9,108 @@ namespace QuestPDF.UnitTests
[TestFixture]
public class RowTests
{
#region Spacing
#region Measure
[Test]
public void Fluent_WithoutSpacing()
public void Measure_ReturnsWrap_WhenLeftChildReturnsWrap()
{
// arrange
var structure = new Container();
var childA = TestPlan.CreateUniqueElement();
var childB = TestPlan.CreateUniqueElement();
var childC = TestPlan.CreateUniqueElement();
// act
structure
.Row(stack =>
TestPlan
.For(x => new SimpleRow
{
stack.Spacing(0);
stack.ConstantColumn(100).Element(childA);
stack.RelativeColumn(2).Element(childB);
stack.RelativeColumn(3).Element(childC);
});
// assert
var expected = new Row()
{
Children = new List<RowElement>
{
new ConstantRowElement(100)
{
Child = childA
},
new RelativeRowElement(2)
{
Child = childB
},
new RelativeRowElement(3)
{
Child = childC
},
}
};
structure.Child.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering().IncludingAllRuntimeProperties());
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), new Wrap())
.CheckMeasureResult(new Wrap());
}
[Test]
public void Fluent_WithSpacing()
public void Measure_ReturnsWrap_WhenRightChildReturnsWrap()
{
// arrange
var structure = new Container();
var childA = TestPlan.CreateUniqueElement();
var childB = TestPlan.CreateUniqueElement();
var childC = TestPlan.CreateUniqueElement();
// act
structure
.Row(stack =>
TestPlan
.For(x => new SimpleRow
{
stack.Spacing(25);
stack.ConstantColumn(100).Element(childA);
stack.RelativeColumn(2).Element(childB);
stack.RelativeColumn(3).Element(childC);
});
// assert
var expected = new Padding
{
Right = -25,
Child = new Row()
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), new FullRender(250, 150))
.ExpectChildMeasure("right", new Size(150, 300), new Wrap())
.CheckMeasureResult(new Wrap());
}
[Test]
public void Measure_ReturnsPartialRender_WhenLeftChildReturnsPartialRender()
{
TestPlan
.For(x => new SimpleRow
{
Children = new List<RowElement>
{
new ConstantRowElement(100)
{
Child = childA
},
new ConstantRowElement(25),
new RelativeRowElement(2)
{
Child = childB
},
new ConstantRowElement(25),
new RelativeRowElement(3)
{
Child = childC
},
new ConstantRowElement(25)
}
}
};
structure.Child.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering().IncludingAllRuntimeProperties());
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), new PartialRender(250, 150))
.ExpectChildMeasure("right", new Size(150, 300), new FullRender(100, 100))
.CheckMeasureResult(new PartialRender(350, 150));
}
[Test]
public void Measure_ReturnsPartialRender_WhenRightChildReturnsPartialRender()
{
TestPlan
.For(x => new SimpleRow
{
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), new FullRender(250, 150))
.ExpectChildMeasure("right", new Size(150, 300), new PartialRender(100, 100))
.CheckMeasureResult(new PartialRender(350, 150));
}
[Test]
public void Measure_ReturnsFullRender_WhenBothChildrenReturnFullRender()
{
TestPlan
.For(x => new SimpleRow
{
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), new FullRender(200, 150))
.ExpectChildMeasure("right", new Size(200, 300), new FullRender(100, 100))
.CheckMeasureResult(new FullRender(300, 150));
}
#endregion
#region Draw
[Test]
public void Draw()
{
TestPlan
.For(x => new SimpleRow
{
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), new FullRender(250, 150))
.ExpectChildDraw("left", new Size(250, 300))
.ExpectCanvasTranslate(250, 0)
.ExpectChildDraw("right", new Size(150, 300))
.ExpectCanvasTranslate(-250, 0)
.CheckDrawResult();
}
#endregion
// TODO: add tests for the spacing property
// TODO: add tests for the tree builder method
// TODO: add tests for relative column
}
}
+188 -243
View File
@@ -1,9 +1,6 @@
using System.Collections.Generic;
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;
@@ -13,278 +10,226 @@ namespace QuestPDF.UnitTests
public class StackTests
{
#region Measure
[Test]
public void Measure_NoChildren_Empty()
public void Measure_ReturnsWrap_WhenFirstChildWraps()
{
TestPlan
.For(x => new Stack())
.MeasureElement(new Size(500, 1000))
.CheckMeasureResult(new FullRender(Size.Zero));
}
[Test]
public void Measure_ReturnsWrap_WhenPageable_AndAnyChildReturnsWrap()
{
TestPlan
.For(x => new Stack
.For(x => new SimpleStack
{
Children = new []
{
x.CreateChild("a"),
x.CreateChild("b"),
x.CreateChild("c"),
x.CreateChild("d")
}
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.MeasureElement(new Size(500, 1000))
.ExpectChildMeasure("a", expectedInput: new Size(500, 1000), returns: new FullRender(500, 200))
.ExpectChildMeasure("b", expectedInput: new Size(500, 800), returns: new FullRender(500, 300))
.ExpectChildMeasure("c", expectedInput: new Size(500, 500), returns: new Wrap())
.CheckMeasureResult(new PartialRender(500, 500));
}
[Test]
public void Measure_ReturnsWrap_WhenPageable_AndFirstChildReturnsWrap()
{
TestPlan
.For(x => new Stack
{
Children = new []
{
x.CreateChild("a"),
x.CreateChild("b"),
x.CreateChild("c")
}
})
.MeasureElement(new Size(500, 1000))
.ExpectChildMeasure("a", expectedInput: new Size(500, 1000), returns: new Wrap())
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), new Wrap())
.CheckMeasureResult(new Wrap());
}
#endregion
#region Combined
[Test]
public void WhenPageable_AndChildReturnsWrap()
public void Measure_ReturnsPartialRender_WhenFirstChildReturnsPartialRender()
{
TestPlan
.For(x => new Stack
.For(x => new SimpleStack
{
Children = new[]
{
x.CreateChild("a"),
x.CreateChild("b"),
x.CreateChild("c")
}
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
// page 1: measure
.MeasureElement(new Size(500, 1000))
.ExpectChildMeasure("a", expectedInput: new Size(500, 1000), returns: new FullRender(400, 200))
.ExpectChildMeasure("b", expectedInput: new Size(500, 800), returns: new Wrap())
.CheckMeasureResult(new PartialRender(500, 200))
// page 1: draw
.DrawElement(new Size(500, 1000))
.ExpectChildMeasure("a", expectedInput: new Size(500, 1000), returns: new FullRender(400, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("a", new Size(500, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildMeasure("b", expectedInput: new Size(500, 800), returns: new Wrap())
.CheckDrawResult()
// // page 2: measure
.MeasureElement(new Size(500, 900))
.ExpectChildMeasure("b", expectedInput: new Size(500, 900), returns: new FullRender(300, 200))
.ExpectChildMeasure("c", expectedInput: new Size(500, 700), returns: new FullRender(300, 300))
.CheckMeasureResult(new FullRender(500, 500))
// page 2: draw
.DrawElement(new Size(500, 900))
.ExpectChildMeasure("b", expectedInput: new Size(500, 900), returns: new FullRender(300, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("b", new Size(500, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildMeasure("c", expectedInput: new Size(500, 700), returns: new FullRender(300, 300))
.ExpectCanvasTranslate(0, 200)
.ExpectChildDraw("c", new Size(500, 300))
.ExpectCanvasTranslate(0, -200)
.CheckDrawResult();
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), new PartialRender(300, 200))
.CheckMeasureResult(new PartialRender(300, 200));
}
[Test]
public void WhenPageable_AndChildReturnsPartialRender()
public void Measure_ReturnsPartialRender_WhenSecondChildWraps()
{
TestPlan
.For(x => new Stack
.For(x => new SimpleStack
{
Children = new[]
{
x.CreateChild("a"),
x.CreateChild("b"),
x.CreateChild("c")
}
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
// page 1: measure
.MeasureElement(new Size(500, 1000))
.ExpectChildMeasure("a", expectedInput: new Size(500, 1000), returns: new FullRender(400, 200))
.ExpectChildMeasure("b", expectedInput: new Size(500, 800), returns: new PartialRender(300, 300))
.CheckMeasureResult(new PartialRender(500, 500))
// page 1: draw
.DrawElement(new Size(500, 1000))
.ExpectChildMeasure("a", expectedInput: new Size(500, 1000), returns: new FullRender(400, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("a", new Size(500, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildMeasure("b", expectedInput: new Size(500, 800), returns: new PartialRender(300, 300))
.ExpectCanvasTranslate(0, 200)
.ExpectChildDraw("b", new Size(500, 300))
.ExpectCanvasTranslate(0, -200)
.CheckDrawResult()
// page 2: measure
.MeasureElement(new Size(500, 900))
.ExpectChildMeasure("b", expectedInput: new Size(500, 900), returns: new FullRender(300, 200))
.ExpectChildMeasure("c", expectedInput: new Size(500, 700), returns: new FullRender(300, 300))
.CheckMeasureResult(new FullRender(500, 500))
// page 2: draw
.DrawElement(new Size(500, 900))
.ExpectChildMeasure("b", expectedInput: new Size(500, 900), returns: new FullRender(300, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("b", new Size(500, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildMeasure("c", expectedInput: new Size(500, 700), returns: new FullRender(300, 300))
.ExpectCanvasTranslate(0, 200)
.ExpectChildDraw("c", new Size(500, 300))
.ExpectCanvasTranslate(0, -200)
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), new FullRender(200, 100))
.ExpectChildMeasure("second", new Size(400, 200), new Wrap())
.CheckMeasureResult(new PartialRender(200, 100));
}
[Test]
public void Measure_ReturnsPartialRender_WhenSecondChildReturnsPartialRender()
{
TestPlan
.For(x => new SimpleStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), new FullRender(200, 100))
.ExpectChildMeasure("second", new Size(400, 200), new PartialRender(300, 150))
.CheckMeasureResult(new PartialRender(300, 250));
}
[Test]
public void Measure_ReturnsFullRender_WhenSecondChildReturnsFullRender()
{
TestPlan
.For(x => new SimpleStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), new FullRender(200, 100))
.ExpectChildMeasure("second", new Size(400, 200), new FullRender(100, 50))
.CheckMeasureResult(new FullRender(200, 150));
}
[Test]
public void Measure_UsesEmpty_WhenFirstChildIsRendered()
{
TestPlan
.For(x => new SimpleStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second"),
IsFirstRendered = true
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("second", new Size(400, 300), new FullRender(200, 300))
.CheckMeasureResult(new FullRender(200, 300));
}
#endregion
#region Draw
[Test]
public void Draw_WhenFirstChildWraps()
{
TestPlan
.For(x => new SimpleStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), new Wrap())
.CheckDrawResult();
}
#endregion
#region Spacing
[Test]
public void Fluent_WithoutSpacing()
public void Draw_WhenFirstChildPartiallyRenders()
{
// arrange
var structure = new Container();
var childA = TestPlan.CreateUniqueElement();
var childB = TestPlan.CreateUniqueElement();
var childC = TestPlan.CreateUniqueElement();
// act
structure
.Stack(stack =>
TestPlan
.For(x => new SimpleStack
{
stack.Spacing(0);
stack.Item().Element(childA);
stack.Item().Element(childB);
stack.Item().Element(childC);
});
// assert
var expected = new Stack
{
Children = new List<Element>
{
new Container
{
Child = childA
},
new Container
{
Child = childB
},
new Container
{
Child = childC
}
}
};
structure.Child.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering().IncludingAllRuntimeProperties());
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), new PartialRender(200, 100))
.ExpectChildDraw("first", new Size(400, 100))
.CheckDrawResult();
}
[Test]
public void Fluent_WithSpacing()
public void Draw_WhenFirstChildFullyRenders_AndSecondChildWraps()
{
// arrange
var structure = new Container();
var childA = TestPlan.CreateUniqueElement();
var childB = TestPlan.CreateUniqueElement();
var childC = TestPlan.CreateUniqueElement();
// act
structure
.Stack(stack =>
TestPlan
.For(x => new SimpleStack
{
stack.Spacing(100);
stack.Item().Element(childA);
stack.Item().Element(childB);
stack.Item().Element(childC);
});
// assert
var expected = new Padding
{
Bottom = -100,
Child = new Stack
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), new FullRender(200, 100))
.ExpectChildDraw("first", new Size(400, 100))
.ExpectChildMeasure("second", new Size(400, 200), new Wrap())
.CheckDrawResult();
}
[Test]
public void Draw_WhenFirstChildFullyRenders_AndSecondChildPartiallyRenders()
{
TestPlan
.For(x => new SimpleStack
{
Children = new List<Element>
{
new Padding
{
Bottom = 100,
Child = new Container
{
Child = childA
}
},
new Padding
{
Bottom = 100,
Child = new Container
{
Child = childB
}
},
new Padding
{
Bottom = 100,
Child = new Container
{
Child = childC
}
},
}
}
};
structure.Child.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering().IncludingAllRuntimeProperties());
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), new FullRender(200, 100))
.ExpectChildDraw("first", new Size(400, 100))
.ExpectChildMeasure("second", new Size(400, 200), new PartialRender(250, 150))
.ExpectCanvasTranslate(0, 100)
.ExpectChildDraw("second", new Size(400, 150))
.ExpectCanvasTranslate(0, -100)
.CheckDrawResult();
}
[Test]
public void Draw_WhenFirstChildFullyRenders_AndSecondChildFullyRenders()
{
TestPlan
.For(x => new SimpleStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), new FullRender(200, 100))
.ExpectChildDraw("first", new Size(400, 100))
.ExpectChildMeasure("second", new Size(400, 200), new FullRender(250, 150))
.ExpectCanvasTranslate(0, 100)
.ExpectChildDraw("second", new Size(400, 150))
.ExpectCanvasTranslate(0, -100)
.CheckDrawResult();
}
[Test]
public void Draw_UsesEmpty_WhenFirstChildIsRendered()
{
TestPlan
.For(x => new SimpleStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second"),
IsFirstRendered = true
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("second", new Size(400, 300), new PartialRender(200, 300))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("second", new Size(400, 300))
.ExpectCanvasTranslate(0, 0)
.CheckState<SimpleStack>(x => x.IsFirstRendered)
.CheckDrawResult();
}
[Test]
public void Draw_TogglesFirstRenderedFlag_WhenSecondFullyRenders()
{
TestPlan
.For(x => new SimpleStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second"),
IsFirstRendered = true
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("second", new Size(400, 300), new FullRender(200, 300))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("second", new Size(400, 300))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult()
.CheckState<SimpleStack>(x => !x.IsFirstRendered);
}
#endregion
// TODO: add tests for the spacing property
// TODO: add tests for the tree builder method
}
}
+36 -23
View File
@@ -49,9 +49,9 @@ namespace QuestPDF.UnitTests.TestEngine
TranslateFunc = position =>
{
var expected = GetExpected<CanvasTranslateOperationBase>();
Assert.AreEqual(expected.Position.X, position.X);
Assert.AreEqual(expected.Position.Y, position.Y);
Assert.AreEqual(expected.Position.X, position.X, "Translate X");
Assert.AreEqual(expected.Position.Y, position.Y, "Translate Y");
//position.Should().BeEquivalentTo(expected.Position);
},
@@ -59,13 +59,13 @@ namespace QuestPDF.UnitTests.TestEngine
{
var expected = GetExpected<CanvasDrawRectangleOperationBase>();
Assert.AreEqual(expected.Position.X, position.X);
Assert.AreEqual(expected.Position.Y, position.Y);
Assert.AreEqual(expected.Position.X, position.X, "Draw rectangle: X");
Assert.AreEqual(expected.Position.Y, position.Y, "Draw rectangle: Y");
Assert.AreEqual(expected.Size.Width, size.Width);
Assert.AreEqual(expected.Size.Height, size.Height);
Assert.AreEqual(expected.Size.Width, size.Width, "Draw rectangle: width");
Assert.AreEqual(expected.Size.Height, size.Height, "Draw rectangle: height");
Assert.AreEqual(expected.Color, color);
Assert.AreEqual(expected.Color, color, "Draw rectangle: color");
/*position.Should().BeEquivalentTo(expected.Position);
size.Should().BeEquivalentTo(expected.Size);
@@ -77,12 +77,12 @@ namespace QuestPDF.UnitTests.TestEngine
Assert.AreEqual(expected.Text, text);
Assert.AreEqual(expected.Position.X, position.X);
Assert.AreEqual(expected.Position.Y, position.Y);
Assert.AreEqual(expected.Position.X, position.X, "Draw text: X");
Assert.AreEqual(expected.Position.Y, position.Y, "Draw text: Y");
Assert.AreEqual(expected.Style.Color, style.Color);
Assert.AreEqual(expected.Style.FontType, style.FontType);
Assert.AreEqual(expected.Style.Size, style.Size);
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);
@@ -92,11 +92,11 @@ namespace QuestPDF.UnitTests.TestEngine
{
var expected = GetExpected<CanvasDrawImageOperationBase>();
Assert.AreEqual(expected.Position.X, position.X);
Assert.AreEqual(expected.Position.Y, position.Y);
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);
Assert.AreEqual(expected.Size.Height, size.Height);
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);*/
@@ -117,8 +117,8 @@ namespace QuestPDF.UnitTests.TestEngine
Assert.AreEqual(expected.ChildId, id);
Assert.AreEqual(expected.Input.Width, availableSpace.Width);
Assert.AreEqual(expected.Input.Height, availableSpace.Height);
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);
@@ -131,8 +131,8 @@ namespace QuestPDF.UnitTests.TestEngine
Assert.AreEqual(expected.ChildId, id);
Assert.AreEqual(expected.Input.Width, availableSpace.Width);
Assert.AreEqual(expected.Input.Height, availableSpace.Height);
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);*/
@@ -214,8 +214,8 @@ namespace QuestPDF.UnitTests.TestEngine
if (expectedSize != null)
{
Assert.AreEqual(expectedSize.Width, actualSize.Width);
Assert.AreEqual(expectedSize.Height, actualSize.Height);
Assert.AreEqual(expectedSize.Width, actualSize.Width, "Measure: width");
Assert.AreEqual(expectedSize.Height, actualSize.Height, "Measure: height");
}
return this;
@@ -227,6 +227,19 @@ namespace QuestPDF.UnitTests.TestEngine
return this;
}
public TestPlan CheckState(Func<Element, bool> condition)
{
Assert.IsTrue(condition(Element), "Checking condition");
return this;
}
public TestPlan CheckState<T>(Func<T, bool> condition) where T : Element
{
Assert.IsTrue(Element is T);
Assert.IsTrue(condition(Element as T), "Checking condition");
return this;
}
public static Element CreateUniqueElement()
{
return new Text
+1 -1
View File
@@ -37,7 +37,7 @@ namespace QuestPDF.Drawing
Typeface = SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant),
TextSize = style.Size,
TextEncoding = SKTextEncoding.Utf32,
TextAlign = style.Alignment switch
{
HorizontalAlignment.Left => SKTextAlign.Left,
+61 -3
View File
@@ -1,13 +1,71 @@
using System;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal enum DecorationType
{
Prepend,
Append
}
internal class SimpleDecoration : Element
{
public Element DecorationElement { get; set; } = Empty.Instance;
public Element ContentElement { get; set; } = Empty.Instance;
public DecorationType Type { get; set; }
internal override ISpacePlan Measure(Size availableSpace)
{
var decorationMeasure = DecorationElement?.Measure(availableSpace);
if (decorationMeasure is Wrap || decorationMeasure is PartialRender)
return new Wrap();
var decorationSize = decorationMeasure as Size ?? Size.Zero;
var contentMeasure = ContentElement?.Measure(new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height)) ?? new FullRender(Size.Zero);
if (contentMeasure is Wrap)
return new Wrap();
var contentSize = contentMeasure as Size ?? Size.Zero;
var resultSize = new Size(availableSpace.Width, decorationSize.Height + contentSize.Height);
if (contentSize is PartialRender)
return new PartialRender(resultSize);
if (contentSize is FullRender)
return new FullRender(resultSize);
throw new NotSupportedException();
}
internal override void Draw(ICanvas canvas, Size availableSpace)
{
var decorationSize = DecorationElement?.Measure(availableSpace) as Size ?? Size.Zero;
var contentSize = new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height);
var translateHeight = Type == DecorationType.Prepend ? decorationSize.Height : contentSize.Height;
Action drawDecoration = () => DecorationElement?.Draw(canvas, new Size(availableSpace.Width, decorationSize.Height));
Action drawContent = () => ContentElement?.Draw(canvas, new Size (availableSpace.Width, contentSize.Height));
var first = Type == DecorationType.Prepend ? drawDecoration : drawContent;
var second = Type == DecorationType.Prepend ? drawContent : drawDecoration;
first();
canvas.Translate(new Position(0, translateHeight));
second();
canvas.Translate(new Position(0, -translateHeight));
}
}
internal class Decoration : IComponent
{
public Element Header { get; set; } = new Empty();
public Element Content { get; set; } = new Empty();
public Element Footer { get; set; } = new Empty();
public Element Header { get; set; } = Empty.Instance;
public Element Content { get; set; } = Empty.Instance;
public Element Footer { get; set; } = Empty.Instance;
public void Compose(IContainer container)
{
+2
View File
@@ -5,6 +5,8 @@ namespace QuestPDF.Elements
{
internal class Empty : Element
{
internal static Empty Instance { get; } = new Empty();
internal override ISpacePlan Measure(Size availableSpace)
{
return new FullRender(Size.Zero);
+1 -1
View File
@@ -30,7 +30,7 @@ namespace QuestPDF.Elements
container.Stack(stack =>
{
stack.Spacing(HorizontalSpacing);
stack.Spacing(VerticalSpacing);
while (ChildrenQueue.Any())
stack.Item().Row(BuildRow);
+7 -7
View File
@@ -5,17 +5,17 @@ namespace QuestPDF.Elements
{
internal class Page : IComponent
{
public Element Header { get; set; } = new Empty();
public Element Content { get; set; } = new Empty();
public Element Footer { get; set; } = new Empty();
public Element Header { get; set; } = Empty.Instance;
public Element Content { get; set; } = Empty.Instance;
public Element Footer { get; set; } = Empty.Instance;
public void Compose(IContainer container)
{
container.Decoration(section =>
container.Decoration(decoration =>
{
section.Header().Element(Header);
section.Content().Extend().Element(Content);
section.Footer().Element(Footer);
decoration.Header().Element(Header);
decoration.Content().Extend().Element(Content);
decoration.Footer().Element(Footer);
});
}
}
+107 -49
View File
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
@@ -31,78 +33,134 @@ namespace QuestPDF.Elements
}
}
internal class Row : Element
internal class SimpleRow : Element
{
public List<RowElement> Children { get; set; } = new List<RowElement>();
float? ConstantWidthSum { get; set; }
float? RelativeWidthSum { get; set; }
internal Element Left { get; set; }
internal Element Right { get; set; }
internal override ISpacePlan Measure(Size availableSpace)
{
var sizes = Children
.Select(x =>
{
var space = GetTargetSize(x, availableSpace);
return x.Child.Measure(space);
})
.ToList();
var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height)) as Size;
if (sizes.Any(x => x is Wrap))
if (leftMeasurement == null)
return new Wrap();
var height = sizes
.Where(x => x is Size)
.Cast<Size>()
.DefaultIfEmpty(Size.Zero)
.Max(x => x.Height);
if (sizes.All(x => x is FullRender))
return new FullRender(availableSpace.Width, height);
if (sizes.Any(x => x is PartialRender))
return new PartialRender(availableSpace.Width, height);
var rightMeasurement = Right.Measure(new Size(availableSpace.Width - leftMeasurement.Width, availableSpace.Height)) as Size;
return new FullRender(Size.Zero);
if (rightMeasurement == null)
return new Wrap();
var totalWidth = leftMeasurement.Width + rightMeasurement.Width;
var totalHeight = Math.Max(leftMeasurement.Height, rightMeasurement.Height);
var targetSize = new Size(totalWidth, totalHeight);
if (leftMeasurement is PartialRender || rightMeasurement is PartialRender)
return new PartialRender(targetSize);
return new FullRender(targetSize);
}
internal override void Draw(ICanvas canvas, Size availableSpace)
{
var targetSpace = Measure(availableSpace) as Size;
if (targetSpace == null)
return;
var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height));
var leftWidth = (leftMeasurement as Size)?.Width ?? 0;
var offset = 0f;
foreach (var column in Children)
{
var space = GetTargetSize(column, targetSpace);
canvas.Translate(new Position(offset, 0));
column.Child.Draw(canvas, space);
canvas.Translate(new Position(-offset, 0));
offset += space.Width;
}
Left.Draw(canvas, new Size(leftWidth, availableSpace.Height));
canvas.Translate(new Position(leftWidth, 0));
Right.Draw(canvas, new Size(availableSpace.Width - leftWidth, availableSpace.Height));
canvas.Translate(new Position(-leftWidth, 0));
}
}
internal class Row : Element
{
public ICollection<RowElement> Children { get; internal set; } = new List<RowElement>();
public float Spacing { get; set; } = 0;
internal override ISpacePlan Measure(Size availableSpace)
{
return Compose(availableSpace.Width).Measure(availableSpace);
}
private Size GetTargetSize(RowElement rowElement, Size availableSpace)
internal override void Draw(ICanvas canvas, Size availableSpace)
{
if (rowElement is ConstantRowElement)
return new Size(rowElement.Width, availableSpace.Height);
ConstantWidthSum ??= Children
Compose(availableSpace.Width).Draw(canvas, availableSpace);
}
#region structure
private Element Compose(float availableWidth)
{
var elements = AddSpacing(Children, Spacing);
var rowElements = ReduceRows(elements, availableWidth);
return BuildTree(rowElements.ToArray());
}
private static ICollection<Element> ReduceRows(ICollection<RowElement> elements, float availableWidth)
{
var constantWidth = elements
.Where(x => x is ConstantRowElement)
.Cast<ConstantRowElement>()
.Sum(x => x.Width);
RelativeWidthSum ??= Children
var relativeWidth = elements
.Where(x => x is RelativeRowElement)
.Cast<RelativeRowElement>()
.Sum(x => x.Width);
var widthPerRelativeUnit = (availableWidth - constantWidth) / relativeWidth;
return new Size((availableSpace.Width - ConstantWidthSum.Value) * rowElement.Width / RelativeWidthSum.Value, availableSpace.Height);
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)
{
if (spacing < Size.Epsilon)
return elements;
return elements
.SelectMany(x => new[] { new ConstantRowElement(spacing), x })
.Skip(1)
.ToList();
}
private static Element BuildTree(Span<Element> elements)
{
if (elements.IsEmpty)
return Empty.Instance;
if (elements.Length == 1)
return elements[0];
var half = elements.Length / 2;
return new SimpleRow
{
Left = BuildTree(elements.Slice(0, half)),
Right = BuildTree(elements.Slice(half))
};
}
#endregion
}
}
-62
View File
@@ -1,62 +0,0 @@
using System;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal enum DecorationType
{
Prepend,
Append
}
internal class SimpleDecoration : Element
{
public Element DecorationElement { get; set; } = new Empty();
public Element ContentElement { get; set; } = new Empty();
public DecorationType Type { get; set; }
internal override ISpacePlan Measure(Size availableSpace)
{
var decorationMeasure = DecorationElement?.Measure(availableSpace);
if (decorationMeasure is Wrap || decorationMeasure is PartialRender)
return new Wrap();
var decorationSize = decorationMeasure as Size ?? Size.Zero;
var contentMeasure = ContentElement?.Measure(new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height)) ?? new FullRender(Size.Zero);
if (contentMeasure is Wrap)
return new Wrap();
var contentSize = contentMeasure as Size ?? Size.Zero;
var resultSize = new Size(availableSpace.Width, decorationSize.Height + contentSize.Height);
if (contentSize is PartialRender)
return new PartialRender(resultSize);
if (contentSize is FullRender)
return new FullRender(resultSize);
throw new NotSupportedException();
}
internal override void Draw(ICanvas canvas, Size availableSpace)
{
var decorationSize = DecorationElement?.Measure(availableSpace) as Size ?? Size.Zero;
var contentSize = new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height);
var translateHeight = Type == DecorationType.Prepend ? decorationSize.Height : contentSize.Height;
Action drawDecoration = () => DecorationElement?.Draw(canvas, new Size(availableSpace.Width, decorationSize.Height));
Action drawContent = () => ContentElement?.Draw(canvas, new Size (availableSpace.Width, contentSize.Height));
var first = Type == DecorationType.Prepend ? drawDecoration : drawContent;
var second = Type == DecorationType.Prepend ? drawContent : drawDecoration;
first();
canvas.Translate(new Position(0, translateHeight));
second();
canvas.Translate(new Position(0, -translateHeight));
}
}
}
+101 -56
View File
@@ -1,81 +1,126 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using IComponent = QuestPDF.Infrastructure.IComponent;
using IContainer = QuestPDF.Infrastructure.IContainer;
namespace QuestPDF.Elements
{
internal class Stack : Element
internal class SimpleStack : Element
{
public ICollection<Element?> Children { get; internal set; } = new List<Element?>();
private Queue<Element?> ChildrenQueue { get; set; } = new Queue<Element?>();
private void Initialize()
{
if (ChildrenQueue.Count == 0)
ChildrenQueue = new Queue<Element>(Children.Where(x => x != null));
}
internal Element First { get; set; } = Empty.Instance;
internal Element Second { get; set; } = Empty.Instance;
internal bool IsFirstRendered { get; set; } = false;
internal override ISpacePlan Measure(Size availableSpace)
{
Initialize();
var firstElement = IsFirstRendered ? Empty.Instance : First;
var firstSize = firstElement.Measure(availableSpace) as Size;
if (firstSize == null)
return new Wrap();
if(!ChildrenQueue.Any())
return new FullRender(Size.Zero);
if (firstSize is PartialRender partialRender)
return partialRender;
var spaceForSecond = new Size(availableSpace.Width, availableSpace.Height - firstSize.Height);
var secondSize = Second.Measure(spaceForSecond) as Size;
var heightOnCurrentPage = 0f;
if (secondSize == null)
return new PartialRender(firstSize);
foreach (var renderer in ChildrenQueue)
{
var space = renderer.Measure(new Size(availableSpace.Width, availableSpace.Height - heightOnCurrentPage));
var totalWidth = Math.Max(firstSize.Width, secondSize.Width);
var totalHeight = firstSize.Height + secondSize.Height;
var targetSize = new Size(totalWidth, totalHeight);
if (space is Wrap)
{
if (heightOnCurrentPage < Size.Epsilon)
return new Wrap();
return new PartialRender(availableSpace.Width, heightOnCurrentPage);
}
var size = space as Size;
heightOnCurrentPage += size.Height;
if (space is PartialRender)
return new PartialRender(availableSpace.Width, heightOnCurrentPage);
}
return new FullRender(availableSpace.Width, heightOnCurrentPage);
if (secondSize is PartialRender)
return new PartialRender(targetSize);
return new FullRender(targetSize);
}
internal override void Draw(ICanvas canvas, Size availableSpace)
{
Initialize();
var firstElement = IsFirstRendered ? Empty.Instance : First;
var firstMeasurement = firstElement.Measure(availableSpace);
if (firstMeasurement is FullRender)
IsFirstRendered = true;
var firstSize = firstMeasurement as Size;
if (firstSize != null)
firstElement.Draw(canvas, new Size(availableSpace.Width, firstSize.Height));
if (firstMeasurement is Wrap || firstMeasurement is PartialRender)
return;
var firstHeight = firstSize?.Height ?? 0;
var spaceForSecond = new Size(availableSpace.Width, availableSpace.Height - firstHeight);
var secondMeasurement = Second?.Measure(spaceForSecond) as Size;
if (secondMeasurement == null)
return;
canvas.Translate(new Position(0, firstHeight));
Second.Draw(canvas, new Size(availableSpace.Width, secondMeasurement.Height));
canvas.Translate(new Position(0, -firstHeight));
var topOffset = 0f;
if (secondMeasurement is FullRender)
IsFirstRendered = false;
}
}
internal class Stack : IComponent
{
public ICollection<Element> Children { get; internal set; } = new List<Element>();
public float Spacing { get; set; } = 0;
public void Compose(IContainer container)
{
var elements = AddSpacing(Spacing, Children);
while (ChildrenQueue.Any())
container
.PaddingBottom(-Spacing)
.Element(BuildTree(elements.ToArray()));
}
static ICollection<Element> AddSpacing(float spacing, ICollection<Element> elements)
{
if (spacing < Size.Epsilon)
return elements;
return elements
.Select(x => new Padding
{
Bottom = spacing,
Child = x
})
.Cast<Element>()
.ToList();
}
static Element BuildTree(Span<Element> elements)
{
if (elements.IsEmpty)
return Empty.Instance;
if (elements.Length == 1)
return elements[0];
var half = elements.Length / 2;
return new SimpleStack
{
var child = ChildrenQueue.Peek();
var restSpace = new Size(availableSpace.Width, availableSpace.Height - topOffset);
var space = child.Measure(restSpace);
if (space is Wrap)
break;
var size = space as Size;
canvas.Translate(new Position(0, topOffset));
child.Draw(canvas, new Size(availableSpace.Width, size.Height));
canvas.Translate(new Position(0, -topOffset));
topOffset += size.Height;
if (space is PartialRender)
break;
ChildrenQueue.Dequeue();
}
First = BuildTree(elements.Slice(0, half)),
Second = BuildTree(elements.Slice(half))
};
}
}
}
+8 -3
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using QuestPDF.Drawing;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using Size = QuestPDF.Infrastructure.Size;
@@ -24,7 +25,8 @@ namespace QuestPDF.Elements
.DefaultIfEmpty(0)
.Max();
var realHeight = lines.Count * LineHeight;
var paint = Style.ToPaint().FontMetrics;
var realHeight = -paint.Ascent + paint.Descent;
if (realHeight > availableSpace.Height + Size.Epsilon)
return new Wrap();
@@ -34,12 +36,15 @@ namespace QuestPDF.Elements
internal override void Draw(ICanvas canvas, Size availableSpace)
{
var paint = Style.ToPaint().FontMetrics;
var offeset = -paint.Ascent; // paint.Ascent - Style.Size;
var lines = BreakLines(availableSpace.Width);
var offsetTop = 0f;
var offsetLeft = GetLeftOffset();
canvas.Translate(new Position(0, Style.Size));
canvas.Translate(new Position(0, offeset));
foreach (var line in lines)
{
@@ -47,7 +52,7 @@ namespace QuestPDF.Elements
offsetTop += LineHeight;
}
canvas.Translate(new Position(0, -Style.Size));
canvas.Translate(new Position(0, -offeset));
float GetLeftOffset()
{
+51
View File
@@ -0,0 +1,51 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using QuestPDF.Drawing;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Elements
{
public struct TextMeasurement
{
public float Width { get; set; }
public SKRect Position { get; set; }
}
internal class TextElement
{
public TextStyle Style { get; set; }
public string Text { get; set; }
private static ConcurrentDictionary<string, TextMeasurement> Measurements = new ConcurrentDictionary<string, TextMeasurement>();
public TextMeasurement Measure()
{
return Measure(Text, Style);
}
public void Draw(ICanvas canvas)
{
(canvas as QuestPDF.Drawing.Canvas).SkiaCanvas.DrawText(Text, 0, 0, Style.ToPaint());
}
internal static TextMeasurement Measure(string text, TextStyle style)
{
var key = $"{text}_{style}";
return Measurements.GetOrAdd(key, x =>
{
var rect = new SKRect();
var width = style.ToPaint().MeasureText(text, ref rect);
return new TextMeasurement
{
Position = rect,
Width = width
};
});
}
}
}
+71
View File
@@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Linq;
using QuestPDF.Drawing;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
namespace QuestPDF.Elements
{
internal class TextRun : Element
{
public List<TextElement> Elements = new List<TextElement>();
internal override ISpacePlan Measure(Size availableSpace)
{
var measurements = Elements.Select(x => x.Measure()).ToList();
var metrics = Elements
.Select(x => x.Style.ToPaint())
.Select(x => x.FontMetrics)
.ToList();
var lineHeight = metrics.Max(x => -x.Ascent) + metrics.Max(x => x.Descent);
var width = measurements.Sum(x => x.Width);
return new FullRender(width, lineHeight);
}
internal override void Draw(ICanvas canvas, Size availableSpace)
{
var measurements = Elements
.Select(x => x.Measure())
.ToList();
var metrics = Elements
.Select(x => x.Style.ToPaint())
.Select(x => x.FontMetrics)
.ToList();
var offset = metrics.Max(x => -x.Ascent);
var lineHeight = metrics.Max(x => -x.Ascent) + metrics.Max(x => x.Descent);
using var typeface = SKTypeface.FromFamilyName("Helvetica");
using var shaper = new SKShaper(typeface);
var shaped = shaper.Shape("Podstawowy łaciński — ✔️ ❤️ ☆ Tabela znaków Unicode", Elements.First().Style.ToPaint());
foreach (var textElement in Elements)
{
var size = textElement.Measure();
canvas.DrawRectangle(new Position(0, 0), new Size(size.Width, lineHeight), textElement.Style.BackgroundColor);
canvas.Translate(new Position(0, offset));
textElement.Draw(canvas);
canvas.Translate(new Position(0, -offset));
canvas.Translate(new Position(size.Width, 0));
}
canvas.Translate(new Position(-measurements.Sum(x => x.Width), 0));
//
}
}
}
+1 -1
View File
@@ -56,7 +56,7 @@ namespace QuestPDF.Fluent
var descriptor = new ComponentDescriptor<T>(component);
handler?.Invoke(descriptor);
component.Compose(element);
component.Compose(element.Container());
}
static void Component<T>(this IContainer element, Action<ComponentDescriptor<T>>? handler = null) where T : IComponent, new()
@@ -45,7 +45,7 @@ namespace QuestPDF.Fluent
}
}
public static class SectionExtensions
public static class DecorationExtensions
{
public static void Decoration(this IContainer element, Action<DecorationDescriptor> handler)
{
+3 -3
View File
@@ -14,7 +14,7 @@ namespace QuestPDF.Fluent
return container;
}
public static T Element<T>(this IContainer element, T child) where T : IElement
internal static T Element<T>(this IContainer element, T child) where T : IElement
{
if (element?.Child != null && element.Child is Empty == false)
{
@@ -33,12 +33,12 @@ namespace QuestPDF.Fluent
public static void Element<TParent>(this TParent parent, Action<IContainer> handler) where TParent : IContainer
{
handler(parent);
handler(parent.Container());
}
public static IContainer Element<TParent>(this TParent parent, Func<IContainer, IContainer> handler) where TParent : IContainer
{
return handler(parent);
return handler(parent.Container()).Container();
}
public static void PageNumber(this IContainer element, string textFormat = "{number}", TextStyle? style = null)
+1 -1
View File
@@ -38,7 +38,7 @@ namespace QuestPDF.Fluent
public void AlignCenter() => Alignment(HorizontalAlignment.Center);
public void AlignRight() => Alignment(HorizontalAlignment.Right);
public IContainer Element(int columns = 1)
public IContainer Item(int columns = 1)
{
var container = new Container();
-15
View File
@@ -14,11 +14,6 @@ namespace QuestPDF.Fluent
Page.Header = container;
return container;
}
public void Header(Action<IContainer> handler)
{
handler?.Invoke(Header());
}
public IContainer Content()
{
@@ -27,22 +22,12 @@ namespace QuestPDF.Fluent
return container;
}
public void Content(Action<IContainer> handler)
{
handler?.Invoke(Content());
}
public IContainer Footer()
{
var container = new Container();
Page.Footer = container;
return container;
}
public void Footer(Action<IContainer> handler)
{
handler?.Invoke(Footer());
}
}
public static class PageExtensions
+6 -34
View File
@@ -8,19 +8,18 @@ namespace QuestPDF.Fluent
{
public class RowDescriptor
{
private List<RowElement> Elements { get; } = new List<RowElement>();
private float RowSpacing { get; set; } = 0;
internal Row Row { get; } = new Row();
public void Spacing(float value)
{
RowSpacing = value;
Row.Spacing = value;
}
public IContainer ConstantColumn(float width)
{
var element = new ConstantRowElement(width);
Elements.Add(element);
Row.Children.Add(element);
return element;
}
@@ -28,36 +27,9 @@ namespace QuestPDF.Fluent
{
var element = new RelativeRowElement(width);
Elements.Add(element);
Row.Children.Add(element);
return element;
}
internal Element CreateRow()
{
if (Elements.Count == 0)
return new Empty();
if (RowSpacing <= Size.Epsilon)
return new Row
{
Children = Elements
};
var children = Elements
.SelectMany(x => new[] {x, new ConstantRowElement(RowSpacing) })
.ToList();
var row = new Row
{
Children = children
};
return new Padding
{
Right = -RowSpacing,
Child = row
};
}
}
public static class RowExtensions
@@ -66,7 +38,7 @@ namespace QuestPDF.Fluent
{
var descriptor = new RowDescriptor();
handler(descriptor);
element.Element(descriptor.CreateRow());
element.Element(descriptor.Row);
}
}
}
+5 -43
View File
@@ -8,57 +8,19 @@ namespace QuestPDF.Fluent
{
public class StackDescriptor
{
private List<Element> Items { get; } = new List<Element>();
private float StackSpacing { get; set; } = 0;
internal Stack Stack { get; } = new Stack();
public void Spacing(float value)
{
StackSpacing = value;
Stack.Spacing = value;
}
public IContainer Item()
{
var container = new Container();
Items.Add(container);
Stack.Children.Add(container);
return container;
}
public void Item(Action<IContainer> handler)
{
handler?.Invoke(Item());
}
internal Element CreateStack()
{
if (Items.Count == 0)
return new Empty();
if (StackSpacing <= Size.Epsilon)
return new Stack
{
Children = Items
};
var children = Items
.Select(x => new Padding
{
Bottom = StackSpacing,
Child = x
})
.Cast<Element>()
.ToList();
var stack = new Stack
{
Children = children
};
return new Padding
{
Bottom = -StackSpacing,
Child = stack
};
}
}
public static class StackExtensions
@@ -67,7 +29,7 @@ namespace QuestPDF.Fluent
{
var descriptor = new StackDescriptor();
handler(descriptor);
element.Element(descriptor.CreateStack());
element.Component(descriptor.Stack);
}
}
}
+5
View File
@@ -18,6 +18,11 @@ namespace QuestPDF.Fluent
return style.Mutate(x => x.Color = value);
}
public static TextStyle BackgroundColor(this TextStyle style, string value)
{
return style.Mutate(x => x.BackgroundColor = value);
}
public static TextStyle FontType(this TextStyle style, string value)
{
return style.Mutate(x => x.FontType = value);
+20 -20
View File
@@ -7,7 +7,7 @@ namespace QuestPDF.Helpers
{
public static class Placeholders
{
private static Random Random = new Random();
public static Random Random = new Random();
#region Word Cache
@@ -155,25 +155,25 @@ namespace QuestPDF.Helpers
private static readonly string[] BackgroundColors =
{
Colors.Red.Lighten2,
Colors.Pink.Lighten2,
Colors.Purple.Lighten2,
Colors.DeepPurple.Lighten2,
Colors.Indigo.Lighten2,
Colors.Blue.Lighten2,
Colors.LightBlue.Lighten2,
Colors.Cyan.Lighten2,
Colors.Teal.Lighten2,
Colors.Green.Lighten2,
Colors.LightGreen.Lighten2,
Colors.Lime.Lighten2,
Colors.Yellow.Lighten2,
Colors.Amber.Lighten2,
Colors.Orange.Lighten2,
Colors.DeepOrange.Lighten2,
Colors.Brown.Lighten2,
Colors.Grey.Lighten2,
Colors.BlueGrey.Lighten2
Colors.Red.Lighten3,
Colors.Pink.Lighten3,
Colors.Purple.Lighten3,
Colors.DeepPurple.Lighten3,
Colors.Indigo.Lighten3,
Colors.Blue.Lighten3,
Colors.LightBlue.Lighten3,
Colors.Cyan.Lighten3,
Colors.Teal.Lighten3,
Colors.Green.Lighten3,
Colors.LightGreen.Lighten3,
Colors.Lime.Lighten3,
Colors.Yellow.Lighten3,
Colors.Amber.Lighten3,
Colors.Orange.Lighten3,
Colors.DeepOrange.Lighten3,
Colors.Brown.Lighten3,
Colors.Grey.Lighten3,
Colors.BlueGrey.Lighten3
};
public static string BackgroundColor()
+1 -1
View File
@@ -5,7 +5,7 @@ namespace QuestPDF.Infrastructure
{
internal abstract class ContainerElement : Element, IContainer
{
internal Element? Child { get; set; } = new Empty();
internal Element? Child { get; set; } = Empty.Instance;
IElement? IContainer.Child
{
+4 -3
View File
@@ -5,18 +5,19 @@ namespace QuestPDF.Infrastructure
public class TextStyle
{
internal string Color { get; set; } = Colors.Black;
internal string BackgroundColor { get; set; } = Colors.Transparent;
internal string FontType { get; set; } = "Calibri";
internal float Size { get; set; } = 12;
internal float LineHeight { get; set; } = 1.2f;
internal float LineHeight { get; set; } = 1f;
internal HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
internal FontWeight FontWeight { get; set; } = FontWeight.Normal;
internal bool IsItalic { get; set; } = false;
public static TextStyle Default => new TextStyle();
public override string ToString()
{
return $"{Color}|{FontType}|{Size}|{LineHeight}|{Alignment}|{FontWeight}|{IsItalic}";
return $"{Color}|{BackgroundColor}|{FontType}|{Size}|{LineHeight}|{Alignment}|{FontWeight}|{IsItalic}";
}
internal TextStyle Clone() => (TextStyle)MemberwiseClone();
+3 -2
View File
@@ -4,9 +4,9 @@
<Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company>
<PackageId>QuestPDF</PackageId>
<Version>2021.5-alpha2</Version>
<Version>2021.5.1</Version>
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
<PackageReleaseNotes>New elements: Grid, Canvas, EnsureSpace, Layers. Predefined colors and fonts.</PackageReleaseNotes>
<PackageReleaseNotes>New elements: Box, Grid, Canvas, EnsureSpance and Layers. Redesigned the Debug element. Added material design colors. Added list of basic fonts. Added spacing property to the Row element. Fluent API improvements and increased stability.</PackageReleaseNotes>
<LangVersion>8</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageIcon>Logo.png</PackageIcon>
@@ -23,6 +23,7 @@
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.80.2" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.2" />
</ItemGroup>
<ItemGroup>