using Elwig.Windows; using PdfiumViewer; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing.Printing; using System.IO; using System.Linq; using System.Net.Sockets; using System.Reflection; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace Elwig.Helpers.Printing { public static class Pdf { private static readonly string WinziPrint = (Assembly.GetEntryAssembly()?.Location.Contains(@"\bin\") ?? false) ? Path.Combine(Assembly.GetEntryAssembly()!.Location.Split(@"\bin\")[0], "../Installer/Files/WinziPrint.exe") : new string[] { App.InstallPath } .Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? []) .Select(x => Path.Combine(x, "WinziPrint.exe")) .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) { // 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, RedirectStandardOutput = true, } }; p.StartInfo.ArgumentList.Add("-D"); p.StartInfo.ArgumentList.Add("-d"); p.StartInfo.ArgumentList.Add(App.TempPath); p.Start(); await p.StandardOutput.ReadLineAsync(); WinziPrintProc = p; evtHandler?.Invoke(); } public static Task Cleanup() { WinziPrintProc?.Kill(true); WinziPrintProc?.Close(); return Task.CompletedTask; } public static async Task<(int Pages, IEnumerable PerDoc)> Convert(string htmlPath, string pdfPath, bool doublePaged = false, CancellationToken? cancelToken = null, IProgress? progress = null) { return await Convert([htmlPath], pdfPath, doublePaged, cancelToken, progress); } public static async Task<(int Pages, IEnumerable PerDoc)> Convert(IEnumerable htmlPath, string pdfPath, bool doublePaged = false, CancellationToken? cancelToken = null, IProgress? progress = null) { if (WinziPrintProc == null) throw new InvalidOperationException("The WinziPrint process has not been initialized yet"); progress?.Report(0.0); using var client = new TcpClient("127.0.0.1", 30983); using var stream = client.GetStream(); string cnxId; using var reader = new StreamReader(stream); var first = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint"); if (first.StartsWith("id:")) { cnxId = first[3..].Trim(); } else { throw new IOException("Invalid response from WinziPrint"); } await stream.WriteAsync(Utils.UTF8.GetBytes( "-e utf-8;-p;" + (doublePaged ? "-2;" : "") + $"{string.Join(';', htmlPath)};{pdfPath}" + "\r\n")); bool cancelled = false; while (true) { if (!cancelled && (cancelToken?.IsCancellationRequested ?? false)) { try { using var cancelClient = new TcpClient("127.0.0.1", 30983); using var cancelStream = cancelClient.GetStream(); using var cancelReader = new StreamReader(cancelStream); await cancelReader.ReadLineAsync(); await cancelStream.WriteAsync(Utils.UTF8.GetBytes($"cancel;{cnxId}\r\n")); } catch { } cancelled = true; } var line = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint"); if (line.StartsWith("error:")) { var msg = line[6..].Trim(); if (msg == "aborted") throw new OperationCanceledException("Dokumentenerzeugung abgebrochen!"); throw new IOException($"WinziPrint: {msg}"); } else if (line.StartsWith("progress:")) { var parts = line[9..].Trim().Split('/').Select(int.Parse).ToArray(); progress?.Report(100.0 * parts[0] / parts[1]); } else if (line.StartsWith("success:")) { var m = Regex.Match(line, @"([0-9]+) pages \(([0-9, ]+)\)"); return (int.Parse(m.Groups[1].Value), m.Groups[2].Value.Split(", ").Select(int.Parse).ToList()); } } } 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 async Task Print(string path, int copies = 1, bool doublePaged = false) { await Print(path, new() { Copies = (short)copies, Collate = true, Duplex = doublePaged ? Duplex.Vertical : Duplex.Simplex, }); } public static Task Print(string path, PrinterSettings settings) { try { using var doc = PdfDocument.Load(path); using var printDoc = doc.CreatePrintDocument(PdfPrintMode.CutMargin); printDoc.PrinterSettings = settings; printDoc.Print(); } catch (Exception e) { MessageBox.Show("Beim Drucken ist ein Fehler aufgetreten:\n\n" + e.Message, "Fehler beim Drucken", MessageBoxButton.OK, MessageBoxImage.Error); } return Task.CompletedTask; } } }