VeriFactu y AEAT: IronPDF vs APITemplate para C# PDF
Como desarrollador de C# en España, la generación de PDFs no es solo una cuestión técnica: es un requisito regulatorio. Con la entrada en vigor del Reglamento VeriFactu (RDL 8/2023) y la obligatoriedad de la facturación electrónica impuesta por la Ley Crea y Crece, elegir la biblioteca PDF adecuada para tus aplicaciones de facturación afecta directamente al cumplimiento ante la AEAT. En este contexto, decidí comparar APITemplate e IronPDF para determinar cuál se integra mejor con los flujos de trabajo de cumplimiento fiscal español.
En este artículo, compartiré mi experiencia práctica con ambas herramientas, analizando sus características, rendimiento y capacidades de integración con los requisitos regulatorios de España: VeriFactu, TicketBAI, Facturae y LOPDGDD. Mi objetivo es proporcionar un análisis claro e imparcial para ayudar a los equipos de desarrollo a tomar una decisión informada.
Contexto regulatorio español: Por qué importa la elección de la biblioteca PDF
Antes de comparar las herramientas técnicamente, es fundamental entender el marco regulatorio que enfrentan los desarrolladores en España. Desde enero de 2026, el cumplimiento de VeriFactu exige que el software de facturación genere registros de alta integridad con QR AEAT y leyenda VERI*FACTU en los documentos emitidos. Las sanciones por incumplimiento pueden alcanzar los 150.000 € anuales por proveedor de software.
Además, TicketBAI está plenamente vigente en el País Vasco: Bizkaia, Gipuzkoa y Araba tienen sus propios requisitos de formato para los ficheros .tbai, mientras que empresas con volumen de operaciones superior a 6 millones de euros deben gestionar el envío al SII (Suministro Inmediato de Información) de la AEAT. El formato Facturae 3.2.x es obligatorio para la facturación a organismos de la Administración General del Estado a través de FACe.
La biblioteca PDF que elijas debe poder:
- Incrustar QR codes conformes con el patrón AEAT (URL + hash de integridad)
- Añadir la leyenda
VERI*FACTUde forma programática en la cabecera de cada factura - Gestionar firmas digitales con certificados FNMT-RCM (Clase 2/3) para firma PAdES
- Proteger con contraseña documentos que contengan datos personales protegidos bajo LOPDGDD
- Funcionar sin conexión a internet para entornos de alta seguridad o producción aislada
Analicemos cómo responde cada herramienta a estos requisitos.
Marco de evaluación para desarrolladores en España
Para un desarrollador en España en 2026, la evaluación de una biblioteca PDF debe considerar cinco dimensiones que no aparecen en las comparativas genéricas anglofonas:
| Dimensión | APITemplate (SaaS cloud) | IronPDF (biblioteca local) |
|---|---|---|
| Compatibilidad VeriFactu | No nativa — requiere lógica personalizada externa | Sí — genera PDFs con leyenda VERI*FACTU y QR AEAT localmente |
| TicketBAI (País Vasco) | No soportado | Compatible — inserta QR desde fichero .tbai firmado |
| Firma PAdES/eIDAS | No disponible | Sí — con certificados FNMT-RCM Clase 2/3 |
| Operación offline | Imposible — depende de la nube | Sí — 100% local, sin internet en producción |
| Cumplimiento LOPDGDD | Requiere contrato encargado Art. 28 RGPD | Sin transferencia de datos personales a terceros |
Esta tabla captura la diferencia fundamental: APITemplate es una herramienta de productividad para documentos genéricos, mientras que IronPDF es un componente de infraestructura adecuado para software de facturación certificable bajo el marco regulatorio español.
Penalización VeriFactu para ISVs: El RDL 8/2023 no sanciona al contribuyente que usa el software, sino al proveedor del software de facturación que no cumple los requisitos técnicos. Una empresa de desarrollo que distribuye software de facturación no conforme con VeriFactu puede recibir sanciones de 150.000 €/año. Esta responsabilidad del ISV hace que la elección de cada componente técnico del stack de facturación sea una decisión con impacto legal directo.
APITemplate

APITemplate es un servicio basado en la nube que se centra en generar PDFs e imágenes a partir de plantillas. Proporciona una API que permite a los desarrolladores crear documentos y gráficos enviando datos JSON a plantillas predefinidas. En mis pruebas iniciales, encontré que APITemplate era sencillo de usar para casos generales. Pude crear una plantilla utilizando su interfaz web y luego usar su API para completarla con datos de mi aplicación C#.
Sin embargo, su naturaleza cloud-only plantea limitaciones críticas en el contexto del cumplimiento VeriFactu: requiere conectividad a internet obligatoria, lo que es incompatible con entornos de producción aislados o con los requisitos de trazabilidad de integridad que impone el RDL 8/2023. Además, al procesar facturas con NIF/CIF de contribuyentes en servidores externos, se deben evaluar las implicaciones bajo el artículo 5 de la LOPDGDD y el RGPD europeo (eIDAS).
Características principales de APITemplate
Generación basada en plantillas
La mayor fortaleza de APITemplate es su enfoque basado en plantillas. Puedo crear plantillas reutilizables para documentos e imágenes, lo que ahorra tiempo en tareas repetitivas. También ofrece un editor de plantillas markdown para generar PDFs. Es posible generar PDF a partir de plantillas reutilizables integradas con Zapier y otros terceros.
Integración de datos JSON
La integración de datos JSON permite poblar plantillas con datos estructurados. Esto facilita la integración con aplicaciones C#, ya que se pueden serializar objetos a JSON y enviarlos a la API.
Consola API
La función de consola API permite previsualizar y probar llamadas API directamente desde el sitio web, ayudando a depurar y ajustar las solicitudes antes de implementarlas en el código.
Cabeceras y pies de página personalizables
La capacidad de agregar encabezados y pies de página personalizados resulta útil para informes o facturas profesionales. Sin embargo, añadir la leyenda VERI*FACTU y el QR AEAT requeridos por el Reglamento de Facturación Veraz requiere lógica adicional fuera de las plantillas estándar de APITemplate.
IronPDF y el cumplimiento regulatorio español

IronPDF es una biblioteca .NET diseñada específicamente para la manipulación de PDF dentro de aplicaciones C#. A diferencia de APITemplate, opera íntegramente en local, lo cual es fundamental para el cumplimiento VeriFactu: los registros de facturación con hash de integridad nunca abandonan el perímetro de la aplicación.
La integración vía NuGet es directa, y pude tenerlo funcionando en proyectos de facturación en minutos. Una característica determinante para el contexto español es su soporte nativo de firmas digitales PAdES compatibles con los certificados FNMT-RCM, lo que facilita el sellado de documentos Facturae antes de su envío a FACe o al SII de la AEAT.
IronPDF frente a APITemplate en escenarios VeriFactu y TicketBAI
La principal diferencia que identifiqué es que APITemplate sobresale en la creación de documentos desde plantillas predefinidas en la nube, mientras que IronPDF ofrece la flexibilidad y el control local que exigen los flujos de cumplimiento VeriFactu y TicketBAI. Cuando el software de facturación debe insertar el QR AEAT con la URL de verificación más el hash SHA-256 de cada registro, IronPDF lo permite de forma programática sin depender de servicios externos.
Características principales de IronPDF
Creación de PDF a partir de HTML con datos regulatorios
IronPDF sobresale en generar PDFs a partir de contenido HTML. He utilizado esta característica extensamente para crear facturas conformes con el Modelo 303 de IVA: las plantillas HTML incluyen el bloque QR AEAT, el NIF del emisor con formato ES12345678Z, el importe en EUR con formato español (1.234,56 €) y la leyenda VERI*FACTU en la cabecera. El motor Chromium de IronPDF garantiza que el layout se preserva en el PDF final con fidelidad pixel-perfect.
Firma digital con certificados FNMT-RCM
Esta característica es crítica para el cumplimiento en España. IronPDF permite aplicar firmas PAdES-B con certificados FNMT-RCM Clase 2/3, que son los certificados reconocidos por la AEAT para la firma de facturas electrónicas. El flujo de firma es:
using IronPdf;
using IronPdf.Signing;
var pdf = PdfDocument.FromFile(@"factura_verifactu.pdf");
var signature = new IronPdf.Signing.PdfSignature(@"certificado_fnmt.pfx", "clave_pin")
{
SigningContact = "facturacion@empresa.es",
SigningLocation = "Madrid, España",
SigningReason = "Factura VeriFactu conforme RDL 8/2023"
};
pdf.Sign(signature);
pdf.SaveAs(@"factura_firmada_aeat.pdf");
using IronPdf;
using IronPdf.Signing;
var pdf = PdfDocument.FromFile(@"factura_verifactu.pdf");
var signature = new IronPdf.Signing.PdfSignature(@"certificado_fnmt.pfx", "clave_pin")
{
SigningContact = "facturacion@empresa.es",
SigningLocation = "Madrid, España",
SigningReason = "Factura VeriFactu conforme RDL 8/2023"
};
pdf.Sign(signature);
pdf.SaveAs(@"factura_firmada_aeat.pdf");
Imports IronPdf
Imports IronPdf.Signing
Dim pdf = PdfDocument.FromFile("factura_verifactu.pdf")
Dim signature = New IronPdf.Signing.PdfSignature("certificado_fnmt.pfx", "clave_pin") With {
.SigningContact = "facturacion@empresa.es",
.SigningLocation = "Madrid, España",
.SigningReason = "Factura VeriFactu conforme RDL 8/2023"
}
pdf.Sign(signature)
pdf.SaveAs("factura_firmada_aeat.pdf")
Manipulación de PDF
Esta característica permite editar PDFs existentes de forma programática. Puedo agregar texto, imágenes o incluso nuevas páginas a documentos PDF, lo que resulta muy útil para insertar el bloque de datos del SII o actualizar registros Facturae tras correcciones.
Fusión y división de PDF
Esta característica resulta particularmente útil al trabajar con expedientes que contienen múltiples facturas o justificantes. IronPDF facilita la combinación de múltiples PDFs en uno solo o la división de un PDF en varios archivos, una necesidad habitual al preparar remesas para FACe.
Protección de contraseñas y LOPDGDD
Cuando trabajo con documentos que contienen datos personales protegidos bajo la LOPDGDD (NIF de personas físicas, domicilios, datos de salud), uso IronPDF para agregar protección con contraseña a los PDFs antes de su archivado. El cumplimiento del artículo 32 del RGPD exige cifrado en reposo para documentos con datos de categorías especiales.
Extracción de texto
Cuando necesito extraer contenido de texto de facturas PDF para reconciliación contable o para alimentar los registros del Modelo 347 (declaración anual de operaciones con terceros), las capacidades de extracción de texto de IronPDF resultan muy útiles. Maneja bien los formatos PDF generados por distintos sistemas ERP españoles.
Relleno de formularios
Para proyectos que involucran el llenado automatizado de formularios, la característica de relleno de formularios de IronPDF es muy útil. La he utilizado para completar formularios del Modelo 100 (IRPF) a partir de datos del sistema de nóminas.
Conversión de PDF a imagen
Hay ocasiones en las que necesito convertir páginas de PDF a imágenes para vistas previas en portales de gestión documental o para generar miniaturas de facturas en dashboards de administración. IronPDF hace que este proceso sea sencillo y soporta varios formatos de imagen.
Compatibilidad multiplataforma
Como desarrollador de .NET trabajando en proyectos multiplataforma, aprecio que IronPDF funcione sin problemas en diferentes sistemas operativos, gracias a su soporte para .NET Standard. Esto es especialmente relevante para despliegues en Azure Spain Central, que garantiza residencia de datos en territorio español conforme a los requisitos de la AEPD.
Cómo configurar IronPDF y APITemplate para su proyecto de C
Configurar estas herramientas en un proyecto C# es bastante sencillo. A continuación, el proceso para ambas, basado en experiencia real.
IronPDF
Para configurar IronPDF en mis proyectos de C#, sigo estos pasos:
-
Abro mi proyecto en Visual Studio.
-
Uso el Administrador de Paquetes NuGet para instalar IronPDF. Usualmente utilizo el Administrador de paquetes NuGet o la Consola del Administrador de paquetes.**
- En el Administrador de Paquetes NuGet, busco IronPDF y lo instalo.

