From 46c97089e7b151ecd1101461e0a6db4815074398 Mon Sep 17 00:00:00 2001
From: Lorenz Stechauner <lorenz.stechauner@necronda.net>
Date: Sat, 2 Mar 2024 19:55:51 +0100
Subject: [PATCH] [#19] Printing/Pdf: Use WinziPrint's daemon function to allow
 parallel usage

---
 Elwig/App.xaml                |  1 +
 Elwig/App.xaml.cs             |  4 ++++
 Elwig/Helpers/Printing/Pdf.cs | 36 +++++++++++++++++++++++------------
 Tests/DocumentTests/Setup.cs  |  4 ++--
 4 files changed, 31 insertions(+), 14 deletions(-)

diff --git a/Elwig/App.xaml b/Elwig/App.xaml
index e351b55..496d359 100644
--- a/Elwig/App.xaml
+++ b/Elwig/App.xaml
@@ -4,6 +4,7 @@
              xmlns:local="clr-namespace:Elwig"
              xmlns:ctrl="clr-namespace:Elwig.Controls"
              StartupUri="Windows\MainWindow.xaml"
+             Exit="Application_Exit"
              xmlns:ui="http://schemas.modernwpf.com/2019">
     <Application.Resources>
         <ctrl:BoolToStringConverter x:Key="BoolToStarConverter" FalseValue="" TrueValue="*"/>
diff --git a/Elwig/App.xaml.cs b/Elwig/App.xaml.cs
index 9bf76a2..f933a7f 100644
--- a/Elwig/App.xaml.cs
+++ b/Elwig/App.xaml.cs
@@ -143,6 +143,10 @@ namespace Elwig {
             base.OnStartup(evt);
         }
 
+        private async void Application_Exit(object sender, ExitEventArgs evt) {
+            await Pdf.Cleanup();
+        }
+
         public static void SetBranch(Branch b) {
             SetBranch((b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
         }
diff --git a/Elwig/Helpers/Printing/Pdf.cs b/Elwig/Helpers/Printing/Pdf.cs
index f56a967..1992493 100644
--- a/Elwig/Helpers/Printing/Pdf.cs
+++ b/Elwig/Helpers/Printing/Pdf.cs
@@ -7,6 +7,8 @@ using System.Collections.Generic;
 using System.Windows;
 using System.Text.RegularExpressions;
 using System.Linq;
+using System.Net.Sockets;
+using System.Text;
 
 namespace Elwig.Helpers.Printing {
     public static class Pdf {
@@ -14,45 +16,55 @@ namespace Elwig.Helpers.Printing {
         private static readonly string PdfToPrinter = new string[] { App.ExePath }
             .Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
             .Select(x => Path.Combine(x, "PDFtoPrinter.exe"))
-            .Where(x => File.Exists(x))
+            .Where(File.Exists)
             .FirstOrDefault() ?? throw new FileNotFoundException("PDFtoPrinter executable not found");
         private static readonly string WinziPrint = new string[] { App.ExePath }
             .Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
             .Select(x => Path.Combine(x, "WinziPrint.exe"))
-            .Where(x => File.Exists(x))
+            .Where(File.Exists)
             .FirstOrDefault() ?? throw new FileNotFoundException("WiniPrint executable not found");
         private static Process? WinziPrintProc;
         public static bool IsReady => WinziPrintProc != null;
 
-        public static async Task Init(Action? evtHandler = null) {
+        public static Task Init(Action? evtHandler = null) {
+            // NOTE: If the WinziPrint daemon is already running this will succeed, but the process will fail.
+            //       Should be no problem, as long as the daemon is not closed
             var p = new Process() { StartInfo = new() {
                 FileName = WinziPrint,
                 CreateNoWindow = true,
                 UseShellExecute = false,
-                RedirectStandardInput = true,
-                RedirectStandardOutput = true
             } };
-            p.StartInfo.ArgumentList.Add("-p");
-            p.StartInfo.ArgumentList.Add("-e");
-            p.StartInfo.ArgumentList.Add("utf-8");
+            p.StartInfo.ArgumentList.Add("-D");
             p.StartInfo.ArgumentList.Add("-d");
             p.StartInfo.ArgumentList.Add(App.TempPath);
-            p.StartInfo.ArgumentList.Add("-");
             p.Start();
             WinziPrintProc = p;
             evtHandler?.Invoke();
+            return Task.CompletedTask;
+        }
+
+        public static Task Cleanup() {
+            WinziPrintProc?.Kill(true);
+            WinziPrintProc?.Close();
+            return Task.CompletedTask;
         }
 
         public static async Task<IEnumerable<int>> Convert(string htmlPath, string pdfPath, bool doubleSided = false, IProgress<double>? progress = null) {
-            return await Convert(new string[] { htmlPath }, pdfPath, doubleSided, progress);
+            return await Convert([htmlPath], pdfPath, doubleSided, progress);
         }
 
         public static async Task<IEnumerable<int>> Convert(IEnumerable<string> htmlPath, string pdfPath, bool doubleSided = false, IProgress<double>? progress = null) {
             if (WinziPrintProc == null) throw new InvalidOperationException("The WinziPrint process has not been initialized yet");
             progress?.Report(0.0);
-            await WinziPrintProc.StandardInput.WriteLineAsync((doubleSided ? "-2;" : "") + $"{string.Join(';', htmlPath)};{pdfPath}");
+            using var client = new TcpClient("127.0.0.1", 30983);
+            using var stream = client.GetStream();
+            await stream.WriteAsync(Encoding.UTF8.GetBytes(
+                "-e utf-8;-p;" + (doubleSided ? "-2;" : "") +
+                $"{string.Join(';', htmlPath)};{pdfPath}" +
+                "\r\n"));
+            using var reader = new StreamReader(stream);
             while (true) {
-                var line = await WinziPrintProc.StandardOutput.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
+                var line = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
                 if (line.StartsWith("error:")) {
                     throw new IOException($"WinziPrint: {line[6..].Trim()}");
                 } else if (line.StartsWith("progress:")) {
diff --git a/Tests/DocumentTests/Setup.cs b/Tests/DocumentTests/Setup.cs
index 0720460..44172fd 100644
--- a/Tests/DocumentTests/Setup.cs
+++ b/Tests/DocumentTests/Setup.cs
@@ -30,8 +30,8 @@ namespace Tests.DocumentTests {
         }
 
         [OneTimeTearDown]
-        public void TeardownPrinting() {
-            // no teardown needed yet
+        public async Task TeardownPrinting() {
+            await Pdf.Cleanup();
         }
     }
 }