using GB5Shared.DTO.Report; using GB5Shared.Export.HybridReport; using HandlebarsDotNet; using PuppeteerSharp; using PuppeteerSharp.Media; namespace GB5Shared.Export.Pivot { public sealed class PivotPdfExport : IPivotPdfExport { public async Task ExportAsync( PivotResultDTO pivotResult, ReportStandardDTO reportStandard, string templatePath, CancellationToken ct = default) { var allColumnHeaders = pivotResult.RowHeaders.Concat(pivotResult.PivotColumns).ToList(); var context = new { Report = reportStandard, ColumnHeaders = allColumnHeaders, RowHeaders = pivotResult.RowHeaders, PivotColumns = pivotResult.PivotColumns, Rows = pivotResult.Rows, GrandTotal = pivotResult.GrandTotal, GeneratedOn = DateTime.UtcNow }; string html; if (System.IO.File.Exists(templatePath)) { var source = await System.IO.File.ReadAllTextAsync(templatePath, ct).ConfigureAwait(false); var hb = Handlebars.Create(); hb.RegisterHelper("formatN2", (writer, _, parameters) => { if (parameters.Length >= 1 && decimal.TryParse(parameters[0]?.ToString(), System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var d)) writer.WriteSafeString(d.ToString("N2")); else writer.WriteSafeString(parameters.FirstOrDefault()?.ToString() ?? ""); }); var template = hb.Compile(source); html = template(context); } else { html = BuildFallbackHtml(pivotResult, allColumnHeaders, reportStandard); } return await RenderToPdfAsync(html, ct).ConfigureAwait(false); } private static string BuildFallbackHtml( PivotResultDTO pivot, List allHeaders, ReportStandardDTO report) { var sb = new System.Text.StringBuilder(); sb.Append(""); sb.Append(""); sb.Append($"

{System.Net.WebUtility.HtmlEncode(report.ReportTitle ?? "Pivot Report")}

"); sb.Append(""); foreach (var h in allHeaders) sb.Append($""); sb.Append(""); foreach (var row in pivot.Rows) { sb.Append(""); foreach (var h in allHeaders) { row.TryGetValue(h, out var val); sb.Append($""); } sb.Append(""); } if (pivot.GrandTotal != null) { sb.Append(""); for (int i = 0; i < allHeaders.Count; i++) { if (i == 0) { sb.Append(""); } else { pivot.GrandTotal.TryGetValue(allHeaders[i], out var val); sb.Append($""); } } sb.Append(""); } sb.Append("
{System.Net.WebUtility.HtmlEncode(h)}
{System.Net.WebUtility.HtmlEncode(val?.ToString() ?? "")}
Grand Total{System.Net.WebUtility.HtmlEncode(val?.ToString() ?? "")}
"); return sb.ToString(); } private static async Task RenderToPdfAsync(string html, CancellationToken ct) { var browser = await GlobalBrowser.GetAsync().ConfigureAwait(false); await using var page = await browser.NewPageAsync().ConfigureAwait(false); await page.SetContentAsync(html, new NavigationOptions { WaitUntil = [WaitUntilNavigation.Networkidle0], Timeout = 0 }).ConfigureAwait(false); ct.ThrowIfCancellationRequested(); return await page.PdfDataAsync(new PdfOptions { Format = PaperFormat.A4, Landscape = true, PrintBackground = true, MarginOptions = new MarginOptions { Top = "40px", Left = "10px", Right = "10px", Bottom = "40px" } }).ConfigureAwait(false); } } }