Built test page for testing tray output for debugging in main app.
This commit is contained in:
+3
-3
@@ -14,9 +14,6 @@ Global
|
|||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{6FE05D84-E68E-4D43-AD80-7A5D8A59D975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{6FE05D84-E68E-4D43-AD80-7A5D8A59D975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{6FE05D84-E68E-4D43-AD80-7A5D8A59D975}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{6FE05D84-E68E-4D43-AD80-7A5D8A59D975}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
@@ -31,4 +28,7 @@ Global
|
|||||||
{42CF28FF-90E4-4912-B427-EA5F675F641E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{42CF28FF-90E4-4912-B427-EA5F675F641E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{42CF28FF-90E4-4912-B427-EA5F675F641E}.Release|Any CPU.Build.0 = Release|Any CPU
|
{42CF28FF-90E4-4912-B427-EA5F675F641E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -30,6 +30,39 @@ public class DocumentTypeConfig
|
|||||||
|
|
||||||
// Text transformation rules
|
// Text transformation rules
|
||||||
public List<TextTransform> Transformations { get; set; } = new();
|
public List<TextTransform> Transformations { get; set; } = new();
|
||||||
|
|
||||||
|
// Rust transformation properties
|
||||||
|
/// <summary>
|
||||||
|
/// Left margin/indent for all lines (in 1/100 inch units)
|
||||||
|
/// </summary>
|
||||||
|
public int LinePadding { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove both "Order #: " label and the order number entirely
|
||||||
|
/// </summary>
|
||||||
|
public bool RemoveOrderNumber { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove "Order #: " label but keep the order number (and make it bold)
|
||||||
|
/// </summary>
|
||||||
|
public bool RemoveOrderNumberLabel { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add spacing before the order number (shifts it right)
|
||||||
|
/// </summary>
|
||||||
|
public string IndentOrderNumber { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Vertical position adjustments per line (line number → adjustment in 1/100 inch)
|
||||||
|
/// Negative values move up, positive values move down
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<int, int> RowShift { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Horizontal character trimming per line (line number → trim configuration)
|
||||||
|
/// Removes characters from Start to End indices
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<int, RowTrimConfig> RowTrim { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -42,3 +75,19 @@ public class PageConfig
|
|||||||
public int TrayNumber { get; set; }
|
public int TrayNumber { get; set; }
|
||||||
public string? TrayLabel { get; set; }
|
public string? TrayLabel { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration for row trimming (horizontal character removal)
|
||||||
|
/// </summary>
|
||||||
|
public class RowTrimConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Start index of characters to remove (0-based)
|
||||||
|
/// </summary>
|
||||||
|
public int Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End index of characters to remove (exclusive)
|
||||||
|
/// </summary>
|
||||||
|
public int End { get; set; }
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ IHost host = Host.CreateDefaultBuilder(args)
|
|||||||
services.AddSingleton<ConfigurationService>();
|
services.AddSingleton<ConfigurationService>();
|
||||||
services.AddSingleton<PrintQueueService>();
|
services.AddSingleton<PrintQueueService>();
|
||||||
services.AddSingleton<FileMonitorService>();
|
services.AddSingleton<FileMonitorService>();
|
||||||
|
services.AddSingleton<ContentTransformer>();
|
||||||
services.AddSingleton<PrinterService>();
|
services.AddSingleton<PrinterService>();
|
||||||
services.AddSingleton<DocumentProcessor>();
|
services.AddSingleton<DocumentProcessor>();
|
||||||
services.AddSingleton<IpcService>();
|
services.AddSingleton<IpcService>();
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
using PrintService.Models;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace PrintService.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles content transformations from Rust CLI (cgwprint) logic
|
||||||
|
/// </summary>
|
||||||
|
public class ContentTransformer
|
||||||
|
{
|
||||||
|
private readonly ILogger<ContentTransformer> _logger;
|
||||||
|
|
||||||
|
public ContentTransformer(ILogger<ContentTransformer> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transform document content according to configuration
|
||||||
|
/// </summary>
|
||||||
|
public TransformedDocument TransformContent(string content, DocumentTypeConfig config, string? orderNumber = null)
|
||||||
|
{
|
||||||
|
// Step 1: Normalize font codes
|
||||||
|
content = NormalizeFontCodes(content);
|
||||||
|
|
||||||
|
// Step 2: Order number manipulation (if order number provided)
|
||||||
|
if (!string.IsNullOrEmpty(orderNumber))
|
||||||
|
{
|
||||||
|
content = ManipulateOrderNumber(content, orderNumber, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Parse font codes to identify bold/normal segments
|
||||||
|
var lines = content.Split(new[] { '\r', '\n' }, StringSplitOptions.None);
|
||||||
|
|
||||||
|
// Step 4: Apply row trim (modifies line content)
|
||||||
|
lines = ApplyRowTrim(lines, config.RowTrim);
|
||||||
|
|
||||||
|
// Step 5: Parse lines into segments with font styling
|
||||||
|
var styledLines = ParseStyledLines(lines);
|
||||||
|
|
||||||
|
return new TransformedDocument
|
||||||
|
{
|
||||||
|
Lines = styledLines,
|
||||||
|
RowShifts = config.RowShift,
|
||||||
|
LinePadding = config.LinePadding
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Step 1: Normalize font codes (pre-processing)
|
||||||
|
/// </summary>
|
||||||
|
private string NormalizeFontCodes(string content)
|
||||||
|
{
|
||||||
|
// Prefix with normal font at start
|
||||||
|
content = "\x1Bw0" + content;
|
||||||
|
|
||||||
|
// Normalize uppercase W to lowercase w
|
||||||
|
content = content.Replace("\x1BW1", "\x1Bw1");
|
||||||
|
content = content.Replace("\x1BW0", "\x1Bw0");
|
||||||
|
|
||||||
|
// Deduplicate consecutive bold sequences
|
||||||
|
content = Regex.Replace(content, @"(\x1Bw1)+", "\x1Bw1");
|
||||||
|
|
||||||
|
// Deduplicate consecutive normal sequences
|
||||||
|
content = Regex.Replace(content, @"(\x1Bw0)+", "\x1Bw0");
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Step 2: Order number manipulation
|
||||||
|
/// </summary>
|
||||||
|
private string ManipulateOrderNumber(string content, string orderNumber, DocumentTypeConfig config)
|
||||||
|
{
|
||||||
|
var orderPattern = $"Order #: {orderNumber}";
|
||||||
|
|
||||||
|
// Only ONE operation executes based on configuration flags
|
||||||
|
if (config.RemoveOrderNumber)
|
||||||
|
{
|
||||||
|
// Replace entire "Order #: 12345" with spaces
|
||||||
|
var replacement = new string(' ', orderPattern.Length);
|
||||||
|
content = content.Replace(orderPattern, replacement);
|
||||||
|
_logger.LogDebug("Removed order number completely: {OrderNumber}", orderNumber);
|
||||||
|
}
|
||||||
|
else if (config.RemoveOrderNumberLabel)
|
||||||
|
{
|
||||||
|
// Replace "Order #: " with spaces, keep number and make it bold
|
||||||
|
var labelLength = "Order #: ".Length;
|
||||||
|
var spaces = new string(' ', labelLength);
|
||||||
|
var replacement = $"{spaces}\x1Bw1{orderNumber}\x1Bw0";
|
||||||
|
content = content.Replace(orderPattern, replacement);
|
||||||
|
_logger.LogDebug("Removed order number label, kept bold number: {OrderNumber}", orderNumber);
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(config.IndentOrderNumber))
|
||||||
|
{
|
||||||
|
// Find and indent the order number (assumes it's already bold)
|
||||||
|
var boldPattern = $@"\x1Bw1{Regex.Escape(orderNumber)}\x1Bw0";
|
||||||
|
content = Regex.Replace(content, boldPattern, $"{config.IndentOrderNumber}\x1Bw1{orderNumber}\x1Bw0");
|
||||||
|
_logger.LogDebug("Indented order number: {OrderNumber}", orderNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Step 3: Apply row trim (horizontal character removal)
|
||||||
|
/// </summary>
|
||||||
|
private string[] ApplyRowTrim(string[] lines, Dictionary<int, RowTrimConfig> rowTrim)
|
||||||
|
{
|
||||||
|
if (rowTrim.Count == 0)
|
||||||
|
return lines;
|
||||||
|
|
||||||
|
var result = new string[lines.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
if (rowTrim.TryGetValue(i, out var trim))
|
||||||
|
{
|
||||||
|
var line = lines[i];
|
||||||
|
|
||||||
|
if (trim.Start >= 0 && trim.End <= line.Length && trim.Start < trim.End)
|
||||||
|
{
|
||||||
|
// Remove characters from Start to End
|
||||||
|
result[i] = line.Substring(0, trim.Start) + line.Substring(trim.End);
|
||||||
|
_logger.LogDebug("Trimmed line {LineNum}: removed chars {Start}-{End}", i, trim.Start, trim.End);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result[i] = line;
|
||||||
|
_logger.LogWarning("Invalid row trim config for line {LineNum}: Start={Start}, End={End}, LineLength={Length}",
|
||||||
|
i, trim.Start, trim.End, line.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result[i] = lines[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Step 4: Parse lines into segments with font styling
|
||||||
|
/// </summary>
|
||||||
|
private List<StyledLine> ParseStyledLines(string[] lines)
|
||||||
|
{
|
||||||
|
var styledLines = new List<StyledLine>();
|
||||||
|
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
var segments = new List<TextSegment>();
|
||||||
|
var currentText = new StringBuilder();
|
||||||
|
var currentStyle = FontStyle.Regular;
|
||||||
|
|
||||||
|
for (int i = 0; i < line.Length; i++)
|
||||||
|
{
|
||||||
|
// Check for font code escape sequence
|
||||||
|
if (i + 2 < line.Length && line[i] == '\x1B' && line[i + 1] == 'w')
|
||||||
|
{
|
||||||
|
// Save current segment if any
|
||||||
|
if (currentText.Length > 0)
|
||||||
|
{
|
||||||
|
segments.Add(new TextSegment
|
||||||
|
{
|
||||||
|
Text = currentText.ToString(),
|
||||||
|
Style = currentStyle
|
||||||
|
});
|
||||||
|
currentText.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse font code
|
||||||
|
char code = line[i + 2];
|
||||||
|
if (code == '1')
|
||||||
|
{
|
||||||
|
currentStyle = FontStyle.Bold;
|
||||||
|
}
|
||||||
|
else if (code == '0')
|
||||||
|
{
|
||||||
|
currentStyle = FontStyle.Regular;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the escape sequence
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentText.Append(line[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add final segment
|
||||||
|
if (currentText.Length > 0 || segments.Count == 0)
|
||||||
|
{
|
||||||
|
segments.Add(new TextSegment
|
||||||
|
{
|
||||||
|
Text = currentText.ToString(),
|
||||||
|
Style = currentStyle
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
styledLines.Add(new StyledLine { Segments = segments });
|
||||||
|
}
|
||||||
|
|
||||||
|
return styledLines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transformed document with styled lines and layout adjustments
|
||||||
|
/// </summary>
|
||||||
|
public class TransformedDocument
|
||||||
|
{
|
||||||
|
public List<StyledLine> Lines { get; set; } = new();
|
||||||
|
public Dictionary<int, int> RowShifts { get; set; } = new();
|
||||||
|
public int LinePadding { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A line with styled text segments
|
||||||
|
/// </summary>
|
||||||
|
public class StyledLine
|
||||||
|
{
|
||||||
|
public List<TextSegment> Segments { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A text segment with font styling
|
||||||
|
/// </summary>
|
||||||
|
public class TextSegment
|
||||||
|
{
|
||||||
|
public string Text { get; set; } = string.Empty;
|
||||||
|
public FontStyle Style { get; set; } = FontStyle.Regular;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum FontStyle
|
||||||
|
{
|
||||||
|
Regular,
|
||||||
|
Bold
|
||||||
|
}
|
||||||
@@ -12,11 +12,16 @@ public class PrinterService
|
|||||||
{
|
{
|
||||||
private readonly AppSettings _settings;
|
private readonly AppSettings _settings;
|
||||||
private readonly ILogger<PrinterService> _logger;
|
private readonly ILogger<PrinterService> _logger;
|
||||||
|
private readonly ContentTransformer _transformer;
|
||||||
|
|
||||||
public PrinterService(IOptions<AppSettings> settings, ILogger<PrinterService> logger)
|
public PrinterService(
|
||||||
|
IOptions<AppSettings> settings,
|
||||||
|
ILogger<PrinterService> logger,
|
||||||
|
ContentTransformer transformer)
|
||||||
{
|
{
|
||||||
_settings = settings.Value;
|
_settings = settings.Value;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_transformer = transformer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -24,16 +29,18 @@ public class PrinterService
|
|||||||
/// Each PageConfig entry defines one output copy per original content page,
|
/// Each PageConfig entry defines one output copy per original content page,
|
||||||
/// so a 4-page document with 4 PageConfig entries produces 16 output pages.
|
/// so a 4-page document with 4 PageConfig entries produces 16 output pages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Print(string content, DocumentTypeConfig config, string originalFileName)
|
public void Print(string content, DocumentTypeConfig config, string originalFileName, string? orderNumber = null)
|
||||||
{
|
{
|
||||||
if (config.Pages.Count == 0)
|
if (config.Pages.Count == 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("No pages/trays defined for document type");
|
throw new InvalidOperationException("No pages/trays defined for document type");
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines = content.Split(new[] { '\r', '\n' }, StringSplitOptions.None);
|
// Transform content according to Rust CLI logic
|
||||||
|
var transformed = _transformer.TransformContent(content, config, orderNumber);
|
||||||
|
|
||||||
var linesPerPage = CalculateLinesPerPage(config.FontSize);
|
var linesPerPage = CalculateLinesPerPage(config.FontSize);
|
||||||
var totalOriginalPages = Math.Max(1, (int)Math.Ceiling(lines.Length / (double)linesPerPage));
|
var totalOriginalPages = Math.Max(1, (int)Math.Ceiling(transformed.Lines.Count / (double)linesPerPage));
|
||||||
var originalPageIndex = 0;
|
var originalPageIndex = 0;
|
||||||
var copyIndex = 0; // index into config.Pages (one entry per tray copy)
|
var copyIndex = 0; // index into config.Pages (one entry per tray copy)
|
||||||
|
|
||||||
@@ -44,7 +51,10 @@ public class PrinterService
|
|||||||
|
|
||||||
var printDoc = new PrintDocument
|
var printDoc = new PrintDocument
|
||||||
{
|
{
|
||||||
PrinterSettings = { PrinterName = primaryPrinterName }
|
PrinterSettings = {
|
||||||
|
PrinterName = primaryPrinterName,
|
||||||
|
Duplex = Duplex.Simplex // Force single-sided printing (no duplexing)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle PDF output if using Microsoft Print to PDF
|
// Handle PDF output if using Microsoft Print to PDF
|
||||||
@@ -67,9 +77,10 @@ public class PrinterService
|
|||||||
throw new InvalidOperationException($"Printer '{primaryPrinterName}' not found");
|
throw new InvalidOperationException($"Printer '{primaryPrinterName}' not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
printDoc.PrintPage += (sender, e) =>
|
// QueryPageSettings fires BEFORE PrintPage - set tray here
|
||||||
|
printDoc.QueryPageSettings += (sender, e) =>
|
||||||
{
|
{
|
||||||
if (e.Graphics == null || e.PageSettings == null)
|
if (e.PageSettings == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var pageConfig = config.Pages[copyIndex];
|
var pageConfig = config.Pages[copyIndex];
|
||||||
@@ -96,9 +107,15 @@ public class PrinterService
|
|||||||
{
|
{
|
||||||
_logger.LogWarning(ex, "Failed to set tray {Tray}, using default", pageConfig.TrayNumber);
|
_logger.LogWarning(ex, "Failed to set tray {Tray}, using default", pageConfig.TrayNumber);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
printDoc.PrintPage += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.Graphics == null)
|
||||||
|
return;
|
||||||
|
|
||||||
// Render the same original page for every copy before moving to next original page.
|
// Render the same original page for every copy before moving to next original page.
|
||||||
RenderPage(e.Graphics, lines, originalPageIndex * linesPerPage, linesPerPage,
|
RenderPage(e.Graphics, transformed, originalPageIndex * linesPerPage, linesPerPage,
|
||||||
config.FontName, config.FontSize, config.HorizontalOffset, config.VerticalOffset);
|
config.FontName, config.FontSize, config.HorizontalOffset, config.VerticalOffset);
|
||||||
|
|
||||||
copyIndex++;
|
copyIndex++;
|
||||||
@@ -122,28 +139,70 @@ public class PrinterService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Render page content using Graphics API
|
/// Render page content using Graphics API with styled text and transformations
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void RenderPage(Graphics graphics, string[] lines, int startLine, int linesPerPage,
|
private void RenderPage(Graphics graphics, TransformedDocument transformed, int startLine, int linesPerPage,
|
||||||
string fontName, float fontSize, int horizontalOffset, int verticalOffset)
|
string fontName, float fontSize, int horizontalOffset, int verticalOffset)
|
||||||
{
|
{
|
||||||
var font = new Font(fontName, fontSize);
|
var normalFont = new Font(fontName, fontSize, System.Drawing.FontStyle.Regular);
|
||||||
|
var boldFont = new Font(fontName, fontSize, System.Drawing.FontStyle.Bold);
|
||||||
var brush = Brushes.Black;
|
var brush = Brushes.Black;
|
||||||
var lineHeight = font.GetHeight(graphics);
|
var lineHeight = normalFont.GetHeight(graphics);
|
||||||
|
|
||||||
var x = (float)horizontalOffset;
|
// Convert line padding from 1/100 inch to pixels (assuming 96 DPI for screen, but printers use their own DPI)
|
||||||
var y = (float)verticalOffset;
|
// For printers, Graphics.DpiX will give the actual printer DPI
|
||||||
|
var linePaddingPixels = (transformed.LinePadding / 100f) * graphics.DpiX;
|
||||||
|
|
||||||
var endLine = Math.Min(startLine + linesPerPage, lines.Length);
|
var endLine = Math.Min(startLine + linesPerPage, transformed.Lines.Count);
|
||||||
|
|
||||||
for (int i = startLine; i < endLine; i++)
|
// Track cumulative vertical shift
|
||||||
|
float cumulativeShift = 0;
|
||||||
|
|
||||||
|
for (int lineIndex = startLine; lineIndex < endLine; lineIndex++)
|
||||||
{
|
{
|
||||||
if (i < lines.Length)
|
if (lineIndex >= transformed.Lines.Count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var styledLine = transformed.Lines[lineIndex];
|
||||||
|
|
||||||
|
// Apply row shift if defined for this line
|
||||||
|
if (transformed.RowShifts.TryGetValue(lineIndex, out var shift))
|
||||||
{
|
{
|
||||||
graphics.DrawString(lines[i], font, brush, x, y);
|
// Convert shift from 1/100 inch to pixels
|
||||||
y += lineHeight;
|
var shiftPixels = (shift / 100f) * graphics.DpiY;
|
||||||
|
cumulativeShift += shiftPixels;
|
||||||
|
|
||||||
|
if (_settings.VerboseLogging)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Line {LineNum}: applying row shift {Shift} units ({Pixels} px)",
|
||||||
|
lineIndex, shift, shiftPixels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate Y position with offsets and shifts
|
||||||
|
var y = verticalOffset + ((lineIndex - startLine) * lineHeight) + cumulativeShift;
|
||||||
|
|
||||||
|
// Start X position with horizontal offset and line padding
|
||||||
|
var x = (float)horizontalOffset + linePaddingPixels;
|
||||||
|
|
||||||
|
// Render each text segment with appropriate font style
|
||||||
|
foreach (var segment in styledLine.Segments)
|
||||||
|
{
|
||||||
|
var font = segment.Style == Services.FontStyle.Bold ? boldFont : normalFont;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(segment.Text))
|
||||||
|
{
|
||||||
|
graphics.DrawString(segment.Text, font, brush, x, y);
|
||||||
|
|
||||||
|
// Measure text width to advance X position for next segment
|
||||||
|
var textSize = graphics.MeasureString(segment.Text, font);
|
||||||
|
x += textSize.Width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
normalFont.Dispose();
|
||||||
|
boldFont.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -130,8 +130,8 @@ public class Worker : BackgroundService
|
|||||||
// Process/transform content
|
// Process/transform content
|
||||||
var processedContent = _documentProcessor.ProcessDocument(content, config);
|
var processedContent = _documentProcessor.ProcessDocument(content, config);
|
||||||
|
|
||||||
// Print to Windows queue
|
// Print to Windows queue (pass order number for transformations)
|
||||||
await Task.Run(() => _printerService.Print(processedContent, config, job.OriginalFilename), cancellationToken);
|
await Task.Run(() => _printerService.Print(processedContent, config, job.OriginalFilename, job.OrderNumber), cancellationToken);
|
||||||
|
|
||||||
// Post-process (archive or delete)
|
// Post-process (archive or delete)
|
||||||
_documentProcessor.PostProcess(job, config);
|
_documentProcessor.PostProcess(job, config);
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ public class DocumentTypeConfig
|
|||||||
{
|
{
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public List<PageConfig> Pages { get; set; } = new();
|
public List<PageConfig> Pages { get; set; } = new();
|
||||||
|
|
||||||
|
// Rust transformation properties (optional - can be edited in JSON directly)
|
||||||
|
public int LinePadding { get; set; } = 0;
|
||||||
|
public bool RemoveOrderNumber { get; set; } = false;
|
||||||
|
public bool RemoveOrderNumberLabel { get; set; } = false;
|
||||||
|
public string IndentOrderNumber { get; set; } = string.Empty;
|
||||||
|
public Dictionary<int, int> RowShift { get; set; } = new();
|
||||||
|
public Dictionary<int, RowTrimConfig> RowTrim { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -27,3 +35,12 @@ public class PageConfig
|
|||||||
public int TrayNumber { get; set; }
|
public int TrayNumber { get; set; }
|
||||||
public string? TrayLabel { get; set; } // Optional: "Pink Paper", "Green Paper", etc.
|
public string? TrayLabel { get; set; } // Optional: "Pink Paper", "Green Paper", etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration for row trimming (horizontal character removal)
|
||||||
|
/// </summary>
|
||||||
|
public class RowTrimConfig
|
||||||
|
{
|
||||||
|
public int Start { get; set; }
|
||||||
|
public int End { get; set; }
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Printing;
|
||||||
|
|
||||||
|
class TrayTest
|
||||||
|
{
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
var printerName = "Brother HL-L6415DW series Printer";
|
||||||
|
var trayNumbers = new int[] { 1, 2, 256, 257, 270 }; // Tray 1, 2, 3, 4, 5
|
||||||
|
var currentPage = 0;
|
||||||
|
|
||||||
|
var printDoc = new PrintDocument
|
||||||
|
{
|
||||||
|
PrinterSettings = {
|
||||||
|
PrinterName = printerName,
|
||||||
|
Duplex = Duplex.Simplex // Force single-sided
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// QueryPageSettings fires BEFORE PrintPage - this is where we set the tray
|
||||||
|
printDoc.QueryPageSettings += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.PageSettings == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var trayNumber = trayNumbers[currentPage];
|
||||||
|
|
||||||
|
// Set the tray for this page
|
||||||
|
PaperSource? paperSource = null;
|
||||||
|
foreach (PaperSource source in printDoc.PrinterSettings.PaperSources)
|
||||||
|
{
|
||||||
|
if (source.RawKind == trayNumber)
|
||||||
|
{
|
||||||
|
paperSource = source;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paperSource != null)
|
||||||
|
{
|
||||||
|
e.PageSettings.PaperSource = paperSource;
|
||||||
|
Console.WriteLine($"Page {currentPage + 1}: Using tray {trayNumber} ({paperSource.SourceName})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Page {currentPage + 1}: WARNING - Tray {trayNumber} not found!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
printDoc.PrintPage += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.Graphics == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Draw the page number
|
||||||
|
var font = new Font("Courier New", 24, FontStyle.Bold);
|
||||||
|
var text = $"Page #{currentPage + 1}";
|
||||||
|
e.Graphics.DrawString(text, font, Brushes.Black, 100, 100);
|
||||||
|
|
||||||
|
currentPage++;
|
||||||
|
e.HasMorePages = currentPage < trayNumbers.Length;
|
||||||
|
};
|
||||||
|
|
||||||
|
Console.WriteLine($"Printing 5 pages to {printerName}...");
|
||||||
|
printDoc.Print();
|
||||||
|
Console.WriteLine("Done!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<EnableDefaultCompileItems>true</EnableDefaultCompileItems>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<RuntimeHostConfigurationOption Include="System.Drawing.EnableUnixSupport" Value="true" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"DocumentTypes": {
|
||||||
|
"test": {
|
||||||
|
"Name": "test",
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"PageNumber": 1,
|
||||||
|
"PrinterName": "Microsoft Print to PDF",
|
||||||
|
"TrayNumber": 1,
|
||||||
|
"TrayLabel": "Default"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"FontName": "Courier New",
|
||||||
|
"FontSize": 10.0,
|
||||||
|
"HorizontalOffset": 0,
|
||||||
|
"VerticalOffset": 0,
|
||||||
|
"ArchiveAfterPrint": true,
|
||||||
|
"ArchivePath": "C:\\Users\\Work\\Desktop\\LAAPC\\Archive\\Test",
|
||||||
|
"OutputPath": "C:\\Users\\Work\\Desktop\\LAAPC\\Output",
|
||||||
|
"SkipPostProcessing": false,
|
||||||
|
"Transformations": [],
|
||||||
|
"LinePadding": 10,
|
||||||
|
"RemoveOrderNumber": false,
|
||||||
|
"RemoveOrderNumberLabel": true,
|
||||||
|
"IndentOrderNumber": "",
|
||||||
|
"RowShift": {
|
||||||
|
"7": -200,
|
||||||
|
"8": -100
|
||||||
|
},
|
||||||
|
"RowTrim": {
|
||||||
|
"8": {
|
||||||
|
"Start": 0,
|
||||||
|
"End": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delivery": {
|
||||||
|
"Name": "delivery",
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"PageNumber": 1,
|
||||||
|
"PrinterName": "Microsoft Print to PDF",
|
||||||
|
"TrayNumber": 3,
|
||||||
|
"TrayLabel": "Tray 3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PageNumber": 2,
|
||||||
|
"PrinterName": "Microsoft Print to PDF",
|
||||||
|
"TrayNumber": 2,
|
||||||
|
"TrayLabel": "Tray 2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"FontName": "Courier New",
|
||||||
|
"FontSize": 10.0,
|
||||||
|
"HorizontalOffset": 0,
|
||||||
|
"VerticalOffset": 0,
|
||||||
|
"ArchiveAfterPrint": true,
|
||||||
|
"ArchivePath": "C:\\Users\\Work\\Desktop\\LAAPC\\Archive\\Orders",
|
||||||
|
"OutputPath": "C:\\Users\\Work\\Desktop\\LAAPC\\Output",
|
||||||
|
"SkipPostProcessing": false,
|
||||||
|
"Transformations": [],
|
||||||
|
"LinePadding": 10,
|
||||||
|
"RemoveOrderNumber": false,
|
||||||
|
"RemoveOrderNumberLabel": false,
|
||||||
|
"IndentOrderNumber": " ",
|
||||||
|
"RowShift": {
|
||||||
|
"0": 625,
|
||||||
|
"7": -200,
|
||||||
|
"8": -100,
|
||||||
|
"14": 100,
|
||||||
|
"19": 172
|
||||||
|
},
|
||||||
|
"RowTrim": {}
|
||||||
|
},
|
||||||
|
"invoice": {
|
||||||
|
"Name": "invoice",
|
||||||
|
"Pages": [
|
||||||
|
{
|
||||||
|
"PageNumber": 1,
|
||||||
|
"PrinterName": "Microsoft Print to PDF",
|
||||||
|
"TrayNumber": 2,
|
||||||
|
"TrayLabel": "White"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PageNumber": 2,
|
||||||
|
"PrinterName": "Microsoft Print to PDF",
|
||||||
|
"TrayNumber": 4,
|
||||||
|
"TrayLabel": "Yellow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PageNumber": 3,
|
||||||
|
"PrinterName": "Microsoft Print to PDF",
|
||||||
|
"TrayNumber": 5,
|
||||||
|
"TrayLabel": "Pink"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"FontName": "Courier New",
|
||||||
|
"FontSize": 10.0,
|
||||||
|
"HorizontalOffset": 0,
|
||||||
|
"VerticalOffset": 0,
|
||||||
|
"ArchiveAfterPrint": true,
|
||||||
|
"ArchivePath": "C:\\Users\\Work\\Desktop\\LAAPC\\Archive\\Orders",
|
||||||
|
"OutputPath": "C:\\Users\\Work\\Desktop\\LAAPC\\Output",
|
||||||
|
"SkipPostProcessing": false,
|
||||||
|
"Transformations": [],
|
||||||
|
"LinePadding": 10,
|
||||||
|
"RemoveOrderNumber": true,
|
||||||
|
"RemoveOrderNumberLabel": false,
|
||||||
|
"IndentOrderNumber": "",
|
||||||
|
"RowShift": {
|
||||||
|
"0": 625,
|
||||||
|
"7": -200,
|
||||||
|
"8": -100,
|
||||||
|
"14": 100,
|
||||||
|
"19": 172
|
||||||
|
},
|
||||||
|
"RowTrim": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Page 1 of 1
|
||||||
Reference in New Issue
Block a user