Rename solution directory to Elwig

This commit is contained in:
2023-03-18 11:04:22 +01:00
parent 649578e8bf
commit 769d80eb81
48 changed files with 7 additions and 11 deletions

View File

@ -0,0 +1,18 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.BusinessDocument>
@model Elwig.Documents.BusinessDocument
@{ Layout = "Document"; }
<div class="info-wrapper">
<div class="address-wrapper">
<div class="sender">
<div>WG Matzen | Schloßstraße 6 | 2243 Matzen</div>
<div>E Österreichische Post AG Eco Brief</div>
</div>
<address>@Model.Address</address>
</div>
<aside></aside>
</div>
<main>
@RenderBody()
</main>

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Elwig.Models;
namespace Elwig.Documents {
public abstract class BusinessDocument : Document {
public BusinessDocument(string title, Member m) : base(title) {
Member = m;
}
public Member Member { get; set; }
public string Address {
get {
// TODO Name/Rechnungsadresse
return $"{Member.GivenName} {Member.FamilyName}\n{Member.Address}";
}
}
}
}

View File

@ -0,0 +1,8 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.BusinessLetter>
@model Elwig.Documents.BusinessLetter
@{ Layout = "BusinessDocument"; }
<p>Sehr geehrtes Mitglied,</p>
<p>nein.</p>
<p>Mit freundlichen Grüßen<br/>Ihre Winzergenossenschaft</p>

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Elwig.Models;
namespace Elwig.Documents {
public class BusinessLetter : BusinessDocument {
public BusinessLetter(string title, Member m) : base(title, m) {
}
}
}

View File

@ -0,0 +1,38 @@
@using RazorLight
@inherits TemplatePage<WGneu.Documents.Document>
@model WGneu.Documents.Document
<!DOCTYPE html>
<html lang="de-AT">
<head>
<title>@Model.Title</title>
<meta charset="UTF-8"/>
<script>
window.PagedConfig = { auto: false };
if (!navigator.webdriver) {
window.addEventListener("beforeprint", async () => { await window.PagedPolyfill.preview(); });
window.addEventListener("afterprint", () => { location.reload(); });
}
</script>
<!-- TODO store paged.js locally to avoid using the internet -->
<script src="https://unpkg.com/pagedjs/dist/paged.polyfill.js"></script>
@{ await IncludeAsync("Style.css"); }
</head>
<body>
<div class="m1"></div>
<div class="m2"></div>
<div class="m3"></div>
<header>@Raw(Model.Header)</header>
<div class="footer-wrapper">
<div class="pre-footer">
<span class="date">@Model.FullDateString</span>
<span class="page"></span>
</div>
<footer>@Raw(Model.Footer)</footer>
</div>
<div class="spacing"></div>
<div class="main-wrapper">
@RenderBody()
</div>
</body>
</html>

View File

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using Elwig.Helpers;
namespace Elwig.Documents {
public abstract class Document : IDisposable {
private TempFile? PdfFile = null;
public Document(string title) {
Title = title;
Header = "<h1>Winzergenossenschaft Matzen</h1>";
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; }
public string Footer { get; set; }
public string FullDateString {
get => Date.ToString("dddd, d. MMMM yyyy");
}
public DateTime Date { get; set; }
private async Task<string> Render() {
string name;
if (this is BusinessLetter) {
name = "BusinessLetter";
} else {
throw new InvalidOperationException();
}
return await Html.CompileRenderAsync(name, this);
}
public async Task Generate() {
var pdf = new TempFile("pdf");
using (var tmpHtml = new TempFile("html")) {
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render());
await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath);
}
Pdf.UpdateMetadata(pdf.FilePath, Title, "Winzergenossenschaft 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);
}
}
}

31
Elwig/Documents/Html.cs Normal file
View File

@ -0,0 +1,31 @@
using RazorLight;
using RazorLight.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Elwig.Documents {
public static class Html {
private static RazorLightEngine? Engine = null;
public static bool IsReady => Engine != null;
public static async Task Init(Action evtHandler) {
var e = new RazorLightEngineBuilder()
.UseFileSystemProject(@"C:\Users\Lorenz\source\repos\WGneu\Elwig\Documents")
.UseMemoryCachingProvider()
.Build();
await e.CompileTemplateAsync("BusinessLetter");
Engine = e;
evtHandler();
}
public static async Task<string> CompileRenderAsync(string key, object model) {
if (Engine == null) throw new InvalidOperationException("The razor engine has not been initialized yet");
return await Engine.CompileRenderAsync(key, model);
}
}
}

