Files
CGW-Printing/PrintService/Services/ContentTransformer.cs

242 lines
7.9 KiB
C#

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
}