diff --git a/Tasks/PdfBuilder.cs b/Tasks/PdfBuilder.cs index d33a587..f7d19ae 100644 --- a/Tasks/PdfBuilder.cs +++ b/Tasks/PdfBuilder.cs @@ -18,45 +18,73 @@ public class PdfBuilder 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 { - if (Directory.Exists(Global._instance.font_path)) - { - var first = Directory.EnumerateFiles(Global._instance.font_path, "*.ttf").FirstOrDefault(); - if (!string.IsNullOrEmpty(first)) - chosenFamily = StripStyleSuffix(Path.GetFileNameWithoutExtension(first)) ?? chosenFamily; - } - } - catch - { - chosenFamily = "Arial"; - } + EnsureFontResolverRegistered(); + _settings = settings ?? new PdfExportSettings(); + + // Select first font from build output fonts folder (AppContext.BaseDirectory/fonts) + var chosenFamily = "Arial"; + try + { + if (Directory.Exists(Global._instance.font_path)) + { + 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 (Exception ex) + { + 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() { - if (GlobalFontSettings.FontResolver != null) return; - //var fontsDir = Path.Combine(AppContext.BaseDirectory, "fonts"); - GlobalFontSettings.FontResolver = new StableFontResolver(Global._instance.font_path); + try + { + 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) { - if (string.IsNullOrEmpty(name)) return name; - var idx = name.IndexOf('-'); - if (idx < 0) idx = name.IndexOf('_'); - if (idx > 0) - return name.Substring(0, idx); - return name; + try + { + if (string.IsNullOrEmpty(name)) return name; + var idx = name.IndexOf('-'); + if (idx < 0) idx = name.IndexOf('_'); + 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, 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(); - - // 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 = "" + owner.sender_address.Replace("\n", " ").Trim() + "\n"; - } - catch - { - senderLine = null; - } + // Find the AddressSet by ID + var addressSet = Settings._instance.addressSets.GetAddressSetByID(addressSetId); + if (addressSet == null) + throw new ArgumentException($"AddressSet with ID {addressSetId} not found"); - 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); - } + if (addressSet.KasPersons == null || addressSet.KasPersons.Count == 0) + throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses"); - CreateAddressLabelPdfWithPlaceholder(addresses, placeholderText, outputPath); + // Generate markdown addresses from all KasPersons in the set + //var addresses = new string?[addressSet.KasPersons.Count]; + var addresses = new List(); + + // 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 = "" + owner.sender_address.Replace("\n", " ").Trim() + "\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 (Exception ex) + { + Logger.Log($"Error while creating address label pdf from address set with placeholder: {ex.Message}",Logger.LogType.Error); + } + } /// @@ -115,26 +151,34 @@ public class PdfBuilder /// Path where the PDF should be saved public void CreateAddressLabelPdfWithPlaceholder(List addresses, string placeholderText, string outputPath) { - if (addresses == null || addresses.Count == 0) - 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) + try { - var page = document.AddPage(); - page.Size = PageSize.A4; + if (addresses == null || addresses.Count == 0) + throw new ArgumentException("Addresses array cannot be null or empty"); - using (var gfx = XGraphics.FromPdfPage(page)) - { - DrawPageWithPlaceholder(gfx, addresses, ref addressIndex, ref isFirstCell, placeholderText); - } + var document = new PdfDocument(); + + 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); } - - document.Save(outputPath); + catch (Exception ex) + { + Logger.Log($"Error while creating address label pdf with placeholder: {ex.Message}",Logger.LogType.Error); + } + } private void DrawPage(XGraphics gfx, List addresses, ref int addressIndex) @@ -159,193 +203,242 @@ public class PdfBuilder private void DrawPageWithPlaceholder(XGraphics gfx, List addresses, ref int addressIndex, ref bool isFirstCell, string placeholderText) { - for (var row = 0; row < _settings.rowsPerPage; row++) - for (var col = 0; col < _settings.columnsPerPage; col++) + try { - var x = MmToPoints(_settings.pageMarginLeftMm + col * GetCellWidthMm()); - var y = MmToPoints(_settings.pageMarginTopMm + row * GetCellHeightMm()); + for (var row = 0; row < _settings.rowsPerPage; row++) + 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 - if (isFirstCell) - { - DrawCell(gfx, x, y, placeholderText); - isFirstCell = false; - } - else if (addressIndex < addresses.Count) - { - DrawCell(gfx, x, y, addresses[addressIndex]); - addressIndex++; - } - else - { - DrawEmptyCell(gfx, x, y); + // First cell: placeholder + if (isFirstCell) + { + DrawCell(gfx, x, y, placeholderText); + isFirstCell = false; + } + else if (addressIndex < addresses.Count) + { + DrawCell(gfx, x, y, addresses[addressIndex]); + addressIndex++; + } + else + { + 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) { - var cellWidthPoints = MmToPoints(GetCellWidthMm()); - var cellHeightPoints = MmToPoints(GetCellHeightMm()); + try + { + var cellWidthPoints = MmToPoints(GetCellWidthMm()); + var cellHeightPoints = MmToPoints(GetCellHeightMm()); - // Draw cell border - var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints); - gfx.DrawRectangle(XPens.Black, rect); + // Draw cell border + var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints); + gfx.DrawRectangle(XPens.Black, rect); - // Draw address content if available - if (!string.IsNullOrEmpty(address)) DrawMarkdownText(gfx, address, x, y, cellWidthPoints, cellHeightPoints); + // 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); + } + } private void DrawEmptyCell(XGraphics gfx, double x, double y) { - var cellWidthPoints = MmToPoints(GetCellWidthMm()); - var cellHeightPoints = MmToPoints(GetCellHeightMm()); + try + { + var cellWidthPoints = MmToPoints(GetCellWidthMm()); + var cellHeightPoints = MmToPoints(GetCellHeightMm()); - var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints); - gfx.DrawRectangle(XPens.Black, rect); + 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); + } + } private void DrawMarkdownText(XGraphics gfx, string text, double x, double y, double cellWidth, double cellHeight) { - var paddingLeftPoints = MmToPoints(_settings.cellPaddingLeftMm); - 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) + try { - // Stop if we've reached the top boundary - if (currentY + lineHeight > y + cellHeight - paddingBottomPoints + 0.001) - break; + var paddingLeftPoints = MmToPoints(_settings.cellPaddingLeftMm); + var paddingRightPoints = MmToPoints(_settings.cellPaddingRightMm); + var paddingTopPoints = MmToPoints(_settings.cellPaddingTopMm); + var paddingBottomPoints = MmToPoints(_settings.cellPaddingBottomMm); - // Parse and draw the line with markdown support - DrawLineWithMarkdown(gfx, line, x + paddingLeftPoints, currentY, maxWidth); - currentY += lineHeight; + 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 + 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) { - if (string.IsNullOrWhiteSpace(line)) return; - var currentX = x; - var i = 0; - - while (i < line.Length) + try { - if (currentX - x >= maxWidth) - break; + if (string.IsNullOrWhiteSpace(line)) return; + var currentX = x; + var i = 0; - var remainingWidth = maxWidth - (currentX - x); - - // Check for small-font tag ... - if (i <= line.Length - 7 && line.Substring(i, 7) == "") + while (i < line.Length) { - var endTag = line.IndexOf("", i + 7, StringComparison.Ordinal); - if (endTag != -1) - { - var inner = line.Substring(i + 7, endTag - (i + 7)); - if (!string.IsNullOrEmpty(inner)) - { - var measuredSmall = gfx.MeasureString(inner, _smallFont); + if (currentX - x >= maxWidth) + break; - if (measuredSmall.Width > remainingWidth) + var remainingWidth = maxWidth - (currentX - x); + + // Check for small-font tag ... + if (i <= line.Length - 7 && line.Substring(i, 7) == "") + { + var endTag = line.IndexOf("", 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); - measuredSmall = gfx.MeasureString(inner, _smallFont); + var 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, - new XRect(currentX, y, remainingWidth, _smallFont.Size * 1.2), - XStringFormats.TopLeft); - currentX += measuredSmall.Width; + i = endTag + 8; // move past + continue; } - - i = endTag + 8; // move past - continue; } - } - // Check for bold marker (**) - if (i < line.Length - 1 && line[i] == '*' && line[i + 1] == '*') - { - // Find closing ** - var endIndex = line.IndexOf("**", i + 2, StringComparison.Ordinal); - if (endIndex != -1) + // Check for bold marker (**) + if (i < line.Length - 1 && line[i] == '*' && line[i + 1] == '*') { - var boldText = line.Substring(i + 2, endIndex - (i + 2)); - var measured = gfx.MeasureString(boldText, _boldFont); + // Find closing ** + 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) { - boldText = TruncateTextToWidth(gfx, boldText, _boldFont, remainingWidth); - measured = gfx.MeasureString(boldText, _boldFont); + regularText = TruncateTextToWidth(gfx, regularText, _regularFont, remainingWidth); + measured = gfx.MeasureString(regularText, _regularFont); } - // Draw bold text and measure width accurately - gfx.DrawString(boldText, _boldFont, XBrushes.Black, - new XRect(currentX, y, remainingWidth, _boldFont.Size * 1.2), - XStringFormats.TopLeft); + gfx.DrawString(regularText, _regularFont, XBrushes.Black, + new XRect(currentX, y, remainingWidth, _regularFont.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) - { - regularText = TruncateTextToWidth(gfx, regularText, _regularFont, remainingWidth); - measured = gfx.MeasureString(regularText, _regularFont); } - gfx.DrawString(regularText, _regularFont, XBrushes.Black, - new XRect(currentX, y, remainingWidth, _regularFont.Size * 1.2), XStringFormats.TopLeft); - currentX += measured.Width; + i = textEnd; } - - 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) { - if (string.IsNullOrEmpty(text)) - return text; - - for (var len = text.Length; len > 0; len--) + try { - var truncated = text.Substring(0, len); - var measured = gfx.MeasureString(truncated, font); - if (measured.Width <= maxWidth) - return truncated; + if (string.IsNullOrEmpty(text)) + return text; + + 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; } ///