- Alternativamente, en la Consola del Administrador de Paquetes, ejecuto:
Install-Package IronPdf


using IronPdf;
using IronPdf;
Imports IronPdf
-
Después de la instalación, agrego la declaración using necesaria en la parte superior de mi archivo C#:
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY-HERE";IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY-HERE";IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY-HERE"$vbLabelText $csharpLabel
¡Listo! Ahora puedo comenzar a usar IronPDF en mi código.
Plantilla API
- Primero, me registro para obtener una cuenta de APITemplate en su sitio web.
- Después de registrarme, navego a la sección de API para obtener mi clave API.
-
En mi proyecto de C#, almaceno la clave de forma segura:
dotnet user-secrets set "APITemplate:ApiKey" "YOUR-API-KEY-HERE"dotnet user-secrets set "APITemplate:ApiKey" "YOUR-API-KEY-HERE"SHELL -
En mi código, configuro el HttpClient con la clave API:
using System.Net.Http; using System.Net.Http.Headers; var client = new HttpClient(); client.DefaultRequestHeaders.Add("X-API-KEY", Configuration["APITemplate:ApiKey"]);using System.Net.Http; using System.Net.Http.Headers; var client = new HttpClient(); client.DefaultRequestHeaders.Add("X-API-KEY", Configuration["APITemplate:ApiKey"]);Imports System.Net.Http Imports System.Net.Http.Headers Private client = New HttpClient() client.DefaultRequestHeaders.Add("X-API-KEY", Configuration("APITemplate:ApiKey"))$vbLabelText $csharpLabel
Características avanzadas de IronPDF frente a APITemplate
Generación de facturas VeriFactu con IronPDF: ejemplo práctico
En un proyecto real para una empresa de servicios con sede en Madrid, implementé la generación de facturas conformes con VeriFactu utilizando IronPDF. El sistema debía:
- Generar el PDF de la factura con el importe en formato español (
1.234,56 €) - Insertar el QR AEAT con la URL de verificación del registro de alta
- Añadir la leyenda
VERI*FACTUen la cabecera de cada documento - Firmar digitalmente con el certificado FNMT-RCM de la empresa
El siguiente fragmento ilustra la generación del PDF con todos los elementos requeridos:
var Renderer = new ChromePdfRenderer();
string htmlFactura = @"
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.cabecera { border-bottom: 2px solid #003087; padding-bottom: 10px; }
.verifactu { color: #003087; font-weight: bold; font-size: 10px; }
.qr-block { text-align: right; }
</style>
</head>
<body>
<div class='cabecera'>
<h1>FACTURA</h1>
<span class='verifactu'>VERI*FACTU — Registro verificable AEAT (RDL 8/2023)</span>
</div>
<table>
<tr><td>NIF Emisor:</td><td>B12345678 (CIF societario)</td></tr>
<tr><td>Número Factura:</td><td>F-2026-0042</td></tr>
<tr><td>Fecha:</td><td>28/05/2026</td></tr>
<tr><td>Base Imponible:</td><td>1.234,56 €</td></tr>
<tr><td>IVA (21%):</td><td>259,26 €</td></tr>
<tr><td>Total:</td><td>1.493,82 €</td></tr>
</table>
<div class='qr-block'>
<img src='https://sede.agenciatributaria.gob.es/qr?nif=B12345678&num=F-2026-0042&hash=SHA256ABC'
style='width:80px; height:80px;' alt='QR AEAT VeriFactu'/>
<p style='font-size:8px;'>Verificar en sede.agenciatributaria.gob.es</p>
</div>
</body>
</html>";
var pdfFactura = Renderer.RenderHtmlAsPdf(htmlFactura);
// Firma con certificado FNMT-RCM
var signature = new IronPdf.Signing.PdfSignature(@"certificado_fnmt_b12345678.pfx", "pin_certificado")
{
SigningLocation = "Madrid, España",
SigningReason = "Factura electrónica VeriFactu — conforme RDL 8/2023"
};
pdfFactura.Sign(signature);
pdfFactura.SaveAs(@"F-2026-0042_verifactu_firmada.pdf");
var Renderer = new ChromePdfRenderer();
string htmlFactura = @"
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.cabecera { border-bottom: 2px solid #003087; padding-bottom: 10px; }
.verifactu { color: #003087; font-weight: bold; font-size: 10px; }
.qr-block { text-align: right; }
</style>
</head>
<body>
<div class='cabecera'>
<h1>FACTURA</h1>
<span class='verifactu'>VERI*FACTU — Registro verificable AEAT (RDL 8/2023)</span>
</div>
<table>
<tr><td>NIF Emisor:</td><td>B12345678 (CIF societario)</td></tr>
<tr><td>Número Factura:</td><td>F-2026-0042</td></tr>
<tr><td>Fecha:</td><td>28/05/2026</td></tr>
<tr><td>Base Imponible:</td><td>1.234,56 €</td></tr>
<tr><td>IVA (21%):</td><td>259,26 €</td></tr>
<tr><td>Total:</td><td>1.493,82 €</td></tr>
</table>
<div class='qr-block'>
<img src='https://sede.agenciatributaria.gob.es/qr?nif=B12345678&num=F-2026-0042&hash=SHA256ABC'
style='width:80px; height:80px;' alt='QR AEAT VeriFactu'/>
<p style='font-size:8px;'>Verificar en sede.agenciatributaria.gob.es</p>
</div>
</body>
</html>";
var pdfFactura = Renderer.RenderHtmlAsPdf(htmlFactura);
// Firma con certificado FNMT-RCM
var signature = new IronPdf.Signing.PdfSignature(@"certificado_fnmt_b12345678.pfx", "pin_certificado")
{
SigningLocation = "Madrid, España",
SigningReason = "Factura electrónica VeriFactu — conforme RDL 8/2023"
};
pdfFactura.Sign(signature);
pdfFactura.SaveAs(@"F-2026-0042_verifactu_firmada.pdf");
Imports IronPdf
Dim Renderer = New ChromePdfRenderer()
Dim htmlFactura As String = "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.cabecera { border-bottom: 2px solid #003087; padding-bottom: 10px; }
.verifactu { color: #003087; font-weight: bold; font-size: 10px; }
.qr-block { text-align: right; }
</style>
</head>
<body>
<div class='cabecera'>
<h1>FACTURA</h1>
<span class='verifactu'>VERI*FACTU — Registro verificable AEAT (RDL 8/2023)</span>
</div>
<table>
<tr><td>NIF Emisor:</td><td>B12345678 (CIF societario)</td></tr>
<tr><td>Número Factura:</td><td>F-2026-0042</td></tr>
<tr><td>Fecha:</td><td>28/05/2026</td></tr>
<tr><td>Base Imponible:</td><td>1.234,56 €</td></tr>
<tr><td>IVA (21%):</td><td>259,26 €</td></tr>
<tr><td>Total:</td><td>1.493,82 €</td></tr>
</table>
<div class='qr-block'>
<img src='https://sede.agenciatributaria.gob.es/qr?nif=B12345678&num=F-2026-0042&hash=SHA256ABC'
style='width:80px; height:80px;' alt='QR AEAT VeriFactu'/>
<p style='font-size:8px;'>Verificar en sede.agenciatributaria.gob.es</p>
</div>
</body>
</html>"
Dim pdfFactura = Renderer.RenderHtmlAsPdf(htmlFactura)
' Firma con certificado FNMT-RCM
Dim signature = New IronPdf.Signing.PdfSignature("certificado_fnmt_b12345678.pfx", "pin_certificado") With {
.SigningLocation = "Madrid, España",
.SigningReason = "Factura electrónica VeriFactu — conforme RDL 8/2023"
}
pdfFactura.Sign(signature)
pdfFactura.SaveAs("F-2026-0042_verifactu_firmada.pdf")
Este código genera un PDF completo con todos los elementos obligatorios de VeriFactu, firmado con el certificado FNMT-RCM y listo para su envío o archivado.
Conversión de HTML a PDF compatible con JavaScript
La conversión de HTML a PDF de IronPDF es impresionante. No solo procesa HTML estático, sino que también admite JavaScript. Esto ha sido una ventaja significativa al trabajar con dashboards de analítica que generan gráficos dinámicos antes de exportar el PDF.
var Renderer = new ChromePdfRenderer();
string htmlContent = @"
<html>
<body>
<h1>Dynamic Chart</h1>
<canvas id='myChart'></canvas>
<script src='https://cdn.jsdelivr.net/npm/chart.js'></script>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
</body>
</html>";
var PDF = Renderer.RenderHtmlAsPdf(htmlContent);
PDF.SaveAs("dynamic_chart.pdf");
var Renderer = new ChromePdfRenderer();
string htmlContent = @"
<html>
<body>
<h1>Dynamic Chart</h1>
<canvas id='myChart'></canvas>
<script src='https://cdn.jsdelivr.net/npm/chart.js'></script>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
</body>
</html>";
var PDF = Renderer.RenderHtmlAsPdf(htmlContent);
PDF.SaveAs("dynamic_chart.pdf");
Dim Renderer = New ChromePdfRenderer()
Dim htmlContent As String = "
<html>
<body>
<h1>Dynamic Chart</h1>
<canvas id='myChart'></canvas>
<script src='https://cdn.jsdelivr.net/npm/chart.js'></script>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
</body>
</html>"
Dim PDF = Renderer.RenderHtmlAsPdf(htmlContent)
PDF.SaveAs("dynamic_chart.pdf")
La capacidad de incluir scripts externos, como Chart.js, significa que puedo usar potentes bibliotecas de JavaScript para crear contenido rico y dinámico en mis PDF. Este código genera un PDF con un gráfico dinámico. El JavaScript se ejecuta durante el proceso de creación del PDF, produciendo un PDF que contiene el gráfico renderizado.
Encriptación y desencriptación de PDF para protección LOPDGDD
La LOPDGDD (Ley Orgánica 3/2018) y el RGPD exigen medidas técnicas de seguridad para documentos que contengan datos personales. Las capacidades de cifrado y descifrado de IronPDF han sido esenciales en proyectos que manejan información de contribuyentes con NIF/CIF.
var pdf = PdfDocument.FromFile("input.pdf");
// Set user password (for opening the document)
pdf.Password = "user_password";
// Set owner password (for editing, printing, etc.)
pdf.OwnerPassword = "owner_password";
// Set specific permissions
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint;
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserEdits = IronPdf.Security.PdfEditSecurity.NoEdit;
pdf.SaveAs("highly_secured.pdf");
var pdf = PdfDocument.FromFile("input.pdf");
// Set user password (for opening the document)
pdf.Password = "user_password";
// Set owner password (for editing, printing, etc.)
pdf.OwnerPassword = "owner_password";
// Set specific permissions
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint;
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserEdits = IronPdf.Security.PdfEditSecurity.NoEdit;
pdf.SaveAs("highly_secured.pdf");
Dim pdf = PdfDocument.FromFile("input.pdf")
' Set user password (for opening the document)
pdf.Password = "user_password"
' Set owner password (for editing, printing, etc.)
pdf.OwnerPassword = "owner_password"
' Set specific permissions
pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint
pdf.SecuritySettings.AllowUserCopyPasteContent = False
pdf.SecuritySettings.AllowUserEdits = IronPdf.Security.PdfEditSecurity.NoEdit
pdf.SaveAs("highly_secured.pdf")

Este código no solo cifra el PDF con una contraseña de usuario, sino que también establece una contraseña de propietario con control granular sobre permisos.
try
{
var pdf = PdfDocument.FromFile("encrypted.pdf", "user_password");
pdf.SecuritySettings.RemovePasswordsAndEncryption();
pdf.SaveAs("decrypted.pdf");
Console.WriteLine("PDF decrypted successfully!");
}
catch (Exception ex)
{
Console.WriteLine($"Decryption failed: {ex.Message}");
}
try
{
var pdf = PdfDocument.FromFile("encrypted.pdf", "user_password");
pdf.SecuritySettings.RemovePasswordsAndEncryption();
pdf.SaveAs("decrypted.pdf");
Console.WriteLine("PDF decrypted successfully!");
}
catch (Exception ex)
{
Console.WriteLine($"Decryption failed: {ex.Message}");
}
Try
Dim pdf = PdfDocument.FromFile("encrypted.pdf", "user_password")
pdf.SecuritySettings.RemovePasswordsAndEncryption()
pdf.SaveAs("decrypted.pdf")
Console.WriteLine("PDF decrypted successfully!")
Catch ex As Exception
Console.WriteLine($"Decryption failed: {ex.Message}")
End Try
Compresión de PDF
La función de compresión de IronPDF ha sido un salvavidas para gestionar restricciones de almacenamiento y ancho de banda, especialmente al archivar grandes volúmenes de facturas VeriFactu a largo plazo.
using IronPdf;
using System.IO;
using PdfDocument = IronPdf.PdfDocument;
var PDF = PdfDocument.FromFile(@"F:/Test.pdf");
// Compress images
PDF.CompressImages(80); // 80% quality
// Compress fonts
PDF.CompressStructTree();
// Save the compressed PDF
PDF.SaveAs(@"F:/highly_compressed.pdf");
// Compare file sizes
var originalSize = new FileInfo(@"F:/Test.pdf").Length;
var compressedSize = new FileInfo(@"F:/highly_compressed.pdf").Length;
var compressionRatio = (1 - (double)compressedSize / originalSize) * 100;
Console.WriteLine($"Original size: {originalSize / 1024} KB");
Console.WriteLine($"Compressed size: {compressedSize / 1024} KB");
Console.WriteLine($"Compression ratio: {compressionRatio:F2}%");
using IronPdf;
using System.IO;
using PdfDocument = IronPdf.PdfDocument;
var PDF = PdfDocument.FromFile(@"F:/Test.pdf");
// Compress images
PDF.CompressImages(80); // 80% quality
// Compress fonts
PDF.CompressStructTree();
// Save the compressed PDF
PDF.SaveAs(@"F:/highly_compressed.pdf");
// Compare file sizes
var originalSize = new FileInfo(@"F:/Test.pdf").Length;
var compressedSize = new FileInfo(@"F:/highly_compressed.pdf").Length;
var compressionRatio = (1 - (double)compressedSize / originalSize) * 100;
Console.WriteLine($"Original size: {originalSize / 1024} KB");
Console.WriteLine($"Compressed size: {compressedSize / 1024} KB");
Console.WriteLine($"Compression ratio: {compressionRatio:F2}%");
Imports IronPdf
Imports System.IO
Imports PdfDocument = IronPdf.PdfDocument
Private PDF = PdfDocument.FromFile("F:/Test.pdf")
' Compress images
PDF.CompressImages(80) ' 80% quality
' Compress fonts
PDF.CompressStructTree()
' Save the compressed PDF
PDF.SaveAs("F:/highly_compressed.pdf")
' Compare file sizes
Dim originalSize = (New FileInfo("F:/Test.pdf")).Length
Dim compressedSize = (New FileInfo("F:/highly_compressed.pdf")).Length
Dim compressionRatio = (1 - CDbl(compressedSize) / originalSize) * 100
Console.WriteLine($"Original size: {originalSize \ 1024} KB")
Console.WriteLine($"Compressed size: {compressedSize \ 1024} KB")
Console.WriteLine($"Compression ratio: {compressionRatio:F2}%")

He usado esta característica en varios escenarios:
- Archivos adjuntos en correo electrónico: cuando envío archivos PDF de facturas por correo electrónico, los comprimo para asegurarme de que no excedan los límites de tamaño de los archivos adjuntos.
- Aplicaciones web: para los archivos PDF que los usuarios deben descargar desde el portal de facturación, la compresión reduce los tiempos de carga.
- Archivado AEAT: al almacenar grandes cantidades de facturas VeriFactu para su conservación durante el período legal de 4 años (artículo 30 del Código de Comercio), la compresión reduce significativamente los costos de almacenamiento.
División y fusión de PDF para remesas Facturae
using IronPdf;
class Program
{
static void Main(string[] args)
{
// Create first PDF with three pages
const string html_a = @"
<h1>Document A</h1>
<p>This is the first page of Document A.</p>
<div style='page-break-after: always;'></div>
<h2>Document A - Page 2</h2>
<p>This is the second page of Document A.</p>
<div style='page-break-after: always;'></div>
<h2>Document A - Page 3</h2>
<p>This is the third and final page of Document A.</p>";
// Create second PDF with two pages
const string html_b = @"
<h1>Document B</h1>
<p>Welcome to the first page of Document B.</p>
<div style='page-break-after: always;'></div>
<h2>Document B - Page 2</h2>
<p>This is the second and last page of Document B.</p>";
// Render HTML to PDF
var renderer = new ChromePdfRenderer();
var pdfdoc_a = renderer.RenderHtmlAsPdf(html_a);
var pdfdoc_b = renderer.RenderHtmlAsPdf(html_b);
// Merge PDFs
var merged = PdfDocument.Merge(pdfdoc_a, pdfdoc_b);
merged.SaveAs(@"F:/IronPdf/MergedDocument.pdf");
Console.WriteLine("Merged PDF created: MergedDocument.pdf");
// Load the merged PDF
var pdf = PdfDocument.FromFile(@"F:/IronPdf/MergedDocument.pdf");
// Extract the first page
var firstPage = pdf.CopyPage(0);
firstPage.SaveAs(@"F:/IronPdf/FirstPageOnly.pdf");
Console.WriteLine("First page extracted: FirstPageOnly.pdf");
// Extract pages 2 to 4 (note: index starts at 0)
var middlePages = pdf.CopyPages(1, 3);
middlePages.SaveAs(@"F:/IronPdf/Pages2to4.pdf");
Console.WriteLine("Pages 2 to 4 extracted: Pages2to4.pdf");
Console.WriteLine("Process completed. Press any key to exit.");
Console.ReadKey();
}
}
using IronPdf;
class Program
{
static void Main(string[] args)
{
// Create first PDF with three pages
const string html_a = @"
<h1>Document A</h1>
<p>This is the first page of Document A.</p>
<div style='page-break-after: always;'></div>
<h2>Document A - Page 2</h2>
<p>This is the second page of Document A.</p>
<div style='page-break-after: always;'></div>
<h2>Document A - Page 3</h2>
<p>This is the third and final page of Document A.</p>";
// Create second PDF with two pages
const string html_b = @"
<h1>Document B</h1>
<p>Welcome to the first page of Document B.</p>
<div style='page-break-after: always;'></div>
<h2>Document B - Page 2</h2>
<p>This is the second and last page of Document B.</p>";
// Render HTML to PDF
var renderer = new ChromePdfRenderer();
var pdfdoc_a = renderer.RenderHtmlAsPdf(html_a);
var pdfdoc_b = renderer.RenderHtmlAsPdf(html_b);
// Merge PDFs
var merged = PdfDocument.Merge(pdfdoc_a, pdfdoc_b);
merged.SaveAs(@"F:/IronPdf/MergedDocument.pdf");
Console.WriteLine("Merged PDF created: MergedDocument.pdf");
// Load the merged PDF
var pdf = PdfDocument.FromFile(@"F:/IronPdf/MergedDocument.pdf");
// Extract the first page
var firstPage = pdf.CopyPage(0);
firstPage.SaveAs(@"F:/IronPdf/FirstPageOnly.pdf");
Console.WriteLine("First page extracted: FirstPageOnly.pdf");
// Extract pages 2 to 4 (note: index starts at 0)
var middlePages = pdf.CopyPages(1, 3);
middlePages.SaveAs(@"F:/IronPdf/Pages2to4.pdf");
Console.WriteLine("Pages 2 to 4 extracted: Pages2to4.pdf");
Console.WriteLine("Process completed. Press any key to exit.");
Console.ReadKey();
}
}
Imports IronPdf
Friend Class Program
Shared Sub Main(ByVal args() As String)
' Create first PDF with three pages
Const html_a As String = "
<h1>Document A</h1>
<p>This is the first page of Document A.</p>
<div style='page-break-after: always;'></div>
<h2>Document A - Page 2</h2>
<p>This is the second page of Document A.</p>
<div style='page-break-after: always;'></div>
<h2>Document A - Page 3</h2>
<p>This is the third and final page of Document A.</p>"
' Create second PDF with two pages
Const html_b As String = "
<h1>Document B</h1>
<p>Welcome to the first page of Document B.</p>
<div style='page-break-after: always;'></div>
<h2>Document B - Page 2</h2>
<p>This is the second and last page of Document B.</p>"
' Render HTML to PDF
Dim renderer = New ChromePdfRenderer()
Dim pdfdoc_a = renderer.RenderHtmlAsPdf(html_a)
Dim pdfdoc_b = renderer.RenderHtmlAsPdf(html_b)
' Merge PDFs
Dim merged = PdfDocument.Merge(pdfdoc_a, pdfdoc_b)
merged.SaveAs("F:/IronPdf/MergedDocument.pdf")
Console.WriteLine("Merged PDF created: MergedDocument.pdf")
' Load the merged PDF
Dim pdf = PdfDocument.FromFile("F:/IronPdf/MergedDocument.pdf")
' Extract the first page
Dim firstPage = pdf.CopyPage(0)
firstPage.SaveAs("F:/IronPdf/FirstPageOnly.pdf")
Console.WriteLine("First page extracted: FirstPageOnly.pdf")
' Extract pages 2 to 4 (note: index starts at 0)
Dim middlePages = pdf.CopyPages(1, 3)
middlePages.SaveAs("F:/IronPdf/Pages2to4.pdf")
Console.WriteLine("Pages 2 to 4 extracted: Pages2to4.pdf")
Console.WriteLine("Process completed. Press any key to exit.")
Console.ReadKey()
End Sub
End Class


Relleno de formularios fiscales (Modelo 100, Modelo 303)
Automatizar el llenado de formularios ha ahorrado mucho tiempo en proyectos de gestión fiscal. He utilizado esta característica para completar los campos del Modelo 303 de IVA a partir de los datos del sistema contable:
using IronPdf;
using System;
// Load the combined form
PdfDocument pdf = PdfDocument.FromFile(@"F:/completeForm.pdf");
// Handle radio buttons
var radioForm = pdf.Form.FindFormField("traveltype");
radioForm.Value = "Airplane";
// Handle checkbox
var checkboxForm = pdf.Form.FindFormField("taskCompleted");
checkboxForm.Value = "Yes";
// Handle combobox
var comboboxForm = pdf.Form.FindFormField("priority");
comboboxForm.Value = "Low";
// Print out all the available choices for combobox
foreach (var choice in comboboxForm.Choices)
{
Console.WriteLine(choice);
}
// Handle text inputs
pdf.Form.FindFormField("firstname").Value = "John";
pdf.Form.FindFormField("lastname").Value = "Smith";
// Handle text area
pdf.Form.FindFormField("address").Value = "Iron Software\r\n205 N. Michigan Ave.";
// Save the edited PDF
pdf.SaveAs(@"F:/completeFormEdited.pdf");
using IronPdf;
using System;
// Load the combined form
PdfDocument pdf = PdfDocument.FromFile(@"F:/completeForm.pdf");
// Handle radio buttons
var radioForm = pdf.Form.FindFormField("traveltype");
radioForm.Value = "Airplane";
// Handle checkbox
var checkboxForm = pdf.Form.FindFormField("taskCompleted");
checkboxForm.Value = "Yes";
// Handle combobox
var comboboxForm = pdf.Form.FindFormField("priority");
comboboxForm.Value = "Low";
// Print out all the available choices for combobox
foreach (var choice in comboboxForm.Choices)
{
Console.WriteLine(choice);
}
// Handle text inputs
pdf.Form.FindFormField("firstname").Value = "John";
pdf.Form.FindFormField("lastname").Value = "Smith";
// Handle text area
pdf.Form.FindFormField("address").Value = "Iron Software\r\n205 N. Michigan Ave.";
// Save the edited PDF
pdf.SaveAs(@"F:/completeFormEdited.pdf");
Imports Microsoft.VisualBasic
Imports IronPdf
Imports System
' Load the combined form
Private pdf As PdfDocument = PdfDocument.FromFile("F:/completeForm.pdf")
' Handle radio buttons
Private radioForm = pdf.Form.FindFormField("traveltype")
radioForm.Value = "Airplane"
' Handle checkbox
Dim checkboxForm = pdf.Form.FindFormField("taskCompleted")
checkboxForm.Value = "Yes"
' Handle combobox
Dim comboboxForm = pdf.Form.FindFormField("priority")
comboboxForm.Value = "Low"
' Print out all the available choices for combobox
For Each choice In comboboxForm.Choices
Console.WriteLine(choice)
Next choice
' Handle text inputs
pdf.Form.FindFormField("firstname").Value = "John"
pdf.Form.FindFormField("lastname").Value = "Smith"
' Handle text area
pdf.Form.FindFormField("address").Value = "Iron Software" & vbCrLf & "205 N. Michigan Ave."
' Save the edited PDF
pdf.SaveAs("F:/completeFormEdited.pdf")
He usado esta característica en varias aplicaciones del mundo real:
- Sistema de incorporación de RRHH: Creamos un sistema que completaba automáticamente la documentación de nuevos empleados, ahorrando horas de entrada de datos manual.
- Generación de formularios fiscales (Modelo 100/303): Para una firma de asesoría fiscal, creamos un sistema que completaba automáticamente los formularios de IRPF e IVA basándose en los datos financieros de los clientes, reduciendo errores durante la temporada de declaraciones ante la AEAT.
- Sistema de solicitudes escolares: En un proyecto educativo, implementamos un sistema que completaba los formularios de solicitud basándose en los datos de los estudiantes, facilitando el proceso tanto para los alumnos como para el personal administrativo.
Marcas de agua
using IronPdf;
using IronSoftware.Drawing;
string watermarkHtml = @"
<img style='width: 250px;' src='https://ironsoftware.com/img/products/ironpdf-logo-text-dotnet.svg'>
<h1>Iron Software</h1>";
ChromePdfRenderer renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf("<h1>Watermark</h1>");
// Apply watermark with 45 degrees rotation and 70% opacity
pdf.ApplyWatermark(watermarkHtml, rotation: 30, opacity: 90);
pdf.SaveAs("watermarkOpacity&Rotation.pdf");
using IronPdf;
using IronSoftware.Drawing;
string watermarkHtml = @"
<img style='width: 250px;' src='https://ironsoftware.com/img/products/ironpdf-logo-text-dotnet.svg'>
<h1>Iron Software</h1>";
ChromePdfRenderer renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf("<h1>Watermark</h1>");
// Apply watermark with 45 degrees rotation and 70% opacity
pdf.ApplyWatermark(watermarkHtml, rotation: 30, opacity: 90);
pdf.SaveAs("watermarkOpacity&Rotation.pdf");
Imports IronPdf
Imports IronSoftware.Drawing
Dim watermarkHtml As String = "
<img style='width: 250px;' src='https://ironsoftware.com/img/products/ironpdf-logo-text-dotnet.svg'>
<h1>Iron Software</h1>"
Dim renderer As New ChromePdfRenderer()
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf("<h1>Watermark</h1>")
' Apply watermark with 45 degrees rotation and 70% opacity
pdf.ApplyWatermark(watermarkHtml, rotation:=30, opacity:=90)
pdf.SaveAs("watermarkOpacity&Rotation.pdf")

Características avanzadas de APITemplate
Generación de plantillas dinámicas
using System;
using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
namespace csharp
{
class ReturnContent
{
public string download_url { get; set; }
public string status { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
var api_key = "a9e4MjA2NTg6MTc3Njk6Uk1ZSzVjTWs1T3d6VE9Mdw=";
var template_id = "bf077b23b4a407ae";
var url = $"https://rest.apitemplate.io/v2/create-pdf?template_id={template_id}";
var data = new
{
date = "15/05/2022",
invoice_no = "435568799",
sender_address1 = "3244 Jurong Drive",
sender_address2 = "Falmouth Maine 1703",
sender_phone = "255-781-6789",
sender_email = "dev@ironsoftware.com",
rece_addess1 = "2354 Lakeside Drive",
rece_addess2 = "New York 234562 ",
rece_phone = "34333-84-223",
rece_email = "info@ironsoftware.com",
items = new[]
{
new { item_name = "Oil", unit = 1, unit_price = 100, total = 100 },
new { item_name = "Rice", unit = 2, unit_price = 200, total = 400 },
new { item_name = "Orange", unit = 7, unit_price = 20, total = 1400 }
},
total = "total",
footer_email = "info@ironsoftware.com"
};
var json_content = JsonSerializer.Serialize(data);
var buffer = System.Text.Encoding.UTF8.GetBytes(json_content);
var byteContent = new ByteArrayContent(buffer);
Console.WriteLine(json_content);
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-KEY", api_key);
var response = await client.PostAsync(url, byteContent);
var ret = await response.Content.ReadAsStringAsync();
var returnContent = JsonSerializer.Deserialize<ReturnContent>(ret);
if (returnContent.status == "success")
{
Console.WriteLine($"Downloading {returnContent.download_url}...");
var download_response = await client.GetAsync(returnContent.download_url);
using (var stream = await download_response.Content.ReadAsStreamAsync())
{
var fileInfo = new FileInfo(@"F:/generated_document.pdf");
using (var fileStream = fileInfo.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
Console.WriteLine("PDF file has been downloaded and saved as 'generated_document.pdf'");
}
}
}
}
using System;
using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
namespace csharp
{
class ReturnContent
{
public string download_url { get; set; }
public string status { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
var api_key = "a9e4MjA2NTg6MTc3Njk6Uk1ZSzVjTWs1T3d6VE9Mdw=";
var template_id = "bf077b23b4a407ae";
var url = $"https://rest.apitemplate.io/v2/create-pdf?template_id={template_id}";
var data = new
{
date = "15/05/2022",
invoice_no = "435568799",
sender_address1 = "3244 Jurong Drive",
sender_address2 = "Falmouth Maine 1703",
sender_phone = "255-781-6789",
sender_email = "dev@ironsoftware.com",
rece_addess1 = "2354 Lakeside Drive",
rece_addess2 = "New York 234562 ",
rece_phone = "34333-84-223",
rece_email = "info@ironsoftware.com",
items = new[]
{
new { item_name = "Oil", unit = 1, unit_price = 100, total = 100 },
new { item_name = "Rice", unit = 2, unit_price = 200, total = 400 },
new { item_name = "Orange", unit = 7, unit_price = 20, total = 1400 }
},
total = "total",
footer_email = "info@ironsoftware.com"
};
var json_content = JsonSerializer.Serialize(data);
var buffer = System.Text.Encoding.UTF8.GetBytes(json_content);
var byteContent = new ByteArrayContent(buffer);
Console.WriteLine(json_content);
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-KEY", api_key);
var response = await client.PostAsync(url, byteContent);
var ret = await response.Content.ReadAsStringAsync();
var returnContent = JsonSerializer.Deserialize<ReturnContent>(ret);
if (returnContent.status == "success")
{
Console.WriteLine($"Downloading {returnContent.download_url}...");
var download_response = await client.GetAsync(returnContent.download_url);
using (var stream = await download_response.Content.ReadAsStreamAsync())
{
var fileInfo = new FileInfo(@"F:/generated_document.pdf");
using (var fileStream = fileInfo.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
Console.WriteLine("PDF file has been downloaded and saved as 'generated_document.pdf'");
}
}
}
}
Imports System
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Namespace csharp
Friend Class ReturnContent
Public Property download_url() As String
Public Property status() As String
End Class
Friend Class Program
Shared Async Function Main(ByVal args() As String) As Task
Dim api_key = "a9e4MjA2NTg6MTc3Njk6Uk1ZSzVjTWs1T3d6VE9Mdw="
Dim template_id = "bf077b23b4a407ae"
Dim url = $"https://rest.apitemplate.io/v2/create-pdf?template_id={template_id}"
Dim data = New With {
Key .date = "15/05/2022",
Key .invoice_no = "435568799",
Key .sender_address1 = "3244 Jurong Drive",
Key .sender_address2 = "Falmouth Maine 1703",
Key .sender_phone = "255-781-6789",
Key .sender_email = "dev@ironsoftware.com",
Key .rece_addess1 = "2354 Lakeside Drive",
Key .rece_addess2 = "New York 234562 ",
Key .rece_phone = "34333-84-223",
Key .rece_email = "info@ironsoftware.com",
Key .items = {
New With {
Key .item_name = "Oil",
Key .unit = 1,
Key .unit_price = 100,
Key .total = 100
},
New With {
Key .item_name = "Rice",
Key .unit = 2,
Key .unit_price = 200,
Key .total = 400
},
New With {
Key .item_name = "Orange",
Key .unit = 7,
Key .unit_price = 20,
Key .total = 1400
}
},
Key .total = "total",
Key .footer_email = "info@ironsoftware.com"
}
Dim json_content = JsonSerializer.Serialize(data)
Dim buffer = System.Text.Encoding.UTF8.GetBytes(json_content)
Dim byteContent = New ByteArrayContent(buffer)
Console.WriteLine(json_content)
Dim client = New HttpClient()
client.DefaultRequestHeaders.Add("X-API-KEY", api_key)
Dim response = Await client.PostAsync(url, byteContent)
Dim ret = Await response.Content.ReadAsStringAsync()
Dim returnContent = JsonSerializer.Deserialize(Of ReturnContent)(ret)
If returnContent.status = "success" Then
Console.WriteLine($"Downloading {returnContent.download_url}...")
Dim download_response = Await client.GetAsync(returnContent.download_url)
Using stream = Await download_response.Content.ReadAsStreamAsync()
Dim fileInfo As New FileInfo("F:/generated_document.pdf")
Using fileStream = fileInfo.OpenWrite()
Await stream.CopyToAsync(fileStream)
End Using
End Using
Console.WriteLine("PDF file has been downloaded and saved as 'generated_document.pdf'")
End If
End Function
End Class
End Namespace

La API de APITemplate admite componentes dinámicos.

Generación de PDF a granel
La función de generación masiva de PDF de APITemplate es una ventaja significativa cuando se trata de crear múltiples documentos en una sola llamada API.
using System;
using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace csharp
{
class ReturnContent
{
public string download_url { get; set; }
public string status { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
var api_key = "a9e4MjA2NTg6MTc3Njk6Uk1ZSzVjTWs1T3d6VE9Mdw=";
var template_id = "d4f77b23b4ab09fa";
var url = $"https://rest.apitemplate.io/v2/create-pdf?template_id={template_id}";
var membershipCards = new List<object>
{
new { name = "Iron Dev 1", email = "dev1@ironsoftware.com", membership_id = "M001", expiry_date = "2024-12-31" },
new { name = "Iron Dev 2", email = "dev2@ironsoftware.com", membership_id = "M002", expiry_date = "2025-06-30" },
new { name = "Iron Dev 3", email = "dev3@ironsoftware.com", membership_id = "M003", expiry_date = "2024-09-15" }
};
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-KEY", api_key);
for (int i = 0; i < membershipCards.Count; i++)
{
var json_content = JsonSerializer.Serialize(membershipCards[i]);
var buffer = System.Text.Encoding.UTF8.GetBytes(json_content);
var byteContent = new ByteArrayContent(buffer);
Console.WriteLine($"Creating PDF for {((dynamic)membershipCards[i]).name}...");
var response = await client.PostAsync(url, byteContent);
var ret = await response.Content.ReadAsStringAsync();
var returnContent = JsonSerializer.Deserialize<ReturnContent>(ret);
if (returnContent.status == "success")
{
Console.WriteLine($"Downloading {returnContent.download_url}...");
var download_response = await client.GetAsync(returnContent.download_url);
using (var stream = await download_response.Content.ReadAsStreamAsync())
{
var fileInfo = new FileInfo($"F:/membership_card_{i + 1}.pdf");
using (var fileStream = fileInfo.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
Console.WriteLine($"PDF file has been downloaded and saved as 'membership_card_{i + 1}.pdf'");
}
else
{
Console.WriteLine($"Failed to create PDF for {((dynamic)membershipCards[i]).name}. Status: {returnContent.status}");
}
}
}
}
}
using System;
using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace csharp
{
class ReturnContent
{
public string download_url { get; set; }
public string status { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
var api_key = "a9e4MjA2NTg6MTc3Njk6Uk1ZSzVjTWs1T3d6VE9Mdw=";
var template_id = "d4f77b23b4ab09fa";
var url = $"https://rest.apitemplate.io/v2/create-pdf?template_id={template_id}";
var membershipCards = new List<object>
{
new { name = "Iron Dev 1", email = "dev1@ironsoftware.com", membership_id = "M001", expiry_date = "2024-12-31" },
new { name = "Iron Dev 2", email = "dev2@ironsoftware.com", membership_id = "M002", expiry_date = "2025-06-30" },
new { name = "Iron Dev 3", email = "dev3@ironsoftware.com", membership_id = "M003", expiry_date = "2024-09-15" }
};
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-KEY", api_key);
for (int i = 0; i < membershipCards.Count; i++)
{
var json_content = JsonSerializer.Serialize(membershipCards[i]);
var buffer = System.Text.Encoding.UTF8.GetBytes(json_content);
var byteContent = new ByteArrayContent(buffer);
Console.WriteLine($"Creating PDF for {((dynamic)membershipCards[i]).name}...");
var response = await client.PostAsync(url, byteContent);
var ret = await response.Content.ReadAsStringAsync();
var returnContent = JsonSerializer.Deserialize<ReturnContent>(ret);
if (returnContent.status == "success")
{
Console.WriteLine($"Downloading {returnContent.download_url}...");
var download_response = await client.GetAsync(returnContent.download_url);
using (var stream = await download_response.Content.ReadAsStreamAsync())
{
var fileInfo = new FileInfo($"F:/membership_card_{i + 1}.pdf");
using (var fileStream = fileInfo.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
Console.WriteLine($"PDF file has been downloaded and saved as 'membership_card_{i + 1}.pdf'");
}
else
{
Console.WriteLine($"Failed to create PDF for {((dynamic)membershipCards[i]).name}. Status: {returnContent.status}");
}
}
}
}
}
'INSTANT VB NOTE: 'Option Strict Off' is used here since dynamic typing is used:
Option Strict Off
Imports System
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Imports System.Collections.Generic
Namespace csharp
Friend Class ReturnContent
Public Property download_url() As String
Public Property status() As String
End Class
Friend Class Program
Shared Async Function Main(ByVal args() As String) As Task
Dim api_key = "a9e4MjA2NTg6MTc3Njk6Uk1ZSzVjTWs1T3d6VE9Mdw="
Dim template_id = "d4f77b23b4ab09fa"
Dim url = $"https://rest.apitemplate.io/v2/create-pdf?template_id={template_id}"
Dim membershipCards = New List(Of Object) From {
New With {
Key .name = "Iron Dev 1",
Key .email = "dev1@ironsoftware.com",
Key .membership_id = "M001",
Key .expiry_date = "2024-12-31"
},
New With {
Key .name = "Iron Dev 2",
Key .email = "dev2@ironsoftware.com",
Key .membership_id = "M002",
Key .expiry_date = "2025-06-30"
},
New With {
Key .name = "Iron Dev 3",
Key .email = "dev3@ironsoftware.com",
Key .membership_id = "M003",
Key .expiry_date = "2024-09-15"
}
}
Dim client = New HttpClient()
client.DefaultRequestHeaders.Add("X-API-KEY", api_key)
For i As Integer = 0 To membershipCards.Count - 1
Dim json_content = JsonSerializer.Serialize(membershipCards(i))
Dim buffer = System.Text.Encoding.UTF8.GetBytes(json_content)
Dim byteContent = New ByteArrayContent(buffer)
'INSTANT VB NOTE: In the following line, Instant VB substituted 'Object' for 'dynamic' - this will work in VB with Option Strict Off:
Console.WriteLine($"Creating PDF for {CType(membershipCards(i), Object).name}...")
Dim response = Await client.PostAsync(url, byteContent)
Dim ret = Await response.Content.ReadAsStringAsync()
Dim returnContent = JsonSerializer.Deserialize(Of ReturnContent)(ret)
If returnContent.status = "success" Then
Console.WriteLine($"Downloading {returnContent.download_url}...")
Dim download_response = Await client.GetAsync(returnContent.download_url)
Using stream = Await download_response.Content.ReadAsStreamAsync()
Dim fileInfo As New FileInfo($"F:/membership_card_{i + 1}.pdf")
Using fileStream = fileInfo.OpenWrite()
Await stream.CopyToAsync(fileStream)
End Using
End Using
Console.WriteLine($"PDF file has been downloaded and saved as 'membership_card_{i + 1}.pdf'")
Else
'INSTANT VB NOTE: In the following line, Instant VB substituted 'Object' for 'dynamic' - this will work in VB with Option Strict Off:
Console.WriteLine($"Failed to create PDF for {CType(membershipCards(i), Object).name}. Status: {returnContent.status}")
End If
Next i
End Function
End Class
End Namespace

Generación de imágenes
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace csharp
{
class ReturnContent
{
public string download_url { get; set; }
public string status { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
var api_key = "a9e4MjA2NTg6MTc3Njk6Uk1ZSzVjTWs1T3d6VE9Mdw=";
var template_id = "f4377b23b4aeeed0";
var url = $"https://rest.apitemplate.io/v2/create-image?template_id={template_id}";
var json_content = @"{
""overrides"": [
{
""name"": ""rect_image_bg"",
""stroke"": ""grey"",
""src"": ""https://images.unsplash.com/photo-1542401886-65d6c61db217?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxOTc1MDZ8MHwxfHNlYXJjaHwzfHxkZXNlcnR8ZW58MHwwfHx8MTYyMTUxOTI2OA&ixlib=rb-1.2.1&q=80&w=1080""
},
{
""name"": ""rect_container"",
""stroke"": ""grey"",
""backgroundColor"": ""rgba(255, 255, 255, 0.62)""
},
{
""name"": ""text_quote"",
""text"": ""Just witnessed the most breathtaking sunset over the Sahara. The way the light dances on the dunes, painting the sky in shades of orange and purple, is truly magical. Nature's artistry at its finest! #DesertAdventures #SaharaSkies"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#414141""
},
{
""name"": ""text_footer"",
""text"": ""2024-07-30 - Twitter - iPhone 16 Pro"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#666666""
},
{
""name"": ""circle_profile"",
""stroke"": ""grey"",
""src"": ""https://images.unsplash.com/photo-1520998116484-6eeb2f72b5b9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxOTc1MDZ8MHwxfHNlYXJjaHw2Mnx8aGFwcHl8ZW58MHwwfHx8MTYyMTY5MjkwNw&ixlib=rb-1.2.1&q=80&w=1080""
},
{
""name"": ""text_name"",
""text"": ""Sarah Wanderlust"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#4E4E4E""
},
{
""name"": ""text_twitter"",
""text"": ""@sarahexplores"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#4E4E4E""
}
]
}";
var buffer = System.Text.Encoding.UTF8.GetBytes(json_content);
var byteContent = new ByteArrayContent(buffer);
Console.WriteLine(json_content);
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-KEY", api_key);
var response = await client.PostAsync(url, byteContent);
var ret = await response.Content.ReadAsStringAsync();
var returnContent = JsonSerializer.Deserialize<ReturnContent>(ret);
if (returnContent.status == "success")
{
Console.WriteLine($"Downloading {returnContent.download_url}...");
var download_response = await client.GetAsync(returnContent.download_url);
using (var stream = await download_response.Content.ReadAsStreamAsync())
{
var fileInfo = new FileInfo("image.jpeg");
using (var fileStream = fileInfo.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
}
}
}
}
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace csharp
{
class ReturnContent
{
public string download_url { get; set; }
public string status { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
var api_key = "a9e4MjA2NTg6MTc3Njk6Uk1ZSzVjTWs1T3d6VE9Mdw=";
var template_id = "f4377b23b4aeeed0";
var url = $"https://rest.apitemplate.io/v2/create-image?template_id={template_id}";
var json_content = @"{
""overrides"": [
{
""name"": ""rect_image_bg"",
""stroke"": ""grey"",
""src"": ""https://images.unsplash.com/photo-1542401886-65d6c61db217?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxOTc1MDZ8MHwxfHNlYXJjaHwzfHxkZXNlcnR8ZW58MHwwfHx8MTYyMTUxOTI2OA&ixlib=rb-1.2.1&q=80&w=1080""
},
{
""name"": ""rect_container"",
""stroke"": ""grey"",
""backgroundColor"": ""rgba(255, 255, 255, 0.62)""
},
{
""name"": ""text_quote"",
""text"": ""Just witnessed the most breathtaking sunset over the Sahara. The way the light dances on the dunes, painting the sky in shades of orange and purple, is truly magical. Nature's artistry at its finest! #DesertAdventures #SaharaSkies"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#414141""
},
{
""name"": ""text_footer"",
""text"": ""2024-07-30 - Twitter - iPhone 16 Pro"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#666666""
},
{
""name"": ""circle_profile"",
""stroke"": ""grey"",
""src"": ""https://images.unsplash.com/photo-1520998116484-6eeb2f72b5b9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxOTc1MDZ8MHwxfHNlYXJjaHw2Mnx8aGFwcHl8ZW58MHwwfHx8MTYyMTY5MjkwNw&ixlib=rb-1.2.1&q=80&w=1080""
},
{
""name"": ""text_name"",
""text"": ""Sarah Wanderlust"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#4E4E4E""
},
{
""name"": ""text_twitter"",
""text"": ""@sarahexplores"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#4E4E4E""
}
]
}";
var buffer = System.Text.Encoding.UTF8.GetBytes(json_content);
var byteContent = new ByteArrayContent(buffer);
Console.WriteLine(json_content);
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-KEY", api_key);
var response = await client.PostAsync(url, byteContent);
var ret = await response.Content.ReadAsStringAsync();
var returnContent = JsonSerializer.Deserialize<ReturnContent>(ret);
if (returnContent.status == "success")
{
Console.WriteLine($"Downloading {returnContent.download_url}...");
var download_response = await client.GetAsync(returnContent.download_url);
using (var stream = await download_response.Content.ReadAsStreamAsync())
{
var fileInfo = new FileInfo("image.jpeg");
using (var fileStream = fileInfo.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
}
}
}
}
Imports System
Imports System.IO
Imports System.Net.Http
Imports System.Text
Imports System.Text.Json
Imports System.Threading.Tasks
Namespace csharp
Friend Class ReturnContent
Public Property download_url() As String
Public Property status() As String
End Class
Friend Class Program
Shared Async Function Main(ByVal args() As String) As Task
Dim api_key = "a9e4MjA2NTg6MTc3Njk6Uk1ZSzVjTWs1T3d6VE9Mdw="
Dim template_id = "f4377b23b4aeeed0"
Dim url = $"https://rest.apitemplate.io/v2/create-image?template_id={template_id}"
Dim json_content = "{
""overrides"": [
{
""name"": ""rect_image_bg"",
""stroke"": ""grey"",
""src"": ""https://images.unsplash.com/photo-1542401886-65d6c61db217?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxOTc1MDZ8MHwxfHNlYXJjaHwzfHxkZXNlcnR8ZW58MHwwfHx8MTYyMTUxOTI2OA&ixlib=rb-1.2.1&q=80&w=1080""
},
{
""name"": ""rect_container"",
""stroke"": ""grey"",
""backgroundColor"": ""rgba(255, 255, 255, 0.62)""
},
{
""name"": ""text_quote"",
""text"": ""Just witnessed the most breathtaking sunset over the Sahara. The way the light dances on the dunes, painting the sky in shades of orange and purple, is truly magical. Nature's artistry at its finest! #DesertAdventures #SaharaSkies"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#414141""
},
{
""name"": ""text_footer"",
""text"": ""2024-07-30 - Twitter - iPhone 16 Pro"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#666666""
},
{
""name"": ""circle_profile"",
""stroke"": ""grey"",
""src"": ""https://images.unsplash.com/photo-1520998116484-6eeb2f72b5b9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxOTc1MDZ8MHwxfHNlYXJjaHw2Mnx8aGFwcHl8ZW58MHwwfHx8MTYyMTY5MjkwNw&ixlib=rb-1.2.1&q=80&w=1080""
},
{
""name"": ""text_name"",
""text"": ""Sarah Wanderlust"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#4E4E4E""
},
{
""name"": ""text_twitter"",
""text"": ""@sarahexplores"",
""textBackgroundColor"": ""rgba(246, 243, 243, 0)"",
""color"": ""#4E4E4E""
}
]
}"
Dim buffer = System.Text.Encoding.UTF8.GetBytes(json_content)
Dim byteContent = New ByteArrayContent(buffer)
Console.WriteLine(json_content)
Dim client = New HttpClient()
client.DefaultRequestHeaders.Add("X-API-KEY", api_key)
Dim response = Await client.PostAsync(url, byteContent)
Dim ret = Await response.Content.ReadAsStringAsync()
Dim returnContent = JsonSerializer.Deserialize(Of ReturnContent)(ret)
If returnContent.status = "success" Then
Console.WriteLine($"Downloading {returnContent.download_url}...")
Dim download_response = Await client.GetAsync(returnContent.download_url)
Using stream = Await download_response.Content.ReadAsStreamAsync()
Dim fileInfo As New FileInfo("image.jpeg")
Using fileStream = fileInfo.OpenWrite()
Await stream.CopyToAsync(fileStream)
End Using
End Using
End If
End Function
End Class
End Namespace

Generación de código QR
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace csharp
{
class ReturnContent
{
public string download_url { get; set; }
public string status { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
var api_key = "API-Key";
var template_id = "Template-Key";
var url = $"https://rest.apitemplate.io/v2/create-image?template_id={template_id}";
var json_content = @"{
""overrides"": [
{
""name"": ""background-color"",
""stroke"": ""grey"",
""backgroundColor"": ""#FFFFFF""
},
{
""name"": ""qr_1"",
""content"": ""http://ironpdf.com/"",
""backgroundColor"": ""white"",
""color"": ""#000000""
}
]
}";
var buffer = System.Text.Encoding.UTF8.GetBytes(json_content);
var byteContent = new ByteArrayContent(buffer);
//Console.WriteLine(json_content);
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-KEY", api_key);
var response = await client.PostAsync(url, byteContent);
var ret = await response.Content.ReadAsStringAsync();
var returnContent = JsonSerializer.Deserialize<ReturnContent>(ret);
Console.WriteLine(returnContent.status);
if (returnContent.status == "success")
{
Console.WriteLine($"Downloading {returnContent.download_url}...");
var download_response = await client.GetAsync(returnContent.download_url);
using (var stream = await download_response.Content.ReadAsStreamAsync())
{
var fileInfo = new FileInfo(@"F:/QRimage.jpeg");
using (var fileStream = fileInfo.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
}
}
}
}
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace csharp
{
class ReturnContent
{
public string download_url { get; set; }
public string status { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
var api_key = "API-Key";
var template_id = "Template-Key";
var url = $"https://rest.apitemplate.io/v2/create-image?template_id={template_id}";
var json_content = @"{
""overrides"": [
{
""name"": ""background-color"",
""stroke"": ""grey"",
""backgroundColor"": ""#FFFFFF""
},
{
""name"": ""qr_1"",
""content"": ""http://ironpdf.com/"",
""backgroundColor"": ""white"",
""color"": ""#000000""
}
]
}";
var buffer = System.Text.Encoding.UTF8.GetBytes(json_content);
var byteContent = new ByteArrayContent(buffer);
//Console.WriteLine(json_content);
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-KEY", api_key);
var response = await client.PostAsync(url, byteContent);
var ret = await response.Content.ReadAsStringAsync();
var returnContent = JsonSerializer.Deserialize<ReturnContent>(ret);
Console.WriteLine(returnContent.status);
if (returnContent.status == "success")
{
Console.WriteLine($"Downloading {returnContent.download_url}...");
var download_response = await client.GetAsync(returnContent.download_url);
using (var stream = await download_response.Content.ReadAsStreamAsync())
{
var fileInfo = new FileInfo(@"F:/QRimage.jpeg");
using (var fileStream = fileInfo.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
}
}
}
}
Imports System
Imports System.IO
Imports System.Net.Http
Imports System.Text
Imports System.Text.Json
Imports System.Threading.Tasks
Namespace csharp
Friend Class ReturnContent
Public Property download_url() As String
Public Property status() As String
End Class
Friend Class Program
Shared Async Function Main(ByVal args() As String) As Task
Dim api_key = "API-Key"
Dim template_id = "Template-Key"
Dim url = $"https://rest.apitemplate.io/v2/create-image?template_id={template_id}"
Dim json_content = "{
""overrides"": [
{
""name"": ""background-color"",
""stroke"": ""grey"",
""backgroundColor"": ""#FFFFFF""
},
{
""name"": ""qr_1"",
""content"": ""http://ironpdf.com/"",
""backgroundColor"": ""white"",
""color"": ""#000000""
}
]
}"
Dim buffer = System.Text.Encoding.UTF8.GetBytes(json_content)
Dim byteContent = New ByteArrayContent(buffer)
'Console.WriteLine(json_content);
Dim client = New HttpClient()
client.DefaultRequestHeaders.Add("X-API-KEY", api_key)
Dim response = Await client.PostAsync(url, byteContent)
Dim ret = Await response.Content.ReadAsStringAsync()
Dim returnContent = JsonSerializer.Deserialize(Of ReturnContent)(ret)
Console.WriteLine(returnContent.status)
If returnContent.status = "success" Then
Console.WriteLine($"Downloading {returnContent.download_url}...")
Dim download_response = Await client.GetAsync(returnContent.download_url)
Using stream = Await download_response.Content.ReadAsStreamAsync()
Dim fileInfo As New FileInfo("F:/QRimage.jpeg")
Using fileStream = fileInfo.OpenWrite()
Await stream.CopyToAsync(fileStream)
End Using
End Using
End If
End Function
End Class
End Namespace

Decisión técnica: cuándo usar IronPDF vs APITemplate en el ecosistema de facturación español
El ecosistema fiscal español de 2026 tiene una estructura de capas que determina directamente qué tipo de componente PDF necesita cada proyecto:
Nivel 1 — Facturación B2C y TPV (VeriFactu obligatorio)
- Facturación a consumidores finales en retail, hostelería, servicios
- Requsitos: leyenda
VERI*FACTU, QR AEAT, hash SHA-256 encadenado - Tecnología: IronPDF genera el PDF offline → el ISV genera el registro VeriFactu → envío opcional al portal AEAT
- Prohibido: servicios cloud que transmitan datos fiscales fuera del perímetro del software certificado
Nivel 2 — Facturación B2B (Crea y Crece, mandatorio por fases)
- Facturación entre empresas con NIF/CIF: transición a Facturae XML con PDF/A adjunto
- Plazos: empresas >8M€ facturación en 2027; resto en 2028
- Tecnología: IronPDF genera el PDF/A-3 que se adjunta al XML Facturae
- APITemplate: sin soporte para PDF/A ni para embedding de XML en el PDF
Nivel 3 — Facturación B2G (FACe, obligatorio)
- Facturas a la Administración General del Estado y Comunidades Autónomas
- Formato: Facturae 3.2.x, firmado con FNMT-RCM Clase 3, enviado a FACe
- Tecnología: IronPDF genera el PDF adjunto + firma PAdES con FNMT-RCM
- APITemplate: incompatible — sin firma PAdES ni certificados FNMT-RCM
Nivel 4 — TicketBAI (Bizkaia, Gipuzkoa, Araba)
- Tiques y facturas simplificadas en el País Vasco
- Requiere: fichero
.tbaiXML firmado + PDF de representación con QR foral - Tecnología: IronPDF genera el PDF de representación con el QR TicketBAI insertado
- APITemplate: sin soporte para integración con sistemas TicketBAI
Para los tres primeros niveles y el cuarto, APITemplate es técnicamente inadecuado no por limitaciones de calidad de PDF, sino por su arquitectura cloud-first que contradice los requisitos de integridad y disponibilidad offline del marco regulatorio español.
Documentación, soporte y cumplimiento
IronPDF
Documentación
La documentación de IronPDF está disponible en https://ironpdf.com/docs/, y es bastante extensa. Aspectos clave que he apreciado:
-
Guía para comenzar: La documentación comienza con una guía clara y paso a paso sobre cómo instalar IronPDF a través de NuGet y crear tu primer PDF.
-
Referencia de API: La referencia de API es exhaustiva, cubriendo todas las clases y métodos.
-
Ejemplos de código: La documentación incluye numerosos fragmentos de código.
-
Tutoriales y guías prácticas: IronPDF proporciona tutoriales detallados para tareas comunes como crear PDFs desde HTML, agregar marcas de agua o trabajar con formularios.
- Sección de solución de problemas: La documentación incluye una sección de solución de problemas que aborda cuestiones comunes.
Soporte
El contacto de IronPDF ha sido receptivo y los canales de soporte han sido extremadamente útiles:
- Soporte por correo electrónico: He utilizado su soporte por correo electrónico para problemas complejos.
- Foro comunitario: IronPDF mantiene un foro comunitario donde los desarrolladores pueden hacer preguntas y compartir soluciones.
- Actualizaciones regulares: IronPDF lanza actualizaciones con frecuencia que incluyen correcciones de errores y nuevas funciones.
- Guías de migración: Cuando ha habido cambios drásticos entre versiones principales, IronPDF ha proporcionado guías de migración.
Plantilla API
Documentación
-
Referencia de API: La documentación proporciona una referencia clara de API, detallando todos los puntos finales disponibles, los parámetros requeridos y los formatos de respuesta.
- Autenticación: Hay una sección dedicada a la autenticación, que explica claramente cómo usar las claves de API.
Soporte
-
Soporte por correo electrónico: Cuando he tenido problemas o preguntas, he utilizado su soporte por correo electrónico.
- Sección de preguntas frecuentes: La documentación incluye una sección de FAQ que aborda preguntas comunes.
IronPDF con Bootstrap 5 para dashboards analíticos conformes con eIDAS
Una de las aplicaciones más demandadas en entornos empresariales españoles es la generación de informes analíticos PDF que se pueden firmar digitalmente bajo el marco eIDAS. IronPDF con Bootstrap 5 resuelve esto elegantemente:
using IronPdf;
var renderer = new ChromePdfRenderer();
string analyticsDashboard = @"
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<style>
.metric-card {
transition: transform 0.2s;
border-left: 4px solid #0d6efd;
}
.metric-card:hover { transform: translateY(-5px); }
.trend-up { color: #198754; }
.trend-down { color: #dc3545; }
@media print {
.metric-card { page-break-inside: avoid; }
}
</style>
</head>
<body class='bg-light'>
<div class='container py-5'>
<div class='row mb-4'>
<div class='col-12'>
<div class='card shadow-sm'>
<div class='card-body'>
<h1 class='display-6 mb-0'>Performance Dashboard</h1>
<p class='text-muted mb-0'>Q4 2024 Analytics Overview</p>
</div>
</div>
</div>
</div>
<div class='row g-4 mb-4'>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Total Revenue</h6>
<h2 class='display-6 mb-3'>$2.4M</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-success' style='width: 85%'></div>
</div>
<small class='trend-up'>↑ 18% from last quarter</small>
</div>
</div>
</div>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Active Users</h6>
<h2 class='display-6 mb-3'>48,592</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-primary' style='width: 72%'></div>
</div>
<small class='trend-up'>↑ 12% growth rate</small>
</div>
</div>
</div>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Conversion Rate</h6>
<h2 class='display-6 mb-3'>3.8%</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-warning' style='width: 38%'></div>
</div>
<small class='trend-down'>↓ 2% needs attention</small>
</div>
</div>
</div>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Satisfaction</h6>
<h2 class='display-6 mb-3'>4.7/5</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-success' style='width: 94%'></div>
</div>
<small class='trend-up'>↑ Excellent rating</small>
</div>
</div>
</div>
</div>
<div class='card shadow-sm'>
<div class='card-body'>
<h5 class='card-title mb-3'>Top Performing Products</h5>
<div class='table-responsive'>
<table class='table table-hover'>
<thead class='table-light'>
<tr>
<th>Product</th>
<th>Revenue</th>
<th>Units</th>
<th>Trend</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Enterprise Suite</strong></td>
<td>$1,240,000</td>
<td>2,150</td>
<td><span class='badge bg-success'>+22%</span></td>
</tr>
<tr>
<td><strong>Professional Plan</strong></td>
<td>$820,000</td>
<td>4,890</td>
<td><span class='badge bg-success'>+15%</span></td>
</tr>
<tr>
<td><strong>Starter Package</strong></td>
<td>$340,000</td>
<td>8,240</td>
<td><span class='badge bg-warning'>+5%</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(analyticsDashboard);
pdf.SaveAs("analytics-dashboard.pdf");
using IronPdf;
var renderer = new ChromePdfRenderer();
string analyticsDashboard = @"
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<style>
.metric-card {
transition: transform 0.2s;
border-left: 4px solid #0d6efd;
}
.metric-card:hover { transform: translateY(-5px); }
.trend-up { color: #198754; }
.trend-down { color: #dc3545; }
@media print {
.metric-card { page-break-inside: avoid; }
}
</style>
</head>
<body class='bg-light'>
<div class='container py-5'>
<div class='row mb-4'>
<div class='col-12'>
<div class='card shadow-sm'>
<div class='card-body'>
<h1 class='display-6 mb-0'>Performance Dashboard</h1>
<p class='text-muted mb-0'>Q4 2024 Analytics Overview</p>
</div>
</div>
</div>
</div>
<div class='row g-4 mb-4'>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Total Revenue</h6>
<h2 class='display-6 mb-3'>$2.4M</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-success' style='width: 85%'></div>
</div>
<small class='trend-up'>↑ 18% from last quarter</small>
</div>
</div>
</div>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Active Users</h6>
<h2 class='display-6 mb-3'>48,592</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-primary' style='width: 72%'></div>
</div>
<small class='trend-up'>↑ 12% growth rate</small>
</div>
</div>
</div>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Conversion Rate</h6>
<h2 class='display-6 mb-3'>3.8%</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-warning' style='width: 38%'></div>
</div>
<small class='trend-down'>↓ 2% needs attention</small>
</div>
</div>
</div>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Satisfaction</h6>
<h2 class='display-6 mb-3'>4.7/5</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-success' style='width: 94%'></div>
</div>
<small class='trend-up'>↑ Excellent rating</small>
</div>
</div>
</div>
</div>
<div class='card shadow-sm'>
<div class='card-body'>
<h5 class='card-title mb-3'>Top Performing Products</h5>
<div class='table-responsive'>
<table class='table table-hover'>
<thead class='table-light'>
<tr>
<th>Product</th>
<th>Revenue</th>
<th>Units</th>
<th>Trend</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Enterprise Suite</strong></td>
<td>$1,240,000</td>
<td>2,150</td>
<td><span class='badge bg-success'>+22%</span></td>
</tr>
<tr>
<td><strong>Professional Plan</strong></td>
<td>$820,000</td>
<td>4,890</td>
<td><span class='badge bg-success'>+15%</span></td>
</tr>
<tr>
<td><strong>Starter Package</strong></td>
<td>$340,000</td>
<td>8,240</td>
<td><span class='badge bg-warning'>+5%</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(analyticsDashboard);
pdf.SaveAs("analytics-dashboard.pdf");
Imports IronPdf
Dim renderer As New ChromePdfRenderer()
Dim analyticsDashboard As String = "
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<style>
.metric-card {
transition: transform 0.2s;
border-left: 4px solid #0d6efd;
}
.metric-card:hover { transform: translateY(-5px); }
.trend-up { color: #198754; }
.trend-down { color: #dc3545; }
@media print {
.metric-card { page-break-inside: avoid; }
}
</style>
</head>
<body class='bg-light'>
<div class='container py-5'>
<div class='row mb-4'>
<div class='col-12'>
<div class='card shadow-sm'>
<div class='card-body'>
<h1 class='display-6 mb-0'>Performance Dashboard</h1>
<p class='text-muted mb-0'>Q4 2024 Analytics Overview</p>
</div>
</div>
</div>
</div>
<div class='row g-4 mb-4'>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Total Revenue</h6>
<h2 class='display-6 mb-3'>$2.4M</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-success' style='width: 85%'></div>
</div>
<small class='trend-up'>↑ 18% from last quarter</small>
</div>
</div>
</div>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Active Users</h6>
<h2 class='display-6 mb-3'>48,592</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-primary' style='width: 72%'></div>
</div>
<small class='trend-up'>↑ 12% growth rate</small>
</div>
</div>
</div>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Conversion Rate</h6>
<h2 class='display-6 mb-3'>3.8%</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-warning' style='width: 38%'></div>
</div>
<small class='trend-down'>↓ 2% needs attention</small>
</div>
</div>
</div>
<div class='col-md-3'>
<div class='card metric-card shadow-sm h-100'>
<div class='card-body'>
<h6 class='text-muted text-uppercase mb-2'>Satisfaction</h6>
<h2 class='display-6 mb-3'>4.7/5</h2>
<div class='progress' style='height: 6px;'>
<div class='progress-bar bg-success' style='width: 94%'></div>
</div>
<small class='trend-up'>↑ Excellent rating</small>
</div>
</div>
</div>
</div>
<div class='card shadow-sm'>
<div class='card-body'>
<h5 class='card-title mb-3'>Top Performing Products</h5>
<div class='table-responsive'>
<table class='table table-hover'>
<thead class='table-light'>
<tr>
<th>Product</th>
<th>Revenue</th>
<th>Units</th>
<th>Trend</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Enterprise Suite</strong></td>
<td>$1,240,000</td>
<td>2,150</td>
<td><span class='badge bg-success'>+22%</span></td>
</tr>
<tr>
<td><strong>Professional Plan</strong></td>
<td>$820,000</td>
<td>4,890</td>
<td><span class='badge bg-success'>+15%</span></td>
</tr>
<tr>
<td><strong>Starter Package</strong></td>
<td>$340,000</td>
<td>8,240</td>
<td><span class='badge bg-warning'>+5%</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>"
Dim pdf = renderer.RenderHtmlAsPdf(analyticsDashboard)
pdf.SaveAs("analytics-dashboard.pdf")
Ventajas clave frente a APITemplate en entornos regulados:
- Procesamiento sin conexión sin dependencias externas, compatible con requisitos de aislamiento VeriFactu
- Soporte completo de Bootstrap 5 sin llamadas API externas ni configuración de plantillas
- Firma PAdES con certificados FNMT-RCM directamente desde el flujo de generación
- Residencia de datos en España (Azure Spain Central) conforme a exigencias AEPD/LOPDGDD
Licencias
Precios y licencias de IronPDF

- Procesamiento sin conexión sin dependencias externas
- Lite: con un precio de $999, este nivel es adecuado para un solo desarrollador que trabaja en un solo proyecto.
- Profesional: En $1,499, esta opción admite hasta 10 desarrolladores, 10 ubicaciones y 10 proyectos.
- Ilimitado: para $2,999, este nivel permite desarrolladores, ubicaciones y proyectos ilimitados.
Es una tarifa única, lo que la hace una opción económicamente predecible para proyectos de cumplimiento VeriFactu a largo plazo, a diferencia de los modelos de suscripción que incrementan el coste en función del volumen de facturas generadas.
Precios y licencias de APITemplate
- Plan Básico: a menudo comienza con un precio más bajo con una cantidad limitada de llamadas API por mes.
- Plan Estándar: Este plan incluye más llamadas API y funciones adicionales, dirigidas a empresas medianas.
- Plan Enterprise: diseñado para usuarios de gran volumen, este plan ofrece la mayor cantidad de llamadas API y funciones premium como soporte prioritario e integraciones personalizadas.
Diferencias clave
- Modelo de precios: IronPDF utiliza un modelo de tarifa única, que puede resultar rentable a largo plazo, especialmente para un uso continuo e intensivo en sistemas de facturación electrónica. APITemplate usa un enfoque basado en la cantidad de llamadas a la API, brindando flexibilidad pero incrementando el coste con el volumen.
- Soporte y actualizaciones: IronPDF incluye un año de soporte y actualizaciones con su compra, con opciones para extender este período. La licencia de APITemplate es sencilla, generalmente vinculada a un modelo de suscripción mensual o anual.
¿Por qué elegir IronPDF para proyectos con requisitos VeriFactu y Facturae?
IronPDF
using IronPdf;
var renderer = new ChromePdfRenderer();
var html = @"
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.header { color: #0066cc; font-size: 24px; }
.content { margin-top: 20px; }
</style>
</head>
<body>
<div class='header'>Dynamic Report</div>
<div class='content'>
<p>This report was generated on: <script>document.write(new Date().toLocaleString());</script></p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("IronPDF_report.pdf");
using IronPdf;
var renderer = new ChromePdfRenderer();
var html = @"
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.header { color: #0066cc; font-size: 24px; }
.content { margin-top: 20px; }
</style>
</head>
<body>
<div class='header'>Dynamic Report</div>
<div class='content'>
<p>This report was generated on: <script>document.write(new Date().toLocaleString());</script></p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("IronPDF_report.pdf");
Imports IronPdf
Private renderer = New ChromePdfRenderer()
Private html = "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.header { color: #0066cc; font-size: 24px; }
.content { margin-top: 20px; }
</style>
</head>
<body>
<div class='header'>Dynamic Report</div>
<div class='content'>
<p>This report was generated on: <script>document.write(new Date().toLocaleString());</script></p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
</body>
</html>"
Private pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("IronPDF_report.pdf")
Plantilla API
using System.Net.Http;
using System.Text.Json;
var apiKey = "your_api_key";
var templateId = "your_template_id";
var url = $"https://rest.apitemplate.io/v2/create-pdf?template_id={templateId}";
var data = new
{
header = "Dynamic Report",
content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
date = DateTime.Now.ToShortDateString()
};
var json = JsonSerializer.Serialize(data);
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-KEY", apiKey);
var response = await client.PostAsync(url, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("APITemplate_report.pdf", pdfBytes);
using System.Net.Http;
using System.Text.Json;
var apiKey = "your_api_key";
var templateId = "your_template_id";
var url = $"https://rest.apitemplate.io/v2/create-pdf?template_id={templateId}";
var data = new
{
header = "Dynamic Report",
content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
date = DateTime.Now.ToShortDateString()
};
var json = JsonSerializer.Serialize(data);
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-KEY", apiKey);
var response = await client.PostAsync(url, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("APITemplate_report.pdf", pdfBytes);
Imports System.Net.Http
Imports System.Text.Json
Private apiKey = "your_api_key"
Private templateId = "your_template_id"
Private url = $"https://rest.apitemplate.io/v2/create-pdf?template_id={templateId}"
Private data = New With {
Key .header = "Dynamic Report",
Key .content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
Key .date = DateTime.Now.ToShortDateString()
}
Private json = JsonSerializer.Serialize(data)
Private content = New StringContent(json, System.Text.Encoding.UTF8, "application/json")
Private client = New HttpClient()
client.DefaultRequestHeaders.Add("X-API-KEY", apiKey)
Dim response = Await client.PostAsync(url, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
File.WriteAllBytes("APITemplate_report.pdf", pdfBytes)
Diferencias clave para entornos de cumplimiento fiscal español
- Compatibilidad con JavaScript: IronPDF admite totalmente la ejecución de JavaScript dentro del HTML, lo que permite la generación de contenido dinámico (como la fecha actual o el hash VeriFactu calculado en tiempo de ejecución). APITemplate nube puede introducir latencia.
- Flexibilidad CSS: Con IronPDF, tienes control total sobre el estilo CSS directamente en tu HTML, incluyendo el layout de los bloques
VERI*FACTUy QR AEAT. - Rendimiento: IronPDF procesa el HTML localmente, lo que resulta en una generación de PDF más rápida, especialmente para documentos complejos de facturación.
- Capacidad sin conexión: IronPDF funciona sin conexión, fundamental para sistemas de facturación con requisitos de alta seguridad o entornos de producción aislados que exige el cumplimiento VeriFactu.
- Firma PAdES/eIDAS: IronPDF permite firmar documentos con certificados FNMT-RCM reconocidos por la AEAT para cumplimiento eIDAS; APITemplate no ofrece esta capacidad nativa.
- Protección LOPDGDD: IronPDF ofrece cifrado granular para proteger documentos con datos personales de contribuyentes (NIF, domicilio fiscal, datos económicos) bajo la LOPDGDD y el RGPD.
- Consistencia: IronPDF garantiza una representación consistente en diferentes entornos gracias a su motor de renderizado. La salida de APITemplate puede variar según la configuración del servidor.
Escenarios reales de ISVs españoles: VeriFactu, TicketBAI y SII
Para contextualizar la decisión entre IronPDF y APITemplate en el mercado español, analizo tres escenarios típicos que he encontrado en proyectos reales:
Caso 1: Software ERP para PYME industrial en el País Vasco (TicketBAI Bizkaia)
Una empresa de fabricación con sede en Bilbao (Bizkaia) necesitaba emitir tiques y facturas conformes con TicketBAI. El sistema debía:
- Generar el fichero
.tbaiXML firmado con la clave privada del certificado FNMT-RCM - Producir el PDF de la factura con el QR TicketBAI en la esquina inferior derecha
- Archivar el PDF de alta integridad durante el período de conservación fiscal de 5 años
Resultado: IronPDF se integró directamente en el pipeline de generación TicketBAI del ERP. El PDF se genera localmente, se inserta el QR del fichero .tbai firmado, y se guarda con cifrado en la base de datos documental. Con APITemplate, la transmisión de los datos del contribuyente (NIF, domicilio, importes) a servidores cloud hubiera requerido un análisis de LOPDGDD y la firma de un contrato de encargado bajo el Art. 28 RGPD con el proveedor SaaS.
Caso 2: Portal de facturación electrónica B2G (Facturae / FACe)
Una empresa de consultoría IT en Madrid desarrolló un portal para que sus clientes PYME presentaran facturas a la Administración General del Estado a través de FACe. Los requisitos incluían:
- Generar el PDF adjunto al fichero Facturae 3.2.x
- Aplicar firma PAdES-B conforme a eIDAS con el certificado de representación de la empresa (FNMT-RCM Clase 3)
- Formatear los importes en EUR con notación española:
1.234,56 € - Incluir el número de Modelo 347 del destinatario cuando supera el umbral de 3.005,06 € anuales
Resultado: IronPDF gestionó todo el flujo sin dependencias externas. La generación offline del PDF antes de la firma garantizó la integridad del documento conforme a los requisitos de FACe. APITemplate carece de soporte nativo para firma PAdES, lo que lo hace inadecuado para este escenario B2G.
Caso 3: Plataforma SaaS de facturación bajo VeriFactu con SII
Una startup fintech de Barcelona desarrolló una plataforma SaaS de facturación para autónomos y micropymes con facturación anual entre 300K€ y 1M€. Al superar los 6 millones de facturación de la plataforma, las facturas debían enviarse también al SII de la AEAT con periodicidad de 4 días. Los requisitos eran:
- Generar PDFs con la leyenda
VERI*FACTUy el QR de verificación AEAT - Calcular e insertar el hash SHA-256 del registro de alta para cada factura
- Gestionar los certificados FNMT-RCM de cada usuario (personas físicas con NIF clase
ES12345678Zo CIF claseB12345678) - Archivar documentos durante 4 años conforme al Código de Comercio
Resultado: IronPDF permitió multitenancy: cada tenant tiene su propio certificado FNMT-RCM cargado en el HSM o fichero .pfx, y la generación se ejecuta en el servidor de la startup sin transmitir datos fiscales a terceros. El coste total de propiedad con licencia perpetua IronPDF fue significativamente inferior al modelo de pago por uso de APITemplate a los 18 meses de operación.
Conclusión
Para equipos de desarrollo en España que trabajan con software de facturación sujeto a VeriFactu (RDL 8/2023), TicketBAI (Bizkaia, Gipuzkoa, Araba), Facturae/FACe o a los requisitos de protección de datos de la LOPDGDD, IronPDF es la opción técnicamente superior. Su operación local, su soporte de firma PAdES con certificados FNMT-RCM y su modelo de licencia de tarifa única lo hacen especialmente adecuado para proyectos de cumplimiento a largo plazo. APITemplate puede ser una solución válida para generación de documentos no regulados en entornos cloud, pero no cubre los requisitos de integridad, firma y residencia de datos que exige el marco regulatorio español.
Preguntas Frecuentes
¿Qué biblioteca PDF es más adecuada para cumplir con VeriFactu (RDL 8/2023) en aplicaciones C#?
IronPDF es más adecuado para el cumplimiento VeriFactu porque opera íntegramente en local, sin dependencias de servicios cloud externos. Permite insertar la leyenda VERI*FACTU, el QR AEAT con hash de integridad y firmar con certificados FNMT-RCM mediante PAdES. APITemplate, al ser un servicio cloud, requiere conectividad a internet constante, lo cual es incompatible con los requisitos de integridad de registros del RDL 8/2023.
¿Cómo genero facturas con el QR AEAT y la leyenda VERI*FACTU usando IronPDF en C#?
IronPDF permite generar facturas VeriFactu renderizando HTML que incluye el bloque QR AEAT (URL de verificación + hash SHA-256) y la leyenda VERI*FACTU mediante ChromePdfRenderer.RenderHtmlAsPdf(). El resultado se puede firmar digitalmente con certificados FNMT-RCM Clase 2/3 usando PdfSignature. El proceso es completamente local, sin llamadas a APIs externas.
¿Cómo protege IronPDF los documentos con datos personales bajo la LOPDGDD?
IronPDF incluye funciones de cifrado completas: contraseña de usuario (apertura), contraseña de propietario (edición/impresión) y permisos granulares (SecuritySettings). Para documentos que contengan NIF, domicilio fiscal o datos económicos protegidos bajo la LOPDGDD (art. 32 RGPD), se recomienda cifrar el PDF antes del archivado con pdf.Password y pdf.OwnerPassword.
¿IronPDF es compatible con la firma PAdES para facturas Facturae enviadas a FACe?
Sí, IronPDF permite aplicar firmas PAdES-B utilizando certificados FNMT-RCM Clase 2/3, que son los certificados reconocidos por la AEAT para la firma de facturas electrónicas enviadas a FACe. La clase IronPdf.Signing.PdfSignature acepta el fichero .pfx del certificado y el PIN, generando un PDF firmado conforme a eIDAS.
¿Qué diferencias hay entre IronPDF y APITemplate para proyectos con TicketBAI en el País Vasco?
Para proyectos con TicketBAI (Bizkaia, Gipuzkoa, Araba), IronPDF es preferible porque permite generar los PDFs de los tiques firmados localmente sin dependencias externas, y soporta los formatos de cabecera y pie de página específicos de cada territorio foral. APITemplate no tiene integración nativa con los esquemas .tbai y su arquitectura cloud complica la trazabilidad de integridad que exigen las Haciendas Forales.
¿Puedo usar IronPDF para rellenar automáticamente formularios fiscales como el Modelo 303 de IVA?
Sí, IronPDF incluye funciones de relleno de formularios PDF (PdfDocument.Form.FindFormField()) que permiten completar los campos del Modelo 303 de IVA, el Modelo 100 de IRPF u otros formularios de la AEAT a partir de datos del sistema contable, reduciendo errores de entrada manual durante las campañas de declaraciones.
¿Qué modelo de licencia es más adecuado para sistemas de facturación VeriFactu a largo plazo?
IronPDF ofrece una tarifa única de licencia (frente al modelo de suscripción de APITemplate), lo que lo hace más predecible en coste para proyectos de cumplimiento VeriFactu a largo plazo. Como el volumen de facturas generadas no afecta al precio de la licencia de IronPDF, resulta especialmente económico para empresas con alta frecuencia de emisión de facturas.
¿IronPDF funciona sin conexión a internet en entornos de producción aislados?
Sí, IronPDF opera completamente en local sin necesidad de conectividad a internet, lo cual es fundamental para sistemas de facturación VeriFactu desplegados en entornos de producción aislados o con requisitos de seguridad elevados. APITemplate siempre requiere conexión a internet, lo que lo hace incompatible con estos escenarios.



