Try con QuestPDF
All checks were successful
Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 7m36s
All checks were successful
Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 7m36s
Se elimina Puppeteer y Chromium. Se utiliza QuestPDF para mayor velocidad y sin Razor.
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Threading.Tasks;
|
||||
using PuppeteerSharp.Media;
|
||||
|
||||
namespace GestionIntegral.Api.Services.Pdf
|
||||
{
|
||||
/// <summary>
|
||||
/// Define las opciones de configuración para la generación de un PDF.
|
||||
/// </summary>
|
||||
public class PdfGenerationOptions
|
||||
{
|
||||
public PaperFormat? Format { get; set; } = PaperFormat.A4;
|
||||
public MarginOptions? Margin { get; set; }
|
||||
public string? HeaderTemplate { get; set; }
|
||||
public string? FooterTemplate { get; set; }
|
||||
public bool PrintBackground { get; set; } = true;
|
||||
public bool Landscape { get; set; } = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Servicio para generar documentos PDF a partir de plantillas Razor.
|
||||
/// </summary>
|
||||
public interface IPdfGeneratorService
|
||||
{
|
||||
/// <summary>
|
||||
/// Genera un archivo PDF a partir de una plantilla Razor y un modelo de datos.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">El tipo del modelo de datos.</typeparam>
|
||||
/// <param name="templatePath">La ruta relativa de la plantilla Razor (ej: "Controllers/Reportes/Templates/MiReporte.cshtml").</param>
|
||||
/// <param name="model">El objeto con los datos para rellenar la plantilla.</param>
|
||||
/// <param name="options">Opciones de configuración para el PDF (márgenes, formato, etc.).</param>
|
||||
/// <returns>Un array de bytes representando el archivo PDF generado.</returns>
|
||||
Task<byte[]> GeneratePdfFromRazorTemplateAsync<T>(string templatePath, T model, PdfGenerationOptions? options = null);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GestionIntegral.Api.Services.Pdf
|
||||
{
|
||||
// Una interfaz simple para mantener la inyección de dependencias
|
||||
public interface IQuestPdfGenerator
|
||||
{
|
||||
Task<byte[]> GeneratePdfAsync(IDocument document);
|
||||
}
|
||||
|
||||
public class QuestPdfGenerator : IQuestPdfGenerator
|
||||
{
|
||||
private readonly ILogger<QuestPdfGenerator> _logger;
|
||||
|
||||
public QuestPdfGenerator(ILogger<QuestPdfGenerator> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
// --- Configuración de la licencia ---
|
||||
QuestPDF.Settings.License = LicenseType.Community;
|
||||
_logger.LogInformation("QuestPDF inicializado con licencia Community.");
|
||||
}
|
||||
|
||||
public async Task<byte[]> GeneratePdfAsync(IDocument document)
|
||||
{
|
||||
_logger.LogInformation("Generando PDF con QuestPDF para el documento de tipo {DocumentType}.", document.GetType().Name);
|
||||
try
|
||||
{
|
||||
// GeneratePdf() es síncrono, pero lo envolvemos en un Task para mantener la interfaz asíncrona.
|
||||
byte[] pdfBytes = await Task.Run(() => document.GeneratePdf());
|
||||
|
||||
_logger.LogInformation("PDF generado exitosamente con QuestPDF ({Length} bytes).", pdfBytes.Length);
|
||||
return pdfBytes;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error durante la generación del PDF con QuestPDF.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user