[chore:] logging for PdfBuilder.cs

This commit is contained in:
2026-05-16 18:19:36 +02:00
parent c6baa6a187
commit 40630dee35
+295 -202
View File
@@ -18,45 +18,73 @@ public class PdfBuilder
public PdfBuilder(PdfExportSettings? settings = null) public PdfBuilder(PdfExportSettings? settings = null)
{ {
EnsureFontResolverRegistered();
_settings = settings ?? new PdfExportSettings();
// Select first font from build output fonts folder (AppContext.BaseDirectory/fonts)
var chosenFamily = "Arial";
try try
{ {
if (Directory.Exists(Global._instance.font_path)) EnsureFontResolverRegistered();
_settings = settings ?? new PdfExportSettings();
// Select first font from build output fonts folder (AppContext.BaseDirectory/fonts)
var chosenFamily = "Arial";
try
{ {
var first = Directory.EnumerateFiles(Global._instance.font_path, "*.ttf").FirstOrDefault(); if (Directory.Exists(Global._instance.font_path))
if (!string.IsNullOrEmpty(first)) {
chosenFamily = StripStyleSuffix(Path.GetFileNameWithoutExtension(first)) ?? chosenFamily; var first = Directory.EnumerateFiles(Global._instance.font_path, "*.ttf").FirstOrDefault();
if (!string.IsNullOrEmpty(first))
chosenFamily = StripStyleSuffix(Path.GetFileNameWithoutExtension(first)) ?? chosenFamily;
}
} }
catch (Exception ex)
{
Logger.Log($"Error while searching for fonts: {ex.Message}",Logger.LogType.Error);
chosenFamily = "Arial";
}
_boldFont = new XFont(chosenFamily, _settings.fontSize, XFontStyleEx.Bold);
_regularFont = new XFont(chosenFamily, _settings.fontSize, XFontStyleEx.Regular);
_smallFont = new XFont(chosenFamily, _settings.smallFontSize, XFontStyleEx.Regular);
} }
catch catch (Exception ex)
{ {
chosenFamily = "Arial"; Logger.Log($"Error while font resolving: {ex.Message}",Logger.LogType.Error);
} }
_boldFont = new XFont(chosenFamily, _settings.fontSize, XFontStyleEx.Bold);
_regularFont = new XFont(chosenFamily, _settings.fontSize, XFontStyleEx.Regular);
_smallFont = new XFont(chosenFamily, _settings.smallFontSize, XFontStyleEx.Regular);
} }
private static void EnsureFontResolverRegistered() private static void EnsureFontResolverRegistered()
{ {
if (GlobalFontSettings.FontResolver != null) return; try
//var fontsDir = Path.Combine(AppContext.BaseDirectory, "fonts"); {
GlobalFontSettings.FontResolver = new StableFontResolver(Global._instance.font_path); if (GlobalFontSettings.FontResolver != null) return;
//var fontsDir = Path.Combine(AppContext.BaseDirectory, "fonts");
GlobalFontSettings.FontResolver = new StableFontResolver(Global._instance.font_path);
}
catch (Exception ex)
{
Logger.Log($"Error while ensuring font resolver register state: {ex.Message}",Logger.LogType.Error);
}
} }
private static string StripStyleSuffix(string name) private static string StripStyleSuffix(string name)
{ {
if (string.IsNullOrEmpty(name)) return name; try
var idx = name.IndexOf('-'); {
if (idx < 0) idx = name.IndexOf('_'); if (string.IsNullOrEmpty(name)) return name;
if (idx > 0) var idx = name.IndexOf('-');
return name.Substring(0, idx); if (idx < 0) idx = name.IndexOf('_');
return name; if (idx > 0)
return name.Substring(0, idx);
return name;
}
catch (Exception ex)
{
Logger.Log($"Error while stripping style suffix: {ex.Message}",Logger.LogType.Error);
}
return null;
} }
@@ -69,42 +97,50 @@ public class PdfBuilder
public void CreateAddressLabelPdfFromAddressSetWithPlaceholder(int addressSetId, string placeholderText, public void CreateAddressLabelPdfFromAddressSetWithPlaceholder(int addressSetId, string placeholderText,
string outputPath) string outputPath)
{ {
// Find the AddressSet by ID
var addressSet = Settings._instance.addressSets.GetAddressSetByID(addressSetId);
if (addressSet == null)
throw new ArgumentException($"AddressSet with ID {addressSetId} not found");
if (addressSet.KasPersons == null || addressSet.KasPersons.Count == 0)
throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses");
// Generate markdown addresses from all KasPersons in the set
//var addresses = new string?[addressSet.KasPersons.Count];
var addresses = new List<string>();
// find customer (owner) to include sender_address
string senderLine = null;
try try
{ {
var owner = Settings._instance.customers.customers.FirstOrDefault(c => c.ID == addressSet.owner_id); // Find the AddressSet by ID
if (owner != null && !string.IsNullOrWhiteSpace(owner.sender_address)) var addressSet = Settings._instance.addressSets.GetAddressSetByID(addressSetId);
senderLine = "<font6>" + owner.sender_address.Replace("\n", " ").Trim() + "</font6>\n"; if (addressSet == null)
throw new ArgumentException($"AddressSet with ID {addressSetId} not found");
if (addressSet.KasPersons == null || addressSet.KasPersons.Count == 0)
throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses");
// Generate markdown addresses from all KasPersons in the set
//var addresses = new string?[addressSet.KasPersons.Count];
var addresses = new List<string>();
// find customer (owner) to include sender_address
string senderLine = null;
try
{
var owner = Settings._instance.customers.customers.FirstOrDefault(c => c.ID == addressSet.owner_id);
if (owner != null && !string.IsNullOrWhiteSpace(owner.sender_address))
senderLine = "<font6>" + owner.sender_address.Replace("\n", " ").Trim() + "</font6>\n";
}
catch
{
senderLine = null;
}
for (var i = 0; i < addressSet.KasPersons.Count; i++)
{
var addr = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].id);
if (string.IsNullOrWhiteSpace(addr)) continue;
if (!string.IsNullOrEmpty(senderLine))
addresses.Add(senderLine + (addr ?? ""));
else
addresses.Add(addr);
}
CreateAddressLabelPdfWithPlaceholder(addresses, placeholderText, outputPath);
} }
catch catch (Exception ex)
{ {
senderLine = null; Logger.Log($"Error while creating address label pdf from address set with placeholder: {ex.Message}",Logger.LogType.Error);
} }
for (var i = 0; i < addressSet.KasPersons.Count; i++)
{
var addr = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].id);
if (string.IsNullOrWhiteSpace(addr)) continue;
if (!string.IsNullOrEmpty(senderLine))
addresses.Add(senderLine + (addr ?? ""));
else
addresses.Add(addr);
}
CreateAddressLabelPdfWithPlaceholder(addresses, placeholderText, outputPath);
} }
/// <summary> /// <summary>
@@ -115,26 +151,34 @@ public class PdfBuilder
/// <param name="outputPath">Path where the PDF should be saved</param> /// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdfWithPlaceholder(List<string> addresses, string placeholderText, string outputPath) public void CreateAddressLabelPdfWithPlaceholder(List<string> addresses, string placeholderText, string outputPath)
{ {
if (addresses == null || addresses.Count == 0) try
throw new ArgumentException("Addresses array cannot be null or empty");
var document = new PdfDocument();
var addressIndex = 0;
var isFirstCell = true;
while (addressIndex < addresses.Count || isFirstCell)
{ {
var page = document.AddPage(); if (addresses == null || addresses.Count == 0)
page.Size = PageSize.A4; throw new ArgumentException("Addresses array cannot be null or empty");
using (var gfx = XGraphics.FromPdfPage(page)) var document = new PdfDocument();
{
DrawPageWithPlaceholder(gfx, addresses, ref addressIndex, ref isFirstCell, placeholderText); var addressIndex = 0;
} var isFirstCell = true;
while (addressIndex < addresses.Count || isFirstCell)
{
var page = document.AddPage();
page.Size = PageSize.A4;
using (var gfx = XGraphics.FromPdfPage(page))
{
DrawPageWithPlaceholder(gfx, addresses, ref addressIndex, ref isFirstCell, placeholderText);
}
}
document.Save(outputPath);
}
catch (Exception ex)
{
Logger.Log($"Error while creating address label pdf with placeholder: {ex.Message}",Logger.LogType.Error);
} }
document.Save(outputPath);
} }
private void DrawPage(XGraphics gfx, List<string> addresses, ref int addressIndex) private void DrawPage(XGraphics gfx, List<string> addresses, ref int addressIndex)
@@ -159,193 +203,242 @@ public class PdfBuilder
private void DrawPageWithPlaceholder(XGraphics gfx, List<string> addresses, ref int addressIndex, private void DrawPageWithPlaceholder(XGraphics gfx, List<string> addresses, ref int addressIndex,
ref bool isFirstCell, string placeholderText) ref bool isFirstCell, string placeholderText)
{ {
for (var row = 0; row < _settings.rowsPerPage; row++) try
for (var col = 0; col < _settings.columnsPerPage; col++)
{ {
var x = MmToPoints(_settings.pageMarginLeftMm + col * GetCellWidthMm()); for (var row = 0; row < _settings.rowsPerPage; row++)
var y = MmToPoints(_settings.pageMarginTopMm + row * GetCellHeightMm()); for (var col = 0; col < _settings.columnsPerPage; col++)
{
var x = MmToPoints(_settings.pageMarginLeftMm + col * GetCellWidthMm());
var y = MmToPoints(_settings.pageMarginTopMm + row * GetCellHeightMm());
// First cell: placeholder // First cell: placeholder
if (isFirstCell) if (isFirstCell)
{ {
DrawCell(gfx, x, y, placeholderText); DrawCell(gfx, x, y, placeholderText);
isFirstCell = false; isFirstCell = false;
} }
else if (addressIndex < addresses.Count) else if (addressIndex < addresses.Count)
{ {
DrawCell(gfx, x, y, addresses[addressIndex]); DrawCell(gfx, x, y, addresses[addressIndex]);
addressIndex++; addressIndex++;
} }
else else
{ {
DrawEmptyCell(gfx, x, y); DrawEmptyCell(gfx, x, y);
}
} }
} }
catch (Exception ex)
{
Logger.Log($"Error while drawing page with placholder: {ex.Message}",Logger.LogType.Error);
}
} }
private void DrawCell(XGraphics gfx, double x, double y, string? address) private void DrawCell(XGraphics gfx, double x, double y, string? address)
{ {
var cellWidthPoints = MmToPoints(GetCellWidthMm()); try
var cellHeightPoints = MmToPoints(GetCellHeightMm()); {
var cellWidthPoints = MmToPoints(GetCellWidthMm());
var cellHeightPoints = MmToPoints(GetCellHeightMm());
// Draw cell border // Draw cell border
var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints); var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints);
gfx.DrawRectangle(XPens.Black, rect); gfx.DrawRectangle(XPens.Black, rect);
// Draw address content if available
if (!string.IsNullOrEmpty(address)) DrawMarkdownText(gfx, address, x, y, cellWidthPoints, cellHeightPoints);
}
catch (Exception ex)
{
Logger.Log($"Error while drawing cell: {ex.Message}",Logger.LogType.Error);
}
// Draw address content if available
if (!string.IsNullOrEmpty(address)) DrawMarkdownText(gfx, address, x, y, cellWidthPoints, cellHeightPoints);
} }
private void DrawEmptyCell(XGraphics gfx, double x, double y) private void DrawEmptyCell(XGraphics gfx, double x, double y)
{ {
var cellWidthPoints = MmToPoints(GetCellWidthMm()); try
var cellHeightPoints = MmToPoints(GetCellHeightMm()); {
var cellWidthPoints = MmToPoints(GetCellWidthMm());
var cellHeightPoints = MmToPoints(GetCellHeightMm());
var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints);
gfx.DrawRectangle(XPens.Black, rect);
}
catch (Exception ex)
{
Logger.Log($"Error while drawing empty cell: {ex.Message}",Logger.LogType.Error);
}
var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints);
gfx.DrawRectangle(XPens.Black, rect);
} }
private void DrawMarkdownText(XGraphics gfx, string text, double x, double y, double cellWidth, double cellHeight) private void DrawMarkdownText(XGraphics gfx, string text, double x, double y, double cellWidth, double cellHeight)
{ {
var paddingLeftPoints = MmToPoints(_settings.cellPaddingLeftMm); try
var paddingRightPoints = MmToPoints(_settings.cellPaddingRightMm);
var paddingTopPoints = MmToPoints(_settings.cellPaddingTopMm);
var paddingBottomPoints = MmToPoints(_settings.cellPaddingBottomMm);
var maxWidth = Math.Max(0, cellWidth - paddingLeftPoints - paddingRightPoints);
// Split text by newlines and remove empty trailing lines
var rawLines = text.Split(new[] { "\n" }, StringSplitOptions.None);
var lines = rawLines.Where(l => l != null).ToArray();
// Use a conservative line height in points
var lineHeight = _regularFont.Size * 1.2;
// Calculate total height of the text block
var totalHeight = lines.Length * lineHeight;
// Start drawing from the top of the cell (align addresses to top)
var startY = y + paddingTopPoints;
var currentY = startY;
foreach (var line in lines)
{ {
// Stop if we've reached the top boundary var paddingLeftPoints = MmToPoints(_settings.cellPaddingLeftMm);
if (currentY + lineHeight > y + cellHeight - paddingBottomPoints + 0.001) var paddingRightPoints = MmToPoints(_settings.cellPaddingRightMm);
break; var paddingTopPoints = MmToPoints(_settings.cellPaddingTopMm);
var paddingBottomPoints = MmToPoints(_settings.cellPaddingBottomMm);
// Parse and draw the line with markdown support var maxWidth = Math.Max(0, cellWidth - paddingLeftPoints - paddingRightPoints);
DrawLineWithMarkdown(gfx, line, x + paddingLeftPoints, currentY, maxWidth);
currentY += lineHeight; // Split text by newlines and remove empty trailing lines
var rawLines = text.Split(new[] { "\n" }, StringSplitOptions.None);
var lines = rawLines.Where(l => l != null).ToArray();
// Use a conservative line height in points
var lineHeight = _regularFont.Size * 1.2;
// Calculate total height of the text block
var totalHeight = lines.Length * lineHeight;
// Start drawing from the top of the cell (align addresses to top)
var startY = y + paddingTopPoints;
var currentY = startY;
foreach (var line in lines)
{
// Stop if we've reached the top boundary
if (currentY + lineHeight > y + cellHeight - paddingBottomPoints + 0.001)
break;
// Parse and draw the line with markdown support
DrawLineWithMarkdown(gfx, line, x + paddingLeftPoints, currentY, maxWidth);
currentY += lineHeight;
}
} }
catch (Exception ex)
{
Logger.Log($"Error while drawing markdown text: {ex.Message}",Logger.LogType.Error);
}
} }
private void DrawLineWithMarkdown(XGraphics gfx, string line, double x, double y, double maxWidth) private void DrawLineWithMarkdown(XGraphics gfx, string line, double x, double y, double maxWidth)
{ {
if (string.IsNullOrWhiteSpace(line)) return; try
var currentX = x;
var i = 0;
while (i < line.Length)
{ {
if (currentX - x >= maxWidth) if (string.IsNullOrWhiteSpace(line)) return;
break; var currentX = x;
var i = 0;
var remainingWidth = maxWidth - (currentX - x); while (i < line.Length)
// Check for small-font tag <font6> ... </font6>
if (i <= line.Length - 7 && line.Substring(i, 7) == "<font6>")
{ {
var endTag = line.IndexOf("</font6>", i + 7, StringComparison.Ordinal); if (currentX - x >= maxWidth)
if (endTag != -1) break;
{
var inner = line.Substring(i + 7, endTag - (i + 7));
if (!string.IsNullOrEmpty(inner))
{
var measuredSmall = gfx.MeasureString(inner, _smallFont);
if (measuredSmall.Width > remainingWidth) var remainingWidth = maxWidth - (currentX - x);
// Check for small-font tag <font6> ... </font6>
if (i <= line.Length - 7 && line.Substring(i, 7) == "<font6>")
{
var endTag = line.IndexOf("</font6>", i + 7, StringComparison.Ordinal);
if (endTag != -1)
{
var inner = line.Substring(i + 7, endTag - (i + 7));
if (!string.IsNullOrEmpty(inner))
{ {
inner = TruncateTextToWidth(gfx, inner, _smallFont, remainingWidth); var measuredSmall = gfx.MeasureString(inner, _smallFont);
measuredSmall = gfx.MeasureString(inner, _smallFont);
if (measuredSmall.Width > remainingWidth)
{
inner = TruncateTextToWidth(gfx, inner, _smallFont, remainingWidth);
measuredSmall = gfx.MeasureString(inner, _smallFont);
}
gfx.DrawString(inner, _smallFont, XBrushes.Black,
new XRect(currentX, y, remainingWidth, _smallFont.Size * 1.2),
XStringFormats.TopLeft);
currentX += measuredSmall.Width;
} }
gfx.DrawString(inner, _smallFont, XBrushes.Black, i = endTag + 8; // move past </font6>
new XRect(currentX, y, remainingWidth, _smallFont.Size * 1.2), continue;
XStringFormats.TopLeft);
currentX += measuredSmall.Width;
} }
i = endTag + 8; // move past </font6>
continue;
} }
}
// Check for bold marker (**) // Check for bold marker (**)
if (i < line.Length - 1 && line[i] == '*' && line[i + 1] == '*') if (i < line.Length - 1 && line[i] == '*' && line[i + 1] == '*')
{
// Find closing **
var endIndex = line.IndexOf("**", i + 2, StringComparison.Ordinal);
if (endIndex != -1)
{ {
var boldText = line.Substring(i + 2, endIndex - (i + 2)); // Find closing **
var measured = gfx.MeasureString(boldText, _boldFont); var endIndex = line.IndexOf("**", i + 2, StringComparison.Ordinal);
if (endIndex != -1)
{
var boldText = line.Substring(i + 2, endIndex - (i + 2));
var measured = gfx.MeasureString(boldText, _boldFont);
if (measured.Width > remainingWidth)
{
boldText = TruncateTextToWidth(gfx, boldText, _boldFont, remainingWidth);
measured = gfx.MeasureString(boldText, _boldFont);
}
// Draw bold text and measure width accurately
gfx.DrawString(boldText, _boldFont, XBrushes.Black,
new XRect(currentX, y, remainingWidth, _boldFont.Size * 1.2),
XStringFormats.TopLeft);
currentX += measured.Width;
i = endIndex + 2;
continue;
}
}
// Regular text until next ** or end of line
var nextBoldIndex = line.IndexOf("**", i, StringComparison.Ordinal);
var textEnd = nextBoldIndex == -1 ? line.Length : nextBoldIndex;
var regularText = line.Substring(i, textEnd - i);
if (!string.IsNullOrEmpty(regularText))
{
var measured = gfx.MeasureString(regularText, _regularFont);
if (measured.Width > remainingWidth) if (measured.Width > remainingWidth)
{ {
boldText = TruncateTextToWidth(gfx, boldText, _boldFont, remainingWidth); regularText = TruncateTextToWidth(gfx, regularText, _regularFont, remainingWidth);
measured = gfx.MeasureString(boldText, _boldFont); measured = gfx.MeasureString(regularText, _regularFont);
} }
// Draw bold text and measure width accurately gfx.DrawString(regularText, _regularFont, XBrushes.Black,
gfx.DrawString(boldText, _boldFont, XBrushes.Black, new XRect(currentX, y, remainingWidth, _regularFont.Size * 1.2), XStringFormats.TopLeft);
new XRect(currentX, y, remainingWidth, _boldFont.Size * 1.2),
XStringFormats.TopLeft);
currentX += measured.Width; currentX += measured.Width;
i = endIndex + 2;
continue;
}
}
// Regular text until next ** or end of line
var nextBoldIndex = line.IndexOf("**", i, StringComparison.Ordinal);
var textEnd = nextBoldIndex == -1 ? line.Length : nextBoldIndex;
var regularText = line.Substring(i, textEnd - i);
if (!string.IsNullOrEmpty(regularText))
{
var measured = gfx.MeasureString(regularText, _regularFont);
if (measured.Width > remainingWidth)
{
regularText = TruncateTextToWidth(gfx, regularText, _regularFont, remainingWidth);
measured = gfx.MeasureString(regularText, _regularFont);
} }
gfx.DrawString(regularText, _regularFont, XBrushes.Black, i = textEnd;
new XRect(currentX, y, remainingWidth, _regularFont.Size * 1.2), XStringFormats.TopLeft);
currentX += measured.Width;
} }
i = textEnd;
} }
catch (Exception ex)
{
Logger.Log($"Error while drawing markdown line: {ex.Message}",Logger.LogType.Error);
}
} }
private string TruncateTextToWidth(XGraphics gfx, string text, XFont font, double maxWidth) private string TruncateTextToWidth(XGraphics gfx, string text, XFont font, double maxWidth)
{ {
if (string.IsNullOrEmpty(text)) try
return text;
for (var len = text.Length; len > 0; len--)
{ {
var truncated = text.Substring(0, len); if (string.IsNullOrEmpty(text))
var measured = gfx.MeasureString(truncated, font); return text;
if (measured.Width <= maxWidth)
return truncated; for (var len = text.Length; len > 0; len--)
{
var truncated = text.Substring(0, len);
var measured = gfx.MeasureString(truncated, font);
if (measured.Width <= maxWidth)
return truncated;
}
return string.Empty;
}
catch (Exception ex)
{
Logger.Log($"Error while truncating text to width: {ex.Message}",Logger.LogType.Error);
} }
return string.Empty; return null;
} }
/// <summary> /// <summary>