Compare commits

...

117 Commits

Author SHA1 Message Date
Marcin Ziąbek 17e2727833 Added missing option to style a line 2021-10-01 01:44:53 +02:00
Marcin Ziąbek 1b9ea70f6e Fixed code highlighting 2021-09-30 23:50:34 +02:00
Marcin Ziąbek 87f4c9774a Update readme.md 2021-09-30 23:48:35 +02:00
Marcin Ziąbek 3bdd03031b Updated csproj 2021-09-30 23:08:47 +02:00
Marcin Ziąbek 26335faa97 Improved and fixed old tests 2021-09-30 22:48:39 +02:00
Marcin Ziąbek 7b81e61840 Merge pull request #26 from QuestPDF/richtext
2021.10 Richtext
2021-09-30 22:05:45 +02:00
Marcin Ziąbek 391c5a6092 Merge branch 'main' into richtext 2021-09-30 22:04:47 +02:00
Marcin Ziąbek d951e0a6b2 Removed not needed nuget dependencies 2021-09-19 01:42:52 +02:00
Marcin Ziąbek 188fbc22a7 Added structural tests for: Row, Stack. 2021-09-18 23:40:21 +02:00
Marcin Ziąbek cc6d1c81c8 Updated tests to use FluentAssertions 6 2021-09-18 20:59:21 +02:00
Marcin Ziąbek 6d333da84c Added tests to check simple, container-like constraints for some elements 2021-09-18 01:21:48 +02:00
Marcin Ziąbek e411554a29 Added tests for elements: Box, Layers, Rotate, Scale, SimpleRotate, Translate, Unconstrained 2021-09-18 01:03:58 +02:00
Marcin Ziąbek 9fddea84ba Cleaned tests suite 2021-09-17 22:05:07 +02:00
Marcin Ziąbek 84e896e13b 2021.10.0-beta.2 Improved API naming 2021-09-14 12:16:49 +02:00
Marcin Ziąbek 569864d48a Fixed rendering text when put in the Box element 2021-09-14 02:13:15 +02:00
Marcin Ziąbek 2c11491d4e Library update 2021-09-14 02:02:45 +02:00
Marcin Ziąbek acdd8164be Optimized the TextLine object 2021-09-14 00:16:16 +02:00
Marcin Ziąbek 094b25a9fb Merge remote-tracking branch 'origin/richtext' into richtext
# Conflicts:
#	QuestPDF.Examples/QuestPDF.Examples.csproj
#	QuestPDF/Drawing/DocumentGenerator.cs
#	QuestPDF/Drawing/FontManager.cs
#	QuestPDF/Elements/Text/Items/TextBlockSpan.cs
2021-09-13 20:38:50 +02:00
Marcin Ziąbek 122f35d0a7 2021.10-beta + Code rebase 2021-09-13 20:36:29 +02:00
Marcin Ziąbek f4c9f8b3c2 Added text rendering tests 2021-09-13 20:30:18 +02:00
Marcin Ziąbek ae7873680d Fixed layout rendering 2021-09-13 20:30:16 +02:00
Marcin Ziąbek 175db288f6 Improved text measuring performance 2021-09-13 20:29:53 +02:00
Marcin Ziąbek 22401be791 Fixed rendering spaces, fixed text alignment 2021-09-13 20:29:53 +02:00
Marcin Ziąbek e2774ef347 Improved book layout in benchmark 2021-09-13 20:29:53 +02:00
Marcin Ziąbek 99f004dd85 Improved text rendering (space corner cases) 2021-09-13 20:29:53 +02:00
Marcin Ziąbek 8ada092531 Fixed text rendering (spacing corner case) 2021-09-13 20:29:53 +02:00
Marcin Ziąbek c334390af0 Better new line handling 2021-09-13 20:29:53 +02:00
Marcin Ziąbek deed219a0a Code structure improvements. Text rendering: support for words longer than available space 2021-09-13 20:29:53 +02:00
Marcin Ziąbek bb8e590fc6 Code structure improvements 2021-09-13 20:29:53 +02:00
Marcin Ziąbek a08a7b85c9 Example report fixes 2021-09-13 20:29:52 +02:00
Marcin Ziąbek 755447e1fd Text: rendering custom elements inside text 2021-09-13 20:29:52 +02:00
Marcin Ziąbek b66a53a189 Text component: internal and external links 2021-09-13 20:29:52 +02:00
Marcin Ziąbek 6d41ac9b9a Fix: the Stack may not show last an element in certain scenarios 2021-09-13 20:29:52 +02:00
Marcin Ziąbek a124d23862 Code cleanup 2021-09-13 20:29:50 +02:00
Marcin Ziąbek 5f65b2daed Text Rendering Optimization: Caching Measurement Results 2021-09-13 20:29:44 +02:00
Marcin Ziąbek 07938a1510 Text rendering optimization: style key, font metrics caching 2021-09-13 20:29:34 +02:00
Marcin Ziąbek f17860f519 Implemented text rendering benchmark 2021-09-13 20:29:24 +02:00
Marcin Ziąbek 6023b4de34 Improved text rendering capabilities 2021-09-13 20:29:17 +02:00
Marcin Ziąbek 748b8f65f5 Initial text rendering improvements 2021-09-13 20:29:17 +02:00
Marcin Ziąbek feab280a27 Fixed detecting infinite layout exception in specific cases 2021-09-13 01:09:45 +02:00
Marcin Ziąbek ae32511172 Added text rendering tests 2021-09-13 01:06:06 +02:00
Marcin Ziąbek 6c4da7285a Fixed layout rendering 2021-09-13 00:55:21 +02:00
Marcin Ziąbek 070b6a20bd Improved text measuring performance 2021-09-12 21:37:46 +02:00
Marcin Ziąbek f7cfdb763f Fixed rendering spaces, fixed text alignment 2021-09-12 21:01:10 +02:00
Marcin Ziąbek 5975b19445 Improved book layout in benchmark 2021-09-07 13:34:04 +02:00
Marcin Ziąbek fb7743e828 Improved text rendering (space corner cases) 2021-09-07 13:16:34 +02:00
Marcin Ziąbek 4152a0f701 Improved row performance and layout structure stability 2021-09-02 00:53:50 +02:00
Marcin Ziąbek 6c80c59996 Fixed misspelling in release notes 2021-09-01 19:19:16 +02:00
Marcin Ziąbek 08a37666fe 2021.9.1 2021-09-01 19:14:52 +02:00
Marcin Ziąbek c2fb68562d 2021.9.0 2021-08-30 23:37:44 +02:00
Marcin Ziąbek 683ab362d8 Added support for registering font types based on the suggestion of brendonparker 2021-08-29 20:15:12 +02:00
Marcin Ziąbek 3bb46ecc42 Fixed text rendering (spacing corner case) 2021-08-29 19:18:58 +02:00
Marcin Ziąbek 3112ee10cf Better new line handling 2021-08-27 20:25:20 +02:00
Marcin Ziąbek 899c138842 Code structure improvements. Text rendering: support for words longer than available space 2021-08-27 19:51:28 +02:00
Marcin Ziąbek 5177dcfd2b Code structure improvements 2021-08-27 12:40:06 +02:00
Marcin Ziąbek 4e6415aee2 Example report fixes 2021-08-27 11:40:33 +02:00
Marcin Ziąbek d9b3f016e8 Text: rendering custom elements inside text 2021-08-27 01:55:20 +02:00
Marcin Ziąbek b9cfaf3846 Text component: internal and external links 2021-08-27 01:38:26 +02:00
Marcin Ziąbek e3e7a22af8 Fix: the Stack may not show last an element in certain scenarios 2021-08-27 01:38:10 +02:00
Marcin Ziąbek c938ea32b0 Code cleanup 2021-08-27 01:10:30 +02:00
Marcin Ziąbek 69419c8440 Text Rendering Optimization: Caching Measurement Results 2021-08-27 00:29:04 +02:00
Marcin Ziąbek 23e8aa3491 Text rendering optimization: style key, font metrics caching 2021-08-26 23:36:43 +02:00
Marcin Ziąbek fd5d51e764 Implemented text rendering benchmark 2021-08-26 22:25:33 +02:00
Marcin Ziąbek dbc479db07 Improved text rendering capabilities 2021-08-25 03:40:16 +02:00
Marcin Ziąbek 04af33fc31 Included examples for DSL and complex layouts 2021-08-23 23:51:42 +02:00
Marcin Ziąbek edc595d199 Initial text rendering improvements 2021-08-09 22:35:39 +02:00
Marcin Ziąbek 7f94ae589a Updated release notes and code example 2021-08-03 01:43:48 +02:00
Marcin Ziąbek adfce6204e Improved testing environment by adding proper result files names, Added more code examples 2021-08-03 01:35:46 +02:00
Marcin Ziąbek 6d3c8d0708 Added elements: Rotate, Unconstrained 2021-08-01 21:09:16 +02:00
Marcin Ziąbek 3b8c300126 Merge pull request #17 from QuestPDF/2021.6
Merge 2021.6 for QuestPdf 2021.8
2021-08-01 16:58:44 +02:00
MarcinZiabek 239a042615 Fixed rendering for the "Rotate" element 2021-07-28 21:36:51 +02:00
Marcin Ziąbek 49fb6dadb3 Improved null safety. Fixed tests 2021-07-26 12:26:31 +02:00
Marcin Ziąbek c79a4a0c08 Fixed rendering images 2021-07-26 11:59:48 +02:00
Marcin Ziąbek 31191efd88 Multipass rendering. Total pages / location position support. Continuous pages. 2021-07-26 11:35:52 +02:00
Marcin Ziąbek 20fe49dd01 Update SECURITY.md 2021-07-18 18:10:55 +02:00
Marcin Ziąbek 7cdc1fcee2 Create SECURITY.md 2021-07-18 18:10:20 +02:00
Marcin Ziąbek 14f11232be New elements: rotate, scale, translate 2021-06-09 23:34:25 +02:00
Marcin Ziąbek c05c5e1597 Update readme.md
Updated code, added the support section
2021-05-15 16:09:01 +02:00
Marcin Ziąbek 07f1eb99b9 Fixed: stack should not apply spacing for empty items 2021-05-05 13:26:46 +02:00
Marcin Ziąbek 44d7538578 Examples to the documentation 2021-05-05 00:10:48 +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 b6e0bd505c Merge branch 'main' into 2021.05 2021-05-03 11:39:17 +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 779245b260 Minor Fluent API changes 2021-05-01 22:46:37 +02:00
Marcin Ziąbek 1698212669 Improved behaviour of the EnsureSpace element 2021-04-29 11:23:54 +02:00
Marcin Ziąbek 388adc5ad1 Update ElementExamples.cs 2021-04-21 23:44:15 +02:00
Marcin Ziąbek 81a9599b99 Updated version: 2021.5-alpha2 2021-04-21 14:12:22 +02:00
MarcinZiabek 48f41db98b Improved sample report styling 2021-04-20 12:55:07 +02:00
MarcinZiabek 3dd8513e42 Code cleanup 2021-04-20 12:54:38 +02:00
MarcinZiabek c3d805e5e5 Removed not needed images (photos and map). Replaced by image placeholder 2021-04-20 12:15:16 +02:00
Marcin Ziąbek f77d8baa63 Added Box element, Redesigned Debug element, Added image placeholder 2021-04-18 23:11:29 +02:00
MarcinZiabek 950d971119 Consolidated classes for Row and Grid elements 2021-04-17 21:55:19 +02:00
MarcinZiabek e4203838cb Cleaned up using statements 2021-04-17 21:52:33 +02:00
MarcinZiabek 25b690fcca Bug fix: maintain origin matrix after drawing the Canvas element 2021-04-17 21:51:17 +02:00
MarcinZiabek 4e3ba1d878 Fixed wrong import in QuestPDF.Examples.csproj 2021-04-16 10:22:41 +02:00
Marcin Ziąbek 3f0b500b1b New elements: Grid, Canvas, EnsureSpace, Layers. Predefined colors and fonts. 2021-04-16 01:19:34 +02:00
Marcin Ziąbek fb3aec4bf8 PageSize names refactorization 2021-04-09 19:17:36 +02:00
Marcin Ziąbek 8c2250ecde Code refactorization 2021-04-09 19:11:31 +02:00
Marcin Ziąbek bb2850b2d8 Added material design colors 2021-04-09 19:11:17 +02:00
Marcin Ziąbek 842b856f7a Added tests for several elements 2021-04-09 19:10:55 +02:00
Marcin Ziąbek 733dbc8746 Stack element: updated tests 2021-04-09 16:32:32 +02:00
Marcin Ziąbek 0b7b05d36c Decoration element: added tests 2021-04-09 16:32:22 +02:00
Marcin Ziąbek a83ebe2fed Simplified implementation for Section and Page elements 2021-04-03 00:13:14 +02:00
Marcin Ziąbek 6f0c6513da Bugfix: exception when providing null content into the Text element 2021-03-31 13:00:29 +02:00
Marcin Ziąbek 500cea1fbf Simplified the layouting algorithm in the Stack element; Added a Spacing property to the Row element; Implemented new Grid element; Updated examples 2021-03-28 17:49:19 +02:00
Laurence Mingle 3369da42ed Additional page sizes (#6)
* Update PageSizes.cs

Added/Updated page sizes using details found in the Adobe PostScript Printer Description File Format Specification version 4.3.

* Update PageSizes.cs

Rounded values

* Update PageSizes.cs

Using float

* Update PageSizes.cs

Formatting

* Update PageSizes.cs

Fewer PageSizes

* Update PageSizes.cs

Addition of PageSize class, dimensions using point values from papersizes.io and extension methods for portrait & landscape orientation.
2021-03-27 11:08:00 +01:00
MarcinZiabek c5cb588857 Fixed: AspectRatio behaviour does not take into account child measurement. Added tests for AspectRatio, Image and DynamicImage elements. Small TestPlan framework improvements. 2021-03-23 18:05:39 +01:00
Marcin Ziąbek 08126b2fa4 simplified AspectRatio code, better exception handling for QuestPdf.Examples, fixed Placeholder image 2021-03-23 14:43:03 +01:00
Marcin Ziąbek 5e9ff7ec1e Added scaling options for AspectRatio and Image component. Added Debug component to the fluent API 2021-03-22 11:49:27 +01:00
Marcin Ziąbek 9c05c182b8 Fix: GeneratePdf closes provided stream 2021-03-21 17:43:55 +01:00
Marcin Ziąbek dee97b22e4 Improved readme 2021-03-10 19:55:58 +01:00
Marcin Ziąbek 1d1d969b5a 2021.3.1 2021-03-01 17:06:37 +01:00
501 changed files with 16900 additions and 2200 deletions
+33
View File
@@ -0,0 +1,33 @@
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));
});
}
}
}
+32
View File
@@ -0,0 +1,32 @@
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
[TestFixture]
public class BarcodeExamples
{
[Test]
public void Barcode()
{
RenderingTest
.Create()
.PageSize(300, 300)
.FileName()
.Render(container =>
{
container
.Background("#FFF")
.Padding(25)
.Stack(stack =>
{
stack.Item().Border(1).Background(Colors.Grey.Lighten3).Padding(5).Text("Barcode Example");
stack.Item().Border(1).Padding(5).AlignCenter().Text("*123456789*", TextStyle.Default.FontType("CarolinaBar-Demo-25E2").Size(20));
});
});
}
}
}
+46
View File
@@ -0,0 +1,46 @@
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);
}
}
}
+824 -102
View File
@@ -1,138 +1,860 @@
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Examples
{
public class ElementExamples : ExampleTestBase
[TestFixture]
public class ElementExamples
{
//[ShowResult]
[ImageSize(200, 150)]
public void Placeholder(IContainer container)
[Test]
public void Placeholder()
{
container
.Background("#FFF")
.Padding(25)
.Placeholder();
}
//[ShowResult]
[ImageSize(300, 300)]
public void Section(IContainer container)
{
container
.Background("#FFF")
.Padding(25)
.Section(section =>
RenderingTest
.Create()
.PageSize(200, 150)
.FileName()
.Render(container =>
{
section
.Header()
.Background("#888")
.Padding(10)
.Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
section
.Content()
.Background("#DDD")
.Padding(10)
.ExtendVertical()
.Text(TextPlaceholder.LoremIpsum());
container
.Background("#FFF")
.Padding(25)
.Placeholder();
});
}
//[ShowResult]
[ImageSize(298, 421)]
public void Page(IContainer container)
[Test]
public void Decoration()
{
container
.Background("#FFF")
.Padding(15)
.Page(page =>
RenderingTest
.Create()
.PageSize(300, 300)
.FileName()
.Render(container =>
{
page.Header()
.Height(60)
.Background("#BBB")
.AlignCenter()
.AlignMiddle()
.Text("Header");
container
.Background("#FFF")
.Padding(25)
.Decoration(decoration =>
{
decoration
.Header()
.Background(Colors.Grey.Medium)
.Padding(10)
.Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
page.Content()
.Background("#DDD")
.AlignCenter()
.AlignMiddle()
.Text("Content");
decoration
.Content()
.Background(Colors.Grey.Lighten3)
.Padding(10)
.ExtendVertical()
.Text(Helpers.Placeholders.LoremIpsum());
});
});
}
[Test]
public void Row()
{
RenderingTest
.Create()
.PageSize(740, 200)
.FileName()
.Render(container =>
{
container
.Background("#FFF")
.Padding(20)
.Stack(stack =>
{
stack.Item()
.PaddingBottom(10)
.AlignCenter()
.Text("This Row element is 700pt wide");
stack.Item().Row(row =>
{
row.ConstantColumn(100)
.Background(Colors.Grey.Lighten1)
.Padding(10)
.ExtendVertical()
.Text("This column is 100 pt wide");
row.RelativeColumn()
.Background(Colors.Grey.Lighten2)
.Padding(10)
.Text("This column takes 1/3 of the available space (200pt)");
row.RelativeColumn(2)
.Background(Colors.Grey.Lighten3)
.Padding(10)
.Text("This column takes 2/3 of the available space (400pt)");
});
});
});
}
[Test]
public void RowSpacing()
{
RenderingTest
.Create()
.PageSize(740, 200)
.FileName()
.Render(container =>
{
container
.Background("#FFF")
.Padding(20)
.Row(row =>
{
row.Spacing(20);
row.RelativeColumn(2).Border(1).Background(Colors.Grey.Lighten1);
row.RelativeColumn(3).Border(1).Background(Colors.Grey.Lighten2);
row.RelativeColumn(4).Border(1).Background(Colors.Grey.Lighten3);
});
});
}
[Test]
public void Stack()
{
RenderingTest
.Create()
.PageSize(500, 360)
.FileName()
.Render(container =>
{
container
.Background("#FFF")
.Padding(15)
.Stack(stack =>
{
stack.Spacing(15);
stack.Item().Background(Colors.Grey.Medium).Height(50);
stack.Item().Background(Colors.Grey.Lighten1).Height(100);
stack.Item().Background(Colors.Grey.Lighten2).Height(150);
});
});
}
[Test]
public void Debug()
{
RenderingTest
.Create()
.PageSize(210, 210)
.FileName()
.Render(container =>
{
container
.Padding(25)
.Debug("Grid example", Colors.Blue.Medium)
.Grid(grid =>
{
grid.Columns(3);
grid.Spacing(5);
foreach (var _ in Enumerable.Range(0, 8))
grid.Item().Height(50).Placeholder();
});
});
}
[Test]
public void ElementEnd()
{
RenderingTest
.Create()
.PageSize(300, 200)
.FileName()
.Render(container =>
{
var text = "";
container
.Padding(10)
.Element(x =>
{
if (string.IsNullOrWhiteSpace(text))
x.Height(10).Width(50).Background("#DDD");
else
x.Text(text);
});
});
}
[Test]
public void GridExample()
{
RenderingTest
.Create()
.PageSize(400, 230)
.FileName()
.Render(container =>
{
var textStyle = TextStyle.Default.Size(14);
container
.Padding(15)
.AlignRight()
.Grid(grid =>
{
grid.VerticalSpacing(10);
grid.HorizontalSpacing(10);
grid.AlignCenter();
grid.Columns(10); // 12 by default
grid.Item(6).Background(Colors.Blue.Lighten1).Height(50);
grid.Item(4).Background(Colors.Blue.Lighten3).Height(50);
grid.Item(2).Background(Colors.Teal.Lighten1).Height(70);
grid.Item(3).Background(Colors.Teal.Lighten2).Height(70);
grid.Item(5).Background(Colors.Teal.Lighten3).Height(70);
grid.Item(2).Background(Colors.Green.Lighten1).Height(50);
grid.Item(2).Background(Colors.Green.Lighten2).Height(50);
grid.Item(2).Background(Colors.Green.Lighten3).Height(50);
});
});
}
[Test]
public void Canvas()
{
RenderingTest
.Create()
.PageSize(300, 200)
.FileName()
.Render(container =>
{
container
.Background("#FFF")
.Padding(25)
.Canvas((canvas, size) =>
{
using var paint = new SKPaint
{
Color = SKColors.Red,
StrokeWidth = 10,
IsStroke = true
};
// move origin to the center of the available space
canvas.Translate(size.Width / 2, size.Height / 2);
// draw a circle
canvas.DrawCircle(0, 0, 50, paint);
});
});
}
[Test]
public void LayersExample()
{
RenderingTest
.Create()
.PageSize(400, 250)
.FileName()
.Render(container =>
{
container
.Padding(25)
.Layers(layers =>
{
// layer below main content
layers
.Layer()
.Height(100)
.Width(100)
.Background(Colors.Grey.Lighten3);
layers
.PrimaryLayer()
.Padding(25)
.Stack(stack =>
{
stack.Spacing(5);
foreach (var _ in Enumerable.Range(0, 7))
stack.Item().Text(Placeholders.Sentence());
});
// layer above the main content
layers
.Layer()
.AlignCenter()
.AlignMiddle()
.Text("Watermark", TextStyle.Default.Size(48).Bold().Color(Colors.Green.Lighten3));
layers
.Layer()
.AlignBottom()
.Text(text => text.CurrentPageNumber(TextStyle.Default.Size(16).Color(Colors.Green.Medium)));
});
});
}
// [Test]
// public void EnsureSpace()
// {
// RenderingTest
// .Create()
// .PageSize(300, 400)
// .Render(container =>
// {
// container
// .Padding(50)
// .Page(page =>
// {
// page.Header().PageNumber("Page {pdf:currentPage}");
//
// page.Content().Height(300).Stack(content =>
// {
// content.Item().Height(200).Background(Colors.Grey.Lighten2);
//
// content.Item().EnsureSpace(100).Stack(stack =>
// {
// stack.Spacing(10);
//
// foreach (var _ in Enumerable.Range(0, 4))
// stack.Item().Height(50).Background(Colors.Green.Lighten1);
// });
// });
// });
// });
// }
[Test]
public void RandomColorMatrix()
{
RenderingTest
.Create()
.PageSize(300, 300)
.FileName()
.Render(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));
});
});
}
[Test]
public void DefinedColors()
{
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,
};
RenderingTest
.Create()
.PageSize(450, 150)
.FileName()
.Render(container =>
{
container
.Padding(25)
.Height(100)
.Row(row =>
{
foreach (var color in colors)
row.RelativeColumn().Background(color);
});
});
}
[Test]
public void DefinedFonts()
{
var fonts = new[]
{
Fonts.Calibri,
Fonts.Candara,
Fonts.Arial,
Fonts.TimesNewRoman,
Fonts.Consolas,
Fonts.Tahoma,
Fonts.Impact,
Fonts.Trebuchet,
Fonts.ComicSans
};
RenderingTest
.Create()
.PageSize(500, 175)
.FileName()
.Render(container =>
{
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));
}
});
});
}
[Test]
public void Layers()
{
RenderingTest
.Create()
.PageSize(300, 300)
.FileName()
.Render(container =>
{
container
.Background("#FFF")
.Padding(25)
.Layers(layers =>
{
layers.Layer().Text("Something else");
layers.PrimaryLayer().Stack(stack =>
{
stack.Item().PaddingTop(20).Text("Text 1");
stack.Item().PaddingTop(40).Text("Text 2");
});
layers.Layer().Canvas((canvas, size) =>
{
using var paint = new SKPaint
{
Color = SKColors.Red,
StrokeWidth = 5
};
canvas.Translate(size.Width / 2, size.Height / 2);
canvas.DrawCircle(0, 0, 50, paint);
});
layers.Layer().Background("#8F00").Extend();
layers.Layer().PaddingTop(40).Text("It works!", TextStyle.Default.Size(24));
});
});
}
[Test]
public void Box()
{
RenderingTest
.Create()
.PageSize(300, 150)
.FileName()
.Render(container =>
{
container
.Background("#FFF")
.Padding(15)
.Border(4)
.BorderColor(Colors.Blue.Medium)
//.Box()
.Background(Colors.Grey.Lighten2)
.Padding(15)
.Text("Test of the \n box element", TextStyle.Default.Size(20));
});
}
[Test]
public void Scale()
{
RenderingTest
.Create()
.PageSize(300, 175)
.FileName()
.Render(container =>
{
container
.Background(Colors.White)
.Padding(10)
.Decoration(decoration =>
{
var headerFontStyle = TextStyle
.Default
.Size(20)
.Color(Colors.Blue.Darken2)
.SemiBold();
decoration
.Header()
.PaddingBottom(10)
.Text("Example: scale component", headerFontStyle);
decoration
.Content()
.Stack(stack =>
{
var scales = new[] { 0.8f, 0.9f, 1.1f, 1.2f };
foreach (var scale in scales)
{
var fontColor = scale <= 1f
? Colors.Red.Lighten4
: Colors.Green.Lighten4;
var fontStyle = TextStyle.Default.Size(16);
stack
.Item()
.Border(1)
.Background(fontColor)
.Scale(scale)
.Padding(5)
.Text($"Content with {scale} scale.", fontStyle);
}
});
});
});
}
[Test]
public void Translate()
{
RenderingTest
.Create()
.PageSize(300, 200)
.FileName()
.Render(container =>
{
container
.Background("#FFF")
.Box()
.Padding(25)
.Background(Colors.Green.Lighten3)
.TranslateX(15)
.TranslateY(15)
.Border(2)
.BorderColor(Colors.Green.Darken1)
.Padding(50)
.Text("Moved text", TextStyle.Default.Size(25));
});
}
[Test]
public void ConstrainedRotate()
{
RenderingTest
.Create()
.PageSize(650, 450)
.FileName()
.Render(container =>
{
container
.Padding(20)
.Grid(grid =>
{
grid.Columns(2);
grid.Spacing(10);
foreach (var turns in Enumerable.Range(0, 4))
{
grid.Item()
.Width(300)
.Height(200)
.Background(Colors.Grey.Lighten2)
.Padding(10)
.Element(element =>
{
foreach (var x in Enumerable.Range(0, turns))
element = element.RotateRight();
return element;
})
.Box()
.Background(Colors.White)
.Padding(10)
.Text($"Rotated {turns * 90}°", TextStyle.Default.Size(16));
}
});
});
}
[Test]
public void FreeRotate()
{
RenderingTest
.Create()
.PageSize(300, 300)
.FileName()
.Render(container =>
{
container
.Padding(25)
.Background(Colors.Grey.Lighten2)
page.Footer()
.Height(30)
.Background("#BBB")
.AlignCenter()
.AlignMiddle()
.Text("Footer");
.Background(Colors.White)
.Rotate(30)
.Width(100)
.Height(100)
.Background(Colors.Blue.Medium);
});
}
//[ShowResult]
[ImageSize(740, 200)]
public void Row(IContainer container)
[Test]
public void FreeRotateCenter()
{
container
.Background("#FFF")
.Padding(20)
.Stack(stack =>
RenderingTest
.Create()
.PageSize(300, 300)
.FileName()
.Render(container =>
{
stack.Element()
.PaddingBottom(10)
container
.Padding(25)
.Background(Colors.Grey.Lighten2)
.AlignCenter()
.Text("This Row element is 700pt wide");
.AlignMiddle()
.Background(Colors.White)
.TranslateX(50)
.TranslateY(50)
.Rotate(30)
stack.Element().Row(row =>
{
row.ConstantColumn(100)
.Background("#DDD")
.Padding(10)
.ExtendVertical()
.Text("This column is 100 pt wide");
.TranslateX(-50)
.TranslateY(-50)
.Width(100)
.Height(100)
.Background(Colors.Blue.Medium);
});
}
row.RelativeColumn()
.Background("#BBB")
.Padding(10)
.Text("This column takes 1/3 of the available space (200pt)");
[Test]
public void Flip()
{
RenderingTest
.Create()
.PageSize(350, 350)
.FileName()
.Render(container =>
{
container
.Padding(20)
.Grid(grid =>
{
grid.Columns(2);
grid.Spacing(10);
foreach (var turns in Enumerable.Range(0, 4))
{
grid.Item()
.Width(150)
.Height(150)
.Background(Colors.Grey.Lighten3)
.Padding(10)
.Element(element =>
{
if (turns == 1 || turns == 2)
element = element.FlipHorizontal();
row.RelativeColumn(2)
.Background("#DDD")
.Padding(10)
.Text("This column takes 2/3 of the available space (400pt)");
});
if (turns == 2 || turns == 3)
element = element.FlipVertical();
return element;
})
.Box()
.Background(Colors.White)
.Padding(10)
.Text($"Flipped {turns}", TextStyle.Default.Size(16));
}
});
});
}
//[ShowResult]
[ImageSize(500, 350)]
public void Column(IContainer container)
[Test]
public void RotateInTable()
{
container
.Background("#FFF")
.Padding(15)
.Stack(column =>
RenderingTest
.Create()
.PageSize(200, 200)
.FileName()
.Render(container =>
{
column.Spacing(10);
column
.Element()
.Background("#999")
.Height(50);
column
.Element()
.Background("#BBB")
.Height(100);
column
.Element()
.Background("#DDD")
.Height(150);
container
.Padding(10)
.Border(2)
.Row(row =>
{
row.ConstantColumn(25)
.Border(1)
.RotateLeft()
.AlignCenter()
.AlignMiddle()
.Text("Sample text");
row.RelativeColumn().Border(1).Padding(5).Text(Placeholders.Paragraph());
});
});
}
[Test]
public void Unconstrained()
{
RenderingTest
.Create()
.PageSize(400, 350)
.FileName()
.Render(container =>
{
container
.Padding(25)
.PaddingLeft(75)
.Stack(stack =>
{
stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
stack
.Item()
// creates an infinite space for its child
.Unconstrained()
// moves the child up and left
.TranslateX(-50)
.TranslateY(-50)
// limits the space for the child
.Width(100)
.Height(100)
.Background(Colors.Blue.Darken1);
stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
});
});
}
[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));
}
});
});
}
}
}
}
@@ -1,89 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Elements;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Examples.Engine
{
[TestFixture]
public class ExampleTestBase
{
private readonly Size DefaultImageSize = new Size(400, 300);
private const string ResultPath = "Result";
[SetUp]
public void Setup()
{
if (Directory.Exists(ResultPath))
Directory.Delete(ResultPath, true);
Directory.CreateDirectory(ResultPath);
}
[Test]
public void RunTest()
{
GetType()
.GetMethods()
.Where(IsExampleMethod)
.ToList()
.ForEach(HandleExample);
}
private bool IsExampleMethod(MethodInfo method)
{
var parameters = method.GetParameters();
return parameters.Length == 1 && parameters.First().ParameterType == typeof(IContainer);
}
private T GetAttribute<T>(MethodInfo methodInfo) where T : Attribute
{
return methodInfo.GetCustomAttributes().FirstOrDefault(y => y is T) as T;
}
private void HandleExample(MethodInfo methodInfo)
{
var size = GetAttribute<ImageSizeAttribute>(methodInfo)?.Size ?? DefaultImageSize;
var fileName = GetAttribute<FileNameAttribute>(methodInfo)?.FileName ?? methodInfo.Name;
var showResult = GetAttribute<ShowResultAttribute>(methodInfo) != null;
var shouldIgnore = GetAttribute<IgnoreAttribute>(methodInfo) != null;
if (shouldIgnore)
return;
var container = new Container();
methodInfo.Invoke(this, new object[] {container});
Func<int, string> fileNameSchema = i => $"{fileName.ToLower()}-${i}.png";
var document = new SimpleDocument(container, size);
document.GenerateImages(fileNameSchema);
if (showResult)
Process.Start("explorer", fileNameSchema(0));
}
private byte[] RenderPage(Element element, Size size)
{
// scale the result so it is more readable
const float scalingFactor = 2;
var imageInfo = new SKImageInfo((int)(size.Width * scalingFactor), (int)(size.Height * scalingFactor));
using var surface = SKSurface.Create(imageInfo);
surface.Canvas.Scale(scalingFactor);
var canvas = new Canvas(surface.Canvas);
element?.Draw(canvas, size);
surface.Canvas.Save();
return surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100).ToArray();
}
}
}
@@ -1,14 +0,0 @@
using System;
namespace QuestPDF.Examples.Engine
{
public class FileNameAttribute : Attribute
{
public string FileName { get; }
public FileNameAttribute(string fileName)
{
FileName = fileName;
}
}
}
@@ -1,9 +0,0 @@
using System;
namespace QuestPDF.Examples.Engine
{
public class IgnoreAttribute : Attribute
{
}
}
@@ -1,19 +0,0 @@
using System;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples.Engine
{
public class ImageSizeAttribute : Attribute
{
private int Width { get; }
private int Height { get; }
public Size Size => new Size(Width, Height);
public ImageSizeAttribute(int width, int height)
{
Width = width;
Height = height;
}
}
}
+95
View File
@@ -0,0 +1,95 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using QuestPDF.Elements;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples.Engine
{
public enum RenderingTestResult
{
Pdf,
Images
}
public class RenderingTest
{
private string FileNamePrefix = "test";
private Size Size { get; set; }
private bool ShowResult { get; set; }
private RenderingTestResult ResultType { get; set; } = RenderingTestResult.Images;
private RenderingTest()
{
}
public static RenderingTest Create()
{
return new RenderingTest();
}
public RenderingTest FileName([CallerMemberName] string fileName = "test")
{
FileNamePrefix = fileName;
return this;
}
public RenderingTest PageSize(Size size)
{
Size = size;
return this;
}
public RenderingTest PageSize(int width, int height)
{
return PageSize(new Size(width, height));
}
public RenderingTest ProducePdf()
{
ResultType = RenderingTestResult.Pdf;
return this;
}
public RenderingTest ProduceImages()
{
ResultType = RenderingTestResult.Images;
return this;
}
public RenderingTest ShowResults()
{
ShowResult = true;
return this;
}
public void Render(Action<IContainer> content)
{
var container = new Container();
content(container);
var maxPages = ResultType == RenderingTestResult.Pdf ? 1000 : 10;
var document = new SimpleDocument(container, Size, maxPages);
if (ResultType == RenderingTestResult.Images)
{
Func<int, string> fileNameSchema = i => $"{FileNamePrefix}-${i}.png";
document.GenerateImages(fileNameSchema);
if (ShowResult)
Process.Start("explorer", fileNameSchema(0));
}
if (ResultType == RenderingTestResult.Pdf)
{
var fileName = $"{FileNamePrefix}.pdf";
document.GeneratePdf(fileName);
if (ShowResult)
Process.Start("explorer", fileName);
}
}
}
}
@@ -1,9 +0,0 @@
using System;
namespace QuestPDF.Examples.Engine
{
public class ShowResultAttribute : Attribute
{
}
}
+15 -6
View File
@@ -1,4 +1,5 @@
using QuestPDF.Drawing;
using QuestPDF.Elements;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
@@ -7,27 +8,35 @@ namespace QuestPDF.Examples.Engine
{
public class SimpleDocument : IDocument
{
public const int ImageScalingFactor = 2;
private IContainer Container { get; }
private Size Size { get; }
private int MaxPages { get; }
public SimpleDocument(IContainer container, Size size)
public SimpleDocument(IContainer container, Size size, int maxPages)
{
Container = container;
Size = size;
MaxPages = maxPages;
}
public DocumentMetadata GetMetadata()
{
return new DocumentMetadata()
{
RasterDpi = PageSizes.PointsPerInch * 2,
Size = Size
RasterDpi = PageSizes.PointsPerInch * ImageScalingFactor,
DocumentLayoutExceptionThreshold = MaxPages
};
}
public void Compose(IContainer container)
public void Compose(IDocumentContainer container)
{
container.Element(Container.Child);
container.Page(page =>
{
page.Size(new PageSize(Size.Width, Size.Height));
page.Content().Container().Background(Colors.White).Element(Container as Container);
});
}
}
}
+24 -17
View File
@@ -1,4 +1,4 @@
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
@@ -12,33 +12,40 @@ namespace QuestPDF.Examples
{
return container
.Border(1)
.Background(dark ? "#EEE" : "#FFF")
.Background(dark ? Colors.Grey.Lighten2 : Colors.White)
.Padding(10);
}
public static IContainer LabelCell(this IContainer container) => container.Cell(true);
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text, TextStyle.Default.Medium());
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
}
public class FrameExample: ExampleTestBase
public class FrameExample
{
[ImageSize(550, 400)]
[ShowResult]
public void Frame(IContainer container)
[Test]
public void Frame()
{
container
.Background("#FFF")
.Padding(25)
.Stack(stack =>
RenderingTest
.Create()
.PageSize(550, 400)
.FileName()
.ShowResults()
.Render(container =>
{
for(var i=1; i<=4; i++)
{
stack.Element().Row(row =>
container
.Background("#FFF")
.Padding(25)
.Stack(stack =>
{
row.RelativeColumn(2).LabelCell().Text(TextPlaceholder.Label());
row.RelativeColumn(3).ValueCell().Text(TextPlaceholder.Paragraph());
for(var i=1; i<=4; i++)
{
stack.Item().Row(row =>
{
row.RelativeColumn(2).LabelCell(Placeholders.Label());
row.RelativeColumn(3).ValueCell().Text(Placeholders.Paragraph());
});
}
});
}
});
}
}
Binary file not shown.
+23 -16
View File
@@ -1,4 +1,5 @@
using System.Net;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
@@ -27,27 +28,33 @@ namespace QuestPDF.Examples
}
}
public class LoremPicsumExample : ExampleTestBase
public class LoremPicsumExample
{
[ShowResult]
[ImageSize(350, 280)]
public void LoremPicsum(IContainer container)
[Test]
public void LoremPicsum()
{
container
.Background("#FFF")
.Padding(25)
.Stack(column =>
RenderingTest
.Create()
.PageSize(350, 280)
.FileName()
.Render(container =>
{
column.Spacing(10);
container
.Background("#FFF")
.Padding(25)
.Stack(column =>
{
column.Spacing(10);
column
.Element()
.Component(new LoremPicsum(true));
column
.Item()
.Component(new LoremPicsum(true));
column
.Element()
.AlignRight()
.Text("From Lorem Picsum");
column
.Item()
.AlignRight()
.Text("From Lorem Picsum");
});
});
}
}
+141 -106
View File
@@ -1,132 +1,167 @@
using QuestPDF.Examples.Engine;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class Examples : ExampleTestBase
public class Examples
{
public void Padding(IContainer container)
[Test]
public void Padding()
{
container
.Background("#FDD")
.Padding(50)
.Background("#AFA")
.PaddingVertical(50)
.Background("#77F")
.PaddingHorizontal(50)
.Background("#444");
}
public void Border(IContainer container)
{
container
.Background("#EEE")
.Padding(25)
.AlignBottom()
.AlignCenter()
.BorderBottom(2)
.BorderColor("#000")
.Background("FFF")
.Padding(5)
.Text("Sample text", TextStyle.Default.FontType("Segoe UI emoji").Alignment(HorizontalAlignment.Center));
}
public void Alignment(IContainer container)
{
container
.Stack(column =>
RenderingTest
.Create()
.PageSize(300, 300)
.FileName()
.ShowResults()
.Render(container =>
{
column
.Element()
.Height(100)
.Background("#FFF")
.AlignLeft()
.AlignMiddle()
container
.Background("#FDD")
.Padding(50)
.Background("#AFA")
.PaddingVertical(50)
.Background("#77F")
.PaddingHorizontal(50)
.Width(50)
.Height(50)
.Background("#444");
column
.Element()
.Height(100)
.Background("#DDD")
.AlignCenter()
.AlignMiddle()
.Width(50)
.Height(50)
.Background("#222");
column
.Element()
.Height(100)
.Background("#BBB")
.AlignRight()
.AlignMiddle()
.Width(50)
.Height(50)
.Background("#000");
});
}
public void Expand(IContainer container)
[Test]
public void Border()
{
container
.Stack(column =>
RenderingTest
.Create()
.PageSize(200, 150)
.FileName()
.Render(container =>
{
column
.Element()
.Height(150)
.Row(row =>
container
.Background("#EEE")
.Padding(25)
.AlignBottom()
.AlignCenter()
.BorderBottom(2)
.BorderColor("#000")
.Background("FFF")
.Padding(5)
.AlignCenter()
.Text("Sample text", TextStyle.Default.FontType("Segoe UI emoji"));
});
}
[Test]
public void Alignment()
{
RenderingTest
.Create()
.PageSize(200, 150)
.FileName()
.Render(container =>
{
container
.Stack(column =>
{
row.RelativeColumn()
.Extend()
.Background("FFF")
column
.Item()
.Height(100)
.Background("#FFF")
.AlignLeft()
.AlignMiddle()
.Height(50)
.Width(50)
.Background("444");
row.RelativeColumn()
.Extend()
.Background("BBB")
.Height(50)
.ExtendHorizontal()
.Background("444");
});
.Background("#444");
column
.Element()
.Height(150)
.Row(row =>
{
row.RelativeColumn()
.Extend()
.Background("BBB")
column
.Item()
.Height(100)
.Background("#DDD")
.AlignCenter()
.AlignMiddle()
.ExtendVertical()
.Width(50)
.Background("444");
row.RelativeColumn()
.Extend()
.Background("BBB")
.Height(50)
.Background("#222");
column
.Item()
.Height(100)
.Background("#BBB")
.AlignRight()
.AlignMiddle()
.ExtendVertical()
.ExtendHorizontal()
.Background("444");
.Width(50)
.Height(50)
.Background("#000");
});
});
}
[Test]
public void Expand()
{
RenderingTest
.Create()
.PageSize(200, 150)
.FileName()
.Render(container =>
{
container
.Stack(column =>
{
column
.Item()
.Height(150)
.Row(row =>
{
row.RelativeColumn()
.Extend()
.Background("FFF")
.Height(50)
.Width(50)
.Background("444");
row.RelativeColumn()
.Extend()
.Background("BBB")
.Height(50)
.ExtendHorizontal()
.Background("444");
});
column
.Item()
.Height(150)
.Row(row =>
{
row.RelativeColumn()
.Extend()
.Background("BBB")
.ExtendVertical()
.Width(50)
.Background("444");
row.RelativeColumn()
.Extend()
.Background("BBB")
.ExtendVertical()
.ExtendHorizontal()
.Background("444");
});
});
});
}
+12 -4
View File
@@ -6,15 +6,23 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="SkiaSharp" Version="2.80.2" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\QuestPDF.InvoiceSample\QuestPDF.InvoiceSample.csproj" />
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="quo-vadis.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="LibreBarcode39-Regular.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
+228
View File
@@ -0,0 +1,228 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class TextBenchmark
{
[Test]
public void Generate()
{
var chapters = GetBookChapters().ToList();
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.FileName()
.ProducePdf()
.ShowResults()
.Render(x => ComposeBook(x, chapters));
}
[Test]
public void Benchmark()
{
var chapters = GetBookChapters().ToList();
var results = PerformTest(16).ToList();
Console.WriteLine($"Min: {results.Min():F}");
Console.WriteLine($"Max: {results.Max():F}");
Console.WriteLine($"Avg: {results.Average():F}");
void GenerateDocument()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.FileName()
.ProducePdf()
.Render(x => ComposeBook(x, chapters));
}
IEnumerable<float> PerformTest(int attempts)
{
foreach (var i in Enumerable.Range(0, attempts))
{
var timer = new Stopwatch();
timer.Start();
GenerateDocument();
timer.Stop();
Console.WriteLine($"Attempt {i}: {timer.ElapsedMilliseconds:F}");
yield return timer.ElapsedMilliseconds;
}
}
}
class BookChapter
{
public string Title { get; set; }
public string Content { get; set; }
}
private static IEnumerable<BookChapter> GetBookChapters()
{
var book = File.ReadAllLines("quo-vadis.txt");
var chapterPointers = book
.Select((line, index) => new
{
LineNumber = index,
Text = line
})
.Where(x => x.Text.Length < 50 && x.Text.Contains("Rozdział") || x.Text.Contains("-----"))
.Select(x => x.LineNumber)
.ToList();
foreach (var index in Enumerable.Range(0, chapterPointers.Count - 1))
{
var chapter = chapterPointers[index];
var title = book[chapter];
var lineFrom = chapterPointers[index];
var lineTo = chapterPointers[index + 1] - 1;
var lines = book.Skip(lineFrom + 1).Take(lineTo - lineFrom).Where(x => !string.IsNullOrWhiteSpace(x));
var content = string.Join(Environment.NewLine, lines);
yield return new BookChapter
{
Title = title,
Content = content
};
}
}
private void ComposeBook(IContainer container, ICollection<BookChapter> chapters)
{
var subtitleStyle = TextStyle.Default.Size(24).SemiBold().Color(Colors.Blue.Medium);
var normalStyle = TextStyle.Default.Size(14);
ComposePage(container);
void ComposePage(IContainer container)
{
container
.Padding(50)
.Decoration(decoration =>
{
decoration
.Content()
.Stack(stack =>
{
stack.Item().Element(Title);
stack.Item().PageBreak();
stack.Item().Element(TableOfContents);
stack.Item().PageBreak();
Chapters(stack);
stack.Item().Element(Acknowledgements);
});
decoration.Footer().Element(Footer);
});
}
void Title(IContainer container)
{
container
.Extend()
.PaddingBottom(200)
.AlignBottom()
.Stack(stack =>
{
stack.Item().Text("Quo Vadis", TextStyle.Default.Size(72).Bold().Color(Colors.Blue.Darken2));
stack.Item().Text("Henryk Sienkiewicz", TextStyle.Default.Size(24).Color(Colors.Grey.Darken2));
});
}
void TableOfContents(IContainer container)
{
container.Stack(stack =>
{
SectionTitle(stack, "Spis treści");
foreach (var chapter in chapters)
{
stack.Item().InternalLink(chapter.Title).Row(row =>
{
row.RelativeColumn().Text(chapter.Title, normalStyle);
row.ConstantColumn(100).AlignRight().Text(text => text.PageNumberOfLocation(chapter.Title, normalStyle));
});
}
});
}
void Chapters(StackDescriptor stack)
{
foreach (var chapter in chapters)
{
stack.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
}
}
void Chapter(IContainer container, string title, string content)
{
container.Stack(stack =>
{
SectionTitle(stack, title);
stack.Item().Text(text =>
{
text.ParagraphSpacing(5);
text.Span(content, normalStyle);
});
stack.Item().PageBreak();
});
}
void Acknowledgements(IContainer container)
{
container.Stack(stack =>
{
SectionTitle(stack, "Podziękowania");
stack.Item().Text(text =>
{
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.Span(". Dziękuję za wspieranie polskiego czytelnictwa!");
});
});
}
void SectionTitle(StackDescriptor stack, string text)
{
stack.Item().Location(text).Text(text, subtitleStyle);
stack.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
}
void Footer(IContainer container)
{
container
.AlignCenter()
.Text(text =>
{
text.CurrentPageNumber();
text.Span(" / ");
text.TotalPages();
});
}
}
}
}
+277
View File
@@ -0,0 +1,277 @@
using System;
using System.Linq;
using System.Text;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
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)
.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.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 =>
{
text.DefaultTextStyle(TextStyle.Default);
text.AlignLeft();
text.ParagraphSpacing(10);
foreach (var i in Enumerable.Range(1, 100))
text.Line($"{i}: {Placeholders.Paragraph()}");
});
});
}
[Test]
public void SpaceIssue()
{
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(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();
}
});
});
}
}
}
File diff suppressed because one or more lines are too long
+22 -24
View File
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using QuestPDF.Helpers;
using QuestPDF.ReportSample.Layouts;
namespace QuestPDF.ReportSample
{
@@ -18,9 +17,9 @@ namespace QuestPDF.ReportSample
Title = "Sample Report Document",
HeaderFields = HeaderFields(),
LogoData = Helpers.GetImage("logo.png"),
Sections = Enumerable.Range(0, 10).Select(x => GenerateSection()).ToList(),
Photos = Enumerable.Range(0, 10).Select(x => GetReportPhotos()).ToList()
LogoData = Helpers.GetImage("Logo.png"),
Sections = Enumerable.Range(0, 40).Select(x => GenerateSection()).ToList(),
Photos = Enumerable.Range(0, 25).Select(x => GetReportPhotos()).ToList()
};
List<ReportHeaderField> HeaderFields()
@@ -41,6 +40,11 @@ namespace QuestPDF.ReportSample
{
Label = "Date",
Value = DateTime.Now.ToString("g")
},
new ReportHeaderField()
{
Label = "Status",
Value = "Completed, found 2 issues"
}
};
}
@@ -48,12 +52,12 @@ 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
{
Title = TextPlaceholder.Label(),
Title = Placeholders.Label(),
Parts = Enumerable.Range(0, sectionLength).Select(x => GetRandomElement()).ToList()
};
}
@@ -62,10 +66,10 @@ namespace QuestPDF.ReportSample
{
var random = Helpers.Random.NextDouble();
if (random < 0.8f)
if (random < 0.9f)
return GetTextElement();
if (random < 0.9f)
if (random < 0.95f)
return GetMapElement();
return GetPhotosElement();
@@ -75,19 +79,17 @@ namespace QuestPDF.ReportSample
{
return new ReportSectionText
{
Label = TextPlaceholder.Label(),
Text = TextPlaceholder.Paragraph()
Label = Placeholders.Label(),
Text = Placeholders.Paragraph()
};
}
ReportSectionMap GetMapElement()
{
var rnd = Helpers.Random.Next(0, 64);
return new ReportSectionMap
{
Label = "Location",
ImageSource = x => Helpers.GetDocumentMap($"{rnd}.jpg"),
ImageSource = Placeholders.Image,
Location = Helpers.RandomLocation()
};
}
@@ -98,28 +100,24 @@ namespace QuestPDF.ReportSample
{
Label = "Photos",
Photos = Enumerable
.Range(0, Helpers.Random.Next(1, 10))
.Select(x => Helpers.Random.Next(0, 128))
.Select(x => Helpers.GetPhoto($"{x}.jpg"))
.Range(0, Helpers.Random.Next(1, 15))
.Select(x => Placeholders.Image(400, 300))
.ToList()
};
}
ReportPhoto GetReportPhotos()
{
var photoId = Helpers.Random.Next(0, 128);
var mapId = Helpers.Random.Next(0, 64);
return new ReportPhoto()
{
PhotoData = Helpers.GetPhoto($"{photoId}.jpg"),
PhotoData = Placeholders.Image(800, 600),
Comments = TextPlaceholder.Paragraph(),
Comments = Placeholders.Sentence(),
Date = DateTime.Now - TimeSpan.FromDays(Helpers.Random.NextDouble() * 100),
Location = Helpers.RandomLocation(),
MapContextSource = x => Helpers.GetContextMap($"{mapId}.jpg"),
MapDetailsSource = x => Helpers.GetDetailsMap($"{mapId}.jpg")
MapContextSource = x => Placeholders.Image(400, 300),
MapDetailsSource = x => Placeholders.Image(400, 300)
};
}
}
+1 -8
View File
@@ -1,6 +1,5 @@
using System;
using System.IO;
using QuestPDF.ReportSample.Layouts;
using SkiaSharp;
namespace QuestPDF.ReportSample
@@ -10,13 +9,7 @@ namespace QuestPDF.ReportSample
public static Random Random { get; } = new Random();
public static string GetTestItem(string path) => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", path);
public static byte[] GetDocumentMap(string name) => GetImage(Path.Combine("Maps", "Document", name));
public static byte[] GetDetailsMap(string name) => GetImage(Path.Combine("Maps", "Details", name));
public static byte[] GetContextMap(string name) => GetImage(Path.Combine("Maps", "Context", name));
public static byte[] GetPhoto(string name) => GetImage(Path.Combine("Photos", name));
public static byte[] GetImage(string name)
{
var photoPath = GetTestItem(name);
+8 -6
View File
@@ -1,27 +1,29 @@
using System;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.ReportSample.Layouts
{
public static class Helpers
{
static IContainer Cell(this IContainer container, string color)
static IContainer Cell(this IContainer container, bool background)
{
return container
.Border(0.5f)
.Background(color)
.BorderColor(Colors.Grey.Lighten1)
.Background(background ? Colors.Grey.Lighten4 : Colors.White)
.Padding(5);
}
public static IContainer LightCell(this IContainer container)
public static IContainer ValueCell(this IContainer container)
{
return container.Cell("#0000");
return container.Cell(false);
}
public static IContainer DarkCell(this IContainer container)
public static IContainer LabelCell(this IContainer container)
{
return container.Cell("#1000");
return container.Cell(true);
}
public static string Format(this Location location)
+20 -21
View File
@@ -1,4 +1,5 @@
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.ReportSample.Layouts
@@ -15,47 +16,45 @@ namespace QuestPDF.ReportSample.Layouts
public void Compose(IContainer container)
{
container
.ShowEntire()
.Stack(stack =>
{
stack.Element(PhotoWithMaps);
stack.Element(PhotoDetails);
stack.Spacing(5);
stack.Item().Element(PhotoWithMaps);
stack.Item().Element(PhotoDetails);
});
}
void PhotoWithMaps(IContainer container)
{
container
.Padding(-3)
.PaddingBottom(3)
.Row(row =>
{
row.RelativeColumn(2).Padding(3).Component(new ImageTemplate(Model.PhotoData));
row.RelativeColumn(2).AspectRatio(4 / 3f).Image(Placeholders.Image);
row.RelativeColumn().Stack(stack =>
row.RelativeColumn().PaddingLeft(5).Stack(stack =>
{
stack.Element().Padding(3).Component(new ImageTemplate(Model.MapDetailsSource));
stack.Element().Padding(3).Component(new ImageTemplate(Model.MapContextSource));
stack.Spacing(7f);
stack.Item().AspectRatio(4 / 3f).Image(Placeholders.Image);
stack.Item().AspectRatio(4 / 3f).Image(Placeholders.Image);
});
});
}
void PhotoDetails(IContainer container)
{
container.Stack(stack =>
container.Border(0.75f).BorderColor(Colors.Grey.Medium).Grid(grid =>
{
stack.Element().Row(row =>
{
row.RelativeColumn().DarkCell().Text("Date", Typography.Normal);
row.RelativeColumn(2).LightCell().Text(Model.Date?.ToString("g") ?? string.Empty, Typography.Normal);
row.RelativeColumn().DarkCell().Text("Location", Typography.Normal);
row.RelativeColumn(2).LightCell().Text(Model.Location.Format(), Typography.Normal);
});
grid.Columns(6);
stack.Element().Row(row =>
{
row.RelativeColumn().DarkCell().Text("Comments", Typography.Normal);
row.RelativeColumn(5).LightCell().Text(Model.Comments, 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.Item().LabelCell().Text("Comments", Typography.Normal);
grid.Item(5).ValueCell().Text(Model.Comments, Typography.Normal);
});
}
}
@@ -1,6 +1,5 @@
using System;
using System.Linq;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.ReportSample.Layouts
@@ -17,31 +16,31 @@ namespace QuestPDF.ReportSample.Layouts
public void Compose(IContainer container)
{
container
.MinHeight(100)
.Section(section =>
.EnsureSpace()
.Decoration(decoration =>
{
section
decoration
.Header()
.PaddingBottom(5)
.Text(Model.Title, Typography.Headline);
section.Content().PageableStack(stack =>
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Stack(stack =>
{
foreach (var part in Model.Parts)
{
stack.Element().Row(row =>
stack.Item().EnsureSpace(25).Row(row =>
{
row.ConstantColumn(150).DarkCell().Text(part.Label, Typography.Normal);
var frame = row.RelativeColumn().LightCell();
row.ConstantColumn(150).LabelCell().Text(part.Label, Typography.Normal);
var frame = row.RelativeColumn().ValueCell();
if (part is ReportSectionText text)
frame.Text(text.Text, Typography.Normal);
frame.ShowEntire().Text(text.Text, Typography.Normal);
if (part is ReportSectionMap map)
frame.Element(container => MapElement(container, map));
frame.Element(x => MapElement(x, map));
if (part is ReportSectionPhotos photos)
frame.Element(container => PhotosElement(container, photos));
frame.Element(x => PhotosElement(x, photos));
});
}
});
@@ -56,12 +55,12 @@ namespace QuestPDF.ReportSample.Layouts
return;
}
container.Stack(stack =>
container.ShowEntire().Stack(stack =>
{
stack.Spacing(5);
stack.Element().Component(new ImageTemplate(model.ImageSource));
stack.Element().Text(model.Location.Format(), Typography.Normal);
stack.Item().MaxWidth(250).AspectRatio(4 / 3f).Image(Placeholders.Image);
stack.Item().Text(model.Location.Format(), Typography.Normal);
});
}
@@ -73,21 +72,12 @@ namespace QuestPDF.ReportSample.Layouts
return;
}
var rowCount = (int) Math.Ceiling(model.Photos.Count / 3f);
container.Padding(-2).Stack(stack =>
container.Debug("Photos").Grid(grid =>
{
foreach (var rowId in Enumerable.Range(0, rowCount))
{
stack.Element().Row(row =>
{
foreach (var id in Enumerable.Range(0, 3))
{
var data = model.Photos.ElementAtOrDefault(rowId + id);
row.RelativeColumn().Padding(2).Component(new ImageTemplate(data));
}
});
}
grid.Spacing(5);
grid.Columns(3);
model.Photos.ForEach(x => grid.Item().AspectRatio(4 / 3f).Image(Placeholders.Image));
});
}
}
+47 -43
View File
@@ -18,79 +18,83 @@ namespace QuestPDF.ReportSample.Layouts
{
return new DocumentMetadata()
{
Title = Model.Title,
Size = PageSizes.A4
Title = Model.Title
};
}
public void Compose(IContainer container)
public void Compose(IDocumentContainer container)
{
container
.PaddingVertical(40)
.PaddingHorizontal(50)
.Page(page =>
{
page.Header(ComposeHeader);
page.Content(ComposeContent);
page.Footer().AlignCenter().PageNumber("Page {number}");
page.MarginVertical(40);
page.MarginHorizontal(50);
page.Size(PageSizes.A4);
page.Header().Element(ComposeHeader);
page.Content().Element(ComposeContent);
page.Footer().AlignCenter().Text(text =>
{
text.DefaultTextStyle(Typography.Normal);
text.CurrentPageNumber();
text.Span(" / ");
text.TotalPages();
});
});
}
private void ComposeHeader(IContainer container)
{
container.Row(row =>
container.Stack(stack =>
{
row.RelativeColumn().MaxWidth(300).Stack(stack =>
stack.Item().Row(row =>
{
stack.Spacing(5);
row.Spacing(50);
stack
.Element()
.PaddingBottom(5)
.Text(Model.Title, Typography.Title);
stack.Element().ShowOnce().Stack(table =>
{
table.Spacing(5);
foreach (var field in Model.HeaderFields)
{
table.Element().Row(row =>
{
row.ConstantColumn(50)
.AlignLeft()
.Text($"{field.Label}:", Typography.Normal);
row.RelativeColumn()
.PaddingLeft(10)
.Text(field.Value, Typography.Normal);
});
}
});
row.RelativeColumn().PaddingTop(-10).Text(Model.Title, Typography.Title);
row.ConstantColumn(150).ExternalLink("https://www.questpdf.com").Image(Model.LogoData);
});
stack.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
row.ConstantColumn(150).ExternalLink("https://www.questpdf.com").Image(Model.LogoData);
stack.Item().ShowOnce().Grid(grid =>
{
grid.Columns(2);
grid.Spacing(5);
foreach (var field in Model.HeaderFields)
{
grid.Item().Text(text =>
{
text.Span($"{field.Label}: ", Typography.Normal.SemiBold());
text.Span(field.Value, Typography.Normal);
});
}
});
});
}
void ComposeContent(IContainer container)
{
container.PaddingVertical(20).PageableStack(stack =>
container.PaddingVertical(20).Stack(stack =>
{
stack.Spacing(20);
stack.Element().Component(new TableOfContentsTemplate(Model.Sections));
stack.Item().Component(new TableOfContentsTemplate(Model.Sections));
stack.Item().PageBreak();
foreach (var section in Model.Sections)
stack.Element().Location(section.Title).Component(new SectionTemplate(section));
stack.Item().Location(section.Title).Component(new SectionTemplate(section));
stack.Element().PageBreak();
stack.Element().Location("Photos");
stack.Item().PageBreak();
stack.Item().Location("Photos");
foreach (var photo in Model.Photos)
stack.Element().Component(new PhotoTemplate(photo));
stack.Item().Component(new PhotoTemplate(photo));
});
}
}
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Generic;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
@@ -18,21 +16,21 @@ namespace QuestPDF.ReportSample.Layouts
public void Compose(IContainer container)
{
container
.Section(section =>
.Decoration(decoration =>
{
section
decoration
.Header()
.PaddingBottom(5)
.Text("Table of contents", Typography.Headline);
section.Content().PageableStack(stack =>
decoration.Content().Stack(stack =>
{
stack.Spacing(5);
for (var i = 0; i < Sections.Count; i++)
stack.Element(c => DrawLink(c, i+1, Sections[i].Title));
stack.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
stack.Element(c => DrawLink(c, Sections.Count+1, "Photos"));
stack.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
});
});
}
@@ -45,6 +43,7 @@ namespace QuestPDF.ReportSample.Layouts
{
row.ConstantColumn(25).Text($"{number}.", Typography.Normal);
row.RelativeColumn().Text(locationName, Typography.Normal);
row.ConstantColumn(150).AlignRight().Text(text => text.PageNumberOfLocation(locationName, Typography.Normal));
});
}
}
@@ -8,19 +8,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DeepCloner" Version="0.10.2" />
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="SkiaSharp" Version="2.80.2" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources" />
</ItemGroup>
<ItemGroup>
<Content Include="Resources\Photos\readme.txt" />
<Content Include="Resources\**\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -29,4 +23,8 @@
<ItemGroup>
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Logo.png" />
</ItemGroup>
</Project>

Before

Width:  |  Height:  |  Size: 993 B

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Some files were not shown because too many files have changed in this diff Show More