Files
CGW-Printing/PrintService/Services/PrinterService.cs
T

200 lines
7.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.Extensions.Options;
using PrintService.Models;
using System.Drawing;
using System.Drawing.Printing;
namespace PrintService.Services;
/// <summary>
/// Handles printing to Windows print queue with tray control
/// </summary>
public class PrinterService
{
private readonly AppSettings _settings;
private readonly ILogger<PrinterService> _logger;
public PrinterService(IOptions<AppSettings> settings, ILogger<PrinterService> logger)
{
_settings = settings.Value;
_logger = logger;
}
/// <summary>
/// Print document using the new DocumentTypeConfig (printer-config.json).
/// Each PageConfig entry defines one output copy per original content page,
/// so a 4-page document with 4 PageConfig entries produces 16 output pages.
/// </summary>
public void Print(string content, DocumentTypeConfig config, string originalFileName)
{
if (config.Pages.Count == 0)
{
throw new InvalidOperationException("No pages/trays defined for document type");
}
var lines = content.Split(new[] { '\r', '\n' }, StringSplitOptions.None);
var linesPerPage = CalculateLinesPerPage(config.FontSize);
var totalOriginalPages = Math.Max(1, (int)Math.Ceiling(lines.Length / (double)linesPerPage));
var originalPageIndex = 0;
var copyIndex = 0; // index into config.Pages (one entry per tray copy)
// All pages within one document type share the same printer in typical use,
// but PageConfig supports per-page overrides — use the first page's printer for
// the PrintDocument; tray (PaperSource) is set per page in the event handler.
var primaryPrinterName = config.Pages[0].PrinterName;
var printDoc = new PrintDocument
{
PrinterSettings = { PrinterName = primaryPrinterName }
};
// Handle PDF output if using Microsoft Print to PDF
if (primaryPrinterName.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 primary printer exists
if (!PrinterSettings.InstalledPrinters.Cast<string>().Contains(primaryPrinterName))
{
throw new InvalidOperationException($"Printer '{primaryPrinterName}' not found");
}
printDoc.PrintPage += (sender, e) =>
{
if (e.Graphics == null || e.PageSettings == null)
return;
var pageConfig = config.Pages[copyIndex];
try
{
var paperSource = GetPaperSource(printDoc.PrinterSettings, pageConfig.TrayNumber);
if (paperSource != null)
{
e.PageSettings.PaperSource = paperSource;
if (_settings.VerboseLogging)
{
_logger.LogDebug(
"Output page {OutputPage}: original page {OriginalPage}, tray {Tray} ({Label}) ({Source})",
(originalPageIndex * config.Pages.Count) + copyIndex + 1,
originalPageIndex + 1,
pageConfig.TrayNumber,
pageConfig.TrayLabel ?? "?",
paperSource.SourceName);
}
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to set tray {Tray}, using default", pageConfig.TrayNumber);
}
// Render the same original page for every copy before moving to next original page.
RenderPage(e.Graphics, lines, originalPageIndex * linesPerPage, linesPerPage,
config.FontName, config.FontSize, config.HorizontalOffset, config.VerticalOffset);
copyIndex++;
if (copyIndex >= config.Pages.Count)
{
copyIndex = 0;
originalPageIndex++;
}
e.HasMorePages = originalPageIndex < totalOriginalPages;
};
_logger.LogInformation(
"Printing to {Printer}: {OriginalPages} original page(s) × {Copies} copies = {Total} output pages",
primaryPrinterName,
totalOriginalPages,
config.Pages.Count,
totalOriginalPages * config.Pages.Count);
printDoc.Print();
}
/// <summary>
/// Render page content using Graphics API
/// </summary>
private void RenderPage(Graphics graphics, string[] lines, int startLine, int linesPerPage,
string fontName, float fontSize, int horizontalOffset, int verticalOffset)
{
var font = new Font(fontName, fontSize);
var brush = Brushes.Black;
var lineHeight = font.GetHeight(graphics);
var x = (float)horizontalOffset;
var y = (float)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;
}
}
}
/// <summary>
/// Calculate lines per page based on font size
/// </summary>
private int CalculateLinesPerPage(float fontSize)
{
// Estimate: standard letter size is 11 inches, at 10pt font ~66 lines
var estimatedLineHeight = fontSize * 1.2f; // points
var pageHeightInPoints = 11 * 72; // 11 inches * 72 points per inch
return (int)Math.Floor(pageHeightInPoints / estimatedLineHeight);
}
/// <summary>
/// Get PaperSource by logical tray number
/// </summary>
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;
}
/// <summary>
/// List available paper sources for a printer (for debugging/configuration)
/// </summary>
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);
}
}
}