using System; using System.Drawing.Printing; using System.IO; using System.Windows; using System.Windows.Controls; using Telerik.Windows.Controls; using Telerik.Windows.Documents.Fixed.FormatProviders.Pdf; using Telerik.Windows.Documents.Fixed.Print; using XP.Common.Logging.Interfaces; using XP.Common.PdfViewer.Exceptions; using XP.Common.PdfViewer.Interfaces; namespace XP.Common.PdfViewer.Implementations { /// /// PDF 打印服务实现 | PDF print service implementation /// 基于 Telerik RadPdfViewer.Print() 实现 | Based on Telerik RadPdfViewer.Print() /// public class PdfPrintService : IPdfPrintService { private readonly ILoggerService _logger; public PdfPrintService(ILoggerService logger) { _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger)); } /// /// 使用指定打印机打印 PDF 文件 | Print PDF file with specified printer /// public void Print(string filePath, string printerName, int? pageFrom = null, int? pageTo = null, int copies = 1) { // 1. 验证文件路径存在 | Validate file path exists ValidateFilePath(filePath); // 2. 验证打印机名称有效 | Validate printer name is valid ValidatePrinterName(printerName); RadPdfViewer? pdfViewer = null; try { // 3. 创建隐藏的 RadPdfViewer 并加载 PDF | Create hidden RadPdfViewer and load PDF pdfViewer = CreatePdfViewerWithDocument(filePath); // 4. 创建 PrintDialog 并配置打印机名称 | Create PrintDialog and configure printer name var printDialog = new PrintDialog(); printDialog.PrintQueue = new System.Printing.PrintQueue( new System.Printing.LocalPrintServer(), printerName); // 5. 配置页面范围 | Configure page range if (pageFrom.HasValue || pageTo.HasValue) { printDialog.PageRangeSelection = PageRangeSelection.UserPages; printDialog.PageRange = new PageRange( pageFrom ?? 1, pageTo ?? int.MaxValue); } // 6. 配置打印份数 | Configure copies if (copies > 1) { printDialog.PrintTicket.CopyCount = copies; } // 7. 配置 Telerik PrintSettings | Configure Telerik PrintSettings var printSettings = new Telerik.Windows.Documents.Fixed.Print.PrintSettings { DocumentName = Path.GetFileName(filePath), PageMargins = new Thickness(0), UseDefaultPrinter = false }; // 8. 调用 RadPdfViewer.Print() 静默打印 | Call RadPdfViewer.Print() for silent printing pdfViewer.Print(printDialog, printSettings); // 9. 记录 Info 日志 | Log info var pageRange = FormatPageRange(pageFrom, pageTo); _logger.Info("打印任务已提交 | Print job submitted: {FileName} → {PrinterName}, 页面范围 | Pages: {PageRange}, 份数 | Copies: {Copies}", Path.GetFileName(filePath), printerName, pageRange, copies); } catch (Exception ex) when (ex is not FileNotFoundException and not PrinterNotFoundException and not PdfLoadException) { _logger.Error(ex, "打印失败 | Print failed: {FileName} → {PrinterName}", Path.GetFileName(filePath), printerName); throw new PrintException($"打印失败 | Print failed: {Path.GetFileName(filePath)}", ex); } finally { // 10. 释放资源 | Release resources DisposePdfViewer(pdfViewer); } } /// /// 打开打印设置对话框并打印 | Open print settings dialog and print /// public bool PrintWithDialog(string filePath) { // 1. 验证文件路径 | Validate file path ValidateFilePath(filePath); RadPdfViewer? pdfViewer = null; try { // 2. 加载 PDF 到隐藏的 RadPdfViewer | Load PDF into hidden RadPdfViewer pdfViewer = CreatePdfViewerWithDocument(filePath); // 3. 配置 PrintSettings | Configure PrintSettings var printSettings = new Telerik.Windows.Documents.Fixed.Print.PrintSettings { DocumentName = Path.GetFileName(filePath), PageMargins = new Thickness(0), UseDefaultPrinter = false }; // 4. 调用 RadPdfViewer.Print() 弹出 PrintDialog | Call RadPdfViewer.Print() to show PrintDialog pdfViewer.Print(printSettings); _logger.Info("用户通过对话框打印 | User printed via dialog: {FileName}", Path.GetFileName(filePath)); return true; } catch (Exception ex) when (ex is not FileNotFoundException and not PdfLoadException) { _logger.Error(ex, "打印失败 | Print failed: {FileName}", Path.GetFileName(filePath)); throw new PrintException($"打印失败 | Print failed: {Path.GetFileName(filePath)}", ex); } finally { // 5. 释放资源 | Release resources DisposePdfViewer(pdfViewer); } } /// /// 打开打印预览对话框 | Open print preview dialog /// public void PrintPreview(string filePath) { // 1. 验证文件路径 | Validate file path ValidateFilePath(filePath); RadPdfViewer? pdfViewer = null; try { // 2. 加载 PDF 到隐藏的 RadPdfViewer | Load PDF into hidden RadPdfViewer pdfViewer = CreatePdfViewerWithDocument(filePath); // 3. 通过 PrintDialog 显示预览 | Show preview via PrintDialog var printDialog = new PrintDialog(); var printSettings = new Telerik.Windows.Documents.Fixed.Print.PrintSettings { DocumentName = Path.GetFileName(filePath), PageMargins = new Thickness(0), UseDefaultPrinter = false }; // 显示打印对话框(含预览功能)| Show print dialog (with preview capability) pdfViewer.Print(printDialog, printSettings); _logger.Info("打印预览已显示 | Print preview shown: {FileName}", Path.GetFileName(filePath)); } catch (Exception ex) when (ex is not FileNotFoundException and not PdfLoadException) { _logger.Error(ex, "打印预览失败 | Print preview failed: {FileName}", Path.GetFileName(filePath)); throw new PrintException($"打印预览失败 | Print preview failed: {Path.GetFileName(filePath)}", ex); } finally { // 4. 释放资源 | Release resources DisposePdfViewer(pdfViewer); } } #region 私有辅助方法 | Private Helper Methods /// /// 验证文件路径是否存在 | Validate file path exists /// private void ValidateFilePath(string filePath) { if (string.IsNullOrWhiteSpace(filePath)) { throw new ArgumentNullException(nameof(filePath), "文件路径不能为空 | File path cannot be null or empty"); } if (!File.Exists(filePath)) { _logger.Error(new FileNotFoundException(filePath), "文件不存在 | File not found: {FilePath}", filePath); throw new FileNotFoundException($"文件不存在 | File not found: {filePath}", filePath); } } /// /// 验证打印机名称是否有效 | Validate printer name is valid /// private void ValidatePrinterName(string printerName) { if (string.IsNullOrWhiteSpace(printerName)) { throw new ArgumentNullException(nameof(printerName), "打印机名称不能为空 | Printer name cannot be null or empty"); } // 通过 PrinterSettings.InstalledPrinters 查询系统已安装的打印机 | Query installed printers bool found = false; foreach (string installedPrinter in PrinterSettings.InstalledPrinters) { if (string.Equals(installedPrinter, printerName, StringComparison.OrdinalIgnoreCase)) { found = true; break; } } if (!found) { _logger.Error(new PrinterNotFoundException(printerName), "打印机未找到 | Printer not found: {PrinterName}", printerName); throw new PrinterNotFoundException(printerName); } } /// /// 创建隐藏的 RadPdfViewer 并加载 PDF 文档 | Create hidden RadPdfViewer and load PDF document /// private RadPdfViewer CreatePdfViewerWithDocument(string filePath) { var pdfViewer = new RadPdfViewer(); try { var provider = new PdfFormatProvider(); using var fileStream = File.OpenRead(filePath); pdfViewer.Document = provider.Import(fileStream); return pdfViewer; } catch (Exception ex) when (ex is not FileNotFoundException) { // 释放已创建的 viewer | Dispose created viewer DisposePdfViewer(pdfViewer); _logger.Error(ex, "PDF 文件加载失败 | PDF file load failed: {FilePath}", filePath); throw new PdfLoadException("PDF 文件加载失败 | PDF file load failed", filePath, ex); } } /// /// 释放 RadPdfViewer 资源 | Dispose RadPdfViewer resources /// private static void DisposePdfViewer(RadPdfViewer? pdfViewer) { if (pdfViewer != null) { pdfViewer.Document = null; } } /// /// 格式化页面范围字符串 | Format page range string /// private static string FormatPageRange(int? pageFrom, int? pageTo) { if (!pageFrom.HasValue && !pageTo.HasValue) { return "全部 | All"; } var from = pageFrom?.ToString() ?? "1"; var to = pageTo?.ToString() ?? "末页 | Last"; return $"{from}-{to}"; } #endregion } }