using System; using System.IO; using System.Printing; namespace Elwig.Helpers.Printing { public class PdfPrinter { public string PrinterName { get; init; } public PdfPrinter(string? printerName = null) { PrinterName = printerName ?? GetDefaultPrinterName() ?? throw new InvalidOperationException("No default Windows printer is configured."); } public void Print(string pdfPath, int copies = 1, bool doublePaged = false) { if (!File.Exists(pdfPath)) throw new FileNotFoundException("PDF file not found.", pdfPath); IntPtr document = PdfiumNative.FPDF_LoadDocument(pdfPath, null); if (document == IntPtr.Zero) throw new InvalidOperationException("PDFium could not open the PDF."); try { int pageCount = PdfiumNative.FPDF_GetPageCount(document); if (pageCount <= 0) return; using var printer = new Win32Printer.Printer(PrinterName); printer.TrySetDuplex(doublePaged ? Duplexing.TwoSidedLongEdge : Duplexing.OneSided); for (int i = 0; i < copies; i++) { printer.StartDocument(Path.GetFileName(pdfPath)); bool documentStarted = true; try { for (int j = 0; j < pageCount; j++) { PrintPage(document, printer, j); } printer.EndDocument(); documentStarted = false; } finally { if (documentStarted) printer.AbortDocument(); } } } finally { PdfiumNative.FPDF_CloseDocument(document); } } private static void PrintPage(IntPtr document, Win32Printer.Printer printer, int pageIndex) { if (!PdfiumNative.FPDF_GetPageSizeByIndex(document, pageIndex, out double pageWidthPt, out double pageHeightPt)) { throw new InvalidOperationException($"Unable to obtain PDF page {pageIndex} size."); } IntPtr page = PdfiumNative.FPDF_LoadPage(document, pageIndex); if (page == IntPtr.Zero) throw new InvalidOperationException($"Unable to load PDF page {pageIndex}."); try { // PDF coordinates are points (1/72 inch). // // HORZRES/VERTRES are the printable area in // device pixels for the current printer DC. int widthPx = printer.PixelWidth; int heightPx = printer.PixelHeight; // Preserve aspect ratio. double scaleX = widthPx / pageWidthPt; double scaleY = heightPx / pageHeightPt; double scale = Math.Min(scaleX, scaleY); int renderWidth = Math.Max(1, (int)Math.Round(pageWidthPt * scale)); int renderHeight = Math.Max(1, (int)Math.Round(pageHeightPt * scale)); int offsetX = (widthPx - renderWidth) / 2; int offsetY = (heightPx - renderHeight) / 2; printer.StartPage(); try { PdfiumNative.FPDF_RenderPage(printer.Hdc, page, offsetX, offsetY, renderWidth, renderHeight, 0, PdfiumNative.FPDF_PRINTING | PdfiumNative.FPDF_ANNOT); } finally { printer.EndPage(); } } finally { PdfiumNative.FPDF_ClosePage(page); } } private static string? GetDefaultPrinterName() { using var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\Windows"); string? printer = key?.GetValue("Device") as string; if (string.IsNullOrWhiteSpace(printer)) return null; // Device has the form: // Printer Name,winspool,PORT int comma = printer.IndexOf(','); return comma >= 0 ? printer[..comma] : printer; } } }