diff --git a/QuestPDF.Examples/DynamicFibonacci.cs b/QuestPDF.Examples/DynamicFibonacci.cs new file mode 100644 index 0000000..4392185 --- /dev/null +++ b/QuestPDF.Examples/DynamicFibonacci.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using QuestPDF.Elements; +using QuestPDF.Examples.Engine; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; + +namespace QuestPDF.Examples +{ + public struct FibonacciHeaderState + { + public int Previous { get; set; } + public int Current { get; set; } + } + + public class FibonacciHeader : IDynamicComponent + { + public FibonacciHeaderState State { get; set; } + + public static readonly string[] ColorsTable = + { + Colors.Red.Lighten2, + Colors.Orange.Lighten2, + Colors.Green.Lighten2, + }; + + public FibonacciHeader(int previous, int current) + { + State = new FibonacciHeaderState + { + Previous = previous, + Current = current + }; + } + + public DynamicComponentComposeResult Compose(DynamicContext context) + { + var content = context.CreateElement(container => + { + var colorIndex = State.Current % ColorsTable.Length; + var color = ColorsTable[colorIndex]; + + var ratio = (float)State.Current / State.Previous; + + container + .Background(color) + .Height(50) + .AlignMiddle() + .AlignCenter() + .Text($"{State.Current} / {State.Previous} = {ratio:N5}"); + }); + + State = new FibonacciHeaderState + { + Previous = State.Current, + Current = State.Previous + State.Current + }; + + return new DynamicComponentComposeResult + { + Content = content, + HasMoreContent = false + }; + } + } + + public static class DynamicFibonacci + { + [Test] + public static void Dynamic() + { + RenderingTest + .Create() + .ShowResults() + .MaxPages(100) + .ProducePdf() + .RenderDocument(container => + { + container.Page(page => + { + page.Size(PageSizes.A6); + page.Margin(1, Unit.Centimetre); + page.DefaultTextStyle(x => x.FontSize(18)); + + page.Header().Dynamic(new FibonacciHeader(17, 19)); + + page.Content().Column(column => + { + foreach (var i in Enumerable.Range(0, 100)) + column.Item().PaddingTop(25).Background(Colors.Grey.Lighten2).Height(50); + }); + }); + }); + } + } +} \ No newline at end of file diff --git a/QuestPDF.Examples/DynamicExamples.cs b/QuestPDF.Examples/DynamicOptimizedExample.cs similarity index 95% rename from QuestPDF.Examples/DynamicExamples.cs rename to QuestPDF.Examples/DynamicOptimizedExample.cs index 8e94e33..3483f14 100644 --- a/QuestPDF.Examples/DynamicExamples.cs +++ b/QuestPDF.Examples/DynamicOptimizedExample.cs @@ -21,12 +21,12 @@ namespace QuestPDF.Examples public int ShownItemsCount { get; set; } } - public class OrdersTable : IDynamicComponent + public class OptimizedOrdersTable : IDynamicComponent { private ICollection Items { get; } public OrdersTableState State { get; set; } - public OrdersTable(ICollection items) + public OptimizedOrdersTable(ICollection items) { Items = items; @@ -69,7 +69,7 @@ namespace QuestPDF.Examples return new DynamicComponentComposeResult { Content = content, - HasMoreContent = State.ShownItemsCount + rows.Count < Items.Count + HasMoreContent = State.ShownItemsCount < Items.Count }; } @@ -145,7 +145,7 @@ namespace QuestPDF.Examples } } - public static class DynamicExamples + public static class DynamicOptimizedExamples { [Test] public static void Dynamic() @@ -177,7 +177,7 @@ namespace QuestPDF.Examples decoration .Content() - .Dynamic(new OrdersTable(items)); + .Dynamic(new OptimizedOrdersTable(items)); }); }); } diff --git a/QuestPDF.Examples/DynamicProgressHeader.cs b/QuestPDF.Examples/DynamicProgressHeader.cs new file mode 100644 index 0000000..0377513 --- /dev/null +++ b/QuestPDF.Examples/DynamicProgressHeader.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using QuestPDF.Elements; +using QuestPDF.Examples.Engine; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; + +namespace QuestPDF.Examples +{ + public class ProgressHeader : IDynamicComponent + { + public int State { get; set; } + + public DynamicComponentComposeResult Compose(DynamicContext context) + { + var content = context.CreateElement(container => + { + var width = context.AvailableSize.Width * context.PageNumber / context.TotalPages; + + container + .Background(Colors.Blue.Lighten2) + .Height(25) + .Width(width) + .Background(Colors.Blue.Darken1); + }); + + return new DynamicComponentComposeResult + { + Content = content, + HasMoreContent = false + }; + } + } + + public static class DynamicProgressHeader + { + [Test] + public static void Dynamic() + { + RenderingTest + .Create() + .ShowResults() + .MaxPages(100) + .ProducePdf() + .RenderDocument(container => + { + container.Page(page => + { + page.Size(PageSizes.A6); + page.Margin(1, Unit.Centimetre); + page.DefaultTextStyle(x => x.FontSize(20)); + + page.Header().Dynamic(new ProgressHeader()); + + page.Content().Column(column => + { + foreach (var i in Enumerable.Range(0, 100)) + column.Item().PaddingTop(25).Background(Colors.Grey.Lighten2).Height(50); + }); + }); + }); + } + } +} \ No newline at end of file diff --git a/QuestPDF.Examples/DynamicSimpleTableExample.cs b/QuestPDF.Examples/DynamicSimpleTableExample.cs new file mode 100644 index 0000000..3369611 --- /dev/null +++ b/QuestPDF.Examples/DynamicSimpleTableExample.cs @@ -0,0 +1,154 @@ +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using NUnit.Framework; +using QuestPDF.Elements; +using QuestPDF.Examples.Engine; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; + +namespace QuestPDF.Examples +{ + public class OrdersTable : IDynamicComponent + { + private IList Items { get; } + public OrdersTableState State { get; set; } + + public OrdersTable(IList items) + { + Items = items; + + State = new OrdersTableState + { + ShownItemsCount = 0 + }; + } + + public DynamicComponentComposeResult Compose(DynamicContext context) + { + var possibleItems = Enumerable + .Range(1, Items.Count - State.ShownItemsCount) + .Select(itemsToDisplay => ComposeContent(context, itemsToDisplay)) + .TakeWhile(x => x.Size.Height <= context.AvailableSize.Height) + .ToList(); + + State = new OrdersTableState + { + ShownItemsCount = State.ShownItemsCount + possibleItems.Count + }; + + return new DynamicComponentComposeResult + { + Content = possibleItems.Last(), + HasMoreContent = State.ShownItemsCount < Items.Count + }; + } + + private IDynamicElement ComposeContent(DynamicContext context, int itemsToDisplay) + { + var total = Items.Skip(State.ShownItemsCount).Take(itemsToDisplay).Sum(x => x.Count * x.Price); + + return context.CreateElement(container => + { + container + .MinimalBox() + .Width(context.AvailableSize.Width) + .Table(table => + { + table.ColumnsDefinition(columns => + { + columns.ConstantColumn(30); + columns.RelativeColumn(); + columns.ConstantColumn(50); + columns.ConstantColumn(50); + columns.ConstantColumn(50); + }); + + table.Header(header => + { + header.Cell().Element(Style).Text("#"); + header.Cell().Element(Style).Text("Item name"); + header.Cell().Element(Style).AlignRight().Text("Count"); + header.Cell().Element(Style).AlignRight().Text("Price"); + header.Cell().Element(Style).AlignRight().Text("Total"); + + IContainer Style(IContainer container) + { + return container + .DefaultTextStyle(x => x.SemiBold()) + .BorderBottom(1) + .BorderColor(Colors.Grey.Darken2) + .Padding(5); + } + }); + + table.Footer(footer => + { + footer + .Cell().ColumnSpan(5) + .AlignRight() + .Text($"Subtotal: {total}$", TextStyle.Default.Size(14).SemiBold()); + }); + + foreach (var index in Enumerable.Range(State.ShownItemsCount, itemsToDisplay)) + { + var item = Items[index]; + + table.Cell().Element(Style).Text(index + 1); + table.Cell().Element(Style).Text(item.ItemName); + table.Cell().Element(Style).AlignRight().Text(item.Count); + table.Cell().Element(Style).AlignRight().Text($"{item.Price}$"); + table.Cell().Element(Style).AlignRight().Text($"{item.Count*item.Price}$"); + + IContainer Style(IContainer container) + { + return container + .BorderBottom(1) + .BorderColor(Colors.Grey.Lighten2) + .Padding(5); + } + } + }); + }); + } + } + + public static class DynamicSimpleTableExample + { + [Test] + public static void Dynamic() + { + RenderingTest + .Create() + .PageSize(PageSizes.A5) + .ShowResults() + .Render(container => + { + var items = Enumerable.Range(0, 25).Select(x => new OrderItem()).ToList(); + + container + .Background(Colors.White) + .Padding(25) + .Decoration(decoration => + { + decoration + .Header() + .PaddingBottom(5) + .Text(text => + { + text.DefaultTextStyle(TextStyle.Default.SemiBold().FontColor(Colors.Blue.Darken2).FontSize(16)); + text.Span("Page "); + text.CurrentPageNumber(); + text.Span(" / "); + text.TotalPages(); + }); + + decoration + .Content() + .Dynamic(new OrdersTable(items)); + }); + }); + } + } +} \ No newline at end of file diff --git a/QuestPDF/Elements/Dynamic.cs b/QuestPDF/Elements/Dynamic.cs index 4c6d21f..ce8ba74 100644 --- a/QuestPDF/Elements/Dynamic.cs +++ b/QuestPDF/Elements/Dynamic.cs @@ -52,6 +52,7 @@ namespace QuestPDF.Elements var context = new DynamicContext { PageNumber = PageContext.CurrentPage, + TotalPages = PageContext.GetLocation(Infrastructure.PageContext.DocumentLocation).PageEnd, PageContext = PageContext, Canvas = Canvas, TextStyle = TextStyle, @@ -75,6 +76,7 @@ namespace QuestPDF.Elements internal TextStyle TextStyle { get; set; } public int PageNumber { get; internal set; } + public int TotalPages { get; internal set; } public Size AvailableSize { get; internal set; } public IDynamicElement CreateElement(Action content) diff --git a/QuestPDF/Resources/ReleaseNotes.txt b/QuestPDF/Resources/ReleaseNotes.txt index 8931772..d295164 100644 --- a/QuestPDF/Resources/ReleaseNotes.txt +++ b/QuestPDF/Resources/ReleaseNotes.txt @@ -1,4 +1,4 @@ -- Implemented the DynamicComponent element (useful for most advanced cases, e.g. per-page totals), -- Extend text rendering capabilities by adding subscript and superscript effects, +- Implemented the DynamicComponent element (useful when you want to generate dynamic and conditional content that is page aware, e.g. per-page totals), +- Extended text rendering capabilities by adding subscript and superscript effects, - Improved table rendering performance, -- Previewer tool stability fixes. \ No newline at end of file +- Previewer tool stability fixes.