Explorar o código

Improved text API

MarcinZiabek %!s(int64=3) %!d(string=hai) anos
pai
achega
fcff989ab2

+ 3 - 1
QuestPDF.Examples/BarcodeExamples.cs

@@ -25,7 +25,9 @@ namespace QuestPDF.Examples
                         .Background(Colors.White)
                         .AlignCenter()
                         .AlignMiddle()
-                        .Text("*QuestPDF*", TextStyle.Default.FontType("Libre Barcode 39").Size(64));
+                        .Text("*QuestPDF*")
+                        .FontType("Libre Barcode 39")
+                        .Size(64);
                 });
         }
     }

+ 4 - 1
QuestPDF.Examples/CanvasExamples.cs

@@ -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")
+                                .Size(16)
+                                .Color(Colors.Blue.Darken2)
+                                .SemiBold();
                         });
                 });
         }

+ 4 - 1
QuestPDF.Examples/ChartExamples.cs

@@ -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")
+                                .Size(20)
+                                .SemiBold()
+                                .Color(Colors.Blue.Medium);
                             
                             column
                                 .Item()

+ 1 - 1
QuestPDF.Examples/ContinousPage.cs

@@ -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}").Color(Placeholders.Color());
                 });
                 
                 page.Footer().Text("Footer");

+ 1 - 1
QuestPDF.Examples/DefaultTextStyleExample.cs

@@ -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()).Color(Colors.Red.Medium);
                             });
                         });
                     });

+ 4 - 3
QuestPDF.Examples/DefaultTextStyleExamples.cs

@@ -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).Color(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)
+                                        .Size(16 + i / 4);   
                                 }
                             });
                         });

+ 23 - 11
QuestPDF.Examples/ElementExamples.cs

@@ -43,7 +43,9 @@ namespace QuestPDF.Examples
                                 .Before()
                                 .Background(Colors.Grey.Medium)
                                 .Padding(10)
-                                .Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
+                                .Text("Notes")
+                                .Size(16)
+                                .Color("#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")
+                                .Size(48)
+                                .Bold()
+                                .Color(Colors.Green.Lighten3);
 
                             layers
                                 .Layer()
                                 .AlignBottom()
-                                .Text(text => text.CurrentPageNumber(TextStyle.Default.Size(16).Color(Colors.Green.Medium)));
+                                .Text(text => text.CurrentPageNumber().Size(16).Color(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)
+                                    .FontType(font)
+                                    .Size(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!").Size(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").Size(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")
+                        .Size(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}°")
+                                    .Size(16);
                             }
                         });
                 });
@@ -689,7 +700,8 @@ namespace QuestPDF.Examples
                                     .MinimalBox()
                                     .Background(Colors.White)
                                     .Padding(10)
-                                    .Text($"Flipped {turns}", TextStyle.Default.Size(16));
+                                    .Text($"Flipped {turns}")
+                                    .Size(16);
                             }
                         });
                 });

+ 1 - 1
QuestPDF.Examples/EnsureSpaceExample.cs

@@ -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 =>
                         {

+ 2 - 2
QuestPDF.Examples/FrameExample.cs

@@ -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

+ 2 - 1
QuestPDF.Examples/InlinedExamples.cs

@@ -74,7 +74,8 @@ namespace QuestPDF.Examples
                                                     .PrimaryLayer()
                                                     .AlignCenter()
                                                     .AlignMiddle()
-                                                    .Text(sizeText, TextStyle.Default.Size(15));
+                                                    .Text(sizeText)
+                                                    .Size(15);
                                             });
                                     }
                                 });

+ 57 - 6
QuestPDF.Examples/MinimalApiExamples.cs

@@ -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().Size(36).Color(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()
+                            .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();
+                            });
+                    });
+                });
+        }
     }
 }

+ 2 - 1
QuestPDF.Examples/Padding.cs

@@ -50,7 +50,8 @@ namespace QuestPDF.Examples
                         .Background("FFF")
                         .Padding(5)
                         .AlignCenter()
