Migrar de Gotenberg a IronPDF: VeriFactu, TicketBAI, LOPDGDD y Crea y Crece para ISVs españoles
La migración de Gotenberg a IronPDF transforma su flujo de trabajo PDF .NET de una arquitectura de microservicios basada en Docker con llamadas a la API HTTP a una biblioteca C# nativa en proceso. Para los ISVs y desarrolladores españoles que construyen software de facturación bajo VeriFactu o generan documentos Facturae para FACe, esta transición elimina también los riesgos de latencia de red y gestión de contenedores en flujos de trabajo críticos de cumplimiento. Esta guía proporciona una ruta de migración completa y paso a paso que elimina la sobrecarga de la infraestructura y la complejidad de la gestión de contenedores para los desarrolladores profesionales de .NET.
Contexto regulatorio español: por qué importa la arquitectura
Antes de entrar en los detalles técnicos de la migración, conviene señalar por qué la arquitectura de la biblioteca PDF tiene implicaciones directas para el cumplimiento normativo en España:
VeriFactu y Crea y Crece (Real Decreto-Ley 15/2025): los proveedores de software de facturación deben incluir la leyenda VERI*FACTU o Factura verificable en la sede electrónica de la AEAT en las representaciones gráficas de las facturas. Una arquitectura Gotenberg, al interponer llamadas HTTP y contenedores Docker entre el código C# y el PDF resultante, añade puntos de fallo que pueden comprometer la cadena de auditoría. IronPDF en proceso elimina esa capa.
TicketBAI (Bizkaia, Gipuzkoa, Araba): la generación de facturas TicketBAI requiere firma XAdES y QR específico por provincia foral. En una arquitectura Gotenberg, la firma debe realizarse fuera del contenedor y coordinarse con las llamadas HTTP; con IronPDF, todo ocurre en el mismo proceso .NET.
Facturae XML / FACe: para el sector público, la generación de documentos PDF que acompañan a los XML Facturae es un paso crítico. La latencia añadida por Gotenberg (10-100 ms por solicitud) puede ser problemática en flujos de trabajo de alto volumen de envíos B2G.
Penalización de hasta 150.000 €/año: el régimen VeriFactu sanciona a los proveedores de software de facturación no conformes. Una arquitectura simplificada con IronPDF reduce las superficies de error y facilita la auditoría del cumplimiento.
Por qué migrar de Gotenberg a IronPDF
El problema de la arquitectura Gotenberg
Gotenberg es una arquitectura de microservicios basada en Docker para la generación de PDF. Aunque potente y flexible, introduce una complejidad significativa para las aplicaciones C#:
-
Gastos generales de infraestructura: requiere Docker, orquestación de contenedores (Kubernetes/Docker Compose), descubrimiento de servicios y equilibrio de carga. Cada despliegue es más complejo.
-
Latencia de red: cada operación de PDF requiere una llamada HTTP a un servicio independiente, lo que añade entre 10 y 100 ms por solicitud. Esta latencia aumenta rápidamente en situaciones de alto volumen.
-
Problemas de inicio en frío: el inicio del contenedor puede agregar entre 2 y 5 segundos a las primeras solicitudes. Cada reinicio de pod, cada evento de escalado y cada despliegue desencadenan arranques en frío.
-
Complejidad operativa: debe administrar la salud, la escalabilidad, el registro y la supervisión del contenedor como preocupaciones separadas de su aplicación principal.
-
Datos de formulario multiparte: cada solicitud requiere la construcción de cargas útiles de datos de formulario/multiparte: detalladas, propensas a errores y tediosas de mantener.
-
Puntos de falla: los tiempos de espera de la red, la falta de disponibilidad del servicio y las fallas de los contenedores pasan a ser su responsabilidad.
- Gestión de versiones: las imágenes de Gotenberg se actualizan por separado de la aplicación; los cambios en la API pueden romper las integraciones de forma inesperada.
Comparación entre Gotenberg e IronPDF
| Aspecto | Gotenberg | IronPDF |
|---|---|---|
| Despliegue | Contenedor Docker + orquestación | Paquete NuGet único |
| Arquitectura | Microservicio (API REST) | Biblioteca en proceso |
| Latencia por solicitud | 10-100ms+ (ida y vuelta en red) | < 1ms de sobrecarga |
| Arranque en frío | 2-5 segundos (init del contenedor) | 1-2 segundos (sólo la primera traducción) |
| Infraestructura | Docker, Kubernetes, balanceadores de carga | No es necesario |
| Modos de fallo | Fallos en redes, contenedores y servicios | Excepciones de .NET Standard |
| Estilo API | REST multipart/form-data | Llamadas a métodos nativos de C# |
| Escala | Horizontal (más contenedores) | Vertical (en proceso) |
| Depuración | Necesidad de rastreo distribuido | Depurador estándar |
| Control de versiones | Etiquetas de la imagen del contenedor | Versiones de los paquetes NuGet |
Para los equipos que planifican la adopción de .NET 10 y C# 14 hasta 2025 y 2026, IronPDF proporciona una base preparada para el futuro con cero dependencias de infraestructura que se integra de forma nativa con los patrones modernos de .NET.
Evaluación de la complejidad de la migración
Esfuerzo estimado por función
| Característica | Complejidad de la migración |
|---|---|
| HTML a PDF | Muy bajo |
| URL a PDF | Muy bajo |
| Tamaño de papel personalizado | Bajo |
| Márgenes | Bajo |
| Fusión de PDF | Bajo |
| Encabezados/pies de página | Medio |
| Retrasos de espera | Bajo |
| Conversión PDF/A | Bajo |
Cambio de paradigma
El cambio fundamental en esta migración de Gotenberg es de llamadas a la API HTTP con datos de formularios multiparte a llamadas a métodos nativos de C#:
Gotenberg: HTTP POST multipart/form-data a contenedor Docker
IronPDF: Llamadas directas a métodos en objetos de C#
Antes de empezar
Prerrequisitos
- Versión .NET: IronPDF es compatible con .NET Framework 4.6.2+ y .NET Core 3.1+ / .NET 5/6/7/8/9+
- Clave de licencia: Obtenga su clave de licencia de IronPDF en IronPDF
- Planificar la eliminación de la infraestructura: documentar los contenedores de Gotenberg para su desmantelamiento posterior a la migración.
Identifique el uso de Gotenberg
# Find direct HTTP calls to Gotenberg
grep -r "gotenberg\|/forms/chromium\|/forms/libreoffice\|/forms/pdfengines" --include="*.cs" .
# Find GotenbergSharpApiClient usage
grep -r "GotenbergSharpClient\|Gotenberg.Sharp\|ChromiumRequest" --include="*.cs" .
# Find Docker/Kubernetes Gotenberg configuration
grep -r "gotenberg/gotenberg\|gotenberg:" --include="*.yml" --include="*.yaml" .
# Find direct HTTP calls to Gotenberg
grep -r "gotenberg\|/forms/chromium\|/forms/libreoffice\|/forms/pdfengines" --include="*.cs" .
# Find GotenbergSharpApiClient usage
grep -r "GotenbergSharpClient\|Gotenberg.Sharp\|ChromiumRequest" --include="*.cs" .
# Find Docker/Kubernetes Gotenberg configuration
grep -r "gotenberg/gotenberg\|gotenberg:" --include="*.yml" --include="*.yaml" .
Cambios en el paquete NuGet
# Remove Gotenberg client (if using)
dotnet remove package Gotenberg.Sharp.API.Client
# Install IronPDF
dotnet add package IronPdf
# Remove Gotenberg client (if using)
dotnet remove package Gotenberg.Sharp.API.Client
# Install IronPDF
dotnet add package IronPdf
Inicio rápido de la migración
Paso 1: Actualizar la configuración de la licencia
Antes (Gotenberg):
Gotenberg no requiere licencia, pero sí una infraestructura Docker con URL de contenedor.
private readonly string _gotenbergUrl = "http://localhost:3000";
private readonly string _gotenbergUrl = "http://localhost:3000";
Private ReadOnly _gotenbergUrl As String = "http://localhost:3000"
Después (IronPDF):
// Set once at application startup
IronPdf.License.LicenseKey = "YOUR-IRONPDF-LICENSE-KEY";
// Set once at application startup
IronPdf.License.LicenseKey = "YOUR-IRONPDF-LICENSE-KEY";
' Set once at application startup
IronPdf.License.LicenseKey = "YOUR-IRONPDF-LICENSE-KEY"
Paso 2: Actualizar las importaciones de espacios de nombres
// Before (Gotenberg)
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
// After (IronPDF)
using IronPdf;
using IronPdf.Rendering;
// Before (Gotenberg)
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
// After (IronPDF)
using IronPdf;
using IronPdf.Rendering;
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Imports IronPdf
Imports IronPdf.Rendering
Referencia completa de la API
Gotenberg Endpoint a IronPDF
| Ruta Gotenberg | Equivalente de IronPDF |
|---|---|
POST /forms/chromium/convert/html |
ChromePdfRenderer.RenderHtmlAsPdf() |
POST /forms/chromium/convert/url |
ChromePdfRenderer.RenderUrlAsPdf() |
POST /forms/pdfengines/merge |
PdfDocument.Merge() |
POST /forms/pdfengines/convert |
pdf.SaveAs() con configuraciones |
GET /health |
N/A |
Mapeo de parámetros de formulario a RenderingOptions
| Parámetro Gotenberg | Propiedad de IronPDF | Notas de conversión |
|---|---|---|
paperWidth (pulgadas) |
RenderingOptions.PaperSize |
Utilizar enum o tamaño personalizado |
paperHeight (pulgadas) |
RenderingOptions.PaperSize |
Utilizar enum o tamaño personalizado |
marginTop (pulgadas) |
RenderingOptions.MarginTop |
Multiplique por 25,4 para mm |
marginBottom (pulgadas) |
RenderingOptions.MarginBottom |
Multiplique por 25,4 para mm |
printBackground |
RenderingOptions.PrintHtmlBackgrounds |
Booleano |
landscape |
RenderingOptions.PaperOrientation |
Landscape enumeración |
waitDelay |
RenderingOptions.RenderDelay |
Convertir a milisegundos |
Ejemplos de migración de código
Ejemplo 1: HTML básico a PDF
Antes (Gotenberg):
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergExample
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Hello from Gotenberg</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("output.pdf", pdfBytes);
Console.WriteLine("PDF generated successfully");
}
}
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergExample
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Hello from Gotenberg</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("output.pdf", pdfBytes);
Console.WriteLine("PDF generated successfully");
}
}
Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Module GotenbergExample
Async Function Main() As Task
Dim gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
Dim html = "<html><body><h1>Hello from Gotenberg</h1></body></html>"
content.Add(New StringContent(html), "files", "index.html")
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("output.pdf", pdfBytes)
Console.WriteLine("PDF generated successfully")
End Using
End Using
End Function
End Module
Después (IronPDF):
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfExample
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var html = "<html><body><h1>Hello from IronPDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully");
}
}
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfExample
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var html = "<html><body><h1>Hello from IronPDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully");
}
}
Imports System
Imports IronPdf
Class IronPdfExample
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim html = "<html><body><h1>Hello from IronPDF</h1></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
Console.WriteLine("PDF generated successfully")
End Sub
End Class
La diferencia es sustancial: Gotenberg requiere construir un HttpClient, construir un MultipartFormDataContent, realizar un HTTP POST asíncrono a un contenedor Docker en ejecución y manejar la respuesta de la matriz de bytes. IronPDF reduce esto a tres líneas con una llamada al método ChromePdfRenderer, sin sobrecarga de red, sin dependencia de contenedor y sin complejidad asincrónica. Consulte la documentación HTML a PDF para obtener más opciones de conversión.
Generación de facturas VeriFactu con IronPDF
Para los desarrolladores españoles que construyen software de facturación, IronPDF permite generar la representación gráfica completa de la factura — incluyendo la leyenda VERI*FACTU y el QR de la AEAT — desde una plantilla HTML sin necesidad de infraestructura Docker:
// NuGet: Install-Package IronPdf
using IronPdf;
class FacturaVeriFactu
{
static void Main()
{
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
var renderer = new ChromePdfRenderer();
string html = @"
<html>
<body style='font-family: Arial, sans-serif; padding: 20px;'>
<h1>FACTURA Nº 2024-0001</h1>
<p>Emisor: Mi Empresa S.L. | NIF: B12345678</p>
<table border='1' cellpadding='5' style='width:100%; border-collapse:collapse;'>
<tr><th>Concepto</th><th>Base</th><th>IVA 21%</th><th>Total</th></tr>
<tr><td>Servicio</td><td>1.000,00 €</td><td>210,00 €</td><td>1.210,00 €</td></tr>
</table>
<div style='margin-top:20px; border:2px solid black; padding:8px; font-weight:bold;'>
VERI*FACTU — Factura verificable en la sede electrónica de la AEAT
</div>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("factura-verifactu.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
class FacturaVeriFactu
{
static void Main()
{
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
var renderer = new ChromePdfRenderer();
string html = @"
<html>
<body style='font-family: Arial, sans-serif; padding: 20px;'>
<h1>FACTURA Nº 2024-0001</h1>
<p>Emisor: Mi Empresa S.L. | NIF: B12345678</p>
<table border='1' cellpadding='5' style='width:100%; border-collapse:collapse;'>
<tr><th>Concepto</th><th>Base</th><th>IVA 21%</th><th>Total</th></tr>
<tr><td>Servicio</td><td>1.000,00 €</td><td>210,00 €</td><td>1.210,00 €</td></tr>
</table>
<div style='margin-top:20px; border:2px solid black; padding:8px; font-weight:bold;'>
VERI*FACTU — Factura verificable en la sede electrónica de la AEAT
</div>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("factura-verifactu.pdf");
}
}
Imports IronPdf
Class FacturaVeriFactu
Shared Sub Main()
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim renderer As New ChromePdfRenderer()
Dim html As String = "
<html>
<body style='font-family: Arial, sans-serif; padding: 20px;'>
<h1>FACTURA Nº 2024-0001</h1>
<p>Emisor: Mi Empresa S.L. | NIF: B12345678</p>
<table border='1' cellpadding='5' style='width:100%; border-collapse:collapse;'>
<tr><th>Concepto</th><th>Base</th><th>IVA 21%</th><th>Total</th></tr>
<tr><td>Servicio</td><td>1.000,00 €</td><td>210,00 €</td><td>1.210,00 €</td></tr>
</table>
<div style='margin-top:20px; border:2px solid black; padding:8px; font-weight:bold;'>
VERI*FACTU — Factura verificable en la sede electrónica de la AEAT
</div>
</body>
</html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("factura-verifactu.pdf")
End Sub
End Class
Con Gotenberg, este flujo requeriría construir el multipart, esperar la respuesta HTTP, gestionar errores de red y mantener el contenedor Docker activo. Con IronPDF, la generación es síncrona, en proceso y sin dependencias de infraestructura.
Ejemplo 2: Conversión de URL a PDF
Antes (Gotenberg):
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergUrlToPdf
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/url";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
content.Add(new StringContent("https://example.com"), "url");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("webpage.pdf", pdfBytes);
Console.WriteLine("PDF from URL generated successfully");
}
}
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergUrlToPdf
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/url";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
content.Add(new StringContent("https://example.com"), "url");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("webpage.pdf", pdfBytes);
Console.WriteLine("PDF from URL generated successfully");
}
}
Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Module GotenbergUrlToPdf
Async Function Main() As Task
Dim gotenbergUrl As String = "http://localhost:3000/forms/chromium/convert/url"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
content.Add(New StringContent("https://example.com"), "url")
Dim response As HttpResponseMessage = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes As Byte() = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("webpage.pdf", pdfBytes)
Console.WriteLine("PDF from URL generated successfully")
End Using
End Using
End Function
End Module
Después (IronPDF):
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfUrlToPdf
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("PDF from URL generated successfully");
}
}
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfUrlToPdf
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("PDF from URL generated successfully");
}
}
Imports System
Imports IronPdf
Class IronPdfUrlToPdf
Shared Sub Main()
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderUrlAsPdf("https://example.com")
pdf.SaveAs("webpage.pdf")
Console.WriteLine("PDF from URL generated successfully")
End Sub
End Class
El enfoque de Gotenberg requiere un punto final diferente (/forms/chromium/convert/url), la creación de contenido multiparte con la URL como campo de formulario y el manejo de respuestas HTTP asincrónicas. El método RenderUrlAsPdf() de IronPDF acepta la URL directamente y devuelve un objeto PdfDocument sincrónicamente. Más información sobre Conversión de URL a PDF.
Ejemplo 3: Tamaño de papel y márgenes personalizados
Antes (Gotenberg):
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergCustomSize
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
content.Add(new StringContent("8.5"), "paperWidth");
content.Add(new StringContent("11"), "paperHeight");
content.Add(new StringContent("0.5"), "marginTop");
content.Add(new StringContent("0.5"), "marginBottom");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("custom-size.pdf", pdfBytes);
Console.WriteLine("Custom size PDF generated successfully");
}
}
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergCustomSize
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
content.Add(new StringContent("8.5"), "paperWidth");
content.Add(new StringContent("11"), "paperHeight");
content.Add(new StringContent("0.5"), "marginTop");
content.Add(new StringContent("0.5"), "marginBottom");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("custom-size.pdf", pdfBytes);
Console.WriteLine("Custom size PDF generated successfully");
}
}
Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Class GotenbergCustomSize
Shared Async Function Main() As Task
Dim gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
Dim html = "<html><body><h1>Custom Size PDF</h1></body></html>"
content.Add(New StringContent(html), "files", "index.html")
content.Add(New StringContent("8.5"), "paperWidth")
content.Add(New StringContent("11"), "paperHeight")
content.Add(New StringContent("0.5"), "marginTop")
content.Add(New StringContent("0.5"), "marginBottom")
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("custom-size.pdf", pdfBytes)
Console.WriteLine("Custom size PDF generated successfully")
End Using
End Using
End Function
End Class
Después (IronPDF):
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
using IronPdf.Rendering;
class IronPdfCustomSize
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 50;
renderer.RenderingOptions.MarginBottom = 50;
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("custom-size.pdf");
Console.WriteLine("Custom size PDF generated successfully");
}
}
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
using IronPdf.Rendering;
class IronPdfCustomSize
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 50;
renderer.RenderingOptions.MarginBottom = 50;
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("custom-size.pdf");
Console.WriteLine("Custom size PDF generated successfully");
}
}
Imports System
Imports IronPdf
Imports IronPdf.Rendering
Module IronPdfCustomSize
Sub Main()
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter
renderer.RenderingOptions.MarginTop = 50
renderer.RenderingOptions.MarginBottom = 50
Dim html As String = "<html><body><h1>Custom Size PDF</h1></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("custom-size.pdf")
Console.WriteLine("Custom size PDF generated successfully")
End Sub
End Module
Gotenberg requiere que se agreguen parámetros basados en cadenas ("8.5", "11", "0.5") a los datos de formularios de varias partes; no hay seguridad de tipos, no hay IntelliSense y es fácil escribirlos mal. IronPDF proporciona propiedades fuertemente tipadas con enumeraciones PdfPaperSize y valores de margen numéricos. Tenga en cuenta que los márgenes de IronPDF están en milímetros (50 mm ≈ 2 pulgadas), mientras que Gotenberg utiliza pulgadas.
Notas de migración críticas
Conversiones de unidades
La conversión más importante en esta migración de Gotenberg son las unidades de margen:
// Gotenberg: margins in inches
content.Add(new StringContent("0.5"), "marginTop"); // 0.5 inches
content.Add(new StringContent("1"), "marginBottom"); // 1 inch
// IronPDF: margins in millimeters
renderer.RenderingOptions.MarginTop = 12.7; // 0.5 inches × 25.4 = 12.7mm
renderer.RenderingOptions.MarginBottom = 25.4; // 1 inch × 25.4 = 25.4mm
// Gotenberg: margins in inches
content.Add(new StringContent("0.5"), "marginTop"); // 0.5 inches
content.Add(new StringContent("1"), "marginBottom"); // 1 inch
// IronPDF: margins in millimeters
renderer.RenderingOptions.MarginTop = 12.7; // 0.5 inches × 25.4 = 12.7mm
renderer.RenderingOptions.MarginBottom = 25.4; // 1 inch × 25.4 = 25.4mm
' Gotenberg: margins in inches
content.Add(New StringContent("0.5"), "marginTop") ' 0.5 inches
content.Add(New StringContent("1"), "marginBottom") ' 1 inch
' IronPDF: margins in millimeters
renderer.RenderingOptions.MarginTop = 12.7 ' 0.5 inches × 25.4 = 12.7mm
renderer.RenderingOptions.MarginBottom = 25.4 ' 1 inch × 25.4 = 25.4mm
Fórmula de conversión: millimeters = inches × 25.4
Síncrono frente a asíncrono
Gotenberg requiere operaciones asíncronas debido a la comunicación HTTP:
// Gotenberg: Forced async due to network calls
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
// IronPDF: Synchronous in-process execution
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
// IronPDF: Async wrapper if needed
var pdf = await Task.Run(() => renderer.RenderHtmlAsPdf(html));
// Gotenberg: Forced async due to network calls
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
// IronPDF: Synchronous in-process execution
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
// IronPDF: Async wrapper if needed
var pdf = await Task.Run(() => renderer.RenderHtmlAsPdf(html));
Imports System.Net.Http
' Gotenberg: Forced async due to network calls
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
' IronPDF: Synchronous in-process execution
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
' IronPDF: Async wrapper if needed
Dim pdf = Await Task.Run(Function() renderer.RenderHtmlAsPdf(html))
Manejo de errores
// Gotenberg: HTTP error handling
try
{
var response = await client.PostAsync(gotenbergUrl, content);
response.EnsureSuccessStatusCode(); // What if 500? 503? Timeout?
}
catch (HttpRequestException ex) { /* Network error */ }
catch (TaskCanceledException ex) { /* Timeout */ }
// IronPDF: Standard .NET exceptions
try
{
var pdf = renderer.RenderHtmlAsPdf(html);
}
catch (Exception ex)
{
Console.WriteLine($"PDF generation failed: {ex.Message}");
}
// Gotenberg: HTTP error handling
try
{
var response = await client.PostAsync(gotenbergUrl, content);
response.EnsureSuccessStatusCode(); // What if 500? 503? Timeout?
}
catch (HttpRequestException ex) { /* Network error */ }
catch (TaskCanceledException ex) { /* Timeout */ }
// IronPDF: Standard .NET exceptions
try
{
var pdf = renderer.RenderHtmlAsPdf(html);
}
catch (Exception ex)
{
Console.WriteLine($"PDF generation failed: {ex.Message}");
}
Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
' Gotenberg: HTTP error handling
Try
Dim response = Await client.PostAsync(gotenbergUrl, content)
response.EnsureSuccessStatusCode() ' What if 500? 503? Timeout?
Catch ex As HttpRequestException
' Network error
Catch ex As TaskCanceledException
' Timeout
End Try
' IronPDF: Standard .NET exceptions
Try
Dim pdf = renderer.RenderHtmlAsPdf(html)
Catch ex As Exception
Console.WriteLine($"PDF generation failed: {ex.Message}")
End Try
Eliminación de infraestructuras
Tras la migración, elimine Gotenberg de su infraestructura:
# REMOVE from docker-compose.yml:
# services:
# gotenberg:
# image: gotenberg/gotenberg:8
# ports:
# - "3000:3000"
# deploy:
# resources:
# limits:
# memory: 2G
# REMOVE from docker-compose.yml:
# services:
# gotenberg:
# image: gotenberg/gotenberg:8
# ports:
# - "3000:3000"
# deploy:
# resources:
# limits:
# memory: 2G
Consideraciones sobre el rendimiento
Comparación de latencias
| Operación | Gotenberg (Caliente) | Gotenberg (Arranque en frío) | IronPDF (Primera versión) | IronPDF (continuación) |
|---|---|---|---|---|
| HTML sencillo | 150-300ms | 2-5 segundos | 1-2 segundos | 50-150ms |
| HTML complejo | 500-1500ms | 3-7 segundos | 1.5-3 segundos | 200-800ms |
| Renderización de URL | 1-5 segundos | 3-10 segundos | 1-5 segundos | 500ms-3s |
Eliminación de costes de infraestructura
| Recurso | Gotenberg | IronPDF |
|---|---|---|
| Contenedores necesarios | 1-N (escalado) | 0 |
| Memoria por contenedor | 512 MB-2 GB | N/A |
| Sobrecarga de red por solicitud | 10-100ms | 0ms |
| Puntos finales del chequeo | Requerido | No es necesario |
| Equilibrador de carga | A menudo se necesita | No es necesario |
Solución de problemas
Tema 1: Los patrones HttpClient no son necesarios
Problema: el código todavía usa HttpClient y MultipartFormDataContent.
Solución: reemplazar completamente con ChromePdfRenderer:
// Remove all of this:
// using var client = new HttpClient();
// using var content = new MultipartFormDataContent();
// content.Add(new StringContent(html), "files", "index.html");
// var response = await client.PostAsync(url, content);
// Replace with:
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
// Remove all of this:
// using var client = new HttpClient();
// using var content = new MultipartFormDataContent();
// content.Add(new StringContent(html), "files", "index.html");
// var response = await client.PostAsync(url, content);
// Replace with:
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf(html)
Tema 2: Unidades de margen erróneas
Problema: los archivos PDF tienen márgenes incorrectos después de la migración.
Solución: convertir pulgadas a milímetros:
// Gotenberg used inches: "0.5"
// IronPDF uses millimeters: 0.5 × 25.4 = 12.7
renderer.RenderingOptions.MarginTop = 12.7;
// Gotenberg used inches: "0.5"
// IronPDF uses millimeters: 0.5 × 25.4 = 12.7
renderer.RenderingOptions.MarginTop = 12.7;
Edición 3: Referencias URL de contenedores
Problema: el código contiene http://gotenberg:3000 o URL similares.
Solución: elimine todas las referencias URL del contenedor; IronPDF se ejecuta en proceso:
// Remove:
// private readonly string _gotenbergUrl = "http://gotenberg:3000";
// IronPDF needs no URL - it's in-process
var renderer = new ChromePdfRenderer();
// Remove:
// private readonly string _gotenbergUrl = "http://gotenberg:3000";
// IronPDF needs no URL - it's in-process
var renderer = new ChromePdfRenderer();
' Remove:
' Private ReadOnly _gotenbergUrl As String = "http://gotenberg:3000"
' IronPDF needs no URL - it's in-process
Dim renderer As New ChromePdfRenderer()
Lista de comprobación de la migración
Pre-Migración
- Inventariar todas las llamadas HTTP de Gotenberg en el código base
- Documentar la configuración actual de Gotenberg (tiempos de espera, márgenes, tamaños de papel)
- Identificar toda la configuración de Gotenberg de Docker/Kubernetes
- Obtener la clave de licencia de IronPDF
- Planificar el desmantelamiento de infraestructuras
Migración de código
- Instalar el paquete NuGet de IronPDF:
dotnet add package IronPdf - Eliminar paquetes de cliente de Gotenberg
- Reemplazar todas las llamadas HTTP a Gotenberg con llamadas al método IronPDF
- Convertir unidades de margen de pulgadas a milímetros
- Actualización del manejo de errores (errores HTTP → excepciones .NET)
- Agregar inicialización de clave de licencia al inicio
- Para software de facturación español: incorporar leyendas VERI*FACTU y QR de la AEAT en plantillas HTML
Migración de infraestructuras
- Eliminar Gotenberg de Docker Compose / Kubernetes
- Actualizar los pipelines de CI/CD (eliminar las extracciones de imágenes de Gotenberg)
- Eliminar los controles de salud de Gotenberg
- Eliminar la URL de Gotenberg de la configuración
Pruebas
- Prueba de conversión de HTML a PDF
- Prueba de conversión de URL a PDF
- Verificar la precisión del margen y del tamaño
- Prueba de rendimiento bajo carga
- Pruebe el tiempo de calentamiento del primer renderizado
- Para software de facturación español: validar leyenda VERI*FACTU, QR AEAT y conformidad Facturae
Posmigración
- Eliminar implementaciones de contenedores de Gotenberg
- Archivar archivos de configuración de Gotenberg
- Actualización de la documentación
- Supervisar el uso de memoria de la aplicación
- Verificar que no haya conexiones de red huérfanas
Cumplimiento LOPDGDD, FNMT-RCM y Crea y Crece: el entorno regulatorio completo para ISVs españoles
Migrar de Gotenberg a IronPDF no es solo una simplificación arquitectónica — para los ISVs de software de facturación en España, es una decisión de cumplimiento regulatorio. Esta tabla sintetiza el mapa de cumplimiento con el calendario español vigente:
| Marco regulatorio | Estado | Acción con IronPDF |
|---|---|---|
| VeriFactu (RDL 15/2025) | Vigente para software vendors desde 29/07/2025 | Leyenda VERI*FACTU + QR AEAT + in-process (sin HTTP) |
| TicketBAI (Bizkaia, Gipuzkoa, Araba) | Vigente | Firma XAdES/PAdES con certificados FNMT-RCM |
| SII grandes empresas | Vigente (tiempo real) | Headless on-premise + PDF/A |
| Crea y Crece (>8M€) | Desde 2027 | Facturae XML en PDF/A-3 (EN 16931/CIUS-ES) |
| Crea y Crece (resto) | Desde 2028 | Mismo estándar para pymes/autónomos |
LOPDGDD y AEPD: Las facturas generadas en flujos de cumplimiento VeriFactu contienen datos personales (NIF, CIF, domicilio fiscal) bajo supervisión de la AEPD. Gotenberg, al enviar el contenido HTML a un contenedor externo (aunque sea local), introduce una superficie de análisis adicional bajo LOPDGDD. IronPDF procesa directamente en proceso — sin transferencias de datos entre servicios, simplificando el análisis de cumplimiento.
eIDAS y FNMT-RCM: Para la firma digital de facturas electrónicas (TicketBAI XAdES para las tres Haciendas Forales, PAdES para FACe), IronPDF soporta certificados PKCS#12 emitidos por la FNMT-RCM, la CA española reconocida por la AEAT. En una arquitectura Gotenberg, la firma XAdES debe coordinarse externamente al contenedor y antes/después de las llamadas HTTP — añadiendo complejidad y puntos de fallo en flujos críticos de cumplimiento.
iText AGPL y VeriFactu: Al migrar desde Gotenberg, algunos equipos evalúan iText/iText7. La licencia AGPL de iText obliga a los ISVs de software de facturación propietario a publicar su código fuente o adquirir licencia comercial. Esta restricción se amplifica bajo el régimen de penalizaciones VeriFactu de hasta 150.000 €/año. IronPDF ofrece licenciamiento comercial predecible sin restricciones de distribución AGPL.
Ejemplo: factura VeriFactu con IronPDF in-process (NIF/CIF, formato EUR español)
A diferencia de Gotenberg (que requeriría serializar el HTML en una petición HTTP multipart hacia el contenedor), IronPDF genera la factura directamente en el proceso .NET:
Preguntas Frecuentes
¿Por qué Gotenberg es problemático para cumplimiento VeriFactu en España?
Gotenberg interpone llamadas HTTP y contenedores Docker entre el código C# y el PDF resultante, añadiendo puntos de fallo en flujos de cumplimiento críticos. VeriFactu (RDL 15/2025) exige cadenas de auditoría íntegras — una arquitectura con dependencias de red puede comprometer esa integridad. Además, la latencia de red de Gotenberg (10-100ms por solicitud) es problemática en sistemas de alto volumen de facturación electrónica integrados con el SII en tiempo real.
¿IronPDF soporta firma XAdES y PAdES con certificados FNMT-RCM para TicketBAI?
Sí. IronPDF incluye XAdES, PAdES y CAdES nativo, compatible con certificados PKCS#12 de la FNMT-RCM. En una arquitectura Gotenberg, la firma XAdES debía coordinarse externamente al contenedor. Con IronPDF in-process, toda la generación y firma ocurre localmente en el mismo proceso .NET.
¿Cómo simplifica IronPDF el cumplimiento LOPDGDD al migrar desde Gotenberg?
Gotenberg envía el contenido HTML al contenedor Docker (incluso si es local). IronPDF procesa directamente en proceso, sin ninguna transferencia de datos de facturas (NIF, CIF) entre servicios. Esto simplifica el análisis de cumplimiento LOPDGDD supervisado por la AEPD para datos fiscales de contribuyentes españoles.
¿IronPDF está preparado para Crea y Crece (EN 16931/CIUS-ES)?
Sí. La Ley Crea y Crece establece facturación electrónica B2B obligatoria (2027-2028) con Facturae XML en PDF/A-3. IronPDF soporta PDF/A nativo en el mismo paquete. Migrar ahora de Gotenberg a IronPDF evita un segundo cambio de motor cuando entren en vigor los plazos de Crea y Crece.
¿Cuál es la diferencia entre Gotenberg, iText AGPL e IronPDF para ISVs de facturación en España?
Gotenberg es open-source sin licencia AGPL pero requiere infraestructura Docker compleja. iText/iText7 tiene AGPL que obliga a ISVs propietarios a publicar código fuente — especialmente problemático bajo la penalización VeriFactu de 150.000 €/año. IronPDF ofrece arquitectura in-process (sin Docker) con licenciamiento comercial predecible sin restricciones AGPL.

