using PuppeteerSharp; using PuppeteerSharp.Media; namespace GB5Shared.Export.HybridReport { public class PdfRenderer { public async Task RenderToPdfBytesAsync(string html, IDictionary? metadata = null) { // Reuse the shared, already-launched browser instead of launching a fresh Chromium // process per call — GlobalBrowser already resolves the executable via // ChromiumResolver and recovers if the cached instance crashed/disconnected. var browser = await GlobalBrowser.GetAsync().ConfigureAwait(false); await using var page = await browser.NewPageAsync(); await page.SetContentAsync(html, new NavigationOptions { WaitUntil = new[] { WaitUntilNavigation.Networkidle0 } }); // Force scaling in browser DOM before printing await page.EvaluateFunctionAsync(@"() => { const page = document.getElementById('pageContainer'); const wrapper = document.getElementById('tableWrapper'); if (!page || !wrapper) return; const pageWidth = page.clientWidth * 0.96; const tableWidth = wrapper.scrollWidth; if (tableWidth <= 0) return; let scale = pageWidth / tableWidth; if (scale > 1.12) scale = 1.12; if (scale < 0.75) scale = 0.75; wrapper.style.transform = `scale(${scale})`; wrapper.style.transformOrigin = 'top center'; const scaledWidth = tableWidth * scale; const offset = (page.clientWidth - scaledWidth) / 2; wrapper.style.marginLeft = offset > 0 ? `${offset}px` : '0'; }"); // Header/Footer templates string headerTemplate = metadata != null && metadata.ContainsKey("headerTemplate") ? metadata["headerTemplate"]?.ToString() ?? string.Empty : string.Empty; string footerTemplate = metadata != null && metadata.ContainsKey("footerTemplate") ? metadata["footerTemplate"]?.ToString() ?? "Page of " : "Page of "; var pdfOptions = new PdfOptions { Format = PaperFormat.A4, PrintBackground = true, DisplayHeaderFooter = true, HeaderTemplate = headerTemplate, FooterTemplate = footerTemplate, MarginOptions = new MarginOptions { Top = "80px", Left = "0px", Right = "20px" } }; await using var pdfStream = await page.PdfStreamAsync(pdfOptions); using var ms = new MemoryStream(); await pdfStream.CopyToAsync(ms); return ms.ToArray(); } public static async Task RenderHtmlToPdfAsync(string html, IDictionary? metadata = null) { var renderer = new PdfRenderer(); return await renderer.RenderToPdfBytesAsync(html, metadata); } } }