-                        .Text("Sample text", TextStyle.Default.FontType("Segoe UI emoji"));
+                        .Text("Sample text")
+                        .FontType("Segoe UI emoji");
                 });
         }
         

+ 1 - 1
QuestPDF.Examples/ShowOnceExample.cs

@@ -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 =>
                         {

+ 1 - 1
QuestPDF.Examples/SkipOnceExample.cs

@@ -31,7 +31,7 @@ namespace QuestPDF.Examples
                         
                         page.Content()
                             .PaddingVertical(10)
-                            .Text(Placeholders.Paragraphs(), TextStyle.Default.Color(Colors.Grey.Medium));
+                            .Text(Placeholders.Paragraphs()).Color(Colors.Grey.Medium);
                         
                         page.Footer().Text(text =>
                         {

+ 7 - 7
QuestPDF.Examples/TextBenchmark.cs

@@ -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").Size(72).Bold().Color(Colors.Blue.Darken2);
+                        column.Item().Text("Henryk Sienkiewicz").Size(24).Color(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/").Color(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();
             }
             

+ 12 - 10
QuestPDF.Examples/TextExamples.cs

@@ -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.").Size(20).Color(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, ").Color(Colors.Red.Medium).Underline();
+                            text.Span("and this is slightly bigger text.").Size(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, ").Color(Colors.Red.Medium).Underline();
+                            text.Span("and this is slightly bigger text.").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);

+ 2 - 2
QuestPDF.ReportSample/Layouts/DifferentHeadersTemplate.cs

@@ -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").Color(Colors.Grey.Darken3).Size(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").Color(Colors.Grey.Darken3).Size(30).Bold();
                     row.RelativeItem(1).AlignRight().MinimalBox().Background(Colors.Blue.Lighten4).Padding(15);
                 });
             });

+ 3 - 2
QuestPDF.ReportSample/Layouts/SectionTemplate.cs

@@ -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;
             }
 

+ 4 - 4
QuestPDF.ReportSample/Layouts/StandardReport.cs

@@ -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);
                         });
                     }

+ 5 - 4
QuestPDF.ReportSample/Layouts/TableOfContentsTemplate.cs

@@ -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);
                     });
                 });
         }

+ 4 - 1
QuestPDF/Elements/DebugArea.cs

@@ -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)
+                        .Color(Color)
+                        .FontType(Fonts.Consolas)
+                        .Size(8);
                 });
         }
     }

+ 1 - 1
QuestPDF/Elements/Placeholder.cs

@@ -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).Size(14);
                 });
         }
     }

+ 90 - 44
QuestPDF/Fluent/TextExtensions.cs

@@ -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,9 @@ namespace QuestPDF.Fluent
             TextBlocks.Last().Items.Add(item);
         }
         
-        public void Span(string? text, TextStyle? style = null)
+        public TextSpanDescriptor Span(string? text)
         {
-            if (IsNullOrEmpty(text))
-                return;
-            
-            style ??= TextStyle.Default;
+            var style = DefaultStyle.Clone();
  
             var items = text
                 .Replace("\r", string.Empty)
@@ -77,78 +107,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 +189,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 +216,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 +265,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 ")]
+        // public static void Text(this IContainer element, object? text, TextStyle style)
+        // {
+        //     element.Text(text).Style(style);
+        // }
+        
+        public static TextSpanDescriptor Text(this IContainer element, object? text)
         {
-            element.Text(x => x.Span(text?.ToString(), style));
+            var descriptor = (TextSpanDescriptor) null;
+            element.Text(x => descriptor = x.Span(text?.ToString()));
+            return descriptor;
         }
     }
 }

+ 126 - 0
QuestPDF/Fluent/TextSpanDescriptorExtensions.cs

@@ -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 Color<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 FontType<T>(this T descriptor, string value) where T : TextSpanDescriptor
+        {
+            descriptor.TextStyle.FontType = value;
+            return descriptor;
+        }
+        
+        public static T Size<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 - 0
QuestPDF/Infrastructure/TextStyle.cs

