Las mejores bibliotecas PDF de C# for .NET en 2026: Guía para el Mercado Español
La selección de una biblioteca PDF de C# afecta a la exposición del proyecto a licencias, la flexibilidad de implementación y el coste de mantenimiento a largo plazo. La mayoría de las bibliotecas que parecen adecuadas durante la evaluación revelan limitaciones en la producción: requisitos AGPL que no esperaba, representación HTML que no coincide con su navegador o fugas de memoria que sólo aparecen en Linux.
Este artículo compara las principales opciones con ejemplos de código, documenta las compensaciones que importan en la práctica e incluye una comparación de código lado a lado generando la misma factura a través de tres bibliotecas diferentes para que pueda ver las diferencias de la API directamente.
Inicio rápido: HTML a PDF en tres líneas
Instalación a través de NuGet:
Install-Package IronPDFGenere un PDF:
using IronPdf;
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>");
pdf.SaveAs("output.pdf");using IronPdf;
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>");
pdf.SaveAs("output.pdf");Imports IronPdf
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>")
pdf.SaveAs("output.pdf")Funciona en Windows, Linux, macOS y Docker sin configuración adicional. El resultado coincide con Chrome porqueIronPDFincorpora el mismo motor de renderizado Chromium.
Criterios de evaluación
Antes de comparar bibliotecas, sepa qué evaluar. Estas son las preguntas que hacen aflorar los problemas de producción en una fase temprana:
| Criterio | Qué hay que comprobar | Por qué es importante |
|---|---|---|
| Traducción HTML/CSS | Alimente sus plantillas actuales con Flexbox/Grid | La mayoría de las bibliotecas afirman ser compatibles con HTML, pero en el mejor de los casos presentan CSS 2.1 |
| Ejecución de JavaScript | Prueba con Chart.js o contenido de tabla dinámica | Las bibliotecas sin soporte JS producen secciones en blanco |
| Modelo de licencia | Lea la licencia completa, no el resumen | La AGPL exige que toda la aplicación sea de código abierto |
| Plataformas | Despliegue en su entorno Linux/Docker/ARM64 de destino | El éxito de Windows no predice el comportamiento de Linux |
| Memoria bajo carga | Generación de más de 100 documentos en bucle | Las pruebas de un solo documento ocultan fugas que bloquean los servidores de producción |
| Precios publicados | Compruebe si el precio está en el sitio web | "Ventas por contacto" suele significar entre 15.000 y 210.000 dólares al año |
Comparación de bibliotecas
IronPDF- Chromium integrado, compatibilidad total con CSS/JS
IronPDF integra Chromium directamente en el paquete NuGet. El renderizado HTML coincide con Chrome porque es el motor de Chrome. CSS Flexbox, Grid, propiedades personalizadas y JavaScriptse ejecutan como se espera.
using IronPdf;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
var pdf = renderer.RenderHtmlAsPdf(@"
<html>
<head>
<style>
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.card { background: linear-gradient(135deg, #f8fafc, #e2e8f0);
border-radius: 8px; padding: 20px; text-align: center; }
</style>
</head>
<body>
<div class='grid'>
<div class='card'><h3>Revenue</h3><p>$1.2M</p></div>
<div class='card'><h3>Users</h3><p>45,230</p></div>
<div class='card'><h3>Uptime</h3><p>99.97%</p></div>
</div>
</body>
</html>");
pdf.SaveAs("dashboard.pdf");using IronPdf;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
var pdf = renderer.RenderHtmlAsPdf(@"
<html>
<head>
<style>
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.card { background: linear-gradient(135deg, #f8fafc, #e2e8f0);
border-radius: 8px; padding: 20px; text-align: center; }
</style>
</head>
<body>
<div class='grid'>
<div class='card'><h3>Revenue</h3><p>$1.2M</p></div>
<div class='card'><h3>Users</h3><p>45,230</p></div>
<div class='card'><h3>Uptime</h3><p>99.97%</p></div>
</div>
</body>
</html>");
pdf.SaveAs("dashboard.pdf");Imports IronPdf
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print
Dim pdf = renderer.RenderHtmlAsPdf("
<html>
<head>
<style>
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.card { background: linear-gradient(135deg, #f8fafc, #e2e8f0);
border-radius: 8px; padding: 20px; text-align: center; }
</style>
</head>
<body>
<div class='grid'>
<div class='card'><h3>Revenue</h3><p>$1.2M</p></div>
<div class='card'><h3>Users</h3><p>45,230</p></div>
<div class='card'><h3>Uptime</h3><p>99.97%</p></div>
</div>
</body>
</html>")
pdf.SaveAs("dashboard.pdf")Desventajas a tener en cuenta: El Chromium incrustado añade ~200MB al paquete de despliegue. Para las implantaciones estándar de servidores y contenedores, se trata de una descarga única sin impacto en el tiempo de ejecución. Para entornos con restricciones de tamaño como el plan de consumo de Azure Functions, compruebe el límite de tamaño de implementación. La primera generación de PDF en un proceso en frío tarda entre 2 y 5 segundos para la inicialización de Chromium; las siguientes generaciones se ejecutan en 100-500 ms. La memoria de referencia es de unos 150-200 MB; planifique los recursos del contenedor en consecuencia.
Licencias: Licencias perpetuas desde $2,998 (1 desarrollador). Precios publicados en ironpdf.com. Sin tarifas por documento, sin AGPL, sin umbrales de ingresos.
iText(iText) — Licenciamiento AGPL, HTML limitado
iText es una biblioteca de manipulación de PDF con una larga historia. El complemento pdfHTML ofrece conversión de HTML a PDF, pero no utiliza un motor de navegador, sino que se aproxima a CSS 2.1 con un analizador personalizado.
Un equipo de producción de una empresa mediana de SaaS descubrió esto cuando migró sus plantillas de facturas de Razor views. Las plantillas utilizan CSS Flexbox para diseños de columnas adaptables. Tras integrar pdfHTML de iText, cada factura se presentó como una pila vertical de una sola columna. Las propiedades display: flex, gap, y justify-content fueron ignoradas silenciosamente. El equipo empleó tres semanas en el desarrollo antes de darse cuenta de que pdfHTML no podía procesar el CSS existente.
La realidad AGPL: iTextutiliza la licencia AGPL. Si su aplicación es accesible a través de la red -lo que incluye todas las aplicaciones web, API y productos SaaS-, deberá publicar todo el código fuente de su aplicación bajo la licencia AGPL. No sólo el módulo PDF. Todo. iTexty su empresa matriz, Apryse, velan activamente por que así sea.
Licencia comercial: iTextpasó a la licencia por suscripción en 2024. Los precios no están publicados - póngase en contacto con ventas para obtener un presupuesto. Los datos de terceros sugieren entre 15.000 y 210.000 dólares anuales en función del volumen de uso.
PDFSharp— Licencia MIT, Sin HTML
PdfSharp es genuinamente gratuito bajo la licencia MIT con 34.9 millones de descargas en NuGet. La contrapartida es la capacidad: proporciona una API de dibujo basada en coordenadas sin analizador HTML, sin motor CSS y sin sistema de plantillas.
Un equipo construyendo un panel de informes eligió PdfSharp porque era gratuito y bien conocido. Pasaron cuatro meses escribiendo código de maquetación basado en coordenadas: calculando las posiciones X/Y de cada elemento de texto, dibujando los bordes de las tablas píxel a píxel y gestionando manualmente los saltos de página. Cuando finalmente compararon su resultado con lo que la misma plantilla HTML producía en un navegador, se dieron cuenta de que habían construido una versión peor de lo que una biblioteca basada en Chromium hace automáticamente.
PdfSharp funciona bien para fusionar PDFs, agregar marcas de agua y construir documentos estructurados simples a partir de datos. Si no necesitas renderizar HTML, sigue siendo una opción legítima.
QuestPDF - API elegante, sin HTML, umbral de ingresos
QuestPDF ofrece una API de C# fluida para crear documentos mediante programación. El diseño de la API es realmente bueno: es una de las mejores API de bibliotecas .NET de cualquier categoría.
Hay dos limitaciones importantes: QuestPDF no renderiza HTML (por diseño - se trata de una elección arquitectónica deliberada, no de una característica que falte), y la Licencia Comunitaria cubre empresas con ingresos brutos anuales inferiores a 1 millón de dólares. Una vez que su empresa cruce ese umbral, será obligatoria una licencia comercial. Las empresas que se acerquen al umbral deben presupuestar esta transición antes de que sea urgente.
A pesar del claro posicionamiento de QuestPDF en contra del HTML, los desarrolladores lo descubren regularmente después de iniciar la implementación porque la biblioteca aparece en los resultados de búsqueda de "biblioteca PDF de C#" junto a bibliotecas con capacidad HTML.
wkhtmltopdf Wrappers - CVEs abandonados, sin parches
el tiempo de wkhtmltopdf ha pasado. La organización GitHub fue archivada en julio de 2024. El motor subyacente QtWebKit fue obsoleto por Qt en 2015. Los CVEs conocidos - incluyendo CVE-2022-35583(CVSS 9.8, SSRF que permite la exfiltración de credenciales de AWS) - nunca serán parcheados.
Envoltorios de C# como DinkToPdf, NReco.PdfGenerator y WkHtmlToXSharp envuelven el mismo binario abandonado. El motor de renderizado está congelado en aproximadamente la capacidad de Safari 2011: sin Flexbox, sin Grid, JavaScriptlimitado. No es una opción viable para proyectos nuevos.
PuppeteerSharp- Renderización completa, complejidad operativa
Puppeteer Sharp controla Headless Chrome a través de enlaces .NET. La calidad de renderizado se corresponde con Chrome porque es Chrome. La contrapartida es operativa: se gestionan los procesos externos del navegador, incluidas las descargas, la agrupación, la supervisión de la memoria y la recuperación de bloqueos.
using PuppeteerSharp;
// Downloads ~280MB Chromium on first run
await new BrowserFetcher().DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(
new LaunchOptions { Headless = true });
await using var page = await browser.NewPageAsync();
await page.SetContentAsync(html);
return await page.PdfAsync(new PdfOptions { Format = PaperFormat.A4, PrintBackground = true });using PuppeteerSharp;
// Downloads ~280MB Chromium on first run
await new BrowserFetcher().DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(
new LaunchOptions { Headless = true });
await using var page = await browser.NewPageAsync();
await page.SetContentAsync(html);
return await page.PdfAsync(new PdfOptions { Format = PaperFormat.A4, PrintBackground = true });Imports PuppeteerSharp
' Downloads ~280MB Chromium on first run
Await (New BrowserFetcher()).DownloadAsync()
Await Using browser = Await Puppeteer.LaunchAsync(New LaunchOptions With {.Headless = True})
Await Using page = Await browser.NewPageAsync()
Await page.SetContentAsync(html)
Return Await page.PdfAsync(New PdfOptions With {.Format = PaperFormat.A4, .PrintBackground = True})
End Using
End UsingEn producción, también se necesita la agrupación de procesos del navegador, la supervisión de fugas de memoria (los procesos de Chromium pueden tener fugas), la recuperación de fallos y la limpieza de recursos. El despliegue en Docker requiere la instalación de las dependencias de Chromium, lo que supone un archivo Dockerfile considerable en comparación con una imagen .NET estándar. PuppeteerSharpes viable si su equipo puede absorber los gastos operativos.
Aspose.PDF - Amplias funciones, problemas de memoria en Linux
Aspose.PDF ofrece una amplia funcionalidad PDF con una buena documentación. El problema más importante es la estabilidad de Linux: Asposedepende de System.Drawing.Common, que requiere libgdiplus en Linux, una biblioteca sin mantenimiento con fugas de memoria documentadas. Los informes para desarrolladores abarcan años:
"Varias docenas de peticiones hacen que el servicio se quede sin memoria en el entorno Unix, pero esto no ocurre en el entorno basado en Windows"
Para las implantaciones solo en Windows, Asposesigue siendo capaz. Para despliegues multiplataforma o en contenedores, la dependencia System.Drawing.Common crea un riesgo continuo. El precio de la licencia comercial es de aproximadamente 999 dólares por desarrollador.
Comparación de características
| Característica | IronPDF | iText | PDFSharp | QuestPDF | wkhtmltopdf | Puppeteer | Aspose |
|---|---|---|---|---|---|---|---|
| HTML a PDF | Completo (Chromium) | Limitado (CSS 2.1) | No | No | Deprecated | Completo (Chrome) | Limitado |
| CSS Flexbox/Grid | Sí | No | No | No | No | Sí | No |
| JavaScript | Sí | No | No | No | Limitado | Sí | No |
| Linux (sin libgdiplus) | Sí | Sí | Parcial* | Sí | N/A | Sí | No |
| Despliegue de Docker | Imagen de .NET Standard | Estándar | Parcial* | Estándar | Complejo | Complejo | Requiere libgdiplus |
| Mantenimiento activo | Sí | Sí | Sí | Sí | Abandonado | Sí | Sí |
| Precios publicados | Sí ($2,998+) | No ($15K-$210K/año) | Gratuito (MIT) | Sí (gratuito <$1M) | Gratis | Gratuito (MIT) | Sí ($999+) |
| Licencia perpetua | Sí | No (suscripción) | N/A | N/A | N/A | N/A | Sí |
| Libre de AGPL | Sí | No (requiere comercial) | Sí | Sí | Sí | Sí | Sí |
*PDFSharp ha documentado problemas específicos de plataforma con algunas configuraciones.
Comparación de prestaciones
Probado en una máquina virtual en la nube de nivel medio (4 vCPU, 8 GB RAM) con una plantilla de factura HTML de 200 elementos, con un promedio de 50 iteraciones después del calentamiento:
| Escenario | IronPDF | PuppeteerSharp | iTextpdfHTML | wkhtmltopdf |
|---|---|---|---|---|
| Página HTML sencilla | ~150ms | ~500ms | ~200ms | ~200ms |
| Diseño CSS complejo (Flexbox/Grid) | ~250ms | ~600ms | Fallos/Parcial | ~400ms (entrecortado) |
| Página con mucho JavaScript | ~350ms | ~800ms | Fallos | Fallos/Parcial |
| Memoria por operación | ~80MB | ~150MB | ~60 MB | ~50 MB |
| Arranque en frío (primera generación) | 2-5s | 3-8s | <1s | <1s |
iText y wkhtmltopdf muestran arranques en frío más rápidos porque no inicializan el motor del navegador, pero tampoco pueden renderizar el mismo contenido. La comparación del rendimiento sólo tiene sentido en situaciones en las que todas las bibliotecas producen resultados correctos.
Comparación de códigos: Misma factura, tres bibliotecas
Las diferencias entre estas bibliotecas son más claras cuando se construye el mismo documento. He aquí una factura generada de tres maneras.
IronPDF- Enfoque HTML/CSS
using IronPdf;
public class InvoiceGenerator
{
public byte[] GenerateInvoice(InvoiceData data)
{
var renderer = new ChromePdfRenderer();
string html = $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: 'Segoe UI', sans-serif; margin: 40px; }}
h1 {{ color: #2c3e50; }}
table {{ width: 100%; border-collapse: collapse; }}
th {{ background: #3498db; color: white; padding: 12px; text-align: left; }}
td {{ border-bottom: 1px solid #e0e0e0; padding: 10px; }}
.total {{ font-weight: bold; font-size: 1.2em; text-align: right; margin-top: 20px; }}
</style>
</head>
<body>
<h1>Invoice #{data.InvoiceNumber}</h1>
<table>
<tr><th>Item</th><th>Qty</th><th>Price</th></tr>
{string.Join("", data.Items.Select(i =>
$"<tr><td>{i.Name}</td><td>{i.Quantity}</td><td>${i.Price:F2}</td></tr>"))}
</table>
<p class='total'>Total: ${data.Total:F2}</p>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
}using IronPdf;
public class InvoiceGenerator
{
public byte[] GenerateInvoice(InvoiceData data)
{
var renderer = new ChromePdfRenderer();
string html = $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: 'Segoe UI', sans-serif; margin: 40px; }}
h1 {{ color: #2c3e50; }}
table {{ width: 100%; border-collapse: collapse; }}
th {{ background: #3498db; color: white; padding: 12px; text-align: left; }}
td {{ border-bottom: 1px solid #e0e0e0; padding: 10px; }}
.total {{ font-weight: bold; font-size: 1.2em; text-align: right; margin-top: 20px; }}
</style>
</head>
<body>
<h1>Invoice #{data.InvoiceNumber}</h1>
<table>
<tr><th>Item</th><th>Qty</th><th>Price</th></tr>
{string.Join("", data.Items.Select(i =>
$"<tr><td>{i.Name}</td><td>{i.Quantity}</td><td>${i.Price:F2}</td></tr>"))}
</table>
<p class='total'>Total: ${data.Total:F2}</p>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
}Imports IronPdf
Public Class InvoiceGenerator
Public Function GenerateInvoice(data As InvoiceData) As Byte()
Dim renderer = New ChromePdfRenderer()
Dim html As String = $"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: 'Segoe UI', sans-serif; margin: 40px; }}
h1 {{ color: #2c3e50; }}
table {{ width: 100%; border-collapse: collapse; }}
th {{ background: #3498db; color: white; padding: 12px; text-align: left; }}
td {{ border-bottom: 1px solid #e0e0e0; padding: 10px; }}
.total {{ font-weight: bold; font-size: 1.2em; text-align: right; margin-top: 20px; }}
</style>
</head>
<body>
<h1>Invoice #{data.InvoiceNumber}</h1>
<table>
<tr><th>Item</th><th>Qty</th><th>Price</th></tr>
{String.Join("", data.Items.Select(Function(i) $"<tr><td>{i.Name}</td><td>{i.Quantity}</td><td>${i.Price:F2}</td></tr>"))}
</table>
<p class='total'>Total: ${data.Total:F2}</p>
</body>
</html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
Return pdf.BinaryData
End Function
End ClassQuestPDF - Enfoque de API fluido
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
public class InvoiceGenerator
{
public byte[] GenerateInvoice(InvoiceData data)
{
var document = Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(40);
page.DefaultTextStyle(x => x.FontFamily("Segoe UI"));
page.Header()
.Text($"Invoice #{data.InvoiceNumber}")
.FontSize(24).FontColor(Colors.Blue.Darken2);
page.Content().Column(column =>
{
column.Item().Table(table =>
{
table.ColumnsDefinition(cols =>
{
cols.RelativeColumn(3);
cols.RelativeColumn(1);
cols.RelativeColumn(1);
});
table.Header(header =>
{
header.Cell().Background(Colors.Blue.Medium).Padding(8)
.Text("Item").FontColor(Colors.White);
header.Cell().Background(Colors.Blue.Medium).Padding(8)
.Text("Qty").FontColor(Colors.White);
header.Cell().Background(Colors.Blue.Medium).Padding(8)
.Text("Price").FontColor(Colors.White);
});
foreach (var item in data.Items)
{
table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
.Padding(8).Text(item.Name);
table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
.Padding(8).Text(item.Quantity.ToString());
table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
.Padding(8).Text($"${item.Price:F2}");
}
});
column.Item().AlignRight().PaddingTop(20)
.Text($"Total: ${data.Total:F2}").FontSize(16).Bold();
});
});
});
using var stream = new MemoryStream();
document.GeneratePdf(stream);
return stream.ToArray();
}
}using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
public class InvoiceGenerator
{
public byte[] GenerateInvoice(InvoiceData data)
{
var document = Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(40);
page.DefaultTextStyle(x => x.FontFamily("Segoe UI"));
page.Header()
.Text($"Invoice #{data.InvoiceNumber}")
.FontSize(24).FontColor(Colors.Blue.Darken2);
page.Content().Column(column =>
{
column.Item().Table(table =>
{
table.ColumnsDefinition(cols =>
{
cols.RelativeColumn(3);
cols.RelativeColumn(1);
cols.RelativeColumn(1);
});
table.Header(header =>
{
header.Cell().Background(Colors.Blue.Medium).Padding(8)
.Text("Item").FontColor(Colors.White);
header.Cell().Background(Colors.Blue.Medium).Padding(8)
.Text("Qty").FontColor(Colors.White);
header.Cell().Background(Colors.Blue.Medium).Padding(8)
.Text("Price").FontColor(Colors.White);
});
foreach (var item in data.Items)
{
table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
.Padding(8).Text(item.Name);
table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
.Padding(8).Text(item.Quantity.ToString());
table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2)
.Padding(8).Text($"${item.Price:F2}");
}
});
column.Item().AlignRight().PaddingTop(20)
.Text($"Total: ${data.Total:F2}").FontSize(16).Bold();
});
});
});
using var stream = new MemoryStream();
document.GeneratePdf(stream);
return stream.ToArray();
}
}Imports QuestPDF.Fluent
Imports QuestPDF.Infrastructure
Imports System.IO
Public Class InvoiceGenerator
Public Function GenerateInvoice(data As InvoiceData) As Byte()
Dim document = Document.Create(Sub(container)
container.Page(Sub(page)
page.Size(PageSizes.A4)
page.Margin(40)
page.DefaultTextStyle(Function(x) x.FontFamily("Segoe UI"))
page.Header() _
.Text($"Invoice #{data.InvoiceNumber}") _
.FontSize(24).FontColor(Colors.Blue.Darken2)
page.Content().Column(Sub(column)
column.Item().Table(Sub(table)
table.ColumnsDefinition(Sub(cols)
cols.RelativeColumn(3)
cols.RelativeColumn(1)
cols.RelativeColumn(1)
End Sub)
table.Header(Sub(header)
header.Cell().Background(Colors.Blue.Medium).Padding(8) _
.Text("Item").FontColor(Colors.White)
header.Cell().Background(Colors.Blue.Medium).Padding(8) _
.Text("Qty").FontColor(Colors.White)
header.Cell().Background(Colors.Blue.Medium).Padding(8) _
.Text("Price").FontColor(Colors.White)
End Sub)
For Each item In data.Items
table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2) _
.Padding(8).Text(item.Name)
table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2) _
.Padding(8).Text(item.Quantity.ToString())
table.Cell().BorderBottom(1).BorderColor(Colors.Grey.Lighten2) _
.Padding(8).Text($"${item.Price:F2}")
Next
End Sub)
column.Item().AlignRight().PaddingTop(20) _
.Text($"Total: ${data.Total:F2}").FontSize(16).Bold()
End Sub)
End Sub)
End Sub)
Using stream As New MemoryStream()
document.GeneratePdf(stream)
Return stream.ToArray()
End Using
End Function
End ClassPDFSharp— Enfoque de Dibujo por Coordenadas
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
public class InvoiceGenerator
{
public byte[] GenerateInvoice(InvoiceData data)
{
var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
var titleFont = new XFont("Arial", 24);
var headerFont = new XFont("Arial", 12, XFontStyleEx.Bold);
var bodyFont = new XFont("Arial", 12);
double y = 40;
gfx.DrawString($"Invoice #{data.InvoiceNumber}", titleFont,
XBrushes.DarkBlue, 40, y);
y += 50;
// Table header — manually positioned
double[] colX = { 40, 300, 400 };
double rowHeight = 30;
gfx.DrawRectangle(XBrushes.SteelBlue, 40, y, 500, rowHeight);
gfx.DrawString("Item", headerFont, XBrushes.White, colX[0] + 10, y + 20);
gfx.DrawString("Qty", headerFont, XBrushes.White, colX[1] + 10, y + 20);
gfx.DrawString("Price", headerFont, XBrushes.White, colX[2] + 10, y + 20);
y += rowHeight;
// Each row drawn individually with explicit coordinates
foreach (var item in data.Items)
{
gfx.DrawRectangle(XPens.LightGray, 40, y, 500, rowHeight);
gfx.DrawString(item.Name, bodyFont, XBrushes.Black, colX[0] + 10, y + 20);
gfx.DrawString(item.Quantity.ToString(), bodyFont, XBrushes.Black, colX[1] + 10, y + 20);
gfx.DrawString($"${item.Price:F2}", bodyFont, XBrushes.Black, colX[2] + 10, y + 20);
y += rowHeight;
}
y += 20;
gfx.DrawString($"Total: ${data.Total:F2}", headerFont, XBrushes.Black, 440, y);
using var stream = new MemoryStream();
document.Save(stream);
return stream.ToArray();
}
}using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
public class InvoiceGenerator
{
public byte[] GenerateInvoice(InvoiceData data)
{
var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
var titleFont = new XFont("Arial", 24);
var headerFont = new XFont("Arial", 12, XFontStyleEx.Bold);
var bodyFont = new XFont("Arial", 12);
double y = 40;
gfx.DrawString($"Invoice #{data.InvoiceNumber}", titleFont,
XBrushes.DarkBlue, 40, y);
y += 50;
// Table header — manually positioned
double[] colX = { 40, 300, 400 };
double rowHeight = 30;
gfx.DrawRectangle(XBrushes.SteelBlue, 40, y, 500, rowHeight);
gfx.DrawString("Item", headerFont, XBrushes.White, colX[0] + 10, y + 20);
gfx.DrawString("Qty", headerFont, XBrushes.White, colX[1] + 10, y + 20);
gfx.DrawString("Price", headerFont, XBrushes.White, colX[2] + 10, y + 20);
y += rowHeight;
// Each row drawn individually with explicit coordinates
foreach (var item in data.Items)
{
gfx.DrawRectangle(XPens.LightGray, 40, y, 500, rowHeight);
gfx.DrawString(item.Name, bodyFont, XBrushes.Black, colX[0] + 10, y + 20);
gfx.DrawString(item.Quantity.ToString(), bodyFont, XBrushes.Black, colX[1] + 10, y + 20);
gfx.DrawString($"${item.Price:F2}", bodyFont, XBrushes.Black, colX[2] + 10, y + 20);
y += rowHeight;
}
y += 20;
gfx.DrawString($"Total: ${data.Total:F2}", headerFont, XBrushes.Black, 440, y);
using var stream = new MemoryStream();
document.Save(stream);
return stream.ToArray();
}
}Imports PdfSharpCore.Drawing
Imports PdfSharpCore.Pdf
Imports System.IO
Public Class InvoiceGenerator
Public Function GenerateInvoice(data As InvoiceData) As Byte()
Dim document As New PdfDocument()
Dim page = document.AddPage()
Dim gfx = XGraphics.FromPdfPage(page)
Dim titleFont As New XFont("Arial", 24)
Dim headerFont As New XFont("Arial", 12, XFontStyleEx.Bold)
Dim bodyFont As New XFont("Arial", 12)
Dim y As Double = 40
gfx.DrawString($"Invoice #{data.InvoiceNumber}", titleFont, XBrushes.DarkBlue, 40, y)
y += 50
' Table header — manually positioned
Dim colX As Double() = {40, 300, 400}
Dim rowHeight As Double = 30
gfx.DrawRectangle(XBrushes.SteelBlue, 40, y, 500, rowHeight)
gfx.DrawString("Item", headerFont, XBrushes.White, colX(0) + 10, y + 20)
gfx.DrawString("Qty", headerFont, XBrushes.White, colX(1) + 10, y + 20)
gfx.DrawString("Price", headerFont, XBrushes.White, colX(2) + 10, y + 20)
y += rowHeight
' Each row drawn individually with explicit coordinates
For Each item In data.Items
gfx.DrawRectangle(XPens.LightGray, 40, y, 500, rowHeight)
gfx.DrawString(item.Name, bodyFont, XBrushes.Black, colX(0) + 10, y + 20)
gfx.DrawString(item.Quantity.ToString(), bodyFont, XBrushes.Black, colX(1) + 10, y + 20)
gfx.DrawString($"${item.Price:F2}", bodyFont, XBrushes.Black, colX(2) + 10, y + 20)
y += rowHeight
Next
y += 20
gfx.DrawString($"Total: ${data.Total:F2}", headerFont, XBrushes.Black, 440, y)
Using stream As New MemoryStream()
document.Save(stream)
Return stream.ToArray()
End Using
End Function
End ClassLa versiónIronPDFutiliza HTML/CSS, habilidades que la mayoría de los desarrolladores ya poseen. La versión QuestPDF requiere el aprendizaje de una API fluida específica del dominio, pero proporciona estructura. La versión PdfSharp requiere calcular manualmente la posición de cada píxel: cada desplazamiento de columna, cada altura de fila, cada borde dibujado individualmente.
¿Qué biblioteca elegir?
Cuando evalúo estas bibliotecas, el árbol de decisión es sencillo:
¿Necesita convertir HTML a PDF con CSS moderno? Las opciones prácticas sonIronPDFo PuppeteerSharp.IronPDFmaneja Chromium internamente; PuppeteerSharprequiere que gestiones procesos externos del navegador. wkhtmltopdf no es una opción para proyectos nuevos. pdfHTML de iTextno puede renderizar Flexbox o Grid.
Crear documentos mediante programación a partir de datos, sin HTML? La fluida API de QuestPDF es productiva y está bien diseñada. PdfSharp proporciona control a nivel más bajo, pero requiere significativamente más código para diseños equivalentes.
Despliegue multiplataforma (Linux, Docker, nube) IronPDF, QuestPDF y PuppeteerSharpfuncionan en Linux sin dependencias de libgdiplus. Aspose.PDF ha documentado fugas de memoria en Linux. PdfSharp tiene soporte parcial de plataformas con problemas conocidos.
¿Restricciones de licencia? PdfSharp (MIT) y PuppeteerSharp(MIT) son gratuitos sin condiciones. QuestPDF es gratuito por debajo de un millón de dólares de ingresos. iTextrequiere el cumplimiento de AGPL o licencia comercial ($15K-$210K/año). La licencia perpetua deIronPDFcomienza en $2,998. Asposecomienza en ~$999.
Antes de comprometerse
Pruebe con su contenido real, no con "Hello World" Despliegue pronto en la plataforma de destino. Mida la memoria en más de 100 documentos, no en uno. Lea el texto completo de la licencia con su equipo jurídico. Comprueba la latencia de arranque en frío si te diriges a serverless.
IronPDF ofrece una versión de prueba con todas las funciones para que pueda evaluar sus requisitos específicos.
Cumplimiento Normativo en España: Criterios de Selección de Biblioteca PDF
Para los equipos de desarrollo que trabajan en el mercado español, la elección de la biblioteca PDF va más allá de la fidelidad de renderizado HTML y el modelo de licencias. El marco regulatorio español impone requisitos técnicos concretos que la biblioteca seleccionada debe ser capaz de satisfacer.
Facturación Electrónica: VeriFactu y SII
La normativa española de facturación electrónica exige la generación de documentos que cumplan con el sistema *VERIFACTU (Verificable Fácilmente por la AEAT) y el SII (Suministro Inmediato de Información). Desde enero de 2026, las empresas sujetas al Impuesto sobre Sociedades con facturación superior a 6 millones de euros deben reportar facturas a la AEAT** en tiempo real.
IronPDF permite incrustar los metadatos y códigos QR exigidos por VERI*FACTU directamente en los documentos PDF generados desde plantillas HTML, facilitando la trazabilidad documental que requiere la AEAT. Las demás bibliotecas analizadas en este artículo —incluyendo iText, PdfSharp, QuestPDF, wkhtmltopdf, PuppeteerSharp y Aspose— no ofrecen soporte nativo para los estándares de registro SII ni para el formato Facturae (el formato XML de factura electrónica reconocido por FACe, el punto general de entrada de facturas electrónicas de la Administración).
Conformidad con la Ley Orgánica de Protección de Datos (LOPDGDD)
La LOPDGDD (Ley Orgánica 3/2018, de Protección de Datos Personales y Garantía de los Derechos Digitales) complementa el RGPD en España y establece obligaciones específicas para el tratamiento de datos personales incluidos en documentos PDF. La AEPD (Agencia Española de Protección de Datos) publica guías técnicas que recomiendan la redacción de datos personales antes de compartir documentos generados automáticamente.
IronPDF soporta redacción programática de regiones del PDF mediante su API de anotaciones y edición, permitiendo a los equipos implementar flujos de trabajo LOPDGDD-conformes con código .NET estándar. Las bibliotecas basadas en wkhtmltopdf como DinkToPdf, cuyo motor subyacente está abandonado y tiene CVEs sin parchear, representan un riesgo adicional en contextos sometidos a auditorías de la AEPD.
Firmas Digitales: eIDAS, PAdES y Certificados FNMT
El reglamento europeo eIDAS (Reglamento UE 910/2014) establece el marco legal para las firmas electrónicas reconocidas en España. Los documentos que requieren firma cualificada deben usar el estándar PAdES (PDF Advanced Electronic Signatures), compatible con los certificados emitidos por la FNMT (Fábrica Nacional de Moneda y Timbre) y otros prestadores cualificados de servicios de confianza.
IronPDF implementa firma digital con PAdES y soporta la incorporación de certificados FNMT en los flujos de firma. Esto es relevante para documentos contractuales, resoluciones administrativas y facturas electrónicas dirigidas a organismos de la Administración Pública española. En contraste, PDFSharp (MIT) requiere implementaciones personalizadas para PAdES, y QuestPDF no soporta firma digital en ninguno de sus niveles de licencia.
Facturación en Territorios Forales: TicketBAI
Las haciendas forales del País Vasco —Bizkaia, Gipuzkoa y Araba— han implantado el sistema TicketBAI (también denominado Batuz), que exige que todos los programas de facturación generen un fichero TicketBAI firmado electrónicamente con cada factura emitida, incluyendo un código QR vinculante. Navarra dispone del sistema NaTicket con requisitos similares.
IronPDF, combinado con las librerías de firma digital .NET adecuadas, puede generar los PDFs con los códigos QR de TicketBAI embebidos y los campos de metadatos requeridos por las haciendas forales. Las soluciones basadas en servicios cloud como api2pdf presentan incompatibilidades con estos requisitos, ya que el procesamiento en servidores de terceros puede vulnerar el artículo 5(1)(f) del RGPD (integridad y confidencialidad) tal como es interpretado por la AEPD.
Sector Público: Esquema Nacional de Seguridad (ENS)
El ENS (Esquema Nacional de Seguridad, Real Decreto 311/2022) establece los requisitos de seguridad para los sistemas de información de las Administraciones Públicas españolas. Las aplicaciones que generan documentación oficial deben cumplir con las categorías ENS correspondientes. La clasificación ENS requiere, entre otros aspectos, que las dependencias de software estén mantenidas activamente y que cuenten con actualizaciones de seguridad.
Este requisito descarta directamente bibliotecas como FO.NET (abandonada), DinkToPdf (última actualización 2018, wkhtmltopdf abandonado 2020) y PDFSharp en ciertas configuraciones con dependencias no mantenidas. IronPDF, con su ciclo de actualizaciones mensuales y parches de seguridad documentados, satisface los criterios de mantenimiento activo que exige el ENS para la categoría media.
Tabla Comparativa: Soporte Regulatorio España
| Criterio de Cumplimiento España | IronPDF | iText | QuestPDF | wkhtmltopdf/DinkToPdf | PuppeteerSharp |
|---|---|---|---|---|---|
| VERI*FACTU / SII metadata | Sí (vía HTML+metadatos) | Parcial | No | No | No |
| LOPDGDD redacción de datos | Sí (API nativa) | Sí | No | No | No |
| PAdES / FNMT firmas eIDAS | Sí | Sí (comercial) | No | No | No |
| TicketBAI QR embedding | Sí | Parcial | No | No | No |
| ENS mantenimiento activo | Sí | Sí | Sí | No (abandonado) | Sí |
| Procesamiento local (LOPDGDD) | Sí | Sí | Sí | Sí | Sí |
Sectores Regulados Adicionales
Las empresas del sector financiero supervisadas por el Banco de España o la CNMV (Comisión Nacional del Mercado de Valores) deben garantizar la trazabilidad e integridad de los documentos generados. IronPDF permite aplicar cifrado AES-256, permisos de documento y firmas de certificación que satisfacen los requisitos de integridad documental de estas entidades.
Para el sector de bebidas espirituosas y tabaco, el sistema SILICIE de la AEAT exige documentación de movimientos con metadatos específicos. Para empresas distribuidoras, el sistema SEVeM (Sistema de Emisión de Vales de Mezcla) requiere documentación trazable. IronPDF puede generar estos documentos con los campos de metadatos XMP requeridos.
La ley Crea y Crece (Ley 18/2022) impulsa la adopción de la factura electrónica en España a partir de 2025-2026, haciendo que la capacidad de generar Facturae XML y PDFs conformes sea un requisito de negocio para la mayoría de las empresas españolas. Al evaluar su biblioteca PDF para proyectos nuevos en 2026, incluya el cumplimiento de VERI*FACTU, LOPDGDD, eIDAS y ENS como criterios de selección junto a los criterios técnicos tradicionales.
