Files
XplorePlane/XP.Common/PdfViewer/Implementations/PdfPrintService.cs
T

277 lines
11 KiB
C#

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
{
/// <summary>
/// PDF 打印服务实现 | PDF print service implementation
/// 基于 Telerik RadPdfViewer.Print() 实现 | Based on Telerik RadPdfViewer.Print()
/// </summary>
public class PdfPrintService : IPdfPrintService
{
private readonly ILoggerService _logger;
public PdfPrintService(ILoggerService logger)
{
_logger = logger?.ForModule<PdfPrintService>()
?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 使用指定打印机打印 PDF 文件 | Print PDF file with specified printer
/// </summary>
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);
}
}
/// <summary>
/// 打开打印设置对话框并打印 | Open print settings dialog and print
/// </summary>
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);
}
}
/// <summary>
/// 打开打印预览对话框 | Open print preview dialog
/// </summary>
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
/// <summary>
/// 验证文件路径是否存在 | Validate file path exists
/// </summary>
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);
}
}
/// <summary>
/// 验证打印机名称是否有效 | Validate printer name is valid
/// </summary>
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);
}
}
/// <summary>
/// 创建隐藏的 RadPdfViewer 并加载 PDF 文档 | Create hidden RadPdfViewer and load PDF document
/// </summary>
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);
}
}
/// <summary>
/// 释放 RadPdfViewer 资源 | Dispose RadPdfViewer resources
/// </summary>
private static void DisposePdfViewer(RadPdfViewer? pdfViewer)
{
if (pdfViewer != null)
{
pdfViewer.Document = null;
}
}
/// <summary>
/// 格式化页面范围字符串 | Format page range string
/// </summary>
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
}
}