foxygit / digitalskrivare_merge Log in
commits tags

/src/Remiss.Core/Printing/SumatraPdfPrinter.cs · 2.75 KB

raw
using System.Diagnostics;
using Remiss.Core.Abstractions;

namespace Remiss.Core.Printing;

/// <summary>
/// Tyst utskrift via buntad SumatraPDF. Retry-logik eftersom spooler/skrivare
/// tidvis är otillgänglig.
/// </summary>
public sealed class SumatraPdfPrinter : IPrinter
{
    private readonly string _exePath;
    private readonly int _retries;
    private readonly int _retryDelayMs;

    public SumatraPdfPrinter(string exePath, int retries = 3, int retryDelayMs = 2000)
    {
        _exePath = exePath;
        _retries = Math.Max(1, retries);
        _retryDelayMs = Math.Max(0, retryDelayMs);
    }

    /// <param name="printerName">
    /// Namngiven skrivare, eller tom/null för Windows standardskrivare.
    /// </param>
    public void Print(string pdfPath, string? printerName)
    {
        if (!File.Exists(_exePath))
            throw new FileNotFoundException(
                $"SumatraPDF hittades inte: {_exePath}. Lägg binären i tools/ (se tools/README.md).", _exePath);

        Exception? last = null;
        for (var attempt = 1; attempt <= _retries; attempt++)
        {
            try
            {
                RunOnce(pdfPath, printerName);
                return;
            }
            catch (Exception ex)
            {
                last = ex;
                if (attempt < _retries)
                    Thread.Sleep(_retryDelayMs);
            }
        }

        throw new InvalidOperationException($"Utskrift misslyckades efter {_retries} försök.", last);
    }

    private void RunOnce(string pdfPath, string? printerName)
    {
        var psi = new ProcessStartInfo
        {
            FileName = _exePath,
            UseShellExecute = false,
            CreateNoWindow = true,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
        };

        if (string.IsNullOrWhiteSpace(printerName))
        {
            psi.ArgumentList.Add("-print-to-default");
        }
        else
        {
            psi.ArgumentList.Add("-print-to");
            psi.ArgumentList.Add(printerName);
        }

        psi.ArgumentList.Add("-silent");
        psi.ArgumentList.Add(pdfPath);

        using var proc = Process.Start(psi)
                         ?? throw new InvalidOperationException("Kunde inte starta SumatraPDF.");

        if (!proc.WaitForExit(60_000))
        {
            try { proc.Kill(entireProcessTree: true); } catch { /* ignore */ }
            throw new TimeoutException("SumatraPDF svarade inte inom 60 sekunder.");
        }

        if (proc.ExitCode != 0)
        {
            var err = proc.StandardError.ReadToEnd().Trim();
            throw new InvalidOperationException(
                $"SumatraPDF avslutades med kod {proc.ExitCode}." + (err.Length > 0 ? $" {err}" : ""));
        }
    }
}