87
Elwig/Documents/Pdf.cs Normal file
View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
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;
using RazorLight;
using Elwig.Helpers;
using Elwig.Windows;
namespace Elwig.Documents {
public static class Pdf {
private static readonly string CHROMIUM = @"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe";
private static IBrowser? Browser = null;
public static bool IsReady => Browser != null;
public static async Task Init(Action evtHandler) {
Browser = await Puppeteer.LaunchAsync(new LaunchOptions {
Headless = true,
ExecutablePath = CHROMIUM,
});
evtHandler();
}
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();
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,
MarginOptions = new() {
Top = "0mm",
Right = "0mm",
Bottom = "0mm",
Left = "0mm",
},
});
}
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;
doc.Info.Author = author;
doc.Save(path);
}
public static void Show(TempFile file, string title) {
App.MainDispatcher.BeginInvoke(() => {
var w = new DocumentViewerWindow(title, file);
w.Show();
});
}
public static void Show(string path, string title) {
App.MainDispatcher.BeginInvoke(() => {
var w = new DocumentViewerWindow(title, path);
w.Show();
});
}
public static void Print(string path) {
// TODO print pdf
}
}
}

View File

@ -0,0 +1,180 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.Document>
<style>
:root {
font-family: "Times New Roman", serif;
line-height: 1;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.m1, .m2, .m3 {
height: 0;
width: 1cm;
position: fixed;
left: 0;
border-top: 1pt solid black;
}
.m1 {top: 105mm;}
.m2 {top: 148.5mm;}
.m3 {top: 210mm;}
header {
height: 45mm;
padding: 5mm;
position: absolute;
top: -25mm;
left: 0;
right: 0;
text-align: center;
}
.spacing {height: 20mm;}
.info-wrapper {
width: 100%;
height:45mm;
margin: 0 0 8.46mm 0;
position: relative;
}
.address-wrapper {
height: 45mm;
width: 85mm;
margin: 0;
padding: 5mm;
position: absolute;
left: 20mm;
top: 0;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.address-wrapper .sender {
flex: 17.7mm 1 1;
font-size: 8pt;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding-bottom: 2mm;
}
address {
flex: 27.3mm 1 1;
white-space: pre-line;
font-size: 12pt;
font-style: normal;
}
aside {
height: 40mm;
width: 75mm;
margin: 0;
position: absolute;
left: 125mm;
top: 5mm;
}
main {
margin: 8.46mm 20mm 4.23mm 25mm;
}
main :first-child {
margin-top: 0;
}
main h1, main p {
font-size: 12pt;
margin: 1em 0;
text-align: justify;
widows: 3;
orphans: 3;
}
main .date {
margin-bottom: 2em;
text-align: right;
}
main h1 {
margin-bottom: 2em;
}
.footer-wrapper {
padding: 0 20mm 0 25mm;
position: running(page-footer);
bottom: 0;
left: 0;
right: 0;
}
.pre-footer {
margin: 4.23mm 0;
font-size: 10pt;
display: flex;
}
.pre-footer .date, .pre-footer .page {
flex: 100px 1 1;
}
.pre-footer .date {text-align: left;}
.pre-footer .page {text-align: right;}
.page::after {
content: "Seite 1 von 1";
}
footer {
font-size: 10pt;
border-top: 1pt solid black;
height: 25mm;
padding-top: 1mm;
text-align: center;
}
@@page {
size: A4;
margin: 25mm 0 35mm 0;
@@bottom-center {
content: element(page-footer);
}
}
@@media screen {
body, header, .footer-wrapper {
width: 210mm;
}
header, .address-wrapper, aside, main {
border: 1px solid lightgray;
}
.m1, .m2, .m3 {display: none;}
header {top: 0;}
.spacing {height: 45mm;}
.main-wrapper {
margin-bottom: 40mm;
}
.footer-wrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
}
}
@@media print {
.page::after {
content: "Seite " counter(page) " von " counter(pages);
}
.footer-wrapper {
display: none;
}
}
</style>