using Microsoft.Extensions.Options; using PrintService.Models; using System.Drawing; using System.Drawing.Printing; namespace PrintService.Services; /// /// Handles printing to Windows print queue with tray control /// public class PrinterService { private readonly AppSettings _settings; private readonly ILogger _logger; public PrinterService(IOptions settings, ILogger logger) { _settings = settings.Value; _logger = logger; } /// /// Print document to specified printer with tray sequence /// public void Print(string content, DocumentConfig config, string originalFileName) { if (config.TraySequence.Length == 0) { throw new InvalidOperationException("No tray sequence defined for document type"); } var lines = content.Split(new[] { '\r', '\n' }, StringSplitOptions.None); var linesPerPage = CalculateLinesPerPage(config); var currentPageIndex = 0; var printDoc = new PrintDocument { PrinterSettings = { PrinterName = config.PrinterName } }; // Handle PDF output if using Microsoft Print to PDF if (config.PrinterName.Equals("Microsoft Print to PDF", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(config.OutputPath)) { Directory.CreateDirectory(config.OutputPath); var pdfFileName = Path.GetFileNameWithoutExtension(originalFileName) + ".pdf"; var pdfPath = Path.Combine(config.OutputPath, pdfFileName); printDoc.PrinterSettings.PrintToFile = true; printDoc.PrinterSettings.PrintFileName = pdfPath; _logger.LogInformation("PDF will be saved to: {Path}", pdfPath); } // Verify printer exists if (!PrinterSettings.InstalledPrinters.Cast().Contains(config.PrinterName)) { throw new InvalidOperationException($"Printer '{config.PrinterName}' not found"); } printDoc.PrintPage += (sender, e) => { if (e.Graphics == null || e.PageSettings == null) return; // Select tray for current page var trayIndex = config.TraySequence[currentPageIndex % config.TraySequence.Length]; try { // Map logical tray number to PaperSource var paperSource = GetPaperSource(printDoc.PrinterSettings, trayIndex); if (paperSource != null) { e.PageSettings.PaperSource = paperSource; if (_settings.VerboseLogging) { _logger.LogDebug("Page {Page}: Using tray {Tray} ({Source})", currentPageIndex + 1, trayIndex, paperSource.SourceName); } } } catch (Exception ex) { _logger.LogWarning(ex, "Failed to set tray {Tray}, using default", trayIndex); } // Render page content RenderPage(e.Graphics, lines, currentPageIndex * linesPerPage, linesPerPage, config); currentPageIndex++; e.HasMorePages = (currentPageIndex < config.TraySequence.Length); }; _logger.LogInformation("Printing to {Printer} with {Pages} pages", config.PrinterName, config.TraySequence.Length); printDoc.Print(); } /// /// Render page content using Graphics API /// private void RenderPage(Graphics graphics, string[] lines, int startLine, int linesPerPage, DocumentConfig config) { var font = new Font(config.FontName, config.FontSize); var brush = Brushes.Black; var lineHeight = font.GetHeight(graphics); var x = (float)config.HorizontalOffset; var y = (float)config.VerticalOffset; var endLine = Math.Min(startLine + linesPerPage, lines.Length); for (int i = startLine; i < endLine; i++) { if (i < lines.Length) { graphics.DrawString(lines[i], font, brush, x, y); y += lineHeight; } } } /// /// Calculate lines per page based on font and page size /// private int CalculateLinesPerPage(DocumentConfig config) { // Estimate: standard letter size is 11 inches, at 10pt font ~66 lines // This is simplified; in production you'd calculate based on actual page dimensions var estimatedLineHeight = config.FontSize * 1.2f; // points var pageHeightInPoints = 11 * 72; // 11 inches * 72 points per inch return (int)Math.Floor(pageHeightInPoints / estimatedLineHeight); } /// /// Get PaperSource by logical tray number /// private PaperSource? GetPaperSource(PrinterSettings printerSettings, int trayNumber) { // Try to find by RawKind (tray number) foreach (PaperSource source in printerSettings.PaperSources) { // Some printers use RawKind directly as tray number if (source.RawKind == trayNumber || source.RawKind == (trayNumber + 256)) // Some drivers offset by 256 { return source; } } // Fallback: use by index if available if (trayNumber > 0 && trayNumber <= printerSettings.PaperSources.Count) { return printerSettings.PaperSources[trayNumber - 1]; } _logger.LogWarning("Could not map tray {Tray} to PaperSource", trayNumber); return null; } /// /// List available paper sources for a printer (for debugging/configuration) /// public void ListPaperSources(string printerName) { var printDoc = new PrintDocument { PrinterSettings = { PrinterName = printerName } }; _logger.LogInformation("Available paper sources for {Printer}:", printerName); foreach (PaperSource source in printDoc.PrinterSettings.PaperSources) { _logger.LogInformation(" - {Name} (RawKind: {Kind})", source.SourceName, source.RawKind); } } }