Cómo Generar un PDF en ASP.NET Usando C#
La generación de archivos PDF mediante programación es un requisito fundamental para las aplicaciones web modernas, ya se trate de facturas, informes, certificados o tickets. Si usted es un desarrollador ASP.NET Core .NET que busca implementar una sólida generación de PDF con una representación perfecta de píxeles y funciones empresariales, está en el lugar adecuado.
Esta completa guía le guiará a través de la generación profesional de archivos PDF utilizando IronPDF, una potente biblioteca .NET que permite la creación de documentos PDF con facilidad. Exploraremos desde la configuración básica hasta el procesamiento avanzado por lotes, mostrando lo eficiente y fácil que puede ser su integración. Al final, dispondrá de un potente conversor de PDF para generar documentos PDF en su aplicación ASP.NET utilizando IronPDF como un profesional.
¿Por qué generar archivos PDF en ASP.NET Core?
La generación de PDF del lado del servidor ofrece ventajas significativas sobre las alternativas del lado del cliente. Cuando se generan archivos PDF en el servidor, se garantiza una salida coherente en todos los navegadores y dispositivos, se elimina la dependencia de los recursos del lado del cliente y se mantiene un mejor control sobre los datos confidenciales. Los escenarios empresariales más comunes para la conversión de HTML a PDF incluyen:
- Documentos financieros: Facturas, estados de cuenta y recibos de transacciones
- Informes de cumplimiento: Presentaciones reglamentarias y documentación de auditoría
- Certificados de usuario: Finalizaciones de formación y certificaciones profesionales
- Entradas para eventos: pases de entrada y tarjetas de embarque con código QR
- Exportaciones de datos: informes analíticos e instantáneas del panel
Además, el enfoque del servidor garantiza que los PDF sean coherentes en todos los navegadores y sistemas operativos. Esto hace que sea muy apreciada en términos de documentos legales y financieros.
¿Cómo transforma IronPDF su generación de PDF?
IronPDF es una biblioteca PDF que destaca en el ecosistema .NET gracias a su conversor HTML, que utiliza un completo motor de renderizado Chrome bajo el capó. Esto significa que los documentos PDF se muestran exactamente igual que en Google Chrome, con compatibilidad total con CSS3 moderno, ejecución de JavaScript y fuentes web. A diferencia de otras bibliotecas, IronPDF le permite convertir directamente sus conocimientos existentes de HTML, CSS y JavaScript en capacidades de generación de PDF dentro de sus aplicaciones ASP.NET Core.
Ventajas clave que transforman su desarrollo:
- Representación basada en Chrome para una precisión de píxeles perfecta, igualando lo que se ve en el navegador al realizar tareas de conversión de HTML a PDF (a menudo en tan solo unas pocas líneas de código)
- Compatibilidad total con HTML5, CSS3 y JavaScript, incluidos marcos modernos como Bootstrap
- Integración nativa de .NET sin dependencias externas ni herramientas de línea de comandos
- Compatibilidad multiplataforma con .NET 6/7/8, .NET Core e incluso .NET Framework 4.6.2+ para aplicaciones heredadas
- Completa API para la manipulación posterior a la generación, incluyendo fusión, marca de agua, y firma digital de sus páginas PDF.
Cómo configurar su proyecto ASP.NET Core
Creemos una nueva aplicación ASP.NET Core MVC configurada para la generación de PDF. Construiremos una configuración lista para producción con inyección de dependencias y gestión de errores adecuados. Se tratará de un nuevo proyecto .NET en Visual Studio; no obstante, también puede utilizar un proyecto existente.
Creación del proyecto
Crearemos este proyecto y le daremos un nombre adecuado ejecutando el siguiente comando:
dotnet new mvc -n PdfGeneratorApp
cd PdfGeneratorAppInstalación de IronPDF
Antes de empezar a generar documentos PDF en ASP.NET, añada IronPDF a su proyecto ejecutando las siguientes líneas en la consola del gestor de paquetes NuGet:
Install-Package IronPdf
Install-Package IronPdf.Extensions.Mvc.Core
Para obtener información detallada sobre las opciones de instalación, como configuración de paquetes NuGet y instalador de Windows, consulte la documentación oficial.
Configuración de servicios en Program.cs
using IronPdf;
using IronPdf.Extensions.Mvc.Core;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
var builder = WebApplication.CreateBuilder(args);
// Configure IronPDF license (use your license key)
License.LicenseKey = "your-license-key";
// Add MVC services
builder.Services.AddControllersWithViews();
// Register IronPDF services
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
builder.Services.AddSingleton<IRazorViewRenderer, RazorViewRenderer>();
// Configure ChromePdfRenderer as a service
builder.Services.AddSingleton<ChromePdfRenderer>(provider =>
{
var renderer = new ChromePdfRenderer();
// Configure rendering options
renderer.RenderingOptions.MarginTop = 25;
renderer.RenderingOptions.MarginBottom = 25;
renderer.RenderingOptions.MarginLeft = 20;
renderer.RenderingOptions.MarginRight = 20;
renderer.RenderingOptions.EnableJavaScript = true;
renderer.RenderingOptions.RenderDelay = 500; // Wait for JS execution
return renderer;
});
var app = builder.Build();
// Configure middleware pipeline
if (!app.Environment.IsDevelopment())
{
app.UseExcepciónHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();using IronPdf;
using IronPdf.Extensions.Mvc.Core;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
var builder = WebApplication.CreateBuilder(args);
// Configure IronPDF license (use your license key)
License.LicenseKey = "your-license-key";
// Add MVC services
builder.Services.AddControllersWithViews();
// Register IronPDF services
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
builder.Services.AddSingleton<IRazorViewRenderer, RazorViewRenderer>();
// Configure ChromePdfRenderer as a service
builder.Services.AddSingleton<ChromePdfRenderer>(provider =>
{
var renderer = new ChromePdfRenderer();
// Configure rendering options
renderer.RenderingOptions.MarginTop = 25;
renderer.RenderingOptions.MarginBottom = 25;
renderer.RenderingOptions.MarginLeft = 20;
renderer.RenderingOptions.MarginRight = 20;
renderer.RenderingOptions.EnableJavaScript = true;
renderer.RenderingOptions.RenderDelay = 500; // Wait for JS execution
return renderer;
});
var app = builder.Build();
// Configure middleware pipeline
if (!app.Environment.IsDevelopment())
{
app.UseExcepciónHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();Esta configuración establece IronPDF como un servicio singleton, lo que garantiza un uso eficiente de los recursos en toda la aplicación. Puede modificar las `RenderingOptions` para adaptarlas a sus necesidades específicas. Llegados a este punto, la estructura de carpetas del Explorador de soluciones de Visual Studio debería tener el siguiente aspecto:
Generación de PDF a partir de Razor Views
El enfoque más potente para generar nuevos documentos PDF en ASP.NET Core implica aprovechar Vistas Razor para la conversión de PDF. Esto le permite utilizar patrones MVC familiares, modelos fuertemente tipados y sintaxis Razor para crear PDF dinámicos a partir de archivos HTML personalizados, páginas web y otras fuentes. Según la documentación de Microsoft sobre Razor Pages, este enfoque proporciona la separación más limpia de preocupaciones para escenarios centrados en páginas.
Creación del modelo de datos
En primer lugar, vamos a crear un modelo completo que represente un documento comercial típico:
namespace PdfGeneratorApp.Models
{
public class InvoiceModel
{
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public DateTime DueDate { get; set; }
public CompanyInfo Vendor { get; set; }
public CompanyInfo Customer { get; set; }
public List<InvoiceItem> Items { get; set; }
public decimal Subtotal => Items?.Sum(x => x.Total) ?? 0;
public decimal TaxRate { get; set; }
public decimal TaxAmount => Subtotal * (TaxRate / 100);
public decimal Total => Subtotal + TaxAmount;
public string Notes { get; set; }
public string PaymentTerms { get; set; }
}
public class CompanyInfo
{
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
public class InvoiceItem
{
public string Description { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal Total => Quantity * UnitPrice;
}
}namespace PdfGeneratorApp.Models
{
public class InvoiceModel
{
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public DateTime DueDate { get; set; }
public CompanyInfo Vendor { get; set; }
public CompanyInfo Customer { get; set; }
public List<InvoiceItem> Items { get; set; }
public decimal Subtotal => Items?.Sum(x => x.Total) ?? 0;
public decimal TaxRate { get; set; }
public decimal TaxAmount => Subtotal * (TaxRate / 100);
public decimal Total => Subtotal + TaxAmount;
public string Notes { get; set; }
public string PaymentTerms { get; set; }
}
public class CompanyInfo
{
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
public class InvoiceItem
{
public string Description { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal Total => Quantity * UnitPrice;
}
}Construyendo la vista Razor
Cree una vista en Views/Invoice/InvoiceTemplate.cshtml que represente su contenido PDF:
@model PdfGeneratorApp.Models.InvoiceModel
@{
Layout = null; // PDFs should not use the site layout
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Invoice @Model.InvoiceNumber</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
}
.invoice-container {
max-width: 800px;
margin: 0 auto;
padding: 30px;
}
.invoice-header {
display: flex;
justify-content: space-between;
margin-bottom: 40px;
padding-bottom: 20px;
border-bottom: 2px solid #2c3e50;
}
.company-details {
flex: 1;
}
.company-details h1 {
color: #2c3e50;
margin-bottom: 10px;
}
.invoice-details {
text-align: right;
}
.invoice-details h2 {
color: #2c3e50;
font-size: 28px;
margin-bottom: 10px;
}
.invoice-details p {
margin: 5px 0;
font-size: 14px;
}
.billing-details {
display: flex;
justify-content: space-between;
margin-bottom: 40px;
}
.billing-section {
flex: 1;
}
.billing-section h3 {
color: #2c3e50;
margin-bottom: 10px;
font-size: 16px;
text-transform: uppercase;
}
.items-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 40px;
}
.items-table thead {
background-color: #2c3e50;
color: white;
}
.items-table th,
.items-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.items-table tbody tr:hover {
background-color: #f5f5f5;
}
.text-right {
text-align: right;
}
.invoice-summary {
display: flex;
justify-content: flex-end;
margin-bottom: 40px;
}
.summary-table {
width: 300px;
}
.summary-table tr {
border-bottom: 1px solid #ddd;
}
.summary-table td {
padding: 8px;
}
.summary-table .total-row {
font-weight: bold;
font-size: 18px;
color: #2c3e50;
border-top: 2px solid #2c3e50;
}
.invoice-footer {
margin-top: 60px;
padding-top: 20px;
border-top: 1px solid #ddd;
}
.footer-notes {
margin-bottom: 20px;
}
.footer-notes h4 {
color: #2c3e50;
margin-bottom: 10px;
}
@@media print {
.invoice-container {
padding: 0;
}
}
</style>
</head>
<body>
<div class="invoice-container">
<div class="invoice-header">
<div class="company-details">
<h1>@Model.Vendor.Name</h1>
<p>@Model.Vendor.Address</p>
<p>@Model.Vendor.City, @Model.Vendor.State @Model.Vendor.ZipCode</p>
<p>Email: @Model.Vendor.Email</p>
<p>Phone: @Model.Vendor.Phone</p>
</div>
<div class="invoice-details">
<h2>INVOICE</h2>
<p><strong>Invoice #:</strong> @Model.InvoiceNumber</p>
<p><strong>Date:</strong> @Model.InvoiceDate.ToString("MMM dd, yyyy")</p>
<p><strong>Due Date:</strong> @Model.DueDate.ToString("MMM dd, yyyy")</p>
</div>
</div>
<div class="billing-details">
<div class="billing-section">
<h3>Bill To:</h3>
<p><strong>@Model.Customer.Name</strong></p>
<p>@Model.Customer.Address</p>
<p>@Model.Customer.City, @Model.Customer.State @Model.Customer.ZipCode</p>
<p>@Model.Customer.Email</p>
</div>
</div>
<table class="items-table">
<thead>
<tr>
<th>Description</th>
<th class="text-right">Quantity</th>
<th class="text-right">Unit Price</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Items)
{
<tr>
<td>@item.Description</td>
<td class="text-right">@item.Quantity</td>
<td class="text-right">@item.UnitPrice.ToString("C")</td>
<td class="text-right">@item.Total.ToString("C")</td>
</tr>
}
</tbody>
</table>
<div class="invoice-summary">
<table class="summary-table">
<tr>
<td>Subtotal:</td>
<td class="text-right">@Model.Subtotal.ToString("C")</td>
</tr>
<tr>
<td>Tax (@Model.TaxRate%):</td>
<td class="text-right">@Model.TaxAmount.ToString("C")</td>
</tr>
<tr class="total-row">
<td>Total:</td>
<td class="text-right">@Model.Total.ToString("C")</td>
</tr>
</table>
</div>
@if (!string.IsNullOrEmpty(Model.Notes))
{
<div class="invoice-footer">
<div class="footer-notes">
<h4>Notes</h4>
<p>@Model.Notes</p>
</div>
</div>
}
@if (!string.IsNullOrEmpty(Model.PaymentTerms))
{
<div class="footer-notes">
<h4>Payment Terms</h4>
<p>@Model.PaymentTerms</p>
</div>
}
</div>
</body>
</html>@model PdfGeneratorApp.Models.InvoiceModel
@{
Layout = null; // PDFs should not use the site layout
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Invoice @Model.InvoiceNumber</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
}
.invoice-container {
max-width: 800px;
margin: 0 auto;
padding: 30px;
}
.invoice-header {
display: flex;
justify-content: space-between;
margin-bottom: 40px;
padding-bottom: 20px;
border-bottom: 2px solid #2c3e50;
}
.company-details {
flex: 1;
}
.company-details h1 {
color: #2c3e50;
margin-bottom: 10px;
}
.invoice-details {
text-align: right;
}
.invoice-details h2 {
color: #2c3e50;
font-size: 28px;
margin-bottom: 10px;
}
.invoice-details p {
margin: 5px 0;
font-size: 14px;
}
.billing-details {
display: flex;
justify-content: space-between;
margin-bottom: 40px;
}
.billing-section {
flex: 1;
}
.billing-section h3 {
color: #2c3e50;
margin-bottom: 10px;
font-size: 16px;
text-transform: uppercase;
}
.items-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 40px;
}
.items-table thead {
background-color: #2c3e50;
color: white;
}
.items-table th,
.items-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.items-table tbody tr:hover {
background-color: #f5f5f5;
}
.text-right {
text-align: right;
}
.invoice-summary {
display: flex;
justify-content: flex-end;
margin-bottom: 40px;
}
.summary-table {
width: 300px;
}
.summary-table tr {
border-bottom: 1px solid #ddd;
}
.summary-table td {
padding: 8px;
}
.summary-table .total-row {
font-weight: bold;
font-size: 18px;
color: #2c3e50;
border-top: 2px solid #2c3e50;
}
.invoice-footer {
margin-top: 60px;
padding-top: 20px;
border-top: 1px solid #ddd;
}
.footer-notes {
margin-bottom: 20px;
}
.footer-notes h4 {
color: #2c3e50;
margin-bottom: 10px;
}
@@media print {
.invoice-container {
padding: 0;
}
}
</style>
</head>
<body>
<div class="invoice-container">
<div class="invoice-header">
<div class="company-details">
<h1>@Model.Vendor.Name</h1>
<p>@Model.Vendor.Address</p>
<p>@Model.Vendor.City, @Model.Vendor.State @Model.Vendor.ZipCode</p>
<p>Email: @Model.Vendor.Email</p>
<p>Phone: @Model.Vendor.Phone</p>
</div>
<div class="invoice-details">
<h2>INVOICE</h2>
<p><strong>Invoice #:</strong> @Model.InvoiceNumber</p>
<p><strong>Date:</strong> @Model.InvoiceDate.ToString("MMM dd, yyyy")</p>
<p><strong>Due Date:</strong> @Model.DueDate.ToString("MMM dd, yyyy")</p>
</div>
</div>
<div class="billing-details">
<div class="billing-section">
<h3>Bill To:</h3>
<p><strong>@Model.Customer.Name</strong></p>
<p>@Model.Customer.Address</p>
<p>@Model.Customer.City, @Model.Customer.State @Model.Customer.ZipCode</p>
<p>@Model.Customer.Email</p>
</div>
</div>
<table class="items-table">
<thead>
<tr>
<th>Description</th>
<th class="text-right">Quantity</th>
<th class="text-right">Unit Price</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Items)
{
<tr>
<td>@item.Description</td>
<td class="text-right">@item.Quantity</td>
<td class="text-right">@item.UnitPrice.ToString("C")</td>
<td class="text-right">@item.Total.ToString("C")</td>
</tr>
}
</tbody>
</table>
<div class="invoice-summary">
<table class="summary-table">
<tr>
<td>Subtotal:</td>
<td class="text-right">@Model.Subtotal.ToString("C")</td>
</tr>
<tr>
<td>Tax (@Model.TaxRate%):</td>
<td class="text-right">@Model.TaxAmount.ToString("C")</td>
</tr>
<tr class="total-row">
<td>Total:</td>
<td class="text-right">@Model.Total.ToString("C")</td>
</tr>
</table>
</div>
@if (!string.IsNullOrEmpty(Model.Notes))
{
<div class="invoice-footer">
<div class="footer-notes">
<h4>Notes</h4>
<p>@Model.Notes</p>
</div>
</div>
}
@if (!string.IsNullOrEmpty(Model.PaymentTerms))
{
<div class="footer-notes">
<h4>Payment Terms</h4>
<p>@Model.PaymentTerms</p>
</div>
}
</div>
</body>
</html>Implementación del controlador con gestión de errores
Ahora vamos a crear un controlador robusto que genere archivos PDF con una gestión de errores completa:
using IronPdf;
using IronPdf.Extensions.Mvc.Core;
using Microsoft.AspNetCore.Mvc;
using PdfGeneratorApp.Models;
using System.Diagnostics;
namespace PdfGeneratorApp.Controllers
{
public class InvoiceController : Controller
{
private readonly ILogger<InvoiceController> _logger;
private readonly IRazorViewRenderer _viewRenderer;
private readonly ChromePdfRenderer _pdfRenderer;
private readonly IWebHostEnvironment _environment;
public InvoiceController(
ILogger<InvoiceController> logger,
IRazorViewRenderer viewRenderer,
ChromePdfRenderer pdfRenderer,
IWebHostEnvironment environment)
{
_logger = logger;
_viewRenderer = viewRenderer;
_pdfRenderer = pdfRenderer;
_environment = environment;
}
[HttpGet]
public IActionResult Index()
{
// Display a form or list of invoices
return View();
}
[HttpGet]
public async Task<IActionResult> GenerateInvoice(string invoiceNumber)
{
var stopwatch = Stopwatch.StartNew();
try
{
// Validate input
if (string.IsNullOrEmpty(invoiceNumber))
{
_logger.LogWarning("Invoice generation attempted without invoice number");
return BadRequest("Invoice number is required");
}
// Generate sample data (in production, fetch from database)
var invoice = CreateSampleInvoice(invoiceNumber);
// Log the generation attempt
_logger.LogInformation($"Generating PDF for invoice {invoiceNumber}");
// Configure PDF rendering options
_pdfRenderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait;
_pdfRenderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
_pdfRenderer.RenderingOptions.PrintHtmlBackgrounds = true;
_pdfRenderer.RenderingOptions.CreatePdfFormsFromHtml = false;
// Add custom header with page numbers
_pdfRenderer.RenderingOptions.TextHeader = new TextHeaderFooter
{
CenterText = $"Invoice {invoice.InvoiceNumber}",
DrawDividerLine = true,
Font = IronSoftware.Drawing.FontTypes.Helvetica,
FontSize = 10
};
// Add footer with page numbers
_pdfRenderer.RenderingOptions.TextFooter = new TextHeaderFooter
{
LeftText = "{date} {time}",
RightText = "Page {page} of {total-pages}",
DrawDividerLine = true,
Font = IronSoftware.Drawing.FontTypes.Helvetica,
FontSize = 8
};
// Render the view to PDF
PdfDocument pdf;
try
{
pdf = _pdfRenderer.RenderRazorViewToPdf(
_viewRenderer,
"Views/Invoice/InvoiceTemplate.cshtml",
invoice);
}
catch (Excepción renderEx)
{
_logger.LogError(renderEx, "Failed to render Razor view to PDF");
throw new InvalidOperationExcepción("PDF rendering failed. Please check the template.", renderEx);
}
// Apply metadata
pdf.MetaData.Author = "PdfGeneratorApp";
pdf.MetaData.Title = $"Invoice {invoice.InvoiceNumber}";
pdf.MetaData.Subject = $"Invoice for {invoice.Customer.Name}";
pdf.MetaData.Keywords = "invoice, billing, payment";
pdf.MetaData.CreationDate = DateTime.UtcNow;
pdf.MetaData.ModifiedDate = DateTime.UtcNow;
// Optional: Add password protection
// pdf.SecuritySettings.UserPassword = "user123";
// pdf.SecuritySettings.OwnerPassword = "owner456";
// pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights;
// Log performance metrics
stopwatch.Stop();
_logger.LogInformation($"PDF generated successfully for invoice {invoiceNumber} in {stopwatch.ElapsedMilliseconds}ms");
// Return the PDF file
Response.Headers.Add("Content-Disposition", $"inline; filename=Invoice_{invoiceNumber}.pdf");
return File(pdf.BinaryData, "application/pdf", $"Invoice_{invoiceNumber}.pdf");
}
catch (Excepción ex)
{
_logger.LogError(ex, $"Error generating PDF for invoice {invoiceNumber}");
// In development, return detailed error
if (_environment.IsDevelopment())
{
return StatusCode(500, new
{
error = "PDF generation failed",
message = ex.Message,
stackTrace = ex.StackTrace
});
}
// In production, return generic error
return StatusCode(500, "An error occurred while generating the PDF");
}
}
private InvoiceModel CreateSampleInvoice(string invoiceNumber)
{
return new InvoiceModel
{
InvoiceNumber = invoiceNumber,
InvoiceDate = DateTime.Now,
DueDate = DateTime.Now.AddDays(30),
Vendor = new CompanyInfo
{
Name = "Tech Solucións Inc.",
Address = "123 Business Ave",
City = "New York",
State = "NY",
ZipCode = "10001",
Email = "billing@techsolutions.com",
Phone = "(555) 123-4567"
},
Customer = new CompanyInfo
{
Name = "Acme Corporation",
Address = "456 Commerce St",
City = "Los Angeles",
State = "CA",
ZipCode = "90001",
Email = "accounts@acmecorp.com",
Phone = "(555) 987-6543"
},
Items = new List<InvoiceItem>
{
new InvoiceItem
{
Description = "Software Development Services - 40 hours",
Quantity = 40,
UnitPrice = 150.00m
},
new InvoiceItem
{
Description = "Project Management - 10 hours",
Quantity = 10,
UnitPrice = 120.00m
},
new InvoiceItem
{
Description = "Quality Assurance Testing",
Quantity = 1,
UnitPrice = 2500.00m
}
},
TaxRate = 8.875m,
Notes = "Payment is due within 30 days. Late payments subject to 1.5% monthly interest.",
PaymentTerms = "Net 30"
};
}
}
}using IronPdf;
using IronPdf.Extensions.Mvc.Core;
using Microsoft.AspNetCore.Mvc;
using PdfGeneratorApp.Models;
using System.Diagnostics;
namespace PdfGeneratorApp.Controllers
{
public class InvoiceController : Controller
{
private readonly ILogger<InvoiceController> _logger;
private readonly IRazorViewRenderer _viewRenderer;
private readonly ChromePdfRenderer _pdfRenderer;
private readonly IWebHostEnvironment _environment;
public InvoiceController(
ILogger<InvoiceController> logger,
IRazorViewRenderer viewRenderer,
ChromePdfRenderer pdfRenderer,
IWebHostEnvironment environment)
{
_logger = logger;
_viewRenderer = viewRenderer;
_pdfRenderer = pdfRenderer;
_environment = environment;
}
[HttpGet]
public IActionResult Index()
{
// Display a form or list of invoices
return View();
}
[HttpGet]
public async Task<IActionResult> GenerateInvoice(string invoiceNumber)
{
var stopwatch = Stopwatch.StartNew();
try
{
// Validate input
if (string.IsNullOrEmpty(invoiceNumber))
{
_logger.LogWarning("Invoice generation attempted without invoice number");
return BadRequest("Invoice number is required");
}
// Generate sample data (in production, fetch from database)
var invoice = CreateSampleInvoice(invoiceNumber);
// Log the generation attempt
_logger.LogInformation($"Generating PDF for invoice {invoiceNumber}");
// Configure PDF rendering options
_pdfRenderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait;
_pdfRenderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
_pdfRenderer.RenderingOptions.PrintHtmlBackgrounds = true;
_pdfRenderer.RenderingOptions.CreatePdfFormsFromHtml = false;
// Add custom header with page numbers
_pdfRenderer.RenderingOptions.TextHeader = new TextHeaderFooter
{
CenterText = $"Invoice {invoice.InvoiceNumber}",
DrawDividerLine = true,
Font = IronSoftware.Drawing.FontTypes.Helvetica,
FontSize = 10
};
// Add footer with page numbers
_pdfRenderer.RenderingOptions.TextFooter = new TextHeaderFooter
{
LeftText = "{date} {time}",
RightText = "Page {page} of {total-pages}",
DrawDividerLine = true,
Font = IronSoftware.Drawing.FontTypes.Helvetica,
FontSize = 8
};
// Render the view to PDF
PdfDocument pdf;
try
{
pdf = _pdfRenderer.RenderRazorViewToPdf(
_viewRenderer,
"Views/Invoice/InvoiceTemplate.cshtml",
invoice);
}
catch (Excepción renderEx)
{
_logger.LogError(renderEx, "Failed to render Razor view to PDF");
throw new InvalidOperationExcepción("PDF rendering failed. Please check the template.", renderEx);
}
// Apply metadata
pdf.MetaData.Author = "PdfGeneratorApp";
pdf.MetaData.Title = $"Invoice {invoice.InvoiceNumber}";
pdf.MetaData.Subject = $"Invoice for {invoice.Customer.Name}";
pdf.MetaData.Keywords = "invoice, billing, payment";
pdf.MetaData.CreationDate = DateTime.UtcNow;
pdf.MetaData.ModifiedDate = DateTime.UtcNow;
// Optional: Add password protection
// pdf.SecuritySettings.UserPassword = "user123";
// pdf.SecuritySettings.OwnerPassword = "owner456";
// pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights;
// Log performance metrics
stopwatch.Stop();
_logger.LogInformation($"PDF generated successfully for invoice {invoiceNumber} in {stopwatch.ElapsedMilliseconds}ms");
// Return the PDF file
Response.Headers.Add("Content-Disposition", $"inline; filename=Invoice_{invoiceNumber}.pdf");
return File(pdf.BinaryData, "application/pdf", $"Invoice_{invoiceNumber}.pdf");
}
catch (Excepción ex)
{
_logger.LogError(ex, $"Error generating PDF for invoice {invoiceNumber}");
// In development, return detailed error
if (_environment.IsDevelopment())
{
return StatusCode(500, new
{
error = "PDF generation failed",
message = ex.Message,
stackTrace = ex.StackTrace
});
}
// In production, return generic error
return StatusCode(500, "An error occurred while generating the PDF");
}
}
private InvoiceModel CreateSampleInvoice(string invoiceNumber)
{
return new InvoiceModel
{
InvoiceNumber = invoiceNumber,
InvoiceDate = DateTime.Now,
DueDate = DateTime.Now.AddDays(30),
Vendor = new CompanyInfo
{
Name = "Tech Solucións Inc.",
Address = "123 Business Ave",
City = "New York",
State = "NY",
ZipCode = "10001",
Email = "billing@techsolutions.com",
Phone = "(555) 123-4567"
},
Customer = new CompanyInfo
{
Name = "Acme Corporation",
Address = "456 Commerce St",
City = "Los Angeles",
State = "CA",
ZipCode = "90001",
Email = "accounts@acmecorp.com",
Phone = "(555) 987-6543"
},
Items = new List<InvoiceItem>
{
new InvoiceItem
{
Description = "Software Development Services - 40 hours",
Quantity = 40,
UnitPrice = 150.00m
},
new InvoiceItem
{
Description = "Project Management - 10 hours",
Quantity = 10,
UnitPrice = 120.00m
},
new InvoiceItem
{
Description = "Quality Assurance Testing",
Quantity = 1,
UnitPrice = 2500.00m
}
},
TaxRate = 8.875m,
Notes = "Payment is due within 30 days. Late payments subject to 1.5% monthly interest.",
PaymentTerms = "Net 30"
};
}
}
}Explicación del código
Para probar y ejecutar el generador de PDF anterior, inicie el proyecto e introduzca la siguiente URL: "https://localhost:[port]/Invoice/GenerateInvoice?invoiceNumber=055" para generar la factura. Recuerde sustituir el puerto por el número de puerto real en el que alojó la aplicación.
Resultado
Cómo generar un PDF en ASP.NET con C#: Figura 4 - Salida PDF
¿Cómo devolver archivos PDF desde puntos finales de API web para obtener la máxima eficacia?
Para las aplicaciones que requieren servir PDF a través de API RESTful, aquí se explica cómo implementar una entrega de PDF eficiente con una gestión de memoria adecuada. Este enfoque es especialmente útil cuando se construyen microservicios o cuando se necesita generar PDF en controladores de API web ASP.NET Core:
using Microsoft.AspNetCore.Mvc;
using IronPdf;
using System.IO;
namespace PdfGeneratorApp.Controllers.Api
{
[ApiController]
[Route("api/[controller]")]
public class PdfApiController : ControllerBase
{
private readonly ChromePdfRenderer _pdfRenderer;
private readonly ILogger<PdfApiController> _logger;
public PdfApiController(
ChromePdfRenderer pdfRenderer,
ILogger<PdfApiController> logger)
{
_pdfRenderer = pdfRenderer;
_logger = logger;
}
[HttpPost("generate-report")]
public async Task<IActionResult> GenerateReport([FromBody] ReportRequest request)
{
try
{
// Validate request
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Build HTML content dynamically
var htmlContent = BuildReportHtml(request);
// Generate PDF with memory-efficient streaming
using var pdfDocument = _pdfRenderer.RenderHtmlAsPdf(htmlContent);
// Apply compression for smaller file size
pdfDocument.CompressImages(60); // 60% quality
// Stream the PDF directly to response
var stream = new MemoryStream();
pdfDocument.SaveAs(stream);
stream.Position = 0;
_logger.LogInformation($"Report generated for {request.ReportType}");
return new FileStreamResult(stream, "application/pdf")
{
FileDownloadName = $"Report_{DateTime.Now:yyyyMMdd_HHmmss}.pdf"
};
}
catch (Excepción ex)
{
_logger.LogError(ex, "Failed to generate report");
return StatusCode(500, new { error = "Report generation failed" });
}
}
[HttpGet("download/{documentId}")]
public async Task<IActionResult> DownloadDocument(string documentId)
{
try
{
// In production, retrieve document from database or storage
var documentPath = Path.Combine("wwwroot", "documents", $"{documentId}.pdf");
if (!System.IO.File.Exists(documentPath))
{
return NotFound(new { error = "Document not found" });
}
var memory = new MemoryStream();
using (var stream = new FileStream(documentPath, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, "application/pdf", $"Document_{documentId}.pdf");
}
catch (Excepción ex)
{
_logger.LogError(ex, $"Failed to download document {documentId}");
return StatusCode(500, new { error = "Download failed" });
}
}
private string BuildReportHtml(ReportRequest request)
{
return $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{
font-family: Arial, sans-serif;
margin: 40px;
}}
h1 {{
color: #2c3e50;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}}
.report-date {{
color: #7f8c8d;
font-size: 14px;
}}
.data-table {{
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}}
.data-table th, .data-table td {{
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}}
.data-table th {{
background-color: #3498db;
color: white;
}}
</style>
</head>
<body>
<h1>{request.ReportType} Report</h1>
<p class='report-date'>Generated: {DateTime.Now:MMMM dd, yyyy HH:mm}</p>
<p>{request.Description}</p>
{GenerateDataTable(request.Data)}
</body>
</html>";
}
private string GenerateDataTable(List<ReportDataItem> data)
{
if (data == null || !data.Any())
return "<p>No data available</p>";
var table = "<table class='data-table'><thead><tr>";
// Add headers
foreach (var prop in typeof(ReportDataItem).GetProperties())
{
table += $"<th>{prop.Name}</th>";
}
table += "</tr></thead><tbody>";
// Add data rows
foreach (var item in data)
{
table += "<tr>";
foreach (var prop in typeof(ReportDataItem).GetProperties())
{
var value = prop.GetValue(item) ?? "";
table += $"<td>{value}</td>";
}
table += "</tr>";
}
table += "</tbody></table>";
return table;
}
}
public class ReportRequest
{
public string ReportType { get; set; }
public string Description { get; set; }
public List<ReportDataItem> Data { get; set; }
}
public class ReportDataItem
{
public string Name { get; set; }
public string Category { get; set; }
public decimal Value { get; set; }
public DateTime Date { get; set; }
}
}using Microsoft.AspNetCore.Mvc;
using IronPdf;
using System.IO;
namespace PdfGeneratorApp.Controllers.Api
{
[ApiController]
[Route("api/[controller]")]
public class PdfApiController : ControllerBase
{
private readonly ChromePdfRenderer _pdfRenderer;
private readonly ILogger<PdfApiController> _logger;
public PdfApiController(
ChromePdfRenderer pdfRenderer,
ILogger<PdfApiController> logger)
{
_pdfRenderer = pdfRenderer;
_logger = logger;
}
[HttpPost("generate-report")]
public async Task<IActionResult> GenerateReport([FromBody] ReportRequest request)
{
try
{
// Validate request
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Build HTML content dynamically
var htmlContent = BuildReportHtml(request);
// Generate PDF with memory-efficient streaming
using var pdfDocument = _pdfRenderer.RenderHtmlAsPdf(htmlContent);
// Apply compression for smaller file size
pdfDocument.CompressImages(60); // 60% quality
// Stream the PDF directly to response
var stream = new MemoryStream();
pdfDocument.SaveAs(stream);
stream.Position = 0;
_logger.LogInformation($"Report generated for {request.ReportType}");
return new FileStreamResult(stream, "application/pdf")
{
FileDownloadName = $"Report_{DateTime.Now:yyyyMMdd_HHmmss}.pdf"
};
}
catch (Excepción ex)
{
_logger.LogError(ex, "Failed to generate report");
return StatusCode(500, new { error = "Report generation failed" });
}
}
[HttpGet("download/{documentId}")]
public async Task<IActionResult> DownloadDocument(string documentId)
{
try
{
// In production, retrieve document from database or storage
var documentPath = Path.Combine("wwwroot", "documents", $"{documentId}.pdf");
if (!System.IO.File.Exists(documentPath))
{
return NotFound(new { error = "Document not found" });
}
var memory = new MemoryStream();
using (var stream = new FileStream(documentPath, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, "application/pdf", $"Document_{documentId}.pdf");
}
catch (Excepción ex)
{
_logger.LogError(ex, $"Failed to download document {documentId}");
return StatusCode(500, new { error = "Download failed" });
}
}
private string BuildReportHtml(ReportRequest request)
{
return $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{
font-family: Arial, sans-serif;
margin: 40px;
}}
h1 {{
color: #2c3e50;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}}
.report-date {{
color: #7f8c8d;
font-size: 14px;
}}
.data-table {{
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}}
.data-table th, .data-table td {{
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}}
.data-table th {{
background-color: #3498db;
color: white;
}}
</style>
</head>
<body>
<h1>{request.ReportType} Report</h1>
<p class='report-date'>Generated: {DateTime.Now:MMMM dd, yyyy HH:mm}</p>
<p>{request.Description}</p>
{GenerateDataTable(request.Data)}
</body>
</html>";
}
private string GenerateDataTable(List<ReportDataItem> data)
{
if (data == null || !data.Any())
return "<p>No data available</p>";
var table = "<table class='data-table'><thead><tr>";
// Add headers
foreach (var prop in typeof(ReportDataItem).GetProperties())
{
table += $"<th>{prop.Name}</th>";
}
table += "</tr></thead><tbody>";
// Add data rows
foreach (var item in data)
{
table += "<tr>";
foreach (var prop in typeof(ReportDataItem).GetProperties())
{
var value = prop.GetValue(item) ?? "";
table += $"<td>{value}</td>";
}
table += "</tr>";
}
table += "</tbody></table>";
return table;
}
}
public class ReportRequest
{
public string ReportType { get; set; }
public string Description { get; set; }
public List<ReportDataItem> Data { get; set; }
}
public class ReportDataItem
{
public string Name { get; set; }
public string Category { get; set; }
public decimal Value { get; set; }
public DateTime Date { get; set; }
}
}Agregación de encabezados, pies de página y estilos profesionales
Los PDF profesionales requieren encabezados, pies de página y estilos coherentes. IronPDF ofrece opciones sencillas basadas en texto y opciones avanzadas basadas en HTML. Utilizando estilos CSS para dar estilo al marcado HTML, podemos crear encabezados y pies de página personalizados para el PDF. El siguiente fragmento de código explora cómo podríamos utilizar esto en nuestro proyecto actual:
using IronPdf;
using IronPdf.Extensions.Mvc.Core;
using Microsoft.AspNetCore.Mvc;
using PdfGeneratorApp.Models;
using PdfGeneratorApp.Services;
using System.Diagnostics;
namespace PdfGeneratorApp.Controllers
{
public class InvoiceController : Controller
{
private readonly ILogger<InvoiceController> _logger;
private readonly IRazorViewRenderer _viewRenderer;
private readonly ChromePdfRenderer _pdfRenderer;
private readonly PdfFormattingService _pdfFormattingService;
private readonly IWebHostEnvironment _environment;
public InvoiceController(
ILogger<InvoiceController> logger,
IRazorViewRenderer viewRenderer,
ChromePdfRenderer pdfRenderer,
PdfFormattingService pdfFormattingService,
IWebHostEnvironment environment)
{
_logger = logger;
_viewRenderer = viewRenderer;
_pdfRenderer = pdfRenderer;
_pdfFormattingService = pdfFormattingService;
_environment = environment;
}
[HttpGet]
public IActionResult Index()
{
// Display a form or list of invoices
return View();
}
private void ConfigurePdfRendererOptions(ChromePdfRenderer renderer, InvoiceModel invoice, PdfStylingOptions options)
{
// Margins
renderer.RenderingOptions.MarginTop = options.MarginTop;
renderer.RenderingOptions.MarginBottom = options.MarginBottom;
renderer.RenderingOptions.MarginLeft = options.MarginLeft;
renderer.RenderingOptions.MarginRight = options.MarginRight;
// Header
if (options.UseHtmlHeader)
{
renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
MaxHeight = 50,
HtmlFragment = $@"
<div style='width: 100%; font-size: 12px; font-family: Arial;'>
<div style='float: left; width: 50%;'>
<!-- Add your logo or dynamic content here -->
<img src='https://ironpdf.com/img/products/ironpdf-logo-text-dotnet.svg' height='40' />
</div>
<div style='float: right; width: 50%; text-align: right;'>
<strong>Invoice {invoice.InvoiceNumber}</strong><br/>
Generated: {DateTime.Now:yyyy-MM-dd}
</div>
</div>",
LoadStylesAndCSSFromMainHtmlDocument = true
};
}
else
{
renderer.RenderingOptions.TextHeader = new TextHeaderFooter
{
CenterText = options.HeaderText,
Font = IronSoftware.Drawing.FontTypes.Arial,
FontSize = 12,
DrawDividerLine = true
};
}
// Footer
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
MaxHeight = 30,
HtmlFragment = @"
<div style='width: 100%; font-size: 10px; color: #666;'>
<div style='float: left; width: 33%;'>
© 2025 Your Company
</div>
<div style='float: center; width: 33%; text-align: center;'>
yourwebsite.com
</div>
<div style='float: right; width: 33%; text-align: right;'>
Page {page} of {total-pages}
</div>
</div>"
};
// Optional: Add watermark here (IronPDF supports adding after PDF is generated, so keep it as-is)
// Margins, paper size etc., can also be set here if needed
renderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait;
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.PrintHtmlBackgrounds = true;
}
[HttpGet]
public async Task<IActionResult> GenerateInvoice(string invoiceNumber)
{
var stopwatch = Stopwatch.StartNew();
try
{
// Validate input
if (string.IsNullOrEmpty(invoiceNumber))
{
_logger.LogWarning("Invoice generation attempted without invoice number");
return BadRequest("Invoice number is required");
}
// Generate sample data (in production, fetch from database)
var invoice = CreateSampleInvoice(invoiceNumber);
// Log the generation attempt
_logger.LogInformation($"Generating PDF for invoice {invoiceNumber}");
// Configure PDF rendering options
_pdfRenderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait;
_pdfRenderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
_pdfRenderer.RenderingOptions.PrintHtmlBackgrounds = true;
_pdfRenderer.RenderingOptions.CreatePdfFormsFromHtml = false;
var options = new PdfStylingOptions
{
MarginTop = 25,
MarginBottom = 25,
MarginLeft = 20,
MarginRight = 20,
UseHtmlHeader = true,
HeaderText = $"Invoice {invoice.InvoiceNumber}",
AddWatermark = false,
ForcePageBreaks = false
};
// Apply the styling to the renderer BEFORE rendering PDF
ConfigurePdfRendererOptions(_pdfRenderer, invoice, options);
// Render the view to PDF
PdfDocument pdf;
try
{
pdf = _pdfRenderer.RenderRazorViewToPdf(
_viewRenderer,
"Views/Invoice/InvoiceTemplate.cshtml",
invoice);
}
catch (Excepción renderEx)
{
_logger.LogError(renderEx, "Failed to render Razor view to PDF");
throw new InvalidOperationExcepción("PDF rendering failed. Please check the template.", renderEx);
}
// Apply metadata
pdf.MetaData.Author = "PdfGeneratorApp";
pdf.MetaData.Title = $"Invoice {invoice.InvoiceNumber}";
pdf.MetaData.Subject = $"Invoice for {invoice.Customer.Name}";
pdf.MetaData.Keywords = "invoice, billing, payment";
pdf.MetaData.CreationDate = DateTime.UtcNow;
pdf.MetaData.ModifiedDate = DateTime.UtcNow;
// Optional: Add password protection
// pdf.SecuritySettings.UserPassword = "user123";
// pdf.SecuritySettings.OwnerPassword = "owner456";
// pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights;
// Log performance metrics
stopwatch.Stop();
_logger.LogInformation($"PDF generated successfully for invoice {invoiceNumber} in {stopwatch.ElapsedMilliseconds}ms");
// Return the PDF file
Response.Headers.Add("Content-Disposition", $"inline; filename=Invoice_{invoiceNumber}.pdf");
return File(pdf.BinaryData, "application/pdf", $"Invoice_{invoiceNumber}.pdf");
}
catch (Excepción ex)
{
_logger.LogError(ex, $"Error generating PDF for invoice {invoiceNumber}");
// In development, return detailed error
if (_environment.IsDevelopment())
{
return StatusCode(500, new
{
error = "PDF generation failed",
message = ex.Message,
stackTrace = ex.StackTrace
});
}
// In production, return generic error
return StatusCode(500, "An error occurred while generating the PDF");
}
}
private InvoiceModel CreateSampleInvoice(string invoiceNumber)
{
return new InvoiceModel
{
InvoiceNumber = invoiceNumber,
InvoiceDate = DateTime.Now,
DueDate = DateTime.Now.AddDays(30),
Vendor = new CompanyInfo
{
Name = "Tech Solucións Inc.",
Address = "123 Business Ave",
City = "New York",
State = "NY",
ZipCode = "10001",
Email = "billing@techsolutions.com",
Phone = "(555) 123-4567"
},
Customer = new CompanyInfo
{
Name = "Acme Corporation",
Address = "456 Commerce St",
City = "Los Angeles",
State = "CA",
ZipCode = "90001",
Email = "accounts@acmecorp.com",
Phone = "(555) 987-6543"
},
Items = new List<InvoiceItem>
{
new InvoiceItem
{
Description = "Software Development Services - 40 hours",
Quantity = 40,
UnitPrice = 150.00m
},
new InvoiceItem
{
Description = "Project Management - 10 hours",
Quantity = 10,
UnitPrice = 120.00m
},
new InvoiceItem
{
Description = "Quality Assurance Testing",
Quantity = 1,
UnitPrice = 2500.00m
}
},
TaxRate = 8.875m,
Notes = "Payment is due within 30 days. Late payments subject to 1.5% monthly interest.",
PaymentTerms = "Net 30"
};
}
}
}using IronPdf;
using IronPdf.Extensions.Mvc.Core;
using Microsoft.AspNetCore.Mvc;
using PdfGeneratorApp.Models;
using PdfGeneratorApp.Services;
using System.Diagnostics;
namespace PdfGeneratorApp.Controllers
{
public class InvoiceController : Controller
{
private readonly ILogger<InvoiceController> _logger;
private readonly IRazorViewRenderer _viewRenderer;
private readonly ChromePdfRenderer _pdfRenderer;
private readonly PdfFormattingService _pdfFormattingService;
private readonly IWebHostEnvironment _environment;
public InvoiceController(
ILogger<InvoiceController> logger,
IRazorViewRenderer viewRenderer,
ChromePdfRenderer pdfRenderer,
PdfFormattingService pdfFormattingService,
IWebHostEnvironment environment)
{
_logger = logger;
_viewRenderer = viewRenderer;
_pdfRenderer = pdfRenderer;
_pdfFormattingService = pdfFormattingService;
_environment = environment;
}
[HttpGet]
public IActionResult Index()
{
// Display a form or list of invoices
return View();
}
private void ConfigurePdfRendererOptions(ChromePdfRenderer renderer, InvoiceModel invoice, PdfStylingOptions options)
{
// Margins
renderer.RenderingOptions.MarginTop = options.MarginTop;
renderer.RenderingOptions.MarginBottom = options.MarginBottom;
renderer.RenderingOptions.MarginLeft = options.MarginLeft;
renderer.RenderingOptions.MarginRight = options.MarginRight;
// Header
if (options.UseHtmlHeader)
{
renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
MaxHeight = 50,
HtmlFragment = $@"
<div style='width: 100%; font-size: 12px; font-family: Arial;'>
<div style='float: left; width: 50%;'>
<!-- Add your logo or dynamic content here -->
<img src='https://ironpdf.com/img/products/ironpdf-logo-text-dotnet.svg' height='40' />
</div>
<div style='float: right; width: 50%; text-align: right;'>
<strong>Invoice {invoice.InvoiceNumber}</strong><br/>
Generated: {DateTime.Now:yyyy-MM-dd}
</div>
</div>",
LoadStylesAndCSSFromMainHtmlDocument = true
};
}
else
{
renderer.RenderingOptions.TextHeader = new TextHeaderFooter
{
CenterText = options.HeaderText,
Font = IronSoftware.Drawing.FontTypes.Arial,
FontSize = 12,
DrawDividerLine = true
};
}
// Footer
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
MaxHeight = 30,
HtmlFragment = @"
<div style='width: 100%; font-size: 10px; color: #666;'>
<div style='float: left; width: 33%;'>
© 2025 Your Company
</div>
<div style='float: center; width: 33%; text-align: center;'>
yourwebsite.com
</div>
<div style='float: right; width: 33%; text-align: right;'>
Page {page} of {total-pages}
</div>
</div>"
};
// Optional: Add watermark here (IronPDF supports adding after PDF is generated, so keep it as-is)
// Margins, paper size etc., can also be set here if needed
renderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait;
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.PrintHtmlBackgrounds = true;
}
[HttpGet]
public async Task<IActionResult> GenerateInvoice(string invoiceNumber)
{
var stopwatch = Stopwatch.StartNew();
try
{
// Validate input
if (string.IsNullOrEmpty(invoiceNumber))
{
_logger.LogWarning("Invoice generation attempted without invoice number");
return BadRequest("Invoice number is required");
}
// Generate sample data (in production, fetch from database)
var invoice = CreateSampleInvoice(invoiceNumber);
// Log the generation attempt
_logger.LogInformation($"Generating PDF for invoice {invoiceNumber}");
// Configure PDF rendering options
_pdfRenderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait;
_pdfRenderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
_pdfRenderer.RenderingOptions.PrintHtmlBackgrounds = true;
_pdfRenderer.RenderingOptions.CreatePdfFormsFromHtml = false;
var options = new PdfStylingOptions
{
MarginTop = 25,
MarginBottom = 25,
MarginLeft = 20,
MarginRight = 20,
UseHtmlHeader = true,
HeaderText = $"Invoice {invoice.InvoiceNumber}",
AddWatermark = false,
ForcePageBreaks = false
};
// Apply the styling to the renderer BEFORE rendering PDF
ConfigurePdfRendererOptions(_pdfRenderer, invoice, options);
// Render the view to PDF
PdfDocument pdf;
try
{
pdf = _pdfRenderer.RenderRazorViewToPdf(
_viewRenderer,
"Views/Invoice/InvoiceTemplate.cshtml",
invoice);
}
catch (Excepción renderEx)
{
_logger.LogError(renderEx, "Failed to render Razor view to PDF");
throw new InvalidOperationExcepción("PDF rendering failed. Please check the template.", renderEx);
}
// Apply metadata
pdf.MetaData.Author = "PdfGeneratorApp";
pdf.MetaData.Title = $"Invoice {invoice.InvoiceNumber}";
pdf.MetaData.Subject = $"Invoice for {invoice.Customer.Name}";
pdf.MetaData.Keywords = "invoice, billing, payment";
pdf.MetaData.CreationDate = DateTime.UtcNow;
pdf.MetaData.ModifiedDate = DateTime.UtcNow;
// Optional: Add password protection
// pdf.SecuritySettings.UserPassword = "user123";
// pdf.SecuritySettings.OwnerPassword = "owner456";
// pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights;
// Log performance metrics
stopwatch.Stop();
_logger.LogInformation($"PDF generated successfully for invoice {invoiceNumber} in {stopwatch.ElapsedMilliseconds}ms");
// Return the PDF file
Response.Headers.Add("Content-Disposition", $"inline; filename=Invoice_{invoiceNumber}.pdf");
return File(pdf.BinaryData, "application/pdf", $"Invoice_{invoiceNumber}.pdf");
}
catch (Excepción ex)
{
_logger.LogError(ex, $"Error generating PDF for invoice {invoiceNumber}");
// In development, return detailed error
if (_environment.IsDevelopment())
{
return StatusCode(500, new
{
error = "PDF generation failed",
message = ex.Message,
stackTrace = ex.StackTrace
});
}
// In production, return generic error
return StatusCode(500, "An error occurred while generating the PDF");
}
}
private InvoiceModel CreateSampleInvoice(string invoiceNumber)
{
return new InvoiceModel
{
InvoiceNumber = invoiceNumber,
InvoiceDate = DateTime.Now,
DueDate = DateTime.Now.AddDays(30),
Vendor = new CompanyInfo
{
Name = "Tech Solucións Inc.",
Address = "123 Business Ave",
City = "New York",
State = "NY",
ZipCode = "10001",
Email = "billing@techsolutions.com",
Phone = "(555) 123-4567"
},
Customer = new CompanyInfo
{
Name = "Acme Corporation",
Address = "456 Commerce St",
City = "Los Angeles",
State = "CA",
ZipCode = "90001",
Email = "accounts@acmecorp.com",
Phone = "(555) 987-6543"
},
Items = new List<InvoiceItem>
{
new InvoiceItem
{
Description = "Software Development Services - 40 hours",
Quantity = 40,
UnitPrice = 150.00m
},
new InvoiceItem
{
Description = "Project Management - 10 hours",
Quantity = 10,
UnitPrice = 120.00m
},
new InvoiceItem
{
Description = "Quality Assurance Testing",
Quantity = 1,
UnitPrice = 2500.00m
}
},
TaxRate = 8.875m,
Notes = "Payment is due within 30 days. Late payments subject to 1.5% monthly interest.",
PaymentTerms = "Net 30"
};
}
}
}Explicación del código
Puede ver las breves diferencias entre las cabeceras básicas y las estilizadas. Con IronPDF, puede incluso añadir encabezados y pies de página HTML personalizados a su factura para estilizarla aún más y hacerla realmente suya.
Para obtener información más detallada sobre la adición de encabezados y pies de página, consulte esta guía práctica.
Resultado
Cómo generar un PDF en ASP.NET usando C#: Figura 5 - Salida PDF con estilo
¿Cómo implementar el procesamiento de PDF por lotes de alto rendimiento?
¿Necesita generar cientos o miles de PDF? A continuación se explica cómo conseguir un rendimiento óptimo con el procesamiento en paralelo a la vez que se gestiona eficientemente la memoria. Descargue nuestro ejemplo de trabajo completo para verlo en acción.
Para las aplicaciones que necesitan generar varios PDF de forma eficiente, he aquí una implementación optimizada de procesamiento por lotes utilizando técnicas de sincronización y multihilo. Este enfoque sigue las mejores prácticas de programación paralela de Microsoft para obtener un rendimiento óptimo al generar PDF en ASP.NET Core utilizando C#:
using System.Collections.Concurrent;
using System.Diagnostics;
public class BatchPdfProcessor
{
private readonly ChromePdfRenderer _renderer;
private readonly ILogger<BatchPdfProcessor> _logger;
private readonly SemaphoreSlim _semaphore;
public BatchPdfProcessor(
ChromePdfRenderer renderer,
ILogger<BatchPdfProcessor> logger)
{
_renderer = renderer;
_logger = logger;
// Limit concurrent PDF generation to prevent memory exhaustion
_semaphore = new SemaphoreSlim(Environment.ProcessorCount);
}
public async Task<BatchProcessingResult> ProcessBatchAsync(
List<BatchPdfRequest> requests,
IProgress<BatchProgressReport> progress = null)
{
var result = new BatchProcessingResult
{
StartTime = DateTime.UtcNow,
TotalRequests = requests.Count
};
var successfulPdfs = new ConcurrentBag<GeneratedPdf>();
var errors = new ConcurrentBag<ProcessingError>();
var stopwatch = Stopwatch.StartNew();
// Process PDFs in parallel with controlled concurrency
var tasks = requests.Select(async (request, index) =>
{
await _semaphore.WaitAsync();
try
{
var taskStopwatch = Stopwatch.StartNew();
// Generate PDF
var pdf = await GeneratePdfAsync(request);
taskStopwatch.Stop();
successfulPdfs.Add(new GeneratedPdf
{
Id = request.Id,
FileName = request.FileName,
Data = pdf.BinaryData,
GenerationTime = taskStopwatch.ElapsedMilliseconds,
PageCount = pdf.PageCount
});
// Report progress
progress?.Report(new BatchProgressReport
{
ProcessedCount = successfulPdfs.Count + errors.Count,
TotalCount = requests.Count,
CurrentFile = request.FileName
});
_logger.LogDebug($"Generated PDF {request.Id} in {taskStopwatch.ElapsedMilliseconds}ms");
}
catch (Excepción ex)
{
errors.Add(new ProcessingError
{
RequestId = request.Id,
FileName = request.FileName,
Error = ex.Message,
StackTrace = ex.StackTrace
});
_logger.LogError(ex, $"Failed to generate PDF for request {request.Id}");
}
finally
{
_semaphore.Release();
}
});
await Task.WhenAll(tasks);
stopwatch.Stop();
// Compile results
result.EndTime = DateTime.UtcNow;
result.TotalProcessingTime = stopwatch.ElapsedMilliseconds;
result.SuccessfulPdfs = successfulPdfs.ToList();
result.Errors = errors.ToList();
result.SuccessCount = successfulPdfs.Count;
result.ErrorCount = errors.Count;
result.AverageGenerationTime = successfulPdfs.Any()
? successfulPdfs.Average(p => p.GenerationTime)
: 0;
result.TotalPages = successfulPdfs.Sum(p => p.PageCount);
result.TotalSizeBytes = successfulPdfs.Sum(p => p.Data.Length);
// Log summary
_logger.LogInformation($"Batch processing completed: {result.SuccessCount} successful, " +
$"{result.ErrorCount} errors, {result.TotalProcessingTime}ms total time");
// Clean up memory after large batch
if (requests.Count > 100)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
return result;
}
private async Task<PdfDocument> GeneratePdfAsync(BatchPdfRequest request)
{
return await Task.Run(() =>
{
// Configure renderer for this specific request
var localRenderer = new ChromePdfRenderer();
localRenderer.RenderingOptions.PaperSize = request.PaperSize;
localRenderer.RenderingOptions.MarginTop = request.MarginTop;
localRenderer.RenderingOptions.MarginBottom = request.MarginBottom;
// Generate PDF
var pdf = localRenderer.RenderHtmlAsPdf(request.HtmlContent);
// Apply compression if requested
if (request.CompressImages)
{
pdf.CompressImages(request.CompressionQuality);
}
return pdf;
});
}
}
public class BatchPdfRequest
{
public string Id { get; set; }
public string FileName { get; set; }
public string HtmlContent { get; set; }
public IronPdf.Rendering.PdfPaperSize PaperSize { get; set; } = IronPdf.Rendering.PdfPaperSize.A4;
public int MarginTop { get; set; } = 25;
public int MarginBottom { get; set; } = 25;
public bool CompressImages { get; set; } = true;
public int CompressionQuality { get; set; } = 80;
}
public class BatchProcessingResult
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public long TotalProcessingTime { get; set; }
public int TotalRequests { get; set; }
public int SuccessCount { get; set; }
public int ErrorCount { get; set; }
public double AverageGenerationTime { get; set; }
public int TotalPages { get; set; }
public long TotalSizeBytes { get; set; }
public List<GeneratedPdf> SuccessfulPdfs { get; set; }
public List<ProcessingError> Errors { get; set; }
}
public class GeneratedPdf
{
public string Id { get; set; }
public string FileName { get; set; }
public byte[] Data { get; set; }
public long GenerationTime { get; set; }
public int PageCount { get; set; }
}
public class ProcessingError
{
public string RequestId { get; set; }
public string FileName { get; set; }
public string Error { get; set; }
public string StackTrace { get; set; }
}
public class BatchProgressReport
{
public int ProcessedCount { get; set; }
public int TotalCount { get; set; }
public string CurrentFile { get; set; }
public double PercentComplete => (double)ProcessedCount / TotalCount * 100;
}using System.Collections.Concurrent;
using System.Diagnostics;
public class BatchPdfProcessor
{
private readonly ChromePdfRenderer _renderer;
private readonly ILogger<BatchPdfProcessor> _logger;
private readonly SemaphoreSlim _semaphore;
public BatchPdfProcessor(
ChromePdfRenderer renderer,
ILogger<BatchPdfProcessor> logger)
{
_renderer = renderer;
_logger = logger;
// Limit concurrent PDF generation to prevent memory exhaustion
_semaphore = new SemaphoreSlim(Environment.ProcessorCount);
}
public async Task<BatchProcessingResult> ProcessBatchAsync(
List<BatchPdfRequest> requests,
IProgress<BatchProgressReport> progress = null)
{
var result = new BatchProcessingResult
{
StartTime = DateTime.UtcNow,
TotalRequests = requests.Count
};
var successfulPdfs = new ConcurrentBag<GeneratedPdf>();
var errors = new ConcurrentBag<ProcessingError>();
var stopwatch = Stopwatch.StartNew();
// Process PDFs in parallel with controlled concurrency
var tasks = requests.Select(async (request, index) =>
{
await _semaphore.WaitAsync();
try
{
var taskStopwatch = Stopwatch.StartNew();
// Generate PDF
var pdf = await GeneratePdfAsync(request);
taskStopwatch.Stop();
successfulPdfs.Add(new GeneratedPdf
{
Id = request.Id,
FileName = request.FileName,
Data = pdf.BinaryData,
GenerationTime = taskStopwatch.ElapsedMilliseconds,
PageCount = pdf.PageCount
});
// Report progress
progress?.Report(new BatchProgressReport
{
ProcessedCount = successfulPdfs.Count + errors.Count,
TotalCount = requests.Count,
CurrentFile = request.FileName
});
_logger.LogDebug($"Generated PDF {request.Id} in {taskStopwatch.ElapsedMilliseconds}ms");
}
catch (Excepción ex)
{
errors.Add(new ProcessingError
{
RequestId = request.Id,
FileName = request.FileName,
Error = ex.Message,
StackTrace = ex.StackTrace
});
_logger.LogError(ex, $"Failed to generate PDF for request {request.Id}");
}
finally
{
_semaphore.Release();
}
});
await Task.WhenAll(tasks);
stopwatch.Stop();
// Compile results
result.EndTime = DateTime.UtcNow;
result.TotalProcessingTime = stopwatch.ElapsedMilliseconds;
result.SuccessfulPdfs = successfulPdfs.ToList();
result.Errors = errors.ToList();
result.SuccessCount = successfulPdfs.Count;
result.ErrorCount = errors.Count;
result.AverageGenerationTime = successfulPdfs.Any()
? successfulPdfs.Average(p => p.GenerationTime)
: 0;
result.TotalPages = successfulPdfs.Sum(p => p.PageCount);
result.TotalSizeBytes = successfulPdfs.Sum(p => p.Data.Length);
// Log summary
_logger.LogInformation($"Batch processing completed: {result.SuccessCount} successful, " +
$"{result.ErrorCount} errors, {result.TotalProcessingTime}ms total time");
// Clean up memory after large batch
if (requests.Count > 100)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
return result;
}
private async Task<PdfDocument> GeneratePdfAsync(BatchPdfRequest request)
{
return await Task.Run(() =>
{
// Configure renderer for this specific request
var localRenderer = new ChromePdfRenderer();
localRenderer.RenderingOptions.PaperSize = request.PaperSize;
localRenderer.RenderingOptions.MarginTop = request.MarginTop;
localRenderer.RenderingOptions.MarginBottom = request.MarginBottom;
// Generate PDF
var pdf = localRenderer.RenderHtmlAsPdf(request.HtmlContent);
// Apply compression if requested
if (request.CompressImages)
{
pdf.CompressImages(request.CompressionQuality);
}
return pdf;
});
}
}
public class BatchPdfRequest
{
public string Id { get; set; }
public string FileName { get; set; }
public string HtmlContent { get; set; }
public IronPdf.Rendering.PdfPaperSize PaperSize { get; set; } = IronPdf.Rendering.PdfPaperSize.A4;
public int MarginTop { get; set; } = 25;
public int MarginBottom { get; set; } = 25;
public bool CompressImages { get; set; } = true;
public int CompressionQuality { get; set; } = 80;
}
public class BatchProcessingResult
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public long TotalProcessingTime { get; set; }
public int TotalRequests { get; set; }
public int SuccessCount { get; set; }
public int ErrorCount { get; set; }
public double AverageGenerationTime { get; set; }
public int TotalPages { get; set; }
public long TotalSizeBytes { get; set; }
public List<GeneratedPdf> SuccessfulPdfs { get; set; }
public List<ProcessingError> Errors { get; set; }
}
public class GeneratedPdf
{
public string Id { get; set; }
public string FileName { get; set; }
public byte[] Data { get; set; }
public long GenerationTime { get; set; }
public int PageCount { get; set; }
}
public class ProcessingError
{
public string RequestId { get; set; }
public string FileName { get; set; }
public string Error { get; set; }
public string StackTrace { get; set; }
}
public class BatchProgressReport
{
public int ProcessedCount { get; set; }
public int TotalCount { get; set; }
public string CurrentFile { get; set; }
public double PercentComplete => (double)ProcessedCount / TotalCount * 100;
}Ejemplo de informe sobre atención sanitaria en el mundo real
He aquí una implementación específica para generar informes médicos conformes con la HIPAA:
public class MedicalReportGenerator
{
private readonly ChromePdfRenderer _renderer;
private readonly ILogger<MedicalReportGenerator> _logger;
public MedicalReportGenerator(
ChromePdfRenderer renderer,
ILogger<MedicalReportGenerator> logger)
{
_renderer = renderer;
_logger = logger;
}
public async Task<PdfDocument> GeneratePatientReport(PatientReportModel model)
{
var stopwatch = Stopwatch.StartNew();
// Configure for medical document standards
_renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;
_renderer.RenderingOptions.MarginTop = 50;
_renderer.RenderingOptions.MarginBottom = 40;
// HIPAA-compliant header
_renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
Height = 45,
HtmlFragment = $@"
<div style='width: 100%; font-size: 10px;'>
<div style='float: left;'>
<strong>CONFIDENTIAL MEDICAL RECORD</strong><br/>
Patient: {model.PatientName} | MRN: {model.MedicalRecordNumber}
</div>
<div style='float: right; text-align: right;'>
Generated: {{date}} {{time}}<br/>
Provider: {model.ProviderName}
</div>
</div>"
};
// Generate report HTML
var html = GenerateMedicalReportHtml(model);
// Create PDF with encryption for HIPAA compliance
var pdf = _renderer.RenderHtmlAsPdf(html);
// Apply 256-bit AES encryption
pdf.SecuritySettings.UserPassword = GenerateSecurePassword();
pdf.SecuritySettings.OwnerPassword = GenerateOwnerPassword();
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint;
pdf.SecuritySettings.AllowUserFormData = false;
pdf.SecuritySettings.AllowUserAnnotations = false;
// Add audit metadata
pdf.MetaData.Author = model.ProviderName;
pdf.MetaData.Title = $"Medical Report - {model.PatientName}";
pdf.MetaData.Keywords = "medical,confidential,hipaa";
pdf.MetaData.CustomProperties.Add("ReportType", model.ReportType);
pdf.MetaData.CustomProperties.Add("GeneratedBy", model.UserId);
pdf.MetaData.CustomProperties.Add("Timestamp", DateTime.UtcNow.ToString("O"));
stopwatch.Stop();
_logger.LogInformation($"Medical report generated in {stopwatch.ElapsedMilliseconds}ms for patient {model.MedicalRecordNumber}");
return pdf;
}
private string GenerateMedicalReportHtml(PatientReportModel model)
{
// Generate comprehensive medical report HTML
// This would typically use a Razor view in production
return $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: 'Segoe UI', Arial, sans-serif; margin: 0; padding: 20px; }}
.header {{ background: #f0f4f8; padding: 20px; margin-bottom: 30px; }}
.patient-info {{ display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px; }}
.section {{ margin-bottom: 30px; }}
.section h2 {{ color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }}
.vital-signs {{ display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }}
.vital-box {{ background: #ecf0f1; padding: 15px; border-radius: 5px; }}
.medication-table {{ width: 100%; border-collapse: collapse; }}
.medication-table th, .medication-table td {{ border: 1px solid #ddd; padding: 10px; text-align: left; }}
.medication-table th {{ background: #3498db; color: white; }}
.alert {{ background: #e74c3c; color: white; padding: 10px; border-radius: 5px; margin-bottom: 20px; }}
</style>
</head>
<body>
<div class='header'>
<h1>Patient Medical Report</h1>
<p>Report Date: {DateTime.Now:MMMM dd, yyyy}</p>
</div>
{(model.HasAllergies ? "<div class='alert'>⚠ PATIENT HAS KNOWN ALLERGIES - SEE ALLERGY SECTION</div>" : "")}
<div class='patient-info'>
<div>
<strong>Patient Name:</strong> {model.PatientName}<br/>
<strong>Date of Birth:</strong> {model.DateOfBirth:MM/dd/yyyy}<br/>
<strong>Age:</strong> {model.Age} years<br/>
<strong>Gender:</strong> {model.Gender}
</div>
<div>
<strong>MRN:</strong> {model.MedicalRecordNumber}<br/>
<strong>Admission Date:</strong> {model.AdmissionDate:MM/dd/yyyy}<br/>
<strong>Provider:</strong> {model.ProviderName}<br/>
<strong>Department:</strong> {model.Department}
</div>
</div>
<div class='section'>
<h2>Vital Signs</h2>
<div class='vital-signs'>
<div class='vital-box'>
<strong>Blood Pressure</strong><br/>
{model.BloodPressure}
</div>
<div class='vital-box'>
<strong>Heart Rate</strong><br/>
{model.HeartRate} bpm
</div>
<div class='vital-box'>
<strong>Temperature</strong><br/>
{model.Temperature}°F
</div>
<div class='vital-box'>
<strong>Respiratory Rate</strong><br/>
{model.RespiratoryRate} /min
</div>
<div class='vital-box'>
<strong>O2 Saturation</strong><br/>
{model.OxygenSaturation}%
</div>
<div class='vital-box'>
<strong>Weight</strong><br/>
{model.Weight} lbs
</div>
</div>
</div>
<div class='section'>
<h2>Current Medications</h2>
<table class='medication-table'>
<thead>
<tr>
<th>Medication</th>
<th>Dosage</th>
<th>Frequency</th>
<th>Route</th>
<th>Start Date</th>
</tr>
</thead>
<tbody>
{string.Join("", model.Medications.Select(m => $@"
<tr>
<td>{m.Name}</td>
<td>{m.Dosage}</td>
<td>{m.Frequency}</td>
<td>{m.Route}</td>
<td>{m.StartDate:MM/dd/yyyy}</td>
</tr>
"))}
</tbody>
</table>
</div>
<div class='section'>
<h2>Clinical Notes</h2>
<p>{model.ClinicalNotes}</p>
</div>
<div class='section'>
<h2>Treatment Plan</h2>
<p>{model.TreatmentPlan}</p>
</div>
</body>
</html>";
}
private string GenerateSecurePassword()
{
// Generate cryptographically secure password
using var rng = System.Security.Cryptography.RandomNumberGenerator.Create();
var bytes = new byte[32];
rng.GetBytes(bytes);
return Convert.ToBase64String(bytes);
}
private string GenerateOwnerPassword()
{
// In production, retrieve from secure configuration
return "SecureOwnerPassword123!";
}
}
public class PatientReportModel
{
public string PatientName { get; set; }
public string MedicalRecordNumber { get; set; }
public DateTime DateOfBirth { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public DateTime AdmissionDate { get; set; }
public string ProviderName { get; set; }
public string Department { get; set; }
public string ReportType { get; set; }
public string UserId { get; set; }
// Vital Signs
public string BloodPressure { get; set; }
public int HeartRate { get; set; }
public decimal Temperature { get; set; }
public int RespiratoryRate { get; set; }
public int OxygenSaturation { get; set; }
public decimal Weight { get; set; }
// Medical Information
public bool HasAllergies { get; set; }
public List<Medication> Medications { get; set; }
public string ClinicalNotes { get; set; }
public string TreatmentPlan { get; set; }
}
public class Medication
{
public string Name { get; set; }
public string Dosage { get; set; }
public string Frequency { get; set; }
public string Route { get; set; }
public DateTime StartDate { get; set; }
}public class MedicalReportGenerator
{
private readonly ChromePdfRenderer _renderer;
private readonly ILogger<MedicalReportGenerator> _logger;
public MedicalReportGenerator(
ChromePdfRenderer renderer,
ILogger<MedicalReportGenerator> logger)
{
_renderer = renderer;
_logger = logger;
}
public async Task<PdfDocument> GeneratePatientReport(PatientReportModel model)
{
var stopwatch = Stopwatch.StartNew();
// Configure for medical document standards
_renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;
_renderer.RenderingOptions.MarginTop = 50;
_renderer.RenderingOptions.MarginBottom = 40;
// HIPAA-compliant header
_renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
Height = 45,
HtmlFragment = $@"
<div style='width: 100%; font-size: 10px;'>
<div style='float: left;'>
<strong>CONFIDENTIAL MEDICAL RECORD</strong><br/>
Patient: {model.PatientName} | MRN: {model.MedicalRecordNumber}
</div>
<div style='float: right; text-align: right;'>
Generated: {{date}} {{time}}<br/>
Provider: {model.ProviderName}
</div>
</div>"
};
// Generate report HTML
var html = GenerateMedicalReportHtml(model);
// Create PDF with encryption for HIPAA compliance
var pdf = _renderer.RenderHtmlAsPdf(html);
// Apply 256-bit AES encryption
pdf.SecuritySettings.UserPassword = GenerateSecurePassword();
pdf.SecuritySettings.OwnerPassword = GenerateOwnerPassword();
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint;
pdf.SecuritySettings.AllowUserFormData = false;
pdf.SecuritySettings.AllowUserAnnotations = false;
// Add audit metadata
pdf.MetaData.Author = model.ProviderName;
pdf.MetaData.Title = $"Medical Report - {model.PatientName}";
pdf.MetaData.Keywords = "medical,confidential,hipaa";
pdf.MetaData.CustomProperties.Add("ReportType", model.ReportType);
pdf.MetaData.CustomProperties.Add("GeneratedBy", model.UserId);
pdf.MetaData.CustomProperties.Add("Timestamp", DateTime.UtcNow.ToString("O"));
stopwatch.Stop();
_logger.LogInformation($"Medical report generated in {stopwatch.ElapsedMilliseconds}ms for patient {model.MedicalRecordNumber}");
return pdf;
}
private string GenerateMedicalReportHtml(PatientReportModel model)
{
// Generate comprehensive medical report HTML
// This would typically use a Razor view in production
return $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: 'Segoe UI', Arial, sans-serif; margin: 0; padding: 20px; }}
.header {{ background: #f0f4f8; padding: 20px; margin-bottom: 30px; }}
.patient-info {{ display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px; }}
.section {{ margin-bottom: 30px; }}
.section h2 {{ color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }}
.vital-signs {{ display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }}
.vital-box {{ background: #ecf0f1; padding: 15px; border-radius: 5px; }}
.medication-table {{ width: 100%; border-collapse: collapse; }}
.medication-table th, .medication-table td {{ border: 1px solid #ddd; padding: 10px; text-align: left; }}
.medication-table th {{ background: #3498db; color: white; }}
.alert {{ background: #e74c3c; color: white; padding: 10px; border-radius: 5px; margin-bottom: 20px; }}
</style>
</head>
<body>
<div class='header'>
<h1>Patient Medical Report</h1>
<p>Report Date: {DateTime.Now:MMMM dd, yyyy}</p>
</div>
{(model.HasAllergies ? "<div class='alert'>⚠ PATIENT HAS KNOWN ALLERGIES - SEE ALLERGY SECTION</div>" : "")}
<div class='patient-info'>
<div>
<strong>Patient Name:</strong> {model.PatientName}<br/>
<strong>Date of Birth:</strong> {model.DateOfBirth:MM/dd/yyyy}<br/>
<strong>Age:</strong> {model.Age} years<br/>
<strong>Gender:</strong> {model.Gender}
</div>
<div>
<strong>MRN:</strong> {model.MedicalRecordNumber}<br/>
<strong>Admission Date:</strong> {model.AdmissionDate:MM/dd/yyyy}<br/>
<strong>Provider:</strong> {model.ProviderName}<br/>
<strong>Department:</strong> {model.Department}
</div>
</div>
<div class='section'>
<h2>Vital Signs</h2>
<div class='vital-signs'>
<div class='vital-box'>
<strong>Blood Pressure</strong><br/>
{model.BloodPressure}
</div>
<div class='vital-box'>
<strong>Heart Rate</strong><br/>
{model.HeartRate} bpm
</div>
<div class='vital-box'>
<strong>Temperature</strong><br/>
{model.Temperature}°F
</div>
<div class='vital-box'>
<strong>Respiratory Rate</strong><br/>
{model.RespiratoryRate} /min
</div>
<div class='vital-box'>
<strong>O2 Saturation</strong><br/>
{model.OxygenSaturation}%
</div>
<div class='vital-box'>
<strong>Weight</strong><br/>
{model.Weight} lbs
</div>
</div>
</div>
<div class='section'>
<h2>Current Medications</h2>
<table class='medication-table'>
<thead>
<tr>
<th>Medication</th>
<th>Dosage</th>
<th>Frequency</th>
<th>Route</th>
<th>Start Date</th>
</tr>
</thead>
<tbody>
{string.Join("", model.Medications.Select(m => $@"
<tr>
<td>{m.Name}</td>
<td>{m.Dosage}</td>
<td>{m.Frequency}</td>
<td>{m.Route}</td>
<td>{m.StartDate:MM/dd/yyyy}</td>
</tr>
"))}
</tbody>
</table>
</div>
<div class='section'>
<h2>Clinical Notes</h2>
<p>{model.ClinicalNotes}</p>
</div>
<div class='section'>
<h2>Treatment Plan</h2>
<p>{model.TreatmentPlan}</p>
</div>
</body>
</html>";
}
private string GenerateSecurePassword()
{
// Generate cryptographically secure password
using var rng = System.Security.Cryptography.RandomNumberGenerator.Create();
var bytes = new byte[32];
rng.GetBytes(bytes);
return Convert.ToBase64String(bytes);
}
private string GenerateOwnerPassword()
{
// In production, retrieve from secure configuration
return "SecureOwnerPassword123!";
}
}
public class PatientReportModel
{
public string PatientName { get; set; }
public string MedicalRecordNumber { get; set; }
public DateTime DateOfBirth { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public DateTime AdmissionDate { get; set; }
public string ProviderName { get; set; }
public string Department { get; set; }
public string ReportType { get; set; }
public string UserId { get; set; }
// Vital Signs
public string BloodPressure { get; set; }
public int HeartRate { get; set; }
public decimal Temperature { get; set; }
public int RespiratoryRate { get; set; }
public int OxygenSaturation { get; set; }
public decimal Weight { get; set; }
// Medical Information
public bool HasAllergies { get; set; }
public List<Medication> Medications { get; set; }
public string ClinicalNotes { get; set; }
public string TreatmentPlan { get; set; }
}
public class Medication
{
public string Name { get; set; }
public string Dosage { get; set; }
public string Frequency { get; set; }
public string Route { get; set; }
public DateTime StartDate { get; set; }
}Consideraciones de seguridad
Cuando se generan archivos PDF en entornos de producción o se trabaja con información confidencial, la seguridad es primordial. IronPDF ofrece varias funciones de seguridad:
Aplicación de la configuración de seguridad
using System.Text.RegularExpressions;
namespace PdfGeneratorApp.Utilities
{
public static class PdfSecurityHelper
{
public static void ApplySecuritySettings(PdfDocument pdf, SecurityLevel level)
{
switch (level)
{
case SecurityLevel.Low:
// Basic protection
pdf.SecuritySettings.AllowUserCopyPasteContent = true;
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights;
break;
case SecurityLevel.Medium:
// Restricted copying
pdf.SecuritySettings.UserPassword = GeneratePassword(8);
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.PrintLowQuality;
break;
case SecurityLevel.High:
// Maximum security
pdf.SecuritySettings.UserPassword = GeneratePassword(16);
pdf.SecuritySettings.OwnerPassword = GeneratePassword(16);
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint;
pdf.SecuritySettings.AllowUserAnnotations = false;
pdf.SecuritySettings.AllowUserFormData = false;
break;
}
}
public enum SecurityLevel
{
Low,
Medium,
High
}
}using System.Text.RegularExpressions;
namespace PdfGeneratorApp.Utilities
{
public static class PdfSecurityHelper
{
public static void ApplySecuritySettings(PdfDocument pdf, SecurityLevel level)
{
switch (level)
{
case SecurityLevel.Low:
// Basic protection
pdf.SecuritySettings.AllowUserCopyPasteContent = true;
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights;
break;
case SecurityLevel.Medium:
// Restricted copying
pdf.SecuritySettings.UserPassword = GeneratePassword(8);
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.PrintLowQuality;
break;
case SecurityLevel.High:
// Maximum security
pdf.SecuritySettings.UserPassword = GeneratePassword(16);
pdf.SecuritySettings.OwnerPassword = GeneratePassword(16);
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint;
pdf.SecuritySettings.AllowUserAnnotations = false;
pdf.SecuritySettings.AllowUserFormData = false;
break;
}
}
public enum SecurityLevel
{
Low,
Medium,
High
}
}En la clase de ayuda anterior, utilizamos el enunciado SecurityLevel para establecer diferentes opciones de seguridad para el PDF. Por ejemplo, el Nivel de seguridad bajo aplica una protección básica, pero permite Derechos de impresión totales y copiar y pegar desde el PDF estableciendo la propiedad AllowUserCopyPasteContent en true. Estas opciones se activan ajustando las propiedades de la clase PDF con IronPDF. Para obtener una lista completa de propiedades y opciones de seguridad, consulte la guía how-to.
Manejo de errores y resolución de problemas
Los problemas comunes y sus soluciones para generar PDF en ASP.NET Core se han debatido ampliamente en la comunidad de desarrolladores. He aquí soluciones probadas para los retos más frecuentes:
Gestión de la memoria
Para una guía detallada sobre manejar las fugas de memoria en IronPDF, implemente el siguiente patrón:
public class PdfMemoryManager : IDisposable
{
private readonly List<PdfDocument> _openDocuments = new();
private readonly ILogger<PdfMemoryManager> _logger;
private bool _disposed;
public PdfMemoryManager(ILogger<PdfMemoryManager> logger)
{
_logger = logger;
}
public PdfDocument CreateDocument(ChromePdfRenderer renderer, string html)
{
try
{
var pdf = renderer.RenderHtmlAsPdf(html);
_openDocuments.Add(pdf);
return pdf;
}
catch (OutOfMemoryExcepción ex)
{
_logger.LogError(ex, "Out of memory while generating PDF");
// Force garbage collection
CleanupMemory();
// Retry with reduced quality
renderer.RenderingOptions.JpegQuality = 50;
var pdf = renderer.RenderHtmlAsPdf(html);
_openDocuments.Add(pdf);
return pdf;
}
}
private void CleanupMemory()
{
// Dispose all open documents
foreach (var doc in _openDocuments)
{
doc?.Dispose();
}
_openDocuments.Clear();
// Force garbage collection
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
_logger.LogInformation("Memory cleanup performed");
}
public void Dispose()
{
if (!_disposed)
{
CleanupMemory();
_disposed = true;
}
}
}public class PdfMemoryManager : IDisposable
{
private readonly List<PdfDocument> _openDocuments = new();
private readonly ILogger<PdfMemoryManager> _logger;
private bool _disposed;
public PdfMemoryManager(ILogger<PdfMemoryManager> logger)
{
_logger = logger;
}
public PdfDocument CreateDocument(ChromePdfRenderer renderer, string html)
{
try
{
var pdf = renderer.RenderHtmlAsPdf(html);
_openDocuments.Add(pdf);
return pdf;
}
catch (OutOfMemoryExcepción ex)
{
_logger.LogError(ex, "Out of memory while generating PDF");
// Force garbage collection
CleanupMemory();
// Retry with reduced quality
renderer.RenderingOptions.JpegQuality = 50;
var pdf = renderer.RenderHtmlAsPdf(html);
_openDocuments.Add(pdf);
return pdf;
}
}
private void CleanupMemory()
{
// Dispose all open documents
foreach (var doc in _openDocuments)
{
doc?.Dispose();
}
_openDocuments.Clear();
// Force garbage collection
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
_logger.LogInformation("Memory cleanup performed");
}
public void Dispose()
{
if (!_disposed)
{
CleanupMemory();
_disposed = true;
}
}
}Problemas de renderizado de fuentes
public class FontTroubleshooter
{
public static void EnsureFontsAvailable(ChromePdfRenderer renderer)
{
// Embed fonts in the PDF
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
// Use web-safe fonts as fallback
var fontFallback = @"
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
@font-face {
font-family: 'CustomFont';
src: url('data:font/woff2;base64,YOUR_BASE64_FONT_HERE') format('woff2');
}
</style>";
// Add to HTML head
renderer.RenderingOptions.CustomCssUrl = "https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap";
}
}public class FontTroubleshooter
{
public static void EnsureFontsAvailable(ChromePdfRenderer renderer)
{
// Embed fonts in the PDF
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
// Use web-safe fonts as fallback
var fontFallback = @"
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
@font-face {
font-family: 'CustomFont';
src: url('data:font/woff2;base64,YOUR_BASE64_FONT_HERE') format('woff2');
}
</style>";
// Add to HTML head
renderer.RenderingOptions.CustomCssUrl = "https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap";
}
}Excepciones comunes y soluciones
Excepción | Causa | Solución |
IronPdf.Excepcións.IronPdfNativeExcepción | Error de inicialización del motor de Chrome | Asegúrese de que están instalados los redistribuibles de Visual C |
System.UnauthorizedAccessExcepción | Permisos insuficientes | Conceder acceso de escritura a la carpeta temporal |
System.TimeoutExcepción | JavaScript tarda demasiado | Aumentar RenderDelay o desactivar JavaScript |
System.OutOfMemoryExcepción | PDF de gran tamaño o procesamiento por lotes | Implementar la paginación o reducir la calidad de la imagen |
IronPdf.Excepcións.IronPdfLicensingExcepción | Licencia no válida o caducada | Verificar la clave de licencia en la configuración |
Consejos de depuración
Con IronPDF, puede depurar y registrar fácilmente todos los procesos ejecutados por su aplicación para buscar y verificar cualquier entrada y salida. Para habilitar el registro, establezca EnableDebugging en true y especifique una ruta de archivo para el registro asignando un valor a la propiedad LogFilePath. Aquí tienes un rápido fragmento de código que muestra cómo hacerlo.
public class PdfDebugger
{
private readonly ILogger<PdfDebugger> _logger;
public PdfDebugger(ILogger<PdfDebugger> logger)
{
_logger = logger;
}
public void EnableDebugging(ChromePdfRenderer renderer)
{
// Enable detailed logging
IronPdf.Logging.Logger.EnableDebugging = true;
IronPdf.Logging.Logger.LogFilePath = "IronPdf.log";
IronPdf.Logging.Logger.LoggingMode = IronPdf.Logging.Logger.LoggingModes.All;
// Log rendering settings
_logger.LogDebug($"Paper Size: {renderer.RenderingOptions.PaperSize}");
_logger.LogDebug($"Margins: T{renderer.RenderingOptions.MarginTop} " +
$"B{renderer.RenderingOptions.MarginBottom} " +
$"L{renderer.RenderingOptions.MarginLeft} " +
$"R{renderer.RenderingOptions.MarginRight}");
_logger.LogDebug($"JavaScript Enabled: {renderer.RenderingOptions.EnableJavaScript}");
_logger.LogDebug($"Render Delay: {renderer.RenderingOptions.RenderDelay}ms");
}
public void SaveDebugHtml(string html, string fileName)
{
// Save HTML for inspection
var debugPath = Path.Combine("debug", $"{fileName}_{DateTime.Now:yyyyMMdd_HHmmss}.html");
Directory.CreateDirectory("debug");
File.WriteAllText(debugPath, html);
_logger.LogDebug($"Debug HTML saved to: {debugPath}");
}
}public class PdfDebugger
{
private readonly ILogger<PdfDebugger> _logger;
public PdfDebugger(ILogger<PdfDebugger> logger)
{
_logger = logger;
}
public void EnableDebugging(ChromePdfRenderer renderer)
{
// Enable detailed logging
IronPdf.Logging.Logger.EnableDebugging = true;
IronPdf.Logging.Logger.LogFilePath = "IronPdf.log";
IronPdf.Logging.Logger.LoggingMode = IronPdf.Logging.Logger.LoggingModes.All;
// Log rendering settings
_logger.LogDebug($"Paper Size: {renderer.RenderingOptions.PaperSize}");
_logger.LogDebug($"Margins: T{renderer.RenderingOptions.MarginTop} " +
$"B{renderer.RenderingOptions.MarginBottom} " +
$"L{renderer.RenderingOptions.MarginLeft} " +
$"R{renderer.RenderingOptions.MarginRight}");
_logger.LogDebug($"JavaScript Enabled: {renderer.RenderingOptions.EnableJavaScript}");
_logger.LogDebug($"Render Delay: {renderer.RenderingOptions.RenderDelay}ms");
}
public void SaveDebugHtml(string html, string fileName)
{
// Save HTML for inspection
var debugPath = Path.Combine("debug", $"{fileName}_{DateTime.Now:yyyyMMdd_HHmmss}.html");
Directory.CreateDirectory("debug");
File.WriteAllText(debugPath, html);
_logger.LogDebug($"Debug HTML saved to: {debugPath}");
}
}Mejores prácticas de despliegue local
Configuración de IIS
Para el despliegue en IIS, asegúrese de que la configuración es correcta:
<!-- web.config -->
<configuration>
<system.webServer>
<!-- Enable 64-bit mode for better memory handling -->
<applicationPool>
<processModel enable32BitAppOnWin64="false" />
</applicationPool>
<!-- Increase request timeout for large PDFs -->
<httpRuntime executionTimeout="300" maxRequestLength="51200" />
<!-- Configure temp folder permissions -->
<system.web>
<compilation tempDirectory="~/App_Data/Temp/" />
</system.web>
</system.webServer>
</configuration><!-- web.config -->
<configuration>
<system.webServer>
<!-- Enable 64-bit mode for better memory handling -->
<applicationPool>
<processModel enable32BitAppOnWin64="false" />
</applicationPool>
<!-- Increase request timeout for large PDFs -->
<httpRuntime executionTimeout="300" maxRequestLength="51200" />
<!-- Configure temp folder permissions -->
<system.web>
<compilation tempDirectory="~/App_Data/Temp/" />
</system.web>
</system.webServer>
</configuration>Dependencias requeridas
Asegúrese de que estos componentes están instalados en su servidor de despliegue:
- .NET Runtime - Versión 6.0 o posterior
- Visual C++ Redistributable - 2015-2022 (x64)
- Windows Server - Se recomienda 2012 R2 o posterior
Permisos del sistema de archivos
# Grant IIS_IUSRS write access to temp folder
icacls "C:\inetpub\wwwroot\YourApp\App_Data\Temp" /grant "IIS_IUSRS:(OI)(CI)M" /T
# Grant access to IronPDF cache folder
icacls "C:\Windows\Temp\IronPdf" /grant "IIS_IUSRS:(OI)(CI)M" /T# Grant IIS_IUSRS write access to temp folder
icacls "C:\inetpub\wwwroot\YourApp\App_Data\Temp" /grant "IIS_IUSRS:(OI)(CI)M" /T
# Grant access to IronPDF cache folder
icacls "C:\Windows\Temp\IronPdf" /grant "IIS_IUSRS:(OI)(CI)M" /TAjuste del rendimiento
// Startup.cs or Program.cs
public void ConfigureServices(IServiceCollection services)
{
// Configure IronPDF for production
services.AddSingleton<ChromePdfRenderer>(provider =>
{
var renderer = new ChromePdfRenderer();
// Production optimizations
renderer.RenderingOptions.RenderDelay = 50; // Minimize delay
renderer.RenderingOptions.Timeout = 120; // 2 minutes max
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
// Enable caching for static resources
Installation.ChromeGpuMode = IronPdf.Engines.Chrome.ChromeGpuModes.Disabled;
Installation.LinuxAndDockerDependenciesAutoConfig = false;
return renderer;
});
// Configure memory cache for generated PDFs
services.AddMemoryCache(options =>
{
options.SizeLimit = 100_000_000; // 100 MB cache
});
}// Startup.cs or Program.cs
public void ConfigureServices(IServiceCollection services)
{
// Configure IronPDF for production
services.AddSingleton<ChromePdfRenderer>(provider =>
{
var renderer = new ChromePdfRenderer();
// Production optimizations
renderer.RenderingOptions.RenderDelay = 50; // Minimize delay
renderer.RenderingOptions.Timeout = 120; // 2 minutes max
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
// Enable caching for static resources
Installation.ChromeGpuMode = IronPdf.Engines.Chrome.ChromeGpuModes.Disabled;
Installation.LinuxAndDockerDependenciesAutoConfig = false;
return renderer;
});
// Configure memory cache for generated PDFs
services.AddMemoryCache(options =>
{
options.SizeLimit = 100_000_000; // 100 MB cache
});
}Resumen de buenas prácticas
- Deshágase siempre de los objetos PdfDocument para evitar fugas de memoria
- Reutilizar instancias de ChromePdfRenderer para mejorar el rendimiento
- Implementar una gestión de errores adecuada con un registro detallado
- Sanitizar todos los datos introducidos por el usuario antes de generar el PDF
- Utilizar patrones async/await para mejorar la escalabilidad
- Configurar los tiempos de espera adecuados para la renderización de JavaScript
- Comprimir imágenes para reducir el tamaño del archivo
- PDFs generados en caché cuando el contenido es estático
- Monitorizar el uso de memoria durante el procesamiento por lotes
- Prueba con volúmenes de datos similares a los de producción antes del despliegue
Transforme su aplicación ASP.NET hoy mismo
Ahora tiene una comprensión completa de la generación de archivos PDF en ASP.NET Core utilizando C# con IronPDF. Desde la conversión básica de vistas Razor hasta el procesamiento avanzado por lotes con optimización del rendimiento, estará equipado para implementar la generación profesional de PDF que coincida exactamente con lo que se ve en Chrome.
Logros clave que has desbloqueado:
- Representación perfecta mediante el motor Chrome de IronPDF que elimina las sorpresas de formato al crear un PDF a partir de páginas HTML, páginas web, etc.
- Plantillas listas para producción con vistas Razor que proporcionan una generación de PDF familiar y fácil de mantener
- Gestión de errores de nivel empresarial y gestión de memoria para un funcionamiento fiable a gran escala
- Procesamiento por lotes optimizado con generación paralela, manejo de miles de documentos
- Características de seguridad profesionales que protegen los documentos confidenciales con un cifrado de 256 bits
Empieza a crear PDF profesionales ahora
¿Está listo para implementar la generación de PDF en su aplicación ASP.NET Core? Descargue la versión de prueba gratuita de IronPDF hoy mismo y experimente la potencia de la generación profesional de PDF sin marcas de agua ni limitaciones durante 30 días. Con documentación exhaustiva, soporte de ingeniería receptivo y una garantía de devolución del dinero de 30 días, puede crear soluciones PDF listas para la producción con total confianza.
Recursos esenciales para su viaje
- 📚 Explora la documentación completa de la API para obtener referencias detalladas de los métodos
- 👥 Únete a la comunidad de desarrolladores para recibir asistencia en tiempo real
- 🚀 Comprar una licencia comercial a partir de $799 para el despliegue en producción
Transforme sus aplicaciones ASP.NET Core con la generación de PDF de nivel empresarial que funciona como se espera y empiece a crear con IronPDF hoy mismo
Preguntas Frecuentes
¿Cuál es el uso principal de IronPDF en aplicaciones ASP.NET?
IronPDF se utiliza principalmente para generar, editar y extraer contenido de documentos PDF, lo que lo convierte en una herramienta esencial para crear facturas, informes, certificados o tickets en aplicaciones ASP.NET.
¿Cómo asegura IronPDF un renderizado de PDF perfecto?
IronPDF asegura un renderizado perfecto utilizando motores de renderizado avanzados y características empresariales para convertir con precisión HTML, imágenes u otros formatos de documentos en PDFs de alta calidad.
¿Puede integrarse IronPDF con aplicaciones ASP.NET Core?
Sí, IronPDF se puede integrar perfectamente con aplicaciones ASP.NET Core, proporcionando a los desarrolladores una potente biblioteca para gestionar diversas tareas PDF de manera eficiente.
¿Cuáles son los beneficios de usar IronPDF para la generación de PDF?
Usar IronPDF para generación de PDFs ofrece beneficios como facilidad de uso, renderizado de alta calidad, soporte para características complejas de documentos y la capacidad de automatizar tareas de PDF dentro de sus aplicaciones.
¿IronPDF soporta la edición de documentos PDF existentes?
Sí, IronPDF soporta la edición de documentos PDF existentes, permitiendo a los desarrolladores modificar el contenido, agregar anotaciones y actualizar metadatos de PDF programáticamente.
¿Es IronPDF adecuado para crear documentos PDF a nivel empresarial?
IronPDF es ideal para crear documentos PDF a nivel empresarial debido a sus características robustas, incluyendo soporte para estructuras de documentos complejas y características de seguridad como cifrado y firmas digitales.
¿Qué formatos de archivo puede convertir IronPDF a PDF?
IronPDF puede convertir varios formatos de archivo a PDF, incluyendo HTML, imágenes y otros tipos de documentos, asegurando flexibilidad y compatibilidad con diferentes fuentes de datos.
¿Cómo gestiona IronPDF la extracción de contenido de PDF?
IronPDF gestiona la extracción de contenido de PDF proporcionando APIs para extraer texto, imágenes y metadatos, facilitando la recuperación y manipulación de datos de documentos PDF.
¿Puede utilizarse IronPDF para automatizar flujos de trabajo de documentos PDF?
Sí, IronPDF puede utilizarse para automatizar flujos de trabajo de documentos PDF, agilizando procesos como la generación, conversión y distribución masiva de archivos PDF en aplicaciones web.
¿Qué tipo de soporte ofrece IronPDF para desarrolladores?
IronPDF ofrece un soporte extenso para desarrolladores, incluyendo documentación detallada, código de muestra y servicio al cliente receptivo para ayudar con la integración y resolución de problemas.
¿IronPDF es compatible con .NET 10 de inmediato?
IronPDF proporciona soporte previo al lanzamiento de .NET 10 y ya es compatible con el lanzamiento de .NET 10 previsto para noviembre de 2025. Los desarrolladores pueden usar IronPDF en proyectos .NET 10 sin necesidad de configuraciones especiales.






