using PrintService.Models; using System.Text; using System.Text.RegularExpressions; namespace PrintService.Services; /// /// Handles content transformations from Rust CLI (cgwprint) logic /// public class ContentTransformer { private readonly ILogger _logger; public ContentTransformer(ILogger logger) { _logger = logger; } /// /// Transform document content according to configuration /// 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 }; } /// /// Step 1: Normalize font codes (pre-processing) /// 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; } /// /// Step 2: Order number manipulation /// 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; } /// /// Step 3: Apply row trim (horizontal character removal) /// private string[] ApplyRowTrim(string[] lines, Dictionary 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; } /// /// Step 4: Parse lines into segments with font styling /// private List ParseStyledLines(string[] lines) { var styledLines = new List(); foreach (var line in lines) { var segments = new List(); 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; } } /// /// Transformed document with styled lines and layout adjustments /// public class TransformedDocument { public List Lines { get; set; } = new(); public Dictionary RowShifts { get; set; } = new(); public int LinePadding { get; set; } } /// /// A line with styled text segments /// public class StyledLine { public List Segments { get; set; } = new(); } /// /// A text segment with font styling /// public class TextSegment { public string Text { get; set; } = string.Empty; public FontStyle Style { get; set; } = FontStyle.Regular; } public enum FontStyle { Regular, Bold }