@@ -60,6 +60,19 @@ namespace QuestPDF.Infrastructure
             HasUnderline ??= parentStyle.HasUnderline;
         }
 
+        internal void OverrideStyle(TextStyle parentStyle)
+        {
+            Color = parentStyle.Color ?? Color;
+            BackgroundColor = parentStyle.BackgroundColor ?? BackgroundColor;
+            FontType = parentStyle.FontType ?? FontType;
+            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();

+ 50 - 181
readme.md

@@ -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
-```
-
-## Documentation
-
-**[Release notes and roadmap](https://www.questpdf.com/documentation/releases.html)** - everything that is planned for future library iterations, description of new features and information about potential breaking changes.
-
-**[Getting started tutorial](https://www.questpdf.com/documentation/getting-started.html)** - a short and easy to follow tutorial showing how to design an invoice document under 200 lines of code.
 
-**[API Reference](https://www.questpdf.com/documentation/api-reference.html)** - a detailed description of behavior of all available components and how to use them with C# Fluent API.
+// .NET CLI
+dotnet add package QuestPDF
 
-**[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.
+// Package reference in .csproj file
+<PackageReference Include="QuestPDF" Version="2022.2.5" />
+```
 
-## Example invoice
+## Documentation
 
-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.
+**[🚀 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.
 
-For tutorial, documentation and API reference, please visit [the QuestPDF documentation](https://www.questpdf.com/documentation/getting-started.html).
+**[📖 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.
 
-<a href="https://github.com/QuestPDF/example-invoice">
-  <img src="https://github.com/QuestPDF/example-invoice/raw/main/images/invoice.png" width="595px">
-</a>
+**[ℹ️ 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.
 
-Here you can find an example code showing how easy is to write and understand the fluent API.
+## Simplicity is the key
 
-**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)
-{
-    container
-        .Page(page =>
-        {
-            page.Margin(50);
-            
-            page.Header().Element(ComposeHeader);
-            page.Content().Element(ComposeContent);
-            
-            page.Footer().AlignCenter().Text(x =>
-            {
-                x.CurrentPageNumber();
-                x.Span(" / ");
-                x.TotalPages();
-            });
-        });
-}
-```
-
-**The header area** consists of basic invoice information along with a logo placeholder.
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
 
-```csharp
-void ComposeHeader(IContainer container)
+// code in your main method
+Document.Create(container =>
 {
-    var titleTextStyle = TextStyle.Default.Size(20).SemiBold().Color(Colors.Blue.Medium);
-    
-    container.Row(row =>
+    container.Page(page =>
     {
-        row.RelativeColumn().Stack(stack =>
-        {
-            stack.Item().Text($"Invoice #{Model.InvoiceNumber}", titleStyle);
-
-            stack.Item().Text(text =>
+        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 =>
             {
-                text.Span("Issue date: ", TextStyle.Default.SemiBold());
-                text.Span($"{Model.IssueDate:d}");
+                x.Spacing(20);
+                
+                x.Item().Text(Placeholders.LoremIpsum());
+                x.Item().Image(Placeholders.Image(200, 100));
             });
-
-            stack.Item().Text(text =>
+        
+        page.Footer()
+            .AlignCenter()
+            .Text(x =>
             {
-                text.Span("Due date: ", TextStyle.Default.SemiBold());
-                text.Span($"{Model.DueDate:d}");
+                x.Span("Page ");
+                x.CurrentPageNumber();
             });
-        });
-        
-        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());
-
-        if (!string.IsNullOrWhiteSpace(Model.Comments))
-            column.Item().PaddingTop(25).Element(ComposeComments);
-    });
-}
-```
-
-**The table and comments** codes are extracted into separate methods to increase clarity:
-
-```csharp
-void ComposeTable(IContainer container)
-{
-    var headerStyle = TextStyle.Default.SemiBold();
-    
-    container.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:
+## Are you ready for more?
 
-```csharp
-public class AddressComponent : IComponent
-{
-    private string Title { get; }
-    private Address Address { get; }
+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).
 
-    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">