Files
2026-02-03 16:40:25 +01:00

372 lines
12 KiB
C#

namespace Inspectron.Epson.TemplateEngine.Rendering;
public class LayoutEngine
{
public string AlignText(string text, string alignment, int lineWidth)
{
if (string.IsNullOrEmpty(text))
return text ?? "";
if (text.Length >= lineWidth)
return text;
return alignment.ToLowerInvariant() switch
{
"center" => CenterText(text, lineWidth),
"right" => text.PadLeft(lineWidth),
_ => text // left-aligned is default (no padding)
};
}
private static string CenterText(string text, int lineWidth)
{
int totalPadding = lineWidth - text.Length;
int leftPadding = totalPadding / 2;
return new string(' ', leftPadding) + text;
}
public string FormatTwoColumns(string left, string right, int lineWidth)
{
left ??= "";
right ??= "";
int halfWidth = lineWidth / 2;
return left.PadRight(halfWidth) + right.PadLeft(lineWidth - halfWidth);
}
public List<string> FormatTwoColumnsWithWrap(string left, string right, int lineWidth)
{
left ??= "";
right ??= "";
int halfWidth = lineWidth / 2;
// Try simple format first
if (left.Length + right.Length <= lineWidth)
{
int spaces = lineWidth - left.Length - right.Length;
if (spaces < 1) spaces = 1;
return new List<string> { left + new string(' ', spaces) + right };
}
// Wrap left side, right-align right on first line
var leftLines = WrapText(left, halfWidth);
var result = new List<string>();
for (int i = 0; i < leftLines.Count; i++)
{
if (i == 0)
{
int spaces = lineWidth - leftLines[i].Length - right.Length;
if (spaces < 1)
{
result.Add(leftLines[i]);
result.Add(right.PadLeft(lineWidth));
}
else
{
result.Add(leftLines[i] + new string(' ', spaces) + right);
}
}
else
{
result.Add(leftLines[i]);
}
}
return result;
}
public string FormatMultiColumn(List<(string text, int width, string align)> columns, int lineWidth)
{
var parts = new List<string>();
// Calculate widths: distribute remaining width among columns without explicit width
int totalExplicit = columns.Where(c => c.width > 0).Sum(c => c.width);
int unspecifiedCount = columns.Count(c => c.width <= 0);
int remaining = lineWidth - totalExplicit;
int defaultWidth = unspecifiedCount > 0 ? remaining / unspecifiedCount : 0;
foreach (var (text, width, align) in columns)
{
int colWidth = width > 0 ? width : defaultWidth;
if (colWidth <= 0) colWidth = 1;
string formatted = align.ToLowerInvariant() switch
{
"center" => CenterInWidth(text ?? "", colWidth),
"right" => (text ?? "").PadLeft(colWidth),
_ => (text ?? "").PadRight(colWidth)
};
// Truncate if too long
if (formatted.Length > colWidth)
formatted = formatted[..colWidth];
parts.Add(formatted);
}
return string.Concat(parts);
}
public List<string> FormatMultiColumnWithWrap(
List<(string text, int width, string align, bool wrap)> columns, int lineWidth)
{
// Calculate widths
int totalExplicit = columns.Where(c => c.width > 0).Sum(c => c.width);
int unspecifiedCount = columns.Count(c => c.width <= 0);
int remaining = lineWidth - totalExplicit;
int defaultWidth = unspecifiedCount > 0 ? remaining / unspecifiedCount : 0;
var colWidths = columns.Select(c => c.width > 0 ? c.width : Math.Max(defaultWidth, 1)).ToList();
// Wrap or truncate each column's text into lines
var allColLines = new List<List<string>>();
for (int i = 0; i < columns.Count; i++)
{
var (text, _, _, wrap) = columns[i];
text ??= "";
int w = colWidths[i];
if (wrap)
allColLines.Add(WrapText(text, w));
else
allColLines.Add(new List<string> { text.Length > w ? text[..w] : text });
}
// Find the tallest column
int maxLines = allColLines.Max(l => l.Count);
// Build each output line
var result = new List<string>();
for (int lineIdx = 0; lineIdx < maxLines; lineIdx++)
{
var parts = new List<string>();
for (int colIdx = 0; colIdx < columns.Count; colIdx++)
{
string cellText = lineIdx < allColLines[colIdx].Count ? allColLines[colIdx][lineIdx] : "";
int w = colWidths[colIdx];
string formatted = columns[colIdx].align.ToLowerInvariant() switch
{
"center" => CenterInWidth(cellText, w),
"right" => cellText.PadLeft(w),
_ => cellText.PadRight(w)
};
if (formatted.Length > w)
formatted = formatted[..w];
parts.Add(formatted);
}
result.Add(string.Concat(parts));
}
return result;
}
public List<string> WrapText(string text, int maxWidth, int indent = 0)
{
var lines = new List<string>();
if (string.IsNullOrEmpty(text) || maxWidth <= 0)
{
lines.Add(text ?? "");
return lines;
}
if (text.Length <= maxWidth)
{
lines.Add(text);
return lines;
}
var words = text.Split(' ');
string currentLine = "";
bool isFirst = true;
foreach (var word in words)
{
int available = isFirst ? maxWidth : maxWidth - indent;
if (available <= 0) available = 1;
if (currentLine.Length == 0)
{
currentLine = word;
}
else if (currentLine.Length + 1 + word.Length <= available)
{
currentLine += " " + word;
}
else
{
if (isFirst)
{
lines.Add(currentLine);
isFirst = false;
}
else
{
lines.Add(new string(' ', indent) + currentLine);
}
currentLine = word;
}
}
if (currentLine.Length > 0)
{
if (isFirst)
lines.Add(currentLine);
else
lines.Add(new string(' ', indent) + currentLine);
}
return lines;
}
public string CreateSeparator(char character, int lineWidth)
{
return new string(character, lineWidth);
}
public List<string> FormatTable(
List<(string text, int width, string align)> columns,
List<List<string>> headerRows,
List<List<string>> dataRows,
int lineWidth,
List<bool>? wrapFlags = null)
{
var result = new List<string>();
// Calculate column widths
var colWidths = CalculateTableColumnWidths(columns, lineWidth);
bool hasWrap = wrapFlags != null && wrapFlags.Any(w => w);
// Top border
result.Add(FormatTableBorder(colWidths));
// Header rows
foreach (var row in headerRows)
{
if (hasWrap)
result.AddRange(FormatTableRows(row, colWidths, columns, wrapFlags!));
else
result.Add(FormatTableRow(row, colWidths, columns));
}
// Separator between header and data
if (headerRows.Count > 0 && dataRows.Count > 0)
{
result.Add(FormatTableBorder(colWidths));
}
// Data rows
foreach (var row in dataRows)
{
if (hasWrap)
result.AddRange(FormatTableRows(row, colWidths, columns, wrapFlags!));
else
result.Add(FormatTableRow(row, colWidths, columns));
}
// Bottom border
result.Add(FormatTableBorder(colWidths));
return result;
}
private List<int> CalculateTableColumnWidths(List<(string text, int width, string align)> columns, int lineWidth)
{
// Account for borders: |col1|col2|col3| = columns.Count + 1 border chars
int availableWidth = lineWidth - columns.Count - 1;
int totalExplicit = columns.Where(c => c.width > 0).Sum(c => c.width);
int unspecifiedCount = columns.Count(c => c.width <= 0);
int remaining = availableWidth - totalExplicit;
int defaultWidth = unspecifiedCount > 0 ? remaining / unspecifiedCount : 0;
var widths = new List<int>();
foreach (var (_, width, _) in columns)
{
widths.Add(width > 0 ? width : Math.Max(defaultWidth, 1));
}
return widths;
}
private string FormatTableBorder(List<int> colWidths)
{
return "+" + string.Join("+", colWidths.Select(w => new string('-', w))) + "+";
}
private string FormatTableRow(List<string> cells, List<int> colWidths, List<(string text, int width, string align)> columns)
{
var parts = new List<string>();
for (int i = 0; i < colWidths.Count; i++)
{
string cell = i < cells.Count ? cells[i] : "";
string align = i < columns.Count ? columns[i].align : "left";
parts.Add(FormatCellContent(cell, colWidths[i], align));
}
return "|" + string.Join("|", parts) + "|";
}
private List<string> FormatTableRows(List<string> cells, List<int> colWidths,
List<(string text, int width, string align)> columns, List<bool> wrapFlags)
{
// Wrap each cell that has wrap enabled
var allColLines = new List<List<string>>();
for (int i = 0; i < colWidths.Count; i++)
{
string cell = i < cells.Count ? cells[i] : "";
bool wrap = i < wrapFlags.Count && wrapFlags[i];
if (wrap)
allColLines.Add(WrapText(cell, colWidths[i]));
else
allColLines.Add(new List<string> { cell.Length > colWidths[i] ? cell[..colWidths[i]] : cell });
}
int maxLines = allColLines.Max(l => l.Count);
var result = new List<string>();
for (int lineIdx = 0; lineIdx < maxLines; lineIdx++)
{
var parts = new List<string>();
for (int colIdx = 0; colIdx < colWidths.Count; colIdx++)
{
string cellText = lineIdx < allColLines[colIdx].Count ? allColLines[colIdx][lineIdx] : "";
string align = colIdx < columns.Count ? columns[colIdx].align : "left";
parts.Add(FormatCellContent(cellText, colWidths[colIdx], align));
}
result.Add("|" + string.Join("|", parts) + "|");
}
return result;
}
private string FormatCellContent(string text, int width, string align)
{
if (text.Length > width)
text = text[..width];
return align.ToLowerInvariant() switch
{
"center" => CenterInWidth(text, width),
"right" => text.PadLeft(width),
_ => text.PadRight(width)
};
}
private static string CenterInWidth(string text, int width)
{
if (string.IsNullOrEmpty(text))
return new string(' ', width);
if (text.Length >= width)
return text[..width];
int totalPadding = width - text.Length;
int leftPadding = totalPadding / 2;
int rightPadding = totalPadding - leftPadding;
return new string(' ', leftPadding) + text + new string(' ', rightPadding);
}
}