98 lines
3.8 KiB
C#
98 lines
3.8 KiB
C#
|
|
using Microsoft.Extensions.Hosting;
|
||
|
|
using Microsoft.Extensions.Logging;
|
||
|
|
using PuppeteerSharp;
|
||
|
|
using PuppeteerSharp.Media;
|
||
|
|
using RazorLight;
|
||
|
|
using System;
|
||
|
|
using System.Threading.Tasks;
|
||
|
|
|
||
|
|
namespace GestionIntegral.Api.Services.Pdf
|
||
|
|
{
|
||
|
|
public class PuppeteerPdfGenerator : IPdfGeneratorService
|
||
|
|
{
|
||
|
|
private readonly IRazorLightEngine _razorEngine;
|
||
|
|
private readonly ILogger<PuppeteerPdfGenerator> _logger;
|
||
|
|
|
||
|
|
public PuppeteerPdfGenerator(IHostEnvironment hostEnvironment, ILogger<PuppeteerPdfGenerator> logger)
|
||
|
|
{
|
||
|
|
_logger = logger;
|
||
|
|
var rootPath = hostEnvironment.ContentRootPath;
|
||
|
|
_razorEngine = new RazorLightEngineBuilder()
|
||
|
|
.UseFileSystemProject(rootPath)
|
||
|
|
.UseMemoryCachingProvider()
|
||
|
|
.Build();
|
||
|
|
|
||
|
|
_logger.LogInformation("Verificando caché de Chromium…");
|
||
|
|
new BrowserFetcher().DownloadAsync().Wait();
|
||
|
|
_logger.LogInformation("Chromium listo en caché.");
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<byte[]> GeneratePdfFromRazorTemplateAsync<T>(string templatePath, T model, PdfGenerationOptions? options = null)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrEmpty(templatePath))
|
||
|
|
throw new ArgumentNullException(nameof(templatePath), "La ruta de la plantilla no puede ser nula o vacía.");
|
||
|
|
|
||
|
|
if (model == null)
|
||
|
|
throw new ArgumentNullException(nameof(model), "El modelo de datos no puede ser nulo.");
|
||
|
|
|
||
|
|
options ??= new PdfGenerationOptions();
|
||
|
|
|
||
|
|
string htmlContent;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
htmlContent = await _razorEngine.CompileRenderAsync(templatePath, model);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
_logger.LogError(ex, "Error al compilar la plantilla Razor: {TemplatePath}", templatePath);
|
||
|
|
throw new InvalidOperationException($"No se pudo renderizar la plantilla Razor '{templatePath}'.", ex);
|
||
|
|
}
|
||
|
|
|
||
|
|
IBrowser? browser = null;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var launchOptions = new LaunchOptions
|
||
|
|
{
|
||
|
|
Headless = true,
|
||
|
|
Args = new[] { "--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage" }
|
||
|
|
};
|
||
|
|
|
||
|
|
_logger.LogInformation("Lanzando Chromium headless…");
|
||
|
|
browser = await Puppeteer.LaunchAsync(launchOptions);
|
||
|
|
await using var page = await browser.NewPageAsync();
|
||
|
|
|
||
|
|
_logger.LogInformation("Estableciendo contenido HTML en la página.");
|
||
|
|
await page.SetContentAsync(htmlContent, new NavigationOptions { WaitUntil = new[] { WaitUntilNavigation.Networkidle0 } });
|
||
|
|
|
||
|
|
_logger.LogInformation("Generando PDF…");
|
||
|
|
var pdfOptions = new PdfOptions
|
||
|
|
{
|
||
|
|
Format = options.Format,
|
||
|
|
HeaderTemplate = options.HeaderTemplate,
|
||
|
|
FooterTemplate = options.FooterTemplate,
|
||
|
|
PrintBackground = options.PrintBackground,
|
||
|
|
Landscape = options.Landscape,
|
||
|
|
MarginOptions = options.Margin ?? new MarginOptions()
|
||
|
|
};
|
||
|
|
|
||
|
|
var pdfBytes = await page.PdfDataAsync(pdfOptions);
|
||
|
|
_logger.LogInformation("PDF generado exitosamente ({Length} bytes).", pdfBytes.Length);
|
||
|
|
|
||
|
|
return pdfBytes;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
_logger.LogError(ex, "Error durante la generación del PDF con PuppeteerSharp.");
|
||
|
|
throw;
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
if (browser is not null && !browser.IsClosed)
|
||
|
|
{
|
||
|
|
await browser.CloseAsync();
|
||
|
|
_logger.LogInformation("Navegador cerrado.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|