From d514a639a946a4b057e9731a070abac24fbaf00c Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Thu, 9 Mar 2023 20:46:01 +0100 Subject: [PATCH] Templates working --- WGneu/App.xaml | 1 + WGneu/App.xaml.cs | 6 +++ WGneu/Documents/BusinessDocument.cshtml | 2 +- WGneu/Documents/BusinessLetter.cshtml | 2 +- WGneu/Documents/Document.cshtml | 20 ++++----- WGneu/Documents/Document.cshtml.cs | 51 +++++++++++++++++----- WGneu/Documents/Html.cs | 4 +- WGneu/Documents/Pdf.cs | 34 ++++++++++++--- WGneu/Documents/Template.cs | 14 ++++-- WGneu/Documents/style.css | 8 ++-- WGneu/Utils.cs | 44 ++++++++++++++++++- WGneu/Windows/DocumentViewerWindow.xaml | 1 + WGneu/Windows/DocumentViewerWindow.xaml.cs | 12 +++++ 13 files changed, 158 insertions(+), 41 deletions(-) diff --git a/WGneu/App.xaml b/WGneu/App.xaml index 3e2bd20..653a741 100644 --- a/WGneu/App.xaml +++ b/WGneu/App.xaml @@ -4,6 +4,7 @@ xmlns:local="clr-namespace:WGneu" StartupUri="Windows\MainWindow.xaml" Startup="App_Startup" + Exit="App_Exit" xmlns:ui="http://schemas.modernwpf.com/2019"> - +
-
@Model.Header
+
@Raw(Model.Header)
diff --git a/WGneu/Documents/Document.cshtml.cs b/WGneu/Documents/Document.cshtml.cs index 80325bd..2c2ed26 100644 --- a/WGneu/Documents/Document.cshtml.cs +++ b/WGneu/Documents/Document.cshtml.cs @@ -4,17 +4,29 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; +using System.Windows; namespace WGneu.Documents { - public abstract class Document { + public abstract class Document : IDisposable { + + private Utils.TemporaryFile? PdfFile = null; public Document(string title) { Title = title; - Header = "Winzergenossenschaft Matzen"; + Header = "

Winzergenossenschaft Matzen

"; Footer = "Winzergenossenschaft für Matzen und Umgebung reg. Gen.m.b.H."; Date = DateTime.Today; } + ~Document() { + Dispose(); + } + + public void Dispose() { + PdfFile?.Dispose(); + PdfFile = null; + } + public string Title { get; set; } public string Header { get; set; } @@ -22,24 +34,41 @@ namespace WGneu.Documents { public string Footer { get; set; } public string FullDateString { - get { - return Date.ToString("dddd, d. MMMM yyyy"); - } + get => Date.ToString("dddd, d. MMMM yyyy"); } public DateTime Date { get; set; } private async Task Render() { - if (this is BusinessLetter bl) { - return await Html.CompileRenderAsync("BusinessLetter", bl); + if (this is BusinessLetter) { + return await Html.CompileRenderAsync("BusinessLetter.cshtml", this); } throw new InvalidOperationException(); } - public async Task Save() { - // TODO tempfile - await File.WriteAllTextAsync("razor_test.html", await Render()); - return ""; + public async Task Generate() { + var pdf = new Utils.TemporaryFile(@"C:\Users\Lorenz\Desktop", ".pdf"); + using (var tmpHtml = new Utils.TemporaryFile(@"C:\Users\Lorenz\Desktop", ".html")) { + await File.WriteAllTextAsync(tmpHtml.FilePath, await Render()); + await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath); + } + Pdf.UpdateMetadata(pdf.FilePath, Title, "Wizergenossenschaft für Matzen und Umgebung reg. Gen.m.b.H."); + PdfFile = pdf; + } + + public void SaveTo(string pdfPath) { + if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet"); + File.Copy(PdfFile.FilePath, pdfPath); + } + + public async Task Print() { + if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet"); + Pdf.Print(PdfFile.FilePath); + } + + public void Show() { + if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet"); + Pdf.Show(PdfFile.NewReference(), Title); } } } diff --git a/WGneu/Documents/Html.cs b/WGneu/Documents/Html.cs index e233994..9690e34 100644 --- a/WGneu/Documents/Html.cs +++ b/WGneu/Documents/Html.cs @@ -13,11 +13,11 @@ namespace WGneu.Documents { public static async Task Init(Action evtHandler) { var e = new RazorLightEngineBuilder() - .UseFileSystemProject(@"C:\Users\Lorenz\source\repos\WGneu\WGneu\Documents") + .UseFileSystemProject(@"C:\Users\Lorenz\source\repos\WGneu\WGneu\Documents", "") .UseMemoryCachingProvider() .Build(); - await e.CompileTemplateAsync("BusinessLetter"); + await e.CompileTemplateAsync("BusinessLetter.cshtml"); Engine = e; evtHandler(); diff --git a/WGneu/Documents/Pdf.cs b/WGneu/Documents/Pdf.cs index 1f82a14..652861f 100644 --- a/WGneu/Documents/Pdf.cs +++ b/WGneu/Documents/Pdf.cs @@ -6,6 +6,8 @@ using System.Linq; using System.Reflection.Metadata.Ecma335; using System.Text; using System.Threading.Tasks; +using System.Windows; +using System.Windows.Shapes; using PdfSharp.Pdf.IO; using PuppeteerSharp; using PuppeteerSharp.Media; @@ -27,12 +29,19 @@ namespace WGneu.Documents { evtHandler(); } - public static async Task Convert(string path_html, string path_pdf) { + public static async Task Close() { + if (Browser == null) return; + await Browser.CloseAsync(); + Browser = null; + } + + public static async Task Convert(string htmlPath, string pdfPath) { if (Browser == null) throw new InvalidOperationException("The puppeteer engine has not been initialized yet"); using var page = await Browser.NewPageAsync(); - await page.GoToAsync("file://" + path_html); - await page.WaitForFunctionAsync("() => window.finished"); - await page.PdfAsync(path_pdf, new() { + page.Console += OnConsole; + await page.GoToAsync("file://" + htmlPath); + await page.EvaluateFunctionAsync("async () => { await window.PagedPolyfill.preview(); }"); + await page.PdfAsync(pdfPath, new() { PreferCSSPageSize = true, //Format = PaperFormat.A4, DisplayHeaderFooter = false, @@ -45,6 +54,10 @@ namespace WGneu.Documents { }); } + private static void OnConsole(object sender, ConsoleEventArgs e) { + MessageBox.Show(e.Message.Text); + } + public static void UpdateMetadata(string path, string title, string author) { using var doc = PdfReader.Open(path); doc.Info.Title = title; @@ -52,9 +65,18 @@ namespace WGneu.Documents { doc.Save(path); } + public static void Show(Utils.TemporaryFile file, string title) { + App.MainDispatcher.BeginInvoke(() => { + var w = new DocumentViewerWindow(title, file); + w.Show(); + }); + } + public static void Show(string path, string title) { - var w = new DocumentViewerWindow(title, path); - w.Show(); + App.MainDispatcher.BeginInvoke(() => { + var w = new DocumentViewerWindow(title, path); + w.Show(); + }); } public static void Print(string path) { diff --git a/WGneu/Documents/Template.cs b/WGneu/Documents/Template.cs index e0b4a9c..8f1194d 100644 --- a/WGneu/Documents/Template.cs +++ b/WGneu/Documents/Template.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading.Tasks; using System.IO; using WGneu.Models; +using System.Windows; namespace WGneu.Documents { class Template { @@ -19,9 +20,16 @@ namespace WGneu.Documents { Pdf.Show(ROOT + "test.pdf", "Test-Dokument"); } - public static async void Generate(WgContext c) { - var letter = new BusinessLetter("Test Dokument", c.Members.First()); - var pdf_path = await letter.Save(); + public static void Generate(WgContext c) { + Task.Run(async () => { + try { + using var letter = new BusinessLetter("Test Dokument", c.Members.First()); + await letter.Generate(); + letter.Show(); + } catch (Exception e) { + MessageBox.Show(e.ToString(), "PDF Generation", MessageBoxButton.OK, MessageBoxImage.Error); + } + }); } } } diff --git a/WGneu/Documents/style.css b/WGneu/Documents/style.css index 7e09e11..c919803 100644 --- a/WGneu/Documents/style.css +++ b/WGneu/Documents/style.css @@ -136,15 +136,15 @@ footer { text-align: center; } -@page { +@@page { size: A4; margin: 25mm 0 35mm 0; - @bottom-center { + @@bottom-center { content: element(page-footer); } } -@media screen { +@@media screen { body, header, .footer-wrapper { width: 210mm; } @@ -166,7 +166,7 @@ footer { } } -@media print { +@@media print { .page::after { content: "Seite " counter(page) " von " counter(pages); } diff --git a/WGneu/Utils.cs b/WGneu/Utils.cs index b7fc5bd..946740e 100644 --- a/WGneu/Utils.cs +++ b/WGneu/Utils.cs @@ -6,9 +6,10 @@ using System.Threading.Tasks; using System.Windows.Media; using System.Windows; using System.Windows.Controls; +using System.IO; namespace WGneu { - class Utils { + public static class Utils { public static void SetInputChanged(Control input) { input.BorderBrush = Brushes.Orange; } @@ -44,5 +45,46 @@ namespace WGneu { throw new ArgumentException("First argument has to be a decimal string"); return a.Select(ch => ch - '0').Aggregate((sum, n) => (sum * 10 + n) % b); } + + public sealed class TemporaryFile : IDisposable { + private int Usages = 0; + public string FilePath { get; private set; } + + public TemporaryFile() : this("") {} + + public TemporaryFile(string ext) : this(Path.GetTempPath(), ext) {} + + public TemporaryFile(string dir, string ext) { + FilePath = Path.Combine(dir, Path.GetRandomFileName() + ext); + Usages++; + Create(); + } + + ~TemporaryFile() { + Delete(); + } + + public void Dispose() { + if (--Usages == 0) { + Delete(); + GC.SuppressFinalize(this); + } + } + + public TemporaryFile NewReference() { + Usages++; + return this; + } + + private void Create() { + using (File.Create(FilePath)) {}; + } + + private void Delete() { + if (FilePath == null) return; + File.Delete(FilePath); + FilePath = null; + } + } } } diff --git a/WGneu/Windows/DocumentViewerWindow.xaml b/WGneu/Windows/DocumentViewerWindow.xaml index 7b0ef13..81be3c4 100644 --- a/WGneu/Windows/DocumentViewerWindow.xaml +++ b/WGneu/Windows/DocumentViewerWindow.xaml @@ -6,6 +6,7 @@ xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf" xmlns:local="clr-namespace:WGneu.Windows" mc:Ignorable="d" + Closed="OnClosed" Title="PDF Ansicht" MinHeight="600" MinWidth="420" Height="750" Width="525"> diff --git a/WGneu/Windows/DocumentViewerWindow.xaml.cs b/WGneu/Windows/DocumentViewerWindow.xaml.cs index 4c7c48e..8a7947a 100644 --- a/WGneu/Windows/DocumentViewerWindow.xaml.cs +++ b/WGneu/Windows/DocumentViewerWindow.xaml.cs @@ -14,10 +14,22 @@ using System.Windows.Shapes; namespace WGneu.Windows { public partial class DocumentViewerWindow : Window { + + private Utils.TemporaryFile? PdfFile = null; + public DocumentViewerWindow(string title, string path) { InitializeComponent(); Title = Title + " - " + title; WebView.Source = new("file://" + path); } + + public DocumentViewerWindow(string title, Utils.TemporaryFile file) : this(title, file.FilePath) { + PdfFile = file; + } + + public void OnClosed(object sender, EventArgs e) { + PdfFile?.Dispose(); + PdfFile = null; + } } }