Templates working
This commit is contained in:
@ -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>
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -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">
|
||||||
|
@ -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>
|
||||||
|
@ -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">
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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) {
|
||||||
var w = new DocumentViewerWindow(title, path);
|
App.MainDispatcher.BeginInvoke(() => {
|
||||||
w.Show();
|
var w = new DocumentViewerWindow(title, path);
|
||||||
|
w.Show();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Print(string path) {
|
public static void Print(string path) {
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user