Compare commits
5 Commits
text-shaping
...
2022.03
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b9b164583 | |||
| 2c14f91ebe | |||
| b533a32b61 | |||
| 262bd3c182 | |||
| fcff989ab2 |
@@ -13,7 +13,7 @@ namespace QuestPDF.Examples
|
||||
[Test]
|
||||
public void Example()
|
||||
{
|
||||
FontManager.RegisterFontType(File.OpenRead("LibreBarcode39-Regular.ttf"));
|
||||
FontManager.RegisterFont(File.OpenRead("LibreBarcode39-Regular.ttf"));
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
@@ -25,7 +25,9 @@ namespace QuestPDF.Examples
|
||||
.Background(Colors.White)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("*QuestPDF*", TextStyle.Default.FontType("Libre Barcode 39").Size(64));
|
||||
.Text("*QuestPDF*")
|
||||
.FontFamily("Libre Barcode 39")
|
||||
.FontSize(64);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,10 @@ namespace QuestPDF.Examples
|
||||
.PrimaryLayer()
|
||||
.PaddingVertical(10)
|
||||
.PaddingHorizontal(20)
|
||||
.Text("Sample text", TextStyle.Default.Size(16).Color(Colors.Blue.Darken2).SemiBold());
|
||||
.Text("Sample text")
|
||||
.FontSize(16)
|
||||
.FontColor(Colors.Blue.Darken2)
|
||||
.SemiBold();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,7 +57,10 @@ namespace QuestPDF.Examples
|
||||
column
|
||||
.Item()
|
||||
.PaddingBottom(10)
|
||||
.Text("Chart example", TextStyle.Default.Size(20).SemiBold().Color(Colors.Blue.Medium));
|
||||
.Text("Chart example")
|
||||
.FontSize(20)
|
||||
.SemiBold()
|
||||
.FontColor(Colors.Blue.Medium);
|
||||
|
||||
column
|
||||
.Item()
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace QuestPDF.Examples
|
||||
page.Content().PaddingVertical(10).Border(1).Padding(10).Column(column =>
|
||||
{
|
||||
foreach (var index in Enumerable.Range(1, 100))
|
||||
column.Item().Text($"Line {index}", TextStyle.Default.Color(Placeholders.Color()));
|
||||
column.Item().Text($"Line {index}").FontColor(Placeholders.Color());
|
||||
});
|
||||
|
||||
page.Footer().Text("Footer");
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace QuestPDF.Examples
|
||||
text.Line(Placeholders.Sentence());
|
||||
|
||||
// this text has size 20 but also semibold and red
|
||||
text.Span(Placeholders.Sentence(), TextStyle.Default.Color(Colors.Red.Medium));
|
||||
text.Span(Placeholders.Sentence()).FontColor(Colors.Red.Medium);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,8 +26,8 @@ namespace QuestPDF.Examples
|
||||
.DefaultTextStyle(TextStyle.Default.Bold().Underline())
|
||||
.Column(column =>
|
||||
{
|
||||
column.Item().Text("Default style applies to all children", TextStyle.Default);
|
||||
column.Item().Text("You can override certain styles", TextStyle.Default.Underline(false).Color(Colors.Green.Darken2));
|
||||
column.Item().Text("Default style applies to all children");
|
||||
column.Item().Text("You can override certain styles").Underline(false).FontColor(Colors.Green.Darken2);
|
||||
|
||||
column.Item().PaddingTop(10).Border(1).Grid(grid =>
|
||||
{
|
||||
@@ -43,7 +43,8 @@ namespace QuestPDF.Examples
|
||||
.Height(50)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text(i, TextStyle.Default.Size(16 + i / 4));
|
||||
.Text(i)
|
||||
.FontSize(16 + i / 4);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,7 +43,9 @@ namespace QuestPDF.Examples
|
||||
.Before()
|
||||
.Background(Colors.Grey.Medium)
|
||||
.Padding(10)
|
||||
.Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
|
||||
.Text("Notes")
|
||||
.FontSize(16)
|
||||
.FontColor("#FFF");
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
@@ -280,12 +282,15 @@ namespace QuestPDF.Examples
|
||||
.Layer()
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("Watermark", TextStyle.Default.Size(48).Bold().Color(Colors.Green.Lighten3));
|
||||
.Text("Watermark")
|
||||
.FontSize(48)
|
||||
.Bold()
|
||||
.FontColor(Colors.Green.Lighten3);
|
||||
|
||||
layers
|
||||
.Layer()
|
||||
.AlignBottom()
|
||||
.Text(text => text.CurrentPageNumber(TextStyle.Default.Size(16).Color(Colors.Green.Medium)));
|
||||
.Text(text => text.CurrentPageNumber().FontSize(16).FontColor(Colors.Green.Medium));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -416,7 +421,9 @@ namespace QuestPDF.Examples
|
||||
.Border(1)
|
||||
.BorderColor(Colors.Grey.Medium)
|
||||
.Padding(10)
|
||||
.Text(font, TextStyle.Default.FontType(font).Size(16));
|
||||
.Text(font)
|
||||
.FontFamily(font)
|
||||
.FontSize(16);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -456,7 +463,7 @@ namespace QuestPDF.Examples
|
||||
});
|
||||
|
||||
layers.Layer().Background("#8F00").Extend();
|
||||
layers.Layer().PaddingTop(40).Text("It works!", TextStyle.Default.Size(24));
|
||||
layers.Layer().PaddingTop(40).Text("It works!").FontSize(24);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -477,7 +484,7 @@ namespace QuestPDF.Examples
|
||||
//.MinimalBox()
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(15)
|
||||
.Text("Test of the \n box element", TextStyle.Default.Size(20));
|
||||
.Text("Test of the \n box element").FontSize(20);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -503,7 +510,8 @@ namespace QuestPDF.Examples
|
||||
decoration
|
||||
.Before()
|
||||
.PaddingBottom(10)
|
||||
.Text("Example: scale component", headerFontStyle);
|
||||
.Text("Example: scale component")
|
||||
.Style(headerFontStyle);
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
@@ -525,7 +533,8 @@ namespace QuestPDF.Examples
|
||||
.Background(fontColor)
|
||||
.Scale(scale)
|
||||
.Padding(5)
|
||||
.Text($"Content with {scale} scale.", fontStyle);
|
||||
.Text($"Content with {scale} scale.")
|
||||
.Style(fontStyle);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -555,7 +564,8 @@ namespace QuestPDF.Examples
|
||||
.BorderColor(Colors.Green.Darken1)
|
||||
|
||||
.Padding(50)
|
||||
.Text("Moved text", TextStyle.Default.Size(25));
|
||||
.Text("Moved text")
|
||||
.FontSize(25);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -591,7 +601,8 @@ namespace QuestPDF.Examples
|
||||
.MinimalBox()
|
||||
.Background(Colors.White)
|
||||
.Padding(10)
|
||||
.Text($"Rotated {turns * 90}°", TextStyle.Default.Size(16));
|
||||
.Text($"Rotated {turns * 90}°")
|
||||
.FontSize(16);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -689,7 +700,8 @@ namespace QuestPDF.Examples
|
||||
.MinimalBox()
|
||||
.Background(Colors.White)
|
||||
.Padding(10)
|
||||
.Text($"Flipped {turns}", TextStyle.Default.Size(16));
|
||||
.Text($"Flipped {turns}")
|
||||
.FontSize(16);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace QuestPDF.Examples
|
||||
page.Size(PageSizes.A7.Landscape());
|
||||
page.Background(Colors.White);
|
||||
|
||||
page.Header().Text("With ensure space", TextStyle.Default.SemiBold());
|
||||
page.Header().Text("With ensure space").SemiBold();
|
||||
|
||||
page.Content().Column(column =>
|
||||
{
|
||||
|
||||
@@ -17,9 +17,9 @@ namespace QuestPDF.Examples
|
||||
.Padding(5);
|
||||
}
|
||||
|
||||
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text, TextStyle.Default.SemiBold());
|
||||
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text).SemiBold();
|
||||
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
|
||||
public static void ValueCell(this IContainer container, string text) => container.ValueCell().Text(text, TextStyle.Default);
|
||||
public static void ValueCell(this IContainer container, string text) => container.ValueCell().Text(text);
|
||||
}
|
||||
|
||||
public class FrameExample
|
||||
|
||||
@@ -74,7 +74,8 @@ namespace QuestPDF.Examples
|
||||
.PrimaryLayer()
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text(sizeText, TextStyle.Default.Size(15));
|
||||
.Text(sizeText)
|
||||
.FontSize(15);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,8 +4,6 @@ using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using ShimSkiaSharp;
|
||||
using SKTypeface = SkiaSharp.SKTypeface;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
@@ -19,26 +17,79 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A5);
|
||||
page.Margin(1.5f, Unit.Centimetre);
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(2, Unit.Centimetre);
|
||||
page.Background(Colors.White);
|
||||
page.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
|
||||
page.Header()
|
||||
.Text("Hello PDF!", TextStyle.Default.SemiBold().Size(20));
|
||||
.Text("Hello PDF!").SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);
|
||||
|
||||
page.Content()
|
||||
.PaddingVertical(1, Unit.Centimetre)
|
||||
.Column(x =>
|
||||
{
|
||||
x.Spacing(10);
|
||||
x.Spacing(20);
|
||||
|
||||
x.Item().Text(Placeholders.LoremIpsum());
|
||||
x.Item().Image(Placeholders.Image(200, 100));
|
||||
});
|
||||
|
||||
page.Footer()
|
||||
.AlignCenter()
|
||||
.Text(x =>
|
||||
{
|
||||
x.Span("Page ");
|
||||
x.CurrentPageNumber();
|
||||
});
|
||||
});
|
||||
})
|
||||
.GeneratePdf("hello.pdf");
|
||||
|
||||
Process.Start("explorer.exe", "hello.pdf");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MinimalApi2()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(2, Unit.Centimetre);
|
||||
page.Background(Colors.White);
|
||||
page.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
|
||||
page.Header()
|
||||
.Text("Hello PDF!")
|
||||
.SemiBold()
|
||||
.FontSize(36)
|
||||
.FontColor(Colors.Blue.Medium);
|
||||
|
||||
page.Content()
|
||||
.PaddingVertical(1, Unit.Centimetre)
|
||||
.Column(x =>
|
||||
{
|
||||
x.Spacing(20);
|
||||
|
||||
x.Item().Text(Placeholders.LoremIpsum());
|
||||
x.Item().Image(Placeholders.Image(200, 100));
|
||||
});
|
||||
|
||||
page.Footer()
|
||||
.AlignCenter()
|
||||
.Text(x =>
|
||||
{
|
||||
x.Span("Page ");
|
||||
x.CurrentPageNumber();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,8 @@ namespace QuestPDF.Examples
|
||||
.Background("FFF")
|
||||
.Padding(5)
|
||||
.AlignCenter()
|
||||
.Text("Sample text", TextStyle.Default.FontType("Segoe UI emoji"));
|
||||
.Text("Sample text")
|
||||
.FontFamily("Segoe UI emoji");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class PageBackgroundForeground
|
||||
{
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(550, 400)
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.RenderDocument(document =>
|
||||
{
|
||||
document.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(1, Unit.Inch);
|
||||
page.DefaultTextStyle(TextStyle.Default.FontSize(16));
|
||||
|
||||
page.Foreground()
|
||||
.AlignMiddle()
|
||||
.AlignCenter()
|
||||
.Text("Watermark")
|
||||
.FontSize(64)
|
||||
.FontColor(Colors.Blue.Lighten3);
|
||||
|
||||
page.Header().Text("Background and foreground").Bold().FontColor(Colors.Blue.Medium).FontSize(24);
|
||||
|
||||
page.Content().PaddingVertical(25).Column(column =>
|
||||
{
|
||||
column.Spacing(25);
|
||||
|
||||
foreach (var i in Enumerable.Range(0, 100))
|
||||
column.Item().Background(Colors.Grey.Lighten2).Height(75);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ namespace QuestPDF.Examples
|
||||
page.Size(PageSizes.A7.Landscape());
|
||||
page.Background(Colors.White);
|
||||
|
||||
page.Header().Text("With show once", TextStyle.Default.SemiBold());
|
||||
page.Header().Text("With show once").SemiBold();
|
||||
|
||||
page.Content().PaddingVertical(5).Row(row =>
|
||||
{
|
||||
|
||||
@@ -31,7 +31,8 @@ namespace QuestPDF.Examples
|
||||
|
||||
page.Content()
|
||||
.PaddingVertical(10)
|
||||
.Text(Placeholders.Paragraphs(), TextStyle.Default.Color(Colors.Grey.Medium));
|
||||
.Text(Placeholders.Paragraphs())
|
||||
.FontColor(Colors.Grey.Medium);
|
||||
|
||||
page.Footer().Text(text =>
|
||||
{
|
||||
|
||||
@@ -141,8 +141,8 @@ namespace QuestPDF.Examples
|
||||
.AlignBottom()
|
||||
.Column(column =>
|
||||
{
|
||||
column.Item().Text("Quo Vadis", TextStyle.Default.Size(72).Bold().Color(Colors.Blue.Darken2));
|
||||
column.Item().Text("Henryk Sienkiewicz", TextStyle.Default.Size(24).Color(Colors.Grey.Darken2));
|
||||
column.Item().Text("Quo Vadis").FontSize(72).Bold().FontColor(Colors.Blue.Darken2);
|
||||
column.Item().Text("Henryk Sienkiewicz").FontSize(24).FontColor(Colors.Grey.Darken2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -156,8 +156,8 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
column.Item().InternalLink(chapter.Title).Row(row =>
|
||||
{
|
||||
row.RelativeItem().Text(chapter.Title, normalStyle);
|
||||
row.ConstantItem(100).AlignRight().Text(text => text.PageNumberOfLocation(chapter.Title, normalStyle));
|
||||
row.RelativeItem().Text(chapter.Title).Style(normalStyle);
|
||||
row.ConstantItem(100).AlignRight().Text(text => text.BeginPageNumberOfSection(chapter.Title).Style(normalStyle));
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -180,7 +180,7 @@ namespace QuestPDF.Examples
|
||||
column.Item().Text(text =>
|
||||
{
|
||||
text.ParagraphSpacing(5);
|
||||
text.Span(content, normalStyle);
|
||||
text.Span(content).Style(normalStyle);
|
||||
});
|
||||
|
||||
column.Item().PageBreak();
|
||||
@@ -198,7 +198,7 @@ namespace QuestPDF.Examples
|
||||
text.DefaultTextStyle(normalStyle);
|
||||
|
||||
text.Span("Ten dokument został wygenerowany na podstawie książki w formacie TXT opublikowanej w serwisie ");
|
||||
text.ExternalLocation("wolnelektury.pl", "https://wolnelektury.pl/", normalStyle.Color(Colors.Blue.Medium).Underline());
|
||||
text.Hyperlink("wolnelektury.pl", "https://wolnelektury.pl/").FontColor(Colors.Blue.Medium).Underline();
|
||||
text.Span(". Dziękuję za wspieranie polskiego czytelnictwa!");
|
||||
});
|
||||
});
|
||||
@@ -206,7 +206,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
void SectionTitle(ColumnDescriptor column, string text)
|
||||
{
|
||||
column.Item().Location(text).Text(text, subtitleStyle);
|
||||
column.Item().Location(text).Text(text).Style(subtitleStyle);
|
||||
column.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
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());
|
||||
text.Span("underlined red text.").FontSize(20).FontColor(Colors.Red.Medium).Underline();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -57,7 +57,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
foreach (var i in Enumerable.Range(1, 3))
|
||||
{
|
||||
text.Span($"Paragraph {i}: ", TextStyle.Default.SemiBold());
|
||||
text.Span($"Paragraph {i}: ").SemiBold();
|
||||
text.Line(Placeholders.Paragraph());
|
||||
}
|
||||
});
|
||||
@@ -120,7 +120,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
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());
|
||||
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? <").Underline();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -173,6 +173,8 @@ namespace QuestPDF.Examples
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(x => x.Bold());
|
||||
|
||||
text.DefaultTextStyle(TextStyle.Default);
|
||||
text.AlignLeft();
|
||||
text.ParagraphSpacing(10);
|
||||
@@ -182,9 +184,9 @@ namespace QuestPDF.Examples
|
||||
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.Span("this is a bold text, ").Bold();
|
||||
text.Span("this is a red and underlined text, ").FontColor(Colors.Red.Medium).Underline();
|
||||
text.Span("and this is slightly bigger text.").FontSize(16);
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
@@ -210,7 +212,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span(Placeholders.Paragraphs(), TextStyle.Default.Italic());
|
||||
text.Span(Placeholders.Paragraphs()).Italic();
|
||||
|
||||
text.Line("This is target text that does not show up. " + Placeholders.Paragraph());
|
||||
});
|
||||
@@ -241,9 +243,9 @@ namespace QuestPDF.Examples
|
||||
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("this is a bold text, ").Bold();
|
||||
text.Span("this is a red and underlined text, ").FontColor(Colors.Red.Medium).Underline();
|
||||
text.Span("and this is slightly bigger text.").FontSize(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);
|
||||
|
||||
@@ -30,12 +30,12 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
column.Item().ShowOnce().Padding(5).AlignMiddle().Row(row =>
|
||||
{
|
||||
row.RelativeItem(2).AlignMiddle().Text("PRIMARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
|
||||
row.RelativeItem(2).AlignMiddle().Text("PRIMARY HEADER").FontColor(Colors.Grey.Darken3).FontSize(30).Bold();
|
||||
row.RelativeItem(1).AlignRight().MinimalBox().AlignMiddle().Background(Colors.Blue.Darken2).Padding(30);
|
||||
});
|
||||
column.Item().SkipOnce().Padding(5).Row(row =>
|
||||
{
|
||||
row.RelativeItem(2).Text("SECONDARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
|
||||
row.RelativeItem(2).Text("SECONDARY HEADER").FontColor(Colors.Grey.Darken3).FontSize(30).Bold();
|
||||
row.RelativeItem(1).AlignRight().MinimalBox().Background(Colors.Blue.Lighten4).Padding(15);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,7 +23,8 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
decoration
|
||||
.Before()
|
||||
.PaddingBottom(5)
|
||||
.Text(Model.Title, Typography.Headline);
|
||||
.Text(Model.Title)
|
||||
.Style(Typography.Headline);
|
||||
|
||||
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Column(column =>
|
||||
{
|
||||
@@ -69,7 +70,7 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
if (model.PhotoCount == 0)
|
||||
{
|
||||
container.Text("No photos", Typography.Normal);
|
||||
container.Text("No photos").Style(Typography.Normal);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
|
||||
page.Footer().AlignCenter().Text(text =>
|
||||
{
|
||||
text.CurrentPageNumber(format: x => x?.FormatAsRomanNumeral() ?? "-----");
|
||||
text.CurrentPageNumber().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
|
||||
text.Span(" / ");
|
||||
text.TotalPages(format: x => x?.FormatAsRomanNumeral() ?? "-----");
|
||||
text.TotalPages().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -54,7 +54,7 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
row.Spacing(50);
|
||||
|
||||
row.RelativeItem().PaddingTop(-10).Text(Model.Title, Typography.Title);
|
||||
row.RelativeItem().PaddingTop(-10).Text(Model.Title).Style(Typography.Title);
|
||||
row.ConstantItem(90).ExternalLink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
|
||||
});
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
grid.Item().Text(text =>
|
||||
{
|
||||
text.Span($"{field.Label}: ", TextStyle.Default.SemiBold());
|
||||
text.Span($"{field.Label}: ").SemiBold();
|
||||
text.Span(field.Value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
decoration
|
||||
.Before()
|
||||
.PaddingBottom(5)
|
||||
.Text("Table of contents", Typography.Headline);
|
||||
.Text("Table of contents")
|
||||
.Style(Typography.Headline);
|
||||
|
||||
decoration.Content().Column(column =>
|
||||
{
|
||||
@@ -53,9 +54,9 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
|
||||
var lengthStyle = TextStyle.Default.Color(Colors.Grey.Medium);
|
||||
|
||||
text.Span(" (", lengthStyle);
|
||||
text.TotalPagesWithinSection(locationName, lengthStyle, x => x == 1 ? "1 page long" : $"{x} pages long");
|
||||
text.Span(")", lengthStyle);
|
||||
text.Span(" (").Style(lengthStyle);
|
||||
text.TotalPagesWithinSection(locationName).Style(lengthStyle).Format(x => x == 1 ? "1 page long" : $"{x} pages long");
|
||||
text.Span(")").Style(lengthStyle);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
Assert.AreEqual(expected.Position.Y, position.Y, "Draw text: Y");
|
||||
|
||||
Assert.AreEqual(expected.Style.Color, style.Color, "Draw text: color");
|
||||
Assert.AreEqual(expected.Style.FontType, style.FontType, "Draw text: font");
|
||||
Assert.AreEqual(expected.Style.FontFamily, style.FontFamily, "Draw text: font");
|
||||
Assert.AreEqual(expected.Style.Size, style.Size, "Draw text: size");
|
||||
},
|
||||
DrawImageFunc = (image, position, size) =>
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace QuestPDF.Drawing
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Since version 2022.3, the FontManager class offers better font type matching support. Please use the RegisterFontType(Stream stream) overload.")]
|
||||
[Obsolete("Since version 2022.3, the FontManager class offers better font type matching support. Please use the RegisterFont(Stream stream) method.")]
|
||||
public static void RegisterFontType(string fontName, Stream stream)
|
||||
{
|
||||
using var fontData = SKData.Create(stream);
|
||||
@@ -38,7 +38,7 @@ namespace QuestPDF.Drawing
|
||||
RegisterFontType(fontData, customName: fontName);
|
||||
}
|
||||
|
||||
public static void RegisterFontType(Stream stream)
|
||||
public static void RegisterFont(Stream stream)
|
||||
{
|
||||
using var fontData = SKData.Create(stream);
|
||||
RegisterFontType(fontData);
|
||||
@@ -78,16 +78,16 @@ namespace QuestPDF.Drawing
|
||||
|
||||
var fontStyle = new SKFontStyle(weight, SKFontStyleWidth.Normal, slant);
|
||||
|
||||
if (StyleSets.TryGetValue(style.FontType, out var fontStyleSet))
|
||||
if (StyleSets.TryGetValue(style.FontFamily, out var fontStyleSet))
|
||||
return fontStyleSet.Match(fontStyle);
|
||||
|
||||
var fontFromDefaultSource = SKFontManager.Default.MatchFamily(style.FontType, fontStyle);
|
||||
var fontFromDefaultSource = SKFontManager.Default.MatchFamily(style.FontFamily, fontStyle);
|
||||
|
||||
if (fontFromDefaultSource != null)
|
||||
return fontFromDefaultSource;
|
||||
|
||||
throw new ArgumentException(
|
||||
$"The typeface '{style.FontType}' could not be found. " +
|
||||
$"The typeface '{style.FontFamily}' could not be found. " +
|
||||
$"Please consider the following options: " +
|
||||
$"1) install the font on your operating system or execution environment. " +
|
||||
$"2) load a font file specifically for QuestPDF usage via the QuestPDF.Drawing.FontManager.RegisterFontType(Stream fileContentStream) static method.");
|
||||
|
||||
@@ -30,7 +30,10 @@ namespace QuestPDF.Elements
|
||||
.MinimalBox()
|
||||
.Background(Colors.White)
|
||||
.Padding(2)
|
||||
.Text(Text, TextStyle.Default.Color(Color).FontType(Fonts.Consolas).Size(8));
|
||||
.Text(Text)
|
||||
.FontColor(Color)
|
||||
.FontFamily(Fonts.Consolas)
|
||||
.FontSize(8);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+48
-30
@@ -20,6 +20,9 @@ namespace QuestPDF.Elements
|
||||
|
||||
public string BackgroundColor { get; set; } = Colors.Transparent;
|
||||
|
||||
public Element Background { get; set; } = Empty.Instance;
|
||||
public Element Foreground { get; set; } = Empty.Instance;
|
||||
|
||||
public Element Header { get; set; } = Empty.Instance;
|
||||
public Element Content { get; set; } = Empty.Instance;
|
||||
public Element Footer { get; set; } = Empty.Instance;
|
||||
@@ -27,39 +30,54 @@ namespace QuestPDF.Elements
|
||||
public void Compose(IContainer container)
|
||||
{
|
||||
container
|
||||
.MinWidth(MinSize.Width)
|
||||
.MinHeight(MinSize.Height)
|
||||
|
||||
.MaxWidth(MaxSize.Width)
|
||||
.MaxHeight(MaxSize.Height)
|
||||
|
||||
.Background(BackgroundColor)
|
||||
|
||||
.PaddingLeft(MarginLeft)
|
||||
.PaddingRight(MarginRight)
|
||||
.PaddingTop(MarginTop)
|
||||
.PaddingBottom(MarginBottom)
|
||||
|
||||
.DefaultTextStyle(DefaultTextStyle)
|
||||
|
||||
.Decoration(decoration =>
|
||||
.Layers(layers =>
|
||||
{
|
||||
decoration
|
||||
.Before()
|
||||
.DebugPointer("Page header")
|
||||
.Element(Header);
|
||||
layers
|
||||
.Layer()
|
||||
.DebugPointer("Page background layer")
|
||||
.Element(Background);
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.Element(x => IsClose(MinSize.Width, MaxSize.Width) ? x.ExtendHorizontal() : x)
|
||||
.Element(x => IsClose(MinSize.Height, MaxSize.Height) ? x.ExtendVertical() : x)
|
||||
.DebugPointer("Page content")
|
||||
.Element(Content);
|
||||
layers
|
||||
.PrimaryLayer()
|
||||
.MinWidth(MinSize.Width)
|
||||
.MinHeight(MinSize.Height)
|
||||
|
||||
.MaxWidth(MaxSize.Width)
|
||||
.MaxHeight(MaxSize.Height)
|
||||
|
||||
.Background(BackgroundColor)
|
||||
|
||||
.PaddingLeft(MarginLeft)
|
||||
.PaddingRight(MarginRight)
|
||||
.PaddingTop(MarginTop)
|
||||
.PaddingBottom(MarginBottom)
|
||||
|
||||
.DefaultTextStyle(DefaultTextStyle)
|
||||
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Before()
|
||||
.DebugPointer("Page header")
|
||||
.Element(Header);
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.Element(x => IsClose(MinSize.Width, MaxSize.Width) ? x.ExtendHorizontal() : x)
|
||||
.Element(x => IsClose(MinSize.Height, MaxSize.Height) ? x.ExtendVertical() : x)
|
||||
.DebugPointer("Page content")
|
||||
.Element(Content);
|
||||
|
||||
decoration
|
||||
.After()
|
||||
.DebugPointer("Page footer")
|
||||
.Element(Footer);
|
||||
});
|
||||
|
||||
decoration
|
||||
.After()
|
||||
.DebugPointer("Page footer")
|
||||
.Element(Footer);
|
||||
layers
|
||||
.Layer()
|
||||
.DebugPointer("Page foreground layer")
|
||||
.Element(Foreground);
|
||||
});
|
||||
|
||||
bool IsClose(float x, float y)
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace QuestPDF.Elements
|
||||
if (string.IsNullOrWhiteSpace(Text))
|
||||
x.MaxHeight(32).Image(ImageData, ImageScaling.FitArea);
|
||||
else
|
||||
x.Text(Text, TextStyle.Default.Size(14));
|
||||
x.Text(Text).FontSize(14);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,15 @@ namespace QuestPDF.Fluent
|
||||
TextStyle = textStyle
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static IContainer DefaultTextStyle(this IContainer element, Func<TextStyle, TextStyle> handler)
|
||||
{
|
||||
return element.Element(new DefaultTextStyle
|
||||
{
|
||||
TextStyle = handler(TextStyle.Default)
|
||||
});
|
||||
}
|
||||
|
||||
public static IContainer StopPaging(this IContainer element)
|
||||
{
|
||||
return element.Element(new StopPaging());
|
||||
|
||||
@@ -83,11 +83,30 @@ namespace QuestPDF.Fluent
|
||||
Page.DefaultTextStyle = textStyle;
|
||||
}
|
||||
|
||||
public void DefaultTextStyle(Func<TextStyle, TextStyle> handler)
|
||||
{
|
||||
DefaultTextStyle(handler(TextStyle.Default));
|
||||
}
|
||||
|
||||
public void Background(string color)
|
||||
{
|
||||
Page.BackgroundColor = color;
|
||||
}
|
||||
|
||||
public IContainer Background()
|
||||
{
|
||||
var container = new Container();
|
||||
Page.Background = container;
|
||||
return container;
|
||||
}
|
||||
|
||||
public IContainer Foreground()
|
||||
{
|
||||
var container = new Container();
|
||||
Page.Foreground = container;
|
||||
return container;
|
||||
}
|
||||
|
||||
public IContainer Header()
|
||||
{
|
||||
var container = new Container();
|
||||
|
||||
@@ -10,6 +10,34 @@ using static System.String;
|
||||
|
||||
namespace QuestPDF.Fluent
|
||||
{
|
||||
public class TextSpanDescriptor
|
||||
{
|
||||
internal TextStyle TextStyle { get; }
|
||||
|
||||
internal TextSpanDescriptor(TextStyle textStyle)
|
||||
{
|
||||
TextStyle = textStyle;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate string PageNumberFormatter(int? pageNumber);
|
||||
|
||||
public class TextPageNumberDescriptor : TextSpanDescriptor
|
||||
{
|
||||
internal PageNumberFormatter FormatFunction { get; private set; } = x => (x ?? 123).ToString();
|
||||
|
||||
internal TextPageNumberDescriptor(TextStyle textStyle) : base(textStyle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public TextPageNumberDescriptor Format(PageNumberFormatter formatter)
|
||||
{
|
||||
FormatFunction = formatter ?? FormatFunction;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public class TextDescriptor
|
||||
{
|
||||
private ICollection<TextBlock> TextBlocks { get; } = new List<TextBlock>();
|
||||
@@ -22,6 +50,11 @@ namespace QuestPDF.Fluent
|
||||
DefaultStyle = style;
|
||||
}
|
||||
|
||||
public void DefaultTextStyle(Func<TextStyle, TextStyle> style)
|
||||
{
|
||||
DefaultStyle = style(TextStyle.Default);
|
||||
}
|
||||
|
||||
public void AlignLeft()
|
||||
{
|
||||
Alignment = HorizontalAlignment.Left;
|
||||
@@ -50,12 +83,15 @@ namespace QuestPDF.Fluent
|
||||
TextBlocks.Last().Items.Add(item);
|
||||
}
|
||||
|
||||
public void Span(string? text, TextStyle? style = null)
|
||||
[Obsolete("This element has been renamed since version 2022.3. Please use the overload that returns a TextSpanDescriptor object which allows to specify text style.")]
|
||||
public void Span(string? text, TextStyle style)
|
||||
{
|
||||
if (IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
style ??= TextStyle.Default;
|
||||
Span(text).Style(style);
|
||||
}
|
||||
|
||||
public TextSpanDescriptor Span(string? text)
|
||||
{
|
||||
var style = DefaultStyle.Clone();
|
||||
|
||||
var items = text
|
||||
.Replace("\r", string.Empty)
|
||||
@@ -77,78 +113,81 @@ namespace QuestPDF.Fluent
|
||||
})
|
||||
.ToList()
|
||||
.ForEach(TextBlocks.Add);
|
||||
|
||||
return new TextSpanDescriptor(style);
|
||||
}
|
||||
|
||||
public void Line(string? text, TextStyle? style = null)
|
||||
public TextSpanDescriptor Line(string? text)
|
||||
{
|
||||
text ??= string.Empty;
|
||||
Span(text + Environment.NewLine, style);
|
||||
return Span(text + Environment.NewLine);
|
||||
}
|
||||
|
||||
public void EmptyLine()
|
||||
public TextSpanDescriptor EmptyLine()
|
||||
{
|
||||
Span(Environment.NewLine);
|
||||
return Span(Environment.NewLine);
|
||||
}
|
||||
|
||||
private static Func<int?, string> DefaultTextFormat = x => (x ?? 123).ToString();
|
||||
|
||||
private void PageNumber(Func<IPageContext, int?> pageNumber, TextStyle? style, Func<int?, string>? format)
|
||||
private TextPageNumberDescriptor PageNumber(Func<IPageContext, int?> pageNumber)
|
||||
{
|
||||
style ??= TextStyle.Default;
|
||||
format ??= DefaultTextFormat;
|
||||
var style = DefaultStyle.Clone();
|
||||
var descriptor = new TextPageNumberDescriptor(DefaultStyle);
|
||||
|
||||
AddItemToLastTextBlock(new TextBlockPageNumber()
|
||||
AddItemToLastTextBlock(new TextBlockPageNumber
|
||||
{
|
||||
Source = context => format(pageNumber(context)),
|
||||
Source = context => descriptor.FormatFunction(pageNumber(context)),
|
||||
Style = style
|
||||
});
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public void CurrentPageNumber(TextStyle? style = null, Func<int?, string>? format = null)
|
||||
public TextPageNumberDescriptor CurrentPageNumber()
|
||||
{
|
||||
PageNumber(x => x.CurrentPage, style, format);
|
||||
return PageNumber(x => x.CurrentPage);
|
||||
}
|
||||
|
||||
public void TotalPages(TextStyle? style = null, Func<int?, string>? format = null)
|
||||
public TextPageNumberDescriptor TotalPages()
|
||||
{
|
||||
PageNumber(x => x.GetLocation(PageContext.DocumentLocation)?.Length, style, format);
|
||||
return PageNumber(x => x.GetLocation(PageContext.DocumentLocation)?.Length);
|
||||
}
|
||||
|
||||
[Obsolete("This element has been renamed since version 2022.3. Please use the BeginPageNumberOfSection method.")]
|
||||
public void PageNumberOfLocation(string locationName, TextStyle? style = null)
|
||||
{
|
||||
BeginPageNumberOfSection(locationName);
|
||||
BeginPageNumberOfSection(locationName).Style(style);
|
||||
}
|
||||
|
||||
public void BeginPageNumberOfSection(string locationName, TextStyle? style = null, Func<int?, string>? format = null)
|
||||
public TextPageNumberDescriptor BeginPageNumberOfSection(string locationName)
|
||||
{
|
||||
PageNumber(x => x.GetLocation(locationName)?.PageStart, style, format);
|
||||
return PageNumber(x => x.GetLocation(locationName)?.PageStart);
|
||||
}
|
||||
|
||||
public void EndPageNumberOfSection(string locationName, TextStyle? style = null, Func<int?, string>? format = null)
|
||||
public TextPageNumberDescriptor EndPageNumberOfSection(string locationName)
|
||||
{
|
||||
PageNumber(x => x.GetLocation(locationName)?.PageEnd, style, format);
|
||||
return PageNumber(x => x.GetLocation(locationName)?.PageEnd);
|
||||
}
|
||||
|
||||
public void PageNumberWithinSection(string locationName, TextStyle? style = null, Func<int?, string>? format = null)
|
||||
public TextPageNumberDescriptor PageNumberWithinSection(string locationName)
|
||||
{
|
||||
PageNumber(x => x.CurrentPage + 1 - x.GetLocation(locationName)?.PageEnd, style, format);
|
||||
return PageNumber(x => x.CurrentPage + 1 - x.GetLocation(locationName)?.PageEnd);
|
||||
}
|
||||
|
||||
public void TotalPagesWithinSection(string locationName, TextStyle? style = null, Func<int?, string>? format = null)
|
||||
public TextPageNumberDescriptor TotalPagesWithinSection(string locationName)
|
||||
{
|
||||
PageNumber(x => x.GetLocation(locationName)?.Length, style, format);
|
||||
return PageNumber(x => x.GetLocation(locationName)?.Length);
|
||||
}
|
||||
|
||||
public void SectionLink(string? text, string sectionName, TextStyle? style = null)
|
||||
public TextSpanDescriptor SectionLink(string? text, string sectionName)
|
||||
{
|
||||
if (IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
if (IsNullOrEmpty(sectionName))
|
||||
throw new ArgumentException(nameof(sectionName));
|
||||
|
||||
style ??= TextStyle.Default;
|
||||
var style = DefaultStyle.Clone();
|
||||
var descriptor = new TextSpanDescriptor(style);
|
||||
|
||||
if (IsNullOrEmpty(text))
|
||||
return descriptor;
|
||||
|
||||
AddItemToLastTextBlock(new TextBlockSectionlLink
|
||||
{
|
||||
@@ -156,23 +195,26 @@ namespace QuestPDF.Fluent
|
||||
Text = text,
|
||||
SectionName = sectionName
|
||||
});
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
[Obsolete("This element has been renamed since version 2022.3. Please use the SectionLink method.")]
|
||||
public void InternalLocation(string? text, string locationName, TextStyle? style = null)
|
||||
{
|
||||
SectionLink(text, locationName, style);
|
||||
SectionLink(text, locationName).Style(style);
|
||||
}
|
||||
|
||||
public void Hyperlink(string? text, string url, TextStyle? style = null)
|
||||
public TextSpanDescriptor Hyperlink(string? text, string url)
|
||||
{
|
||||
if (IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
if (IsNullOrEmpty(url))
|
||||
throw new ArgumentException(nameof(url));
|
||||
|
||||
style ??= TextStyle.Default;
|
||||
|
||||
var style = DefaultStyle.Clone();
|
||||
var descriptor = new TextSpanDescriptor(style);
|
||||
|
||||
if (IsNullOrEmpty(text))
|
||||
return descriptor;
|
||||
|
||||
AddItemToLastTextBlock(new TextBlockHyperlink
|
||||
{
|
||||
@@ -180,12 +222,14 @@ namespace QuestPDF.Fluent
|
||||
Text = text,
|
||||
Url = url
|
||||
});
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
[Obsolete("This element has been renamed since version 2022.3. Please use the Hyperlink method.")]
|
||||
public void ExternalLocation(string? text, string url, TextStyle? style = null)
|
||||
{
|
||||
Hyperlink(text, url, style);
|
||||
Hyperlink(text, url).Style(style);
|
||||
}
|
||||
|
||||
public IContainer Element()
|
||||
@@ -227,9 +271,17 @@ namespace QuestPDF.Fluent
|
||||
descriptor.Compose(element);
|
||||
}
|
||||
|
||||
public static void Text(this IContainer element, object? text, TextStyle? style = null)
|
||||
[Obsolete("This element has been renamed since version 2022.3. Please use the overload that returns a TextSpanDescriptor object which allows to specify text style.")]
|
||||
public static void Text(this IContainer element, object? text, TextStyle style)
|
||||
{
|
||||
element.Text(x => x.Span(text?.ToString(), style));
|
||||
element.Text(text).Style(style);
|
||||
}
|
||||
|
||||
public static TextSpanDescriptor Text(this IContainer element, object? text)
|
||||
{
|
||||
var descriptor = (TextSpanDescriptor) null;
|
||||
element.Text(x => descriptor = x.Span(text?.ToString()));
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Fluent
|
||||
{
|
||||
public static class TextSpanDescriptorExtensions
|
||||
{
|
||||
public static T Style<T>(this T descriptor, TextStyle style) where T : TextSpanDescriptor
|
||||
{
|
||||
if (style == null)
|
||||
return descriptor;
|
||||
|
||||
descriptor.TextStyle.OverrideStyle(style);
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static T FontColor<T>(this T descriptor, string value) where T : TextSpanDescriptor
|
||||
{
|
||||
descriptor.TextStyle.Color = value;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static T BackgroundColor<T>(this T descriptor, string value) where T : TextSpanDescriptor
|
||||
{
|
||||
descriptor.TextStyle.BackgroundColor = value;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static T FontFamily<T>(this T descriptor, string value) where T : TextSpanDescriptor
|
||||
{
|
||||
descriptor.TextStyle.FontFamily = value;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static T FontSize<T>(this T descriptor, float value) where T : TextSpanDescriptor
|
||||
{
|
||||
descriptor.TextStyle.Size = value;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static T LineHeight<T>(this T descriptor, float value) where T : TextSpanDescriptor
|
||||
{
|
||||
descriptor.TextStyle.LineHeight = value;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static T Italic<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
||||
{
|
||||
descriptor.TextStyle.IsItalic = value;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static T Strikethrough<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
||||
{
|
||||
descriptor.TextStyle.HasStrikethrough = value;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static T Underline<T>(this T descriptor, bool value = true) where T : TextSpanDescriptor
|
||||
{
|
||||
descriptor.TextStyle.HasUnderline = value;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
#region Weight
|
||||
|
||||
public static T Weight<T>(this T descriptor, FontWeight weight) where T : TextSpanDescriptor
|
||||
{
|
||||
descriptor.TextStyle.FontWeight = weight;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static T Thin<T>(this T descriptor) where T : TextSpanDescriptor
|
||||
{
|
||||
return descriptor.Weight(FontWeight.Thin);
|
||||
}
|
||||
|
||||
public static T ExtraLight<T>(this T descriptor) where T : TextSpanDescriptor
|
||||
{
|
||||
return descriptor.Weight(FontWeight.ExtraLight);
|
||||
}
|
||||
|
||||
public static T Light<T>(this T descriptor) where T : TextSpanDescriptor
|
||||
{
|
||||
return descriptor.Weight(FontWeight.Light);
|
||||
}
|
||||
|
||||
public static T NormalWeight<T>(this T descriptor) where T : TextSpanDescriptor
|
||||
{
|
||||
return descriptor.Weight(FontWeight.Normal);
|
||||
}
|
||||
|
||||
public static T Medium<T>(this T descriptor) where T : TextSpanDescriptor
|
||||
{
|
||||
return descriptor.Weight(FontWeight.Medium);
|
||||
}
|
||||
|
||||
public static T SemiBold<T>(this T descriptor) where T : TextSpanDescriptor
|
||||
{
|
||||
return descriptor.Weight(FontWeight.SemiBold);
|
||||
}
|
||||
|
||||
public static T Bold<T>(this T descriptor) where T : TextSpanDescriptor
|
||||
{
|
||||
return descriptor.Weight(FontWeight.Bold);
|
||||
}
|
||||
|
||||
public static T ExtraBold<T>(this T descriptor) where T : TextSpanDescriptor
|
||||
{
|
||||
return descriptor.Weight(FontWeight.ExtraBold);
|
||||
}
|
||||
|
||||
public static T Black<T>(this T descriptor) where T : TextSpanDescriptor
|
||||
{
|
||||
return descriptor.Weight(FontWeight.Black);
|
||||
}
|
||||
|
||||
public static T ExtraBlack<T>(this T descriptor) where T : TextSpanDescriptor
|
||||
{
|
||||
return descriptor.Weight(FontWeight.ExtraBlack);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,13 @@ namespace QuestPDF.Fluent
|
||||
return style;
|
||||
}
|
||||
|
||||
[Obsolete("This element has been renamed since version 2022.3. Please use the FontColor method.")]
|
||||
public static TextStyle Color(this TextStyle style, string value)
|
||||
{
|
||||
return style.FontColor(value);
|
||||
}
|
||||
|
||||
public static TextStyle FontColor(this TextStyle style, string value)
|
||||
{
|
||||
return style.Mutate(x => x.Color = value);
|
||||
}
|
||||
@@ -23,12 +29,24 @@ namespace QuestPDF.Fluent
|
||||
return style.Mutate(x => x.BackgroundColor = value);
|
||||
}
|
||||
|
||||
[Obsolete("This element has been renamed since version 2022.3. Please use the FontFamily method.")]
|
||||
public static TextStyle FontType(this TextStyle style, string value)
|
||||
{
|
||||
return style.Mutate(x => x.FontType = value);
|
||||
return style.FontFamily(value);
|
||||
}
|
||||
|
||||
public static TextStyle FontFamily(this TextStyle style, string value)
|
||||
{
|
||||
return style.Mutate(x => x.FontFamily = value);
|
||||
}
|
||||
|
||||
[Obsolete("This element has been renamed since version 2022.3. Please use the FontSize method.")]
|
||||
public static TextStyle Size(this TextStyle style, float value)
|
||||
{
|
||||
return style.FontSize(value);
|
||||
}
|
||||
|
||||
public static TextStyle FontSize(this TextStyle style, float value)
|
||||
{
|
||||
return style.Mutate(x => x.Size = value);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace QuestPDF.Infrastructure
|
||||
|
||||
internal string? Color { get; set; }
|
||||
internal string? BackgroundColor { get; set; }
|
||||
internal string? FontType { get; set; }
|
||||
internal string? FontFamily { get; set; }
|
||||
internal float? Size { get; set; }
|
||||
internal float? LineHeight { get; set; }
|
||||
internal FontWeight? FontWeight { get; set; }
|
||||
@@ -24,7 +24,7 @@ namespace QuestPDF.Infrastructure
|
||||
{
|
||||
Color = Colors.Black,
|
||||
BackgroundColor = Colors.Transparent,
|
||||
FontType = Fonts.Calibri,
|
||||
FontFamily = Fonts.Calibri,
|
||||
Size = 12,
|
||||
LineHeight = 1.2f,
|
||||
FontWeight = Infrastructure.FontWeight.Normal,
|
||||
@@ -43,15 +43,15 @@ namespace QuestPDF.Infrastructure
|
||||
HasGlobalStyleApplied = true;
|
||||
|
||||
ApplyParentStyle(globalStyle);
|
||||
PaintKey ??= (FontType, Size, FontWeight, IsItalic, Color);
|
||||
FontMetricsKey ??= (FontType, Size, FontWeight, IsItalic);
|
||||
PaintKey ??= (FontFamily, Size, FontWeight, IsItalic, Color);
|
||||
FontMetricsKey ??= (FontFamily, Size, FontWeight, IsItalic);
|
||||
}
|
||||
|
||||
internal void ApplyParentStyle(TextStyle parentStyle)
|
||||
{
|
||||
Color ??= parentStyle.Color;
|
||||
BackgroundColor ??= parentStyle.BackgroundColor;
|
||||
FontType ??= parentStyle.FontType;
|
||||
FontFamily ??= parentStyle.FontFamily;
|
||||
Size ??= parentStyle.Size;
|
||||
LineHeight ??= parentStyle.LineHeight;
|
||||
FontWeight ??= parentStyle.FontWeight;
|
||||
@@ -60,6 +60,19 @@ namespace QuestPDF.Infrastructure
|
||||
HasUnderline ??= parentStyle.HasUnderline;
|
||||
}
|
||||
|
||||
internal void OverrideStyle(TextStyle parentStyle)
|
||||
{
|
||||
Color = parentStyle.Color ?? Color;
|
||||
BackgroundColor = parentStyle.BackgroundColor ?? BackgroundColor;
|
||||
FontFamily = parentStyle.FontFamily ?? FontFamily;
|
||||
Size = parentStyle.Size ?? Size;
|
||||
LineHeight = parentStyle.LineHeight ?? LineHeight;
|
||||
FontWeight = parentStyle.FontWeight ?? FontWeight;
|
||||
IsItalic = parentStyle.IsItalic ?? IsItalic;
|
||||
HasStrikethrough = parentStyle.HasStrikethrough ?? HasStrikethrough;
|
||||
HasUnderline = parentStyle.HasUnderline ?? HasUnderline;
|
||||
}
|
||||
|
||||
internal TextStyle Clone()
|
||||
{
|
||||
var clone = (TextStyle)MemberwiseClone();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<Authors>MarcinZiabek</Authors>
|
||||
<Company>CodeFlint</Company>
|
||||
<PackageId>QuestPDF</PackageId>
|
||||
<Version>2022.2.3</Version>
|
||||
<Version>2022.3.0</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>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
|
||||
<LangVersion>9</LangVersion>
|
||||
|
||||
@@ -8,215 +8,84 @@ I have designed this layouting engine with full paging support in mind. The docu
|
||||
|
||||
## Support QuestPDF
|
||||
|
||||
All great frameworks and libraries started from zero. Please help me make QuestPDF a commonly known library and an obvious choice in case of generating PDF documents. Please give it a start ⭐ and share with your colleagues 💬👨💻.
|
||||
All great frameworks and libraries started from zero. Please help me make QuestPDF a commonly known library and an obvious choice for generating PDF documents.
|
||||
|
||||
- ⭐ Give this repository a star,
|
||||
- 💬 Share it with your team members.
|
||||
|
||||
## Installation
|
||||
|
||||
The library is available as a nuget package. You can install it as any other nuget package from your IDE, try to search by `QuestPDF`. You can find package details [on this webpage](https://www.nuget.org/packages/QuestPDF/).
|
||||
|
||||
```
|
||||
```c#
|
||||
// Package Manager
|
||||
Install-Package QuestPDF
|
||||
|
||||
// .NET CLI
|
||||
dotnet add package QuestPDF
|
||||
|
||||
// Package reference in .csproj file
|
||||
<PackageReference Include="QuestPDF" Version="2022.2.5" />
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
**[Release notes and roadmap](https://www.questpdf.com/documentation/releases.html)** - everything that is planned for future library iterations, description of new features and information about potential breaking changes.
|
||||
**[🚀 Getting started tutorial](https://www.questpdf.com/documentation/getting-started.html)** - a short and easy to follow tutorial showing how to design an invoice document under 200 lines of code.
|
||||
|
||||
**[Getting started tutorial](https://www.questpdf.com/documentation/getting-started.html)** - a short and easy to follow tutorial showing how to design an invoice document under 200 lines of code.
|
||||
**[📖 API Reference](https://www.questpdf.com/documentation/api-reference.html)** - a detailed description of behavior of all available components and how to use them with C# Fluent API.
|
||||
|
||||
**[API Reference](https://www.questpdf.com/documentation/api-reference.html)** - a detailed description of behavior of all available components and how to use them with C# Fluent API.
|
||||
**[ℹ️ Patterns and practices](https://www.questpdf.com/documentation/patterns-and-practices.html#document-metadata)** - everything that may help you designing great reports and create reusable code that is easy to maintain.
|
||||
|
||||
**[Patterns and practices](https://www.questpdf.com/documentation/patterns-and-practices.html#document-metadata)** - everything that may help you designing great reports and reusable code that is easy to maintain.
|
||||
## Simplicity is the key
|
||||
|
||||
## Example invoice
|
||||
|
||||
Do you believe that creating a complete invoice document can take less than 200 lines of code? We have prepared for you a step-by-step instruction that shows every detail of this implementation and describes the best patterns and practices.
|
||||
|
||||
For tutorial, documentation and API reference, please visit [the QuestPDF documentation](https://www.questpdf.com/documentation/getting-started.html).
|
||||
|
||||
<a href="https://github.com/QuestPDF/example-invoice">
|
||||
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/invoice.png" width="595px">
|
||||
</a>
|
||||
|
||||
Here you can find an example code showing how easy is to write and understand the fluent API.
|
||||
|
||||
**General document structure** with header, content and footer:
|
||||
How easy it is to start and prototype with QuestPDF? Really easy thanks to its minimal API! Please analyse the code below:
|
||||
|
||||
```csharp
|
||||
public void Compose(IDocumentContainer container)
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
// code in your main method
|
||||
Document.Create(container =>
|
||||
{
|
||||
container
|
||||
.Page(page =>
|
||||
{
|
||||
page.Margin(50);
|
||||
|
||||
page.Header().Element(ComposeHeader);
|
||||
page.Content().Element(ComposeContent);
|
||||
|
||||
page.Footer().AlignCenter().Text(x =>
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(2, Unit.Centimetre);
|
||||
page.Background(Colors.White);
|
||||
page.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
|
||||
page.Header()
|
||||
.Text("Hello PDF!", TextStyle.Default.SemiBold().Size(36).Color(Colors.Blue.Medium));
|
||||
|
||||
page.Content()
|
||||
.PaddingVertical(1, Unit.Centimetre)
|
||||
.Column(x =>
|
||||
{
|
||||
x.Spacing(20);
|
||||
|
||||
x.Item().Text(Placeholders.LoremIpsum());
|
||||
x.Item().Image(Placeholders.Image(200, 100));
|
||||
});
|
||||
|
||||
page.Footer()
|
||||
.AlignCenter()
|
||||
.Text(x =>
|
||||
{
|
||||
x.Span("Page ");
|
||||
x.CurrentPageNumber();
|
||||
x.Span(" / ");
|
||||
x.TotalPages();
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**The header area** consists of basic invoice information along with a logo placeholder.
|
||||
|
||||
```csharp
|
||||
void ComposeHeader(IContainer container)
|
||||
{
|
||||
var titleTextStyle = TextStyle.Default.Size(20).SemiBold().Color(Colors.Blue.Medium);
|
||||
|
||||
container.Row(row =>
|
||||
{
|
||||
row.RelativeColumn().Stack(stack =>
|
||||
{
|
||||
stack.Item().Text($"Invoice #{Model.InvoiceNumber}", titleStyle);
|
||||
|
||||
stack.Item().Text(text =>
|
||||
{
|
||||
text.Span("Issue date: ", TextStyle.Default.SemiBold());
|
||||
text.Span($"{Model.IssueDate:d}");
|
||||
});
|
||||
|
||||
stack.Item().Text(text =>
|
||||
{
|
||||
text.Span("Due date: ", TextStyle.Default.SemiBold());
|
||||
text.Span($"{Model.DueDate:d}");
|
||||
});
|
||||
});
|
||||
|
||||
row.ConstantColumn(100).Height(50).Placeholder();
|
||||
});
|
||||
}
|
||||
})
|
||||
.GeneratePdf("hello.pdf");
|
||||
```
|
||||
|
||||
Implementation of **the content area** that contains seller and customer details, then listing of all bought products, then a comments section.
|
||||
And compare it to the produced PDF file:
|
||||
|
||||
```csharp
|
||||
void ComposeContent(IContainer container)
|
||||
{
|
||||
container.PaddingVertical(40).Stack(column =>
|
||||
{
|
||||
column.Spacing(20);
|
||||
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn().Component(new AddressComponent("From", Model.SellerAddress));
|
||||
row.ConstantColumn(50);
|
||||
row.RelativeColumn().Component(new AddressComponent("For", Model.CustomerAddress));
|
||||
});
|
||||
|
||||
column.Item().Element(ComposeTable);
|
||||
|
||||
var totalPrice = Model.Items.Sum(x => x.Price * x.Quantity);
|
||||
|
||||
column
|
||||
.Item()
|
||||
.PaddingRight(5)
|
||||
.AlignRight()
|
||||
.Text($"Grand total: {totalPrice}$", TextStyle.Default.SemiBold());
|
||||
## Are you ready for more?
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Model.Comments))
|
||||
column.Item().PaddingTop(25).Element(ComposeComments);
|
||||
});
|
||||
}
|
||||
```
|
||||
The Fluent API of QuestPDF scales really well. It is easy to create and maintain even most complex documents. Read [the Getting started tutorial](https://www.questpdf.com/documentation/getting-started.html) to learn QuestPDF basics and implement an invoice under 200 lines of code. You can also investigate and play with the code from [the example repository](https://github.com/QuestPDF/example-invoice).
|
||||
|
||||
**The table and comments** codes are extracted into separate methods to increase clarity:
|
||||
|
||||
```csharp
|
||||
void ComposeTable(IContainer container)
|
||||
{
|
||||
var headerStyle = TextStyle.Default.SemiBold();
|
||||
|
||||
container.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.ConstantColumn(25);
|
||||
columns.RelativeColumn(3);
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
table.Header(header =>
|
||||
{
|
||||
header.Cell().Text("#", headerStyle);
|
||||
header.Cell().Text("Product", headerStyle);
|
||||
header.Cell().AlignRight().Text("Unit price", headerStyle);
|
||||
header.Cell().AlignRight().Text("Quantity", headerStyle);
|
||||
header.Cell().AlignRight().Text("Total", headerStyle);
|
||||
|
||||
header.Cell().ColumnSpan(5)
|
||||
.PaddingVertical(5).BorderBottom(1).BorderColor(Colors.Black);
|
||||
});
|
||||
|
||||
foreach (var item in Model.Items)
|
||||
{
|
||||
table.Cell().Element(CellStyle).Text(Model.Items.IndexOf(item) + 1);
|
||||
table.Cell().Element(CellStyle).Text(item.Name);
|
||||
table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price}$");
|
||||
table.Cell().Element(CellStyle).AlignRight().Text(item.Quantity);
|
||||
table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price * item.Quantity}$");
|
||||
|
||||
static IContainer CellStyle(IContainer container)
|
||||
{
|
||||
container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
```csharp
|
||||
void ComposeComments(IContainer container)
|
||||
{
|
||||
container.ShowEntire().Background(Colors.Grey.Lighten3).Padding(10).Stack(message =>
|
||||
{
|
||||
message.Spacing(5);
|
||||
message.Item().Text("Comments", TextStyle.Default.Size(14).SemiBold());
|
||||
message.Item().Text(Model.Comments);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**The address details section** is implemented using components. This way the code can be easily reused for both seller and customer:
|
||||
|
||||
```csharp
|
||||
public class AddressComponent : IComponent
|
||||
{
|
||||
private string Title { get; }
|
||||
private Address Address { get; }
|
||||
|
||||
public AddressComponent(string title, Address address)
|
||||
{
|
||||
Title = title;
|
||||
Address = address;
|
||||
}
|
||||
|
||||
public void Compose(IContainer container)
|
||||
{
|
||||
container.ShowEntire().Stack(column =>
|
||||
{
|
||||
column.Spacing(5);
|
||||
|
||||
column
|
||||
.Item()
|
||||
.BorderBottom(1)
|
||||
.PaddingBottom(5)
|
||||
.Text(Title, TextStyle.Default.SemiBold());
|
||||
|
||||
column.Item().Text(Address.CompanyName);
|
||||
column.Item().Text(Address.Street);
|
||||
column.Item().Text($"{Address.City}, {Address.State}");
|
||||
column.Item().Text(Address.Email);
|
||||
column.Item().Text(Address.Phone);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/invoice.png" width="595px">
|
||||
Reference in New Issue
Block a user