using CloudPrint.Client.Models; using System.Diagnostics; using System.IO; namespace CloudPrint.Client.Services.Printing; public sealed class PdfPrintJobExecutor : IPrintJobExecutor { private readonly IWindowsPrintQueueService _queueService; public PdfPrintJobExecutor(IWindowsPrintQueueService queueService) { _queueService = queueService; } public bool CanHandle(PrintJob job) { return string.Equals(Path.GetExtension(job.FilePath), ".pdf", StringComparison.OrdinalIgnoreCase); } public async Task ExecuteAsync(PrintJob job, IProgress? progress, CancellationToken cancellationToken = default) { PrintFileGuards.EnsurePrintableFile(job); progress?.Report(10); var sumatraPath = FindSumatraPdfExecutable(); if (!string.IsNullOrWhiteSpace(sumatraPath)) { await PrintWithSumatraAsync(sumatraPath, job, cancellationToken).ConfigureAwait(true); } else { await PrintWithShellAsync(job, cancellationToken).ConfigureAwait(true); } CaptureWindowsJob(job); progress?.Report(100); } private void CaptureWindowsJob(PrintJob job) { var documentName = Path.GetFileName(job.FilePath); var queueJob = _queueService.FindLatestJob(job.PrinterName, documentName); if (queueJob is null) { return; } job.WindowsJobId = queueJob.JobId; job.WindowsJobStatus = queueJob.Status; } private static async Task PrintWithSumatraAsync(string sumatraPath, PrintJob job, CancellationToken cancellationToken) { var startInfo = new ProcessStartInfo { FileName = sumatraPath, Arguments = $"-print-to \"{job.PrinterName}\" -silent -exit-when-done \"{job.FilePath}\"", CreateNoWindow = true, UseShellExecute = false }; using var process = Process.Start(startInfo) ?? throw new InvalidOperationException("启动 SumatraPDF 失败"); await process.WaitForExitAsync(cancellationToken).ConfigureAwait(true); if (process.ExitCode != 0) { throw new InvalidOperationException($"SumatraPDF 打印失败,退出码:{process.ExitCode}"); } } private static async Task PrintWithShellAsync(PrintJob job, CancellationToken cancellationToken) { var startInfo = new ProcessStartInfo { FileName = job.FilePath, Verb = "printto", Arguments = $"\"{job.PrinterName}\"", UseShellExecute = true, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden }; using var process = Process.Start(startInfo) ?? throw new InvalidOperationException("无法调用系统默认 PDF 打印程序,请安装 SumatraPDF 或配置 PDF 默认打印程序"); await process.WaitForExitAsync(cancellationToken).ConfigureAwait(true); } private static string? FindSumatraPdfExecutable() { var candidates = new[] { Environment.GetEnvironmentVariable("SUMATRA_PDF_PATH"), @"C:\Program Files\SumatraPDF\SumatraPDF.exe", @"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe" }; return candidates.FirstOrDefault(path => !string.IsNullOrWhiteSpace(path) && File.Exists(path)); } }