Templates working

This commit is contained in:
2023-03-09 20:46:01 +01:00
parent a55678e5ef
commit d514a639a9
13 changed files with 158 additions and 41 deletions

View File

@ -4,6 +4,7 @@
xmlns:local="clr-namespace:WGneu" xmlns:local="clr-namespace:WGneu"
StartupUri="Windows\MainWindow.xaml" StartupUri="Windows\MainWindow.xaml"
Startup="App_Startup" Startup="App_Startup"
Exit="App_Exit"
xmlns:ui="http://schemas.modernwpf.com/2019"> xmlns:ui="http://schemas.modernwpf.com/2019">
<Application.Resources> <Application.Resources>
<!--<ResourceDictionary> <!--<ResourceDictionary>

View File

@ -11,9 +11,11 @@ namespace WGneu {
public partial class App : Application { public partial class App : Application {
public static bool IsPrintingReady => Documents.Html.IsReady && Documents.Pdf.IsReady; public static bool IsPrintingReady => Documents.Html.IsReady && Documents.Pdf.IsReady;
public static System.Windows.Threading.Dispatcher MainDispatcher { get; private set; }
public App() : base() { public App() : base() {
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
MainDispatcher = Dispatcher;
} }
public void App_Startup(object sender, EventArgs e) { public void App_Startup(object sender, EventArgs e) {
@ -21,6 +23,10 @@ namespace WGneu {
Task.Run(() => Documents.Pdf.Init(PrintingReadyChanged)); Task.Run(() => Documents.Pdf.Init(PrintingReadyChanged));
} }
public void App_Exit(object sender, EventArgs e) {
Task.Run(() => Documents.Pdf.Close());
}
private void PrintingReadyChanged() { private void PrintingReadyChanged() {
Dispatcher.BeginInvoke(App_PrintingReadyChanged, this, new EventArgs()); Dispatcher.BeginInvoke(App_PrintingReadyChanged, this, new EventArgs());
} }

View File

@ -1,7 +1,7 @@
@using RazorLight @using RazorLight
@inherits TemplatePage<WGneu.Documents.BusinessDocument> @inherits TemplatePage<WGneu.Documents.BusinessDocument>
@model WGneu.Documents.BusinessDocument @model WGneu.Documents.BusinessDocument
@{ Layout = "Document"; } @{ Layout = "Document.cshtml"; }
<div class="info-wrapper"> <div class="info-wrapper">
<div class="address-wrapper"> <div class="address-wrapper">

View File

@ -1,7 +1,7 @@
@using RazorLight @using RazorLight
@inherits TemplatePage<WGneu.Documents.BusinessLetter> @inherits TemplatePage<WGneu.Documents.BusinessLetter>
@model WGneu.Documents.BusinessLetter @model WGneu.Documents.BusinessLetter
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument.cshtml"; }
<p>Sehr geehrtes Mitglied,</p> <p>Sehr geehrtes Mitglied,</p>
<p>nein.</p> <p>nein.</p>

View File

@ -8,33 +8,29 @@
<title>@Model.Title</title> <title>@Model.Title</title>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<script> <script>
window.finished = false; window.PagedConfig = { auto: false };
window.PagedConfig = { auto: navigator.webdriver };
if (!navigator.webdriver) { if (!navigator.webdriver) {
window.addEventListener("beforeprint", async () => { window.addEventListener("beforeprint", async () => { await window.PagedPolyfill.preview(); });
await window.PagedPolyfill.preview(); window.addEventListener("afterprint", () => { location.reload(); });
window.finished = true;
});
window.addEventListener("afterprint", () => {
location.reload();
});
} }
</script> </script>
<!-- TODO store paged.js locally to avoid using the internet --> <!-- TODO store paged.js locally to avoid using the internet -->
<script src="https://unpkg.com/pagedjs/dist/paged.polyfill.js"></script> <script src="https://unpkg.com/pagedjs/dist/paged.polyfill.js"></script>
<link rel="stylesheet" href="style.css" type="text/css"/> <style>
@{ await IncludeAsync("style.css"); }
</style>
</head> </head>
<body> <body>
<div class="m1"></div> <div class="m1"></div>
<div class="m2"></div> <div class="m2"></div>
<div class="m3"></div> <div class="m3"></div>
<header>@Model.Header</header> <header>@Raw(Model.Header)</header>
<div class="footer-wrapper"> <div class="footer-wrapper">
<div class="pre-footer"> <div class="pre-footer">
<span class="date">@Model.FullDateString</span> <span class="date">@Model.FullDateString</span>
<span class="page"></span> <span class="page"></span>
</div> </div>
<footer>@Model.Footer</footer> <footer>@Raw(Model.Footer)</footer>
</div> </div>
<div class="spacing"></div> <div class="spacing"></div>
<div class="main-wrapper"> <div class="main-wrapper">

View File

@ -4,17 +4,29 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.IO; using System.IO;
using System.Windows;
namespace WGneu.Documents { namespace WGneu.Documents {
public abstract class Document { public abstract class Document : IDisposable {
private Utils.TemporaryFile? PdfFile = null;
public Document(string title) { public Document(string title) {
Title = title; Title = title;
Header = "Winzergenossenschaft Matzen"; Header = "<h1>Winzergenossenschaft Matzen</h1>";
Footer = "Winzergenossenschaft für Matzen und Umgebung reg. Gen.m.b.H."; Footer = "Winzergenossenschaft für Matzen und Umgebung reg. Gen.m.b.H.";
Date = DateTime.Today; Date = DateTime.Today;
} }
~Document() {
Dispose();
}
public void Dispose() {
PdfFile?.Dispose();
PdfFile = null;
}
public string Title { get; set; } public string Title { get; set; }
public string Header { get; set; } public string Header { get; set; }
@ -22,24 +34,41 @@ namespace WGneu.Documents {
public string Footer { get; set; } public string Footer { get; set; }
public string FullDateString { public string FullDateString {
get { get => Date.ToString("dddd, d. MMMM yyyy");
return Date.ToString("dddd, d. MMMM yyyy");
}
} }
public DateTime Date { get; set; } public DateTime Date { get; set; }
private async Task<string> Render() { private async Task<string> Render() {
if (this is BusinessLetter bl) { if (this is BusinessLetter) {
return await Html.CompileRenderAsync("BusinessLetter", bl); return await Html.CompileRenderAsync("BusinessLetter.cshtml", this);
} }
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
public async Task<string> Save() { public async Task Generate() {
// TODO tempfile var pdf = new Utils.TemporaryFile(@"C:\Users\Lorenz\Desktop", ".pdf");
await File.WriteAllTextAsync("razor_test.html", await Render()); using (var tmpHtml = new Utils.TemporaryFile(@"C:\Users\Lorenz\Desktop", ".html")) {
return ""; 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);
} }
} }
} }

View File

@ -13,11 +13,11 @@ namespace WGneu.Documents {
public static async Task Init(Action evtHandler) { public static async Task Init(Action evtHandler) {
var e = new RazorLightEngineBuilder() var e = new RazorLightEngineBuilder()
.UseFileSystemProject(@"C:\Users\Lorenz\source\repos\WGneu\WGneu\Documents") .UseFileSystemProject(@"C:\Users\Lorenz\source\repos\WGneu\WGneu\Documents", "")
.UseMemoryCachingProvider() .UseMemoryCachingProvider()
.Build(); .Build();
await e.CompileTemplateAsync("BusinessLetter"); await e.CompileTemplateAsync("BusinessLetter.cshtml");
Engine = e; Engine = e;
evtHandler(); evtHandler();

View File

@ -6,6 +6,8 @@ using System.Linq;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using System.Windows.Shapes;
using PdfSharp.Pdf.IO; using PdfSharp.Pdf.IO;
using PuppeteerSharp; using PuppeteerSharp;
using PuppeteerSharp.Media; using PuppeteerSharp.Media;
@ -27,12 +29,19 @@ namespace WGneu.Documents {
evtHandler(); 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"); if (Browser == null) throw new InvalidOperationException("The puppeteer engine has not been initialized yet");
using var page = await Browser.NewPageAsync(); using var page = await Browser.NewPageAsync();
await page.GoToAsync("file://" + path_html); page.Console += OnConsole;
await page.WaitForFunctionAsync("() => window.finished"); await page.GoToAsync("file://" + htmlPath);
await page.PdfAsync(path_pdf, new() { await page.EvaluateFunctionAsync("async () => { await window.PagedPolyfill.preview(); }");
await page.PdfAsync(pdfPath, new() {
PreferCSSPageSize = true, PreferCSSPageSize = true,
//Format = PaperFormat.A4, //Format = PaperFormat.A4,
DisplayHeaderFooter = false, 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) { public static void UpdateMetadata(string path, string title, string author) {
using var doc = PdfReader.Open(path); using var doc = PdfReader.Open(path);
doc.Info.Title = title; doc.Info.Title = title;
@ -52,9 +65,18 @@ namespace WGneu.Documents {
doc.Save(path); 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) { public static void Show(string path, string title) {
App.MainDispatcher.BeginInvoke(() => {
var w = new DocumentViewerWindow(title, path); var w = new DocumentViewerWindow(title, path);
w.Show(); w.Show();
});
} }
public static void Print(string path) { public static void Print(string path) {

View File

@ -7,6 +7,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.IO; using System.IO;
using WGneu.Models; using WGneu.Models;
using System.Windows;
namespace WGneu.Documents { namespace WGneu.Documents {
class Template { class Template {
@ -19,9 +20,16 @@ namespace WGneu.Documents {
Pdf.Show(ROOT + "test.pdf", "Test-Dokument"); Pdf.Show(ROOT + "test.pdf", "Test-Dokument");
} }
public static async void Generate(WgContext c) { public static void Generate(WgContext c) {
var letter = new BusinessLetter("Test Dokument", c.Members.First()); Task.Run(async () => {
var pdf_path = await letter.Save(); 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);
}
});
} }
} }
} }

View File

@ -136,15 +136,15 @@ footer {
text-align: center; text-align: center;
} }
@page { @@page {
size: A4; size: A4;
margin: 25mm 0 35mm 0; margin: 25mm 0 35mm 0;
@bottom-center { @@bottom-center {
content: element(page-footer); content: element(page-footer);
} }
} }
@media screen { @@media screen {
body, header, .footer-wrapper { body, header, .footer-wrapper {
width: 210mm; width: 210mm;
} }
@ -166,7 +166,7 @@ footer {
} }
} }
@media print { @@media print {
.page::after { .page::after {
content: "Seite " counter(page) " von " counter(pages); content: "Seite " counter(page) " von " counter(pages);
} }

View File

@ -6,9 +6,10 @@ using System.Threading.Tasks;
using System.Windows.Media; using System.Windows.Media;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.IO;
namespace WGneu { namespace WGneu {
class Utils { public static class Utils {
public static void SetInputChanged(Control input) { public static void SetInputChanged(Control input) {
input.BorderBrush = Brushes.Orange; input.BorderBrush = Brushes.Orange;
} }
@ -44,5 +45,46 @@ namespace WGneu {
throw new ArgumentException("First argument has to be a decimal string"); throw new ArgumentException("First argument has to be a decimal string");
return a.Select(ch => ch - '0').Aggregate((sum, n) => (sum * 10 + n) % b); 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;
}
}
} }
} }

View File

@ -6,6 +6,7 @@
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf" xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
xmlns:local="clr-namespace:WGneu.Windows" xmlns:local="clr-namespace:WGneu.Windows"
mc:Ignorable="d" mc:Ignorable="d"
Closed="OnClosed"
Title="PDF Ansicht" Title="PDF Ansicht"
MinHeight="600" MinWidth="420" Height="750" Width="525"> MinHeight="600" MinWidth="420" Height="750" Width="525">
<Grid> <Grid>

View File

@ -14,10 +14,22 @@ using System.Windows.Shapes;
namespace WGneu.Windows { namespace WGneu.Windows {
public partial class DocumentViewerWindow : Window { public partial class DocumentViewerWindow : Window {
private Utils.TemporaryFile? PdfFile = null;
public DocumentViewerWindow(string title, string path) { public DocumentViewerWindow(string title, string path) {
InitializeComponent(); InitializeComponent();
Title = Title + " - " + title; Title = Title + " - " + title;
WebView.Source = new("file://" + path); 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;
}
} }
} }