From 8551e1f33f2041b286efed5bc7a2722a837829f6 Mon Sep 17 00:00:00 2001 From: MarcinZiabek Date: Sat, 19 Mar 2022 03:08:37 +0100 Subject: [PATCH] Basic previewer implementation --- QuestPDF.Previewer.Example/Program.cs | 80 +++++++++++ .../QuestPDF.Previewer.Example.csproj | 18 +++ .../DocumentPreviewerExtensions.cs | 125 ++++++++++++++++++ QuestPDF.Previewer/HotReloadManager.cs | 9 ++ QuestPDF.Previewer/QuestPDF.Previewer.csproj | 24 ++++ QuestPDF.Previewer/index.html | 117 ++++++++++++++++ QuestPDF.sln | 12 ++ 7 files changed, 385 insertions(+) create mode 100644 QuestPDF.Previewer.Example/Program.cs create mode 100644 QuestPDF.Previewer.Example/QuestPDF.Previewer.Example.csproj create mode 100644 QuestPDF.Previewer/DocumentPreviewerExtensions.cs create mode 100644 QuestPDF.Previewer/HotReloadManager.cs create mode 100644 QuestPDF.Previewer/QuestPDF.Previewer.csproj create mode 100644 QuestPDF.Previewer/index.html diff --git a/QuestPDF.Previewer.Example/Program.cs b/QuestPDF.Previewer.Example/Program.cs new file mode 100644 index 0000000..7271b1d --- /dev/null +++ b/QuestPDF.Previewer.Example/Program.cs @@ -0,0 +1,80 @@ +using System.Diagnostics; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; +using QuestPDF.Previewer; + +Document + .Create(container => + { + container.Page(page => + { + page.Size(PageSizes.A4); + page.Margin(2, Unit.Centimetre); + page.PageColor(Colors.White); + page.DefaultTextStyle(x => x.FontSize(14)); + + page.Header() + .Text("Hello PDF!") + .SemiBold().FontSize(36).FontColor(Colors.Blue.Medium); + + page.Content() + .PaddingVertical(1, Unit.Centimetre) + .Table(table => + { + table.ColumnsDefinition(columns => + { + columns.ConstantColumn(30); + columns.RelativeColumn(); + columns.ConstantColumn(75); + columns.ConstantColumn(75); + columns.ConstantColumn(75); + }); + + table.Header(header => + { + var textStyle = TextStyle.Default.SemiBold(); + + header.Cell().Element(DefaultCellStyle).Text("#").Style(textStyle); + header.Cell().Element(DefaultCellStyle).Text("Item name").Style(textStyle); + header.Cell().Element(DefaultCellStyle).AlignRight().Text("Price").Style(textStyle); + header.Cell().Element(DefaultCellStyle).AlignRight().Text("Count").Style(textStyle); + header.Cell().Element(DefaultCellStyle).AlignRight().Text("Total").Style(textStyle); + + static IContainer DefaultCellStyle(IContainer container) + { + return container.PaddingBottom(5).BorderBottom(1).PaddingBottom(5); + } + }); + + foreach(var i in Enumerable.Range(1, 100)) + { + var price = Placeholders.Random.Next(100, 999) / 100f; + var count = Placeholders.Random.Next(1, 10); + var total = price * count; + + table.Cell().Element(DefaultCellStyle).Text(i); + table.Cell().Element(DefaultCellStyle).Text(Placeholders.Label()); + table.Cell().Element(DefaultCellStyle).AlignRight().Text($"{price:N2} $"); + table.Cell().Element(DefaultCellStyle).AlignRight().Text(count); + table.Cell().Element(DefaultCellStyle).AlignRight().Text($"{total:N2} $"); + + static IContainer DefaultCellStyle(IContainer container) + { + return container.PaddingBottom(5).BorderBottom(1).BorderColor(Colors.Grey.Lighten1).PaddingBottom(5); + } + } + }); + + page.Footer() + .AlignCenter() + .Text(x => + { + x.Span("Page "); + x.CurrentPageNumber(); + x.Span(" of "); + x.TotalPages(); + }); + }); + }) + .ShowInPreviewer(); \ No newline at end of file diff --git a/QuestPDF.Previewer.Example/QuestPDF.Previewer.Example.csproj b/QuestPDF.Previewer.Example/QuestPDF.Previewer.Example.csproj new file mode 100644 index 0000000..5631e65 --- /dev/null +++ b/QuestPDF.Previewer.Example/QuestPDF.Previewer.Example.csproj @@ -0,0 +1,18 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + diff --git a/QuestPDF.Previewer/DocumentPreviewerExtensions.cs b/QuestPDF.Previewer/DocumentPreviewerExtensions.cs new file mode 100644 index 0000000..effdd57 --- /dev/null +++ b/QuestPDF.Previewer/DocumentPreviewerExtensions.cs @@ -0,0 +1,125 @@ +using System.Diagnostics; +using System.Reflection; +using Microsoft.AspNetCore.Builder; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; + +namespace QuestPDF.Previewer; + +public static class DocumentPreviewerExtensions +{ + public static void ShowInPreviewer(this IDocument document) + { + ArgumentNullException.ThrowIfNull(document); + + var builder = WebApplication.CreateBuilder(); + var app = builder.Build(); + + var pdfDocumentCache = GeneratePdf(document); + var refreshFlag = false; + + static byte[] GenerateDocumentAboutException(Exception exception) + { + return Document.Create(document => + { + document.Page(page => + { + page.Size(PageSizes.A4); + page.Margin(1, Unit.Inch); + page.PageColor(Colors.Red.Lighten4); + page.DefaultTextStyle(x => x.FontSize(16)); + + page.Header() + .BorderBottom(2) + .BorderColor(Colors.Red.Medium) + .PaddingBottom(5) + .Text("Ooops! Something went wrong...").FontSize(28).FontColor(Colors.Red.Medium).Bold(); + + page.Content().PaddingVertical(20).Column(column => + { + var currentException = exception; + + while (currentException != null) + { + column.Item().Text(exception.GetType().Name).FontSize(20).SemiBold(); + column.Item().Text(exception.Message).FontSize(14); + column.Item().PaddingTop(10).Text(exception.StackTrace).FontSize(10).Light(); + + currentException = currentException.InnerException; + + if (currentException != null) + column.Item().PaddingVertical(15).LineHorizontal(2).LineColor(Colors.Red.Medium); + } + }); + }); + }).GeneratePdf(); + } + + static byte[] GeneratePdf(IDocument document) + { + try + { + return document.GeneratePdf(); + } + catch(Exception exception) + { + return GenerateDocumentAboutException(exception); + } + } + + HotReloadManager.OnApplicationChanged += () => + { + pdfDocumentCache = GeneratePdf(document); + refreshFlag = true; + }; + + app.MapGet("/", () => + { + var assembly = Assembly.GetExecutingAssembly(); + var resourceName = "QuestPDF.Previewer.index.html"; + + using var stream = assembly.GetManifestResourceStream(resourceName); + using var reader = new StreamReader(stream); + var result = reader.ReadToEnd(); + return Results.Content(result, "text/html"); + }); + + app.MapGet("/render", () => + { + refreshFlag = false; + return Results.File(pdfDocumentCache, "application/pdf"); + }); + + app.MapGet("/listen", async () => + { + foreach (var i in Enumerable.Range(0, 1000)) + { + await Task.Delay(TimeSpan.FromMilliseconds(100)); + + if (!refreshFlag) + continue; + + return Results.Text("true"); + } + + return Results.Text("false"); + }); + + app.Lifetime.ApplicationStarted.Register(() => + { + var openBrowserProcess = new Process() + { + StartInfo = new() + { + UseShellExecute = true, + FileName = app.Urls.First() + } + }; + + openBrowserProcess.Start(); + }); + + app.Run(); + } +} \ No newline at end of file diff --git a/QuestPDF.Previewer/HotReloadManager.cs b/QuestPDF.Previewer/HotReloadManager.cs new file mode 100644 index 0000000..60928cc --- /dev/null +++ b/QuestPDF.Previewer/HotReloadManager.cs @@ -0,0 +1,9 @@ +using System.Reflection.Metadata; + +[assembly: MetadataUpdateHandler(typeof(HotReloadManager))] + +internal static class HotReloadManager +{ + public static event Action? OnApplicationChanged; + public static void UpdateApplication(Type[]? _) => OnApplicationChanged?.Invoke(); +} \ No newline at end of file diff --git a/QuestPDF.Previewer/QuestPDF.Previewer.csproj b/QuestPDF.Previewer/QuestPDF.Previewer.csproj new file mode 100644 index 0000000..46765f8 --- /dev/null +++ b/QuestPDF.Previewer/QuestPDF.Previewer.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + enable + enable + Library + + + + + + + + + + + + + + + + + diff --git a/QuestPDF.Previewer/index.html b/QuestPDF.Previewer/index.html new file mode 100644 index 0000000..1f53742 --- /dev/null +++ b/QuestPDF.Previewer/index.html @@ -0,0 +1,117 @@ + + + + + QuestPDF Previewer + + + + + + + + + +
+ Cannot connect to QuestPDF previewer host. Possibly your debugging session has ended. Please close this browser. +
+ + + + + \ No newline at end of file diff --git a/QuestPDF.sln b/QuestPDF.sln index db070d2..d708fcf 100644 --- a/QuestPDF.sln +++ b/QuestPDF.sln @@ -8,6 +8,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.UnitTests", "Quest EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Examples", "QuestPDF.Examples\QuestPDF.Examples.csproj", "{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Previewer", "QuestPDF.Previewer\QuestPDF.Previewer.csproj", "{B4D3CBBC-2F38-434F-9347-6E5297737047}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Previewer.Example", "QuestPDF.Previewer.Example\QuestPDF.Previewer.Example.csproj", "{012F05A0-C30B-4112-8EB0-E70CE1D31E57}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,5 +34,13 @@ Global {8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU {8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU {8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.Build.0 = Release|Any CPU + {B4D3CBBC-2F38-434F-9347-6E5297737047}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4D3CBBC-2F38-434F-9347-6E5297737047}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4D3CBBC-2F38-434F-9347-6E5297737047}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4D3CBBC-2F38-434F-9347-6E5297737047}.Release|Any CPU.Build.0 = Release|Any CPU + {012F05A0-C30B-4112-8EB0-E70CE1D31E57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {012F05A0-C30B-4112-8EB0-E70CE1D31E57}.Debug|Any CPU.Build.0 = Debug|Any CPU + {012F05A0-C30B-4112-8EB0-E70CE1D31E57}.Release|Any CPU.ActiveCfg = Release|Any CPU + {012F05A0-C30B-4112-8EB0-E70CE1D31E57}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal