using System.Runtime.InteropServices; using Microsoft.Extensions.Configuration; using PuppeteerSharp; using PuppeteerSharp.Media; using Transversal.Interfaces; using Transversal.Models; namespace Transversal.Services { public class PuppeteerPdfGeneratorService : IPdfGeneratorService, IAsyncDisposable { private readonly Browser _browser; public PuppeteerPdfGeneratorService(IConfiguration config) { string? executablePath = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/usr/bin/chromium" // Docker/Linux (NO TOCAR) : ResolveWindowsBrowserPath(config); // Windows local if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { if (string.IsNullOrWhiteSpace(executablePath) || !File.Exists(executablePath)) throw new InvalidOperationException( $"No se encontró un navegador. Verifique Puppeteer:ExecutablePath. Path evaluado: '{executablePath}'."); } _browser = Puppeteer.LaunchAsync(new LaunchOptions { Headless = true, ExecutablePath = executablePath, Args = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? new[] { "--no-sandbox", "--disable-setuid-sandbox" } : Array.Empty() }).GetAwaiter().GetResult(); } private static string? ResolveWindowsBrowserPath(IConfiguration config) { // 1) appsettings: "Puppeteer:ExecutablePath" var configured = config["Puppeteer:ExecutablePath"]; if (!string.IsNullOrWhiteSpace(configured)) return configured; // 2) fallbacks típicos (Chrome/Edge) var candidates = new[] { @"C:\Program Files\Google\Chrome\Application\chrome.exe", @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe", @"C:\Program Files\Microsoft\Edge\Application\msedge.exe", @"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe", }; return candidates.FirstOrDefault(File.Exists); } public async Task GeneratePdfFromHtmlAsync(string htmlContent, PdfGenerationOptions options = null) { options ??= new PdfGenerationOptions(); using var page = await _browser.NewPageAsync(); await page.SetContentAsync(htmlContent); var pdfOptions = new PdfOptions { Format = options.Format, Landscape = options.Landscape, PrintBackground = options.PrintBackground, Scale = options.Scale, MarginOptions = options.Margins ?? new MarginOptions(), DisplayHeaderFooter = !string.IsNullOrEmpty(options.HeaderTemplate) || !string.IsNullOrEmpty(options.FooterTemplate), HeaderTemplate = options.HeaderTemplate, FooterTemplate = options.FooterTemplate }; return await page.PdfDataAsync(pdfOptions); } public async ValueTask DisposeAsync() { if (_browser != null) await _browser.CloseAsync(); } } }