De WebView2 a IronPDF: cumplimiento VeriFactu, TicketBAI y Facturae en .NET para España
Para los equipos de desarrollo .NET en España, WebView2 presenta una doble problemática: es técnicamente inadecuado para entornos de producción como servicio PDF, y es completamente incompatible con el marco regulatorio de facturación electrónica española. El Real Decreto-Ley 15/2025 (VeriFactu) exige que el software de facturación genere PDF con la leyenda VERI\*FACTU e incruste la cadena de huella encadenada — operaciones que WebView2 no puede realizar desde un servicio en segundo plano (requiere hilo STA con bomba de mensajes). Los proveedores de software de facturación se exponen a penalizaciones de hasta 150.000 € anuales ante la AEAT por incumplir estos requisitos.
TicketBAI —obligatorio en Bizkaia, Gipuzkoa y Araba— requiere firmar facturas con XAdES conforme a eIDAS y adjuntarlas como PDF firmado con PAdES usando certificados FNMT-RCM. WebView2 no implementa firma digital. La Ley Crea y Crece impone la factura electrónica B2B con Facturae XML incrustado en PDF/A-3 compatible con FACe, bajo el estándar EN 16931 y el perfil CIUS-ES. IronPDF cubre todas estas obligaciones y procesa documentos on-premise sin enviar datos a terceros, facilitando el cumplimiento con la LOPDGDD supervisada por la AEPD.
WebView2, el control de navegador Edge/Chromium integrable de Microsoft, está diseñado para aplicaciones de interfaz de usuario, no para la generación de PDF en producción. Esta guía proporciona una ruta de migración completa de WebView2 a IronPDF, con instrucciones paso a paso, comparaciones de código y un ejemplo de factura VeriFactu con datos en formato peninsular (NIF, EUR 1.234,56 €, IVA 21%).
Advertencia crítica: WebView2NO es adecuado para la generación de PDF
Antes de examinar la ruta de migración, los equipos de desarrollo deben entender por qué WebView2crea problemas significativos cuando se utiliza para la creación de PDF:
| Problema | Impacto | Gravedad |
|---|---|---|
| Fugas de memoria | WebView2tiene fugas de memoria bien documentadas en procesos de larga duración. Su servidor se bloqueará. | CRÍTICA |
| Sólo para Windows | Cero compatibilidad con Linux, macOS, Docker o entornos en la nube | CRÍTICA |
| Se requiere hilo de interfaz de usuario | Debe ejecutarse en un hilo STA con bomba de mensajes. No puede funcionar en servidores web o API. | CRÍTICA |
| No diseñado para PDF | PrintToPdfAsync es una idea de último momento, no una característica principal |
ALTA |
| Instable en Servicios | Bloqueos y cuelgues comunes en los servicios de Windows y los trabajadores en segundo plano | ALTA |
| Flujo asíncrono complejo | Eventos de navegación, devoluciones de llamada de finalización, condiciones de carrera | ALTA |
| Dependencia de tiempo de ejecución de Edge | Requiere que Edge WebView2Runtime esté instalado en el equipo de destino | MEDIO |
| Sin modo Headless | Crea siempre elementos de interfaz de usuario aunque estén ocultos | MEDIO |
| Rendimiento | Arranque lento, gran consumo de recursos | MEDIO |
| Sin Soporte Profesional | Microsoft no admite el caso de uso de generación de PDF | MEDIO |
Escenarios de fallos en el mundo real
Estos patrones de código provocan fallos de producción:
// DANGER: This code WILL cause problems in production
// Problema1: Memory leak - creates new WebView2for each PDF
public async Task<byte[]> GeneratePdf(string html) // Called 1000x/day = server crash
{
using var webView = new WebView2(); // Memory not fully released!
await webView.EnsureCoreWebView2Async();
webView.CoreWebView2.NavigateToString(html);
// ... memory accumulates until OOM
}
// Problema2: UI thread requirement - crashes in ASP.NET
public IActionResult GenerateReport() // FAILS - no STA thread
{
var webView = new WebView2(); // InvalidOperationException
}
// Problema3: Windows Service instability
public class PdfService : BackgroundService // Random crashes
{
protected override async Task ExecuteAsync(CancellationToken token)
{
// WebView2+ no message pump = hangs, crashes, undefined behavior
}
}
// DANGER: This code WILL cause problems in production
// Problema1: Memory leak - creates new WebView2for each PDF
public async Task<byte[]> GeneratePdf(string html) // Called 1000x/day = server crash
{
using var webView = new WebView2(); // Memory not fully released!
await webView.EnsureCoreWebView2Async();
webView.CoreWebView2.NavigateToString(html);
// ... memory accumulates until OOM
}
// Problema2: UI thread requirement - crashes in ASP.NET
public IActionResult GenerateReport() // FAILS - no STA thread
{
var webView = new WebView2(); // InvalidOperationException
}
// Problema3: Windows Service instability
public class PdfService : BackgroundService // Random crashes
{
protected override async Task ExecuteAsync(CancellationToken token)
{
// WebView2+ no message pump = hangs, crashes, undefined behavior
}
}
' DANGER: This code WILL cause problems in production
' Problema1: Memory leak - creates new WebView2 for each PDF
Public Async Function GeneratePdf(html As String) As Task(Of Byte()) ' Called 1000x/day = server crash
Using webView As New WebView2() ' Memory not fully released!
Await webView.EnsureCoreWebView2Async()
webView.CoreWebView2.NavigateToString(html)
' ... memory accumulates until OOM
End Using
End Function
' Problema2: UI thread requirement - crashes in ASP.NET
Public Function GenerateReport() As IActionResult ' FAILS - no STA thread
Dim webView As New WebView2() ' InvalidOperationException
' Additional code needed here to return an IActionResult
End Function
' Problema3: Windows Service instability
Public Class PdfService
Inherits BackgroundService ' Random crashes
Protected Overrides Async Function ExecuteAsync(token As CancellationToken) As Task
' WebView2+ no message pump = hangs, crashes, undefined behavior
End Function
End Class
IronPDFfrente a WebView2: comparación de características
Comprender las diferencias arquitectónicas ayuda a los responsables técnicos a evaluar la inversión en migración:
| Aspecto | WebView2 | IronPDF |
|---|---|---|
| Objetivo | Control del navegador (UI) | Biblioteca PDF (diseñada para PDF) |
| Listo para producción | NO | SÍ |
| Gestión de memoria | Filtraciones en | Estable y con la disposición adecuada |
| Soporte de Plataforma | Sólo para Windows | Windows, Linux, macOS, Docker |
| Requisitos de la traducción | STA + Bomba de mensajes | Cualquier tema |
| Servidor/Nube | No soportado | Se admite |
| Azure/AWS/GCP | Problemática | Funciona a la perfección |
| Docker | No es posible | Imágenes oficiales disponibles |
| ASP.NET Core | No puede trabajar | Asistencia de primera clase |
| Servicios de fondo | Inestable | Estable |
| Contextos admitidos | Sólo WinForms/WPF | Cualquier contexto .NET: consola, web, escritorio |
| HTML a PDF | Básico | Completo |
| URL a PDF | Básico | Completo |
| Cabeceras/Pies de página | NO | Sí (HTML) |
| Marcas de agua | NO | Sí |
| Fusionar PDF | NO | Sí |
| Dividir PDF | NO | Sí |
| Firmas digitales | NO | Sí |
| Protección de contraseñas | NO | Sí |
| Cumplimiento de PDF/A | NO | Sí |
| Soporte profesional | Ninguno para PDF | Sí |
| Documentación | Limitado | Amplia |
Inicio rápido: Migración de WebView2a IronPDF
La migración puede comenzar inmediatamente con estos pasos básicos.
Paso 1: Eliminar el paquete WebView2
dotnet remove package Microsoft.Web.WebView2
dotnet remove package Microsoft.Web.WebView2
O eliminar de su archivo de proyecto:
<PackageReference Include="Microsoft.Web.WebView2" Version="*" Remove />
<PackageReference Include="Microsoft.Web.WebView2" Version="*" Remove />
Paso 2: Instalar IronPDF
dotnet add package IronPdf
dotnet add package IronPdf
Paso 3: Actualizar los espacios de nombres
Sustituya los espacios de nombres WebView2por el espacio de nombres IronPdf:
// Before (WebView2)
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
// After (IronPDF)
using IronPdf;
// Before (WebView2)
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
// After (IronPDF)
using IronPdf;
Imports Microsoft.Web.WebView2.Core
Imports Microsoft.Web.WebView2.WinForms
' After (IronPDF)
Imports IronPdf
Paso 4: Inicializar licencia
Añadir inicialización de licencia al inicio de la aplicación:
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"
Ejemplos de migración de código
Convertir HTML a PDF
La operación más fundamental revela la diferencia de complejidad entre estos enfoques de .NET PDF.
Enfoque de WebView2:
// NuGet: Install-Package Microsoft.Web.WebView2.WinForms
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;
class Program
{
static async Task Main()
{
var webView = new WebView2();
await webView.EnsureCoreWebView2Async();
webView.CoreWebView2.NavigateToString("<html><body><h1>Hello World</h1></body></html>");
await Task.Delay(2000);
await webView.CoreWebView2.CallDevToolsProtocolMethodAsync(
"Page.printToPDF",
"{}"
);
}
}
// NuGet: Install-Package Microsoft.Web.WebView2.WinForms
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;
class Program
{
static async Task Main()
{
var webView = new WebView2();
await webView.EnsureCoreWebView2Async();
webView.CoreWebView2.NavigateToString("<html><body><h1>Hello World</h1></body></html>");
await Task.Delay(2000);
await webView.CoreWebView2.CallDevToolsProtocolMethodAsync(
"Page.printToPDF",
"{}"
);
}
}
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports Microsoft.Web.WebView2.WinForms
Imports Microsoft.Web.WebView2.Core
Module Program
Async Function Main() As Task
Dim webView As New WebView2()
Await webView.EnsureCoreWebView2Async()
webView.CoreWebView2.NavigateToString("<html><body><h1>Hello World</h1></body></html>")
Await Task.Delay(2000)
Await webView.CoreWebView2.CallDevToolsProtocolMethodAsync(
"Page.printToPDF",
"{}"
)
End Function
End Module
Enfoque IronPDF:
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<html><body><h1>Hello World</h1></body></html>");
pdf.SaveAs("output.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<html><body><h1>Hello World</h1></body></html>");
pdf.SaveAs("output.pdf");
}
}
Imports IronPdf
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf("<html><body><h1>Hello World</h1></body></html>")
pdf.SaveAs("output.pdf")
End Sub
End Class
La versión WebView2requiere inicialización asincrónica con EnsureCoreWebView2Async(), navegación a través de NavigateToString(), un Task.Delay(2000) poco confiable para esperar la representación e interacción con el protocolo DevTools.IronPDFelimina por completo esta ceremonia: crear un renderizador, renderizar HTML, guardar.
Para situaciones avanzadas de conversión de HTML a PDF, consulte la Guía de conversión de HTML a PDF.
Convertir URL en PDF
La conversión de URL a PDF demuestra el complejo flujo de navegación asíncrono de WebView2.
Enfoque de WebView2:
// NuGet: Install-Package Microsoft.Web.WebView2.WinForms
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;
class Program
{
static async Task Main()
{
var webView = new WebView2();
await webView.EnsureCoreWebView2Async();
var tcs = new TaskCompletionSource<bool>();
webView.CoreWebView2.NavigationCompleted += (s, e) => tcs.SetResult(true);
webView.CoreWebView2.Navigate("https://example.com");
await tcs.Task;
await Task.Delay(1000);
var result = await webView.CoreWebView2.CallDevToolsProtocolMethodAsync(
"Page.printToPDF",
"{\"printBackground\": true}"
);
var base64 = System.Text.Json.JsonDocument.Parse(result).RootElement.GetProperty("data").GetString();
File.WriteAllBytes("output.pdf", Convert.FromBase64String(base64));
}
}
// NuGet: Install-Package Microsoft.Web.WebView2.WinForms
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;
class Program
{
static async Task Main()
{
var webView = new WebView2();
await webView.EnsureCoreWebView2Async();
var tcs = new TaskCompletionSource<bool>();
webView.CoreWebView2.NavigationCompleted += (s, e) => tcs.SetResult(true);
webView.CoreWebView2.Navigate("https://example.com");
await tcs.Task;
await Task.Delay(1000);
var result = await webView.CoreWebView2.CallDevToolsProtocolMethodAsync(
"Page.printToPDF",
"{\"printBackground\": true}"
);
var base64 = System.Text.Json.JsonDocument.Parse(result).RootElement.GetProperty("data").GetString();
File.WriteAllBytes("output.pdf", Convert.FromBase64String(base64));
}
}
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports Microsoft.Web.WebView2.WinForms
Imports Microsoft.Web.WebView2.Core
Module Program
Async Function Main() As Task
Dim webView As New WebView2()
Await webView.EnsureCoreWebView2Async()
Dim tcs As New TaskCompletionSource(Of Boolean)()
AddHandler webView.CoreWebView2.NavigationCompleted, Sub(s, e) tcs.SetResult(True)
webView.CoreWebView2.Navigate("https://example.com")
Await tcs.Task
Await Task.Delay(1000)
Dim result As String = Await webView.CoreWebView2.CallDevToolsProtocolMethodAsync(
"Page.printToPDF",
"{""printBackground"": true}"
)
Dim base64 As String = System.Text.Json.JsonDocument.Parse(result).RootElement.GetProperty("data").GetString()
File.WriteAllBytes("output.pdf", Convert.FromBase64String(base64))
End Function
End Module
Enfoque IronPDF:
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("output.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("output.pdf");
}
}
Imports IronPdf
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim pdf = renderer.RenderUrlAsPdf("https://example.com")
pdf.SaveAs("output.pdf")
End Sub
End Class
WebView2 requiere crear un TaskCompletionSource, suscribirse a eventos NavigationCompleted, llamar a CallDevToolsProtocolMethodAsync, analizar respuestas JSON y decodificar datos base64.IronPDFproporciona un método RenderUrlAsPdf dedicado que maneja toda la complejidad internamente.
Explore la URL a la documentación PDF para conocer las opciones de autenticación y encabezado personalizado.
Configuración personalizada de PDF a partir de archivos HTML
La configuración de la orientación de la página, los márgenes y el tamaño del papel requiere distintos enfoques.
Enfoque de WebView2:
// NuGet: Install-Package Microsoft.Web.WebView2.WinForms
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
class Program
{
static async Task Main()
{
var webView = new WebView2();
await webView.EnsureCoreWebView2Async();
string htmlFile = Path.Combine(Directory.GetCurrentDirectory(), "input.html");
webView.CoreWebView2.Navigate(htmlFile);
await Task.Delay(3000);
var printSettings = webView.CoreWebView2.Environment.CreatePrintSettings();
printSettings.Orientation = CoreWebView2PrintOrientation.Landscape;
printSettings.MarginTop = 0.5;
printSettings.MarginBottom = 0.5;
using (var stream = await webView.CoreWebView2.PrintToPdfAsync("custom.pdf", printSettings))
{
Console.WriteLine("Custom PDF created");
}
}
}
// NuGet: Install-Package Microsoft.Web.WebView2.WinForms
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
class Program
{
static async Task Main()
{
var webView = new WebView2();
await webView.EnsureCoreWebView2Async();
string htmlFile = Path.Combine(Directory.GetCurrentDirectory(), "input.html");
webView.CoreWebView2.Navigate(htmlFile);
await Task.Delay(3000);
var printSettings = webView.CoreWebView2.Environment.CreatePrintSettings();
printSettings.Orientation = CoreWebView2PrintOrientation.Landscape;
printSettings.MarginTop = 0.5;
printSettings.MarginBottom = 0.5;
using (var stream = await webView.CoreWebView2.PrintToPdfAsync("custom.pdf", printSettings))
{
Console.WriteLine("Custom PDF created");
}
}
}
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports Microsoft.Web.WebView2.Core
Imports Microsoft.Web.WebView2.WinForms
Module Program
Async Function Main() As Task
Dim webView As New WebView2()
Await webView.EnsureCoreWebView2Async()
Dim htmlFile As String = Path.Combine(Directory.GetCurrentDirectory(), "input.html")
webView.CoreWebView2.Navigate(htmlFile)
Await Task.Delay(3000)
Dim printSettings = webView.CoreWebView2.Environment.CreatePrintSettings()
printSettings.Orientation = CoreWebView2PrintOrientation.Landscape
printSettings.MarginTop = 0.5
printSettings.MarginBottom = 0.5
Using stream = Await webView.CoreWebView2.PrintToPdfAsync("custom.pdf", printSettings)
Console.WriteLine("Custom PDF created")
End Using
End Function
End Module
Enfoque IronPDF:
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
using System.IO;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
renderer.RenderingOptions.MarginTop = 50;
renderer.RenderingOptions.MarginBottom = 50;
string htmlFile = Path.Combine(Directory.GetCurrentDirectory(), "input.html");
var pdf = renderer.RenderHtmlFileAsPdf(htmlFile);
pdf.SaveAs("custom.pdf");
Console.WriteLine("Custom PDF created");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
using System.IO;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
renderer.RenderingOptions.MarginTop = 50;
renderer.RenderingOptions.MarginBottom = 50;
string htmlFile = Path.Combine(Directory.GetCurrentDirectory(), "input.html");
var pdf = renderer.RenderHtmlFileAsPdf(htmlFile);
pdf.SaveAs("custom.pdf");
Console.WriteLine("Custom PDF created");
}
}
Imports IronPdf
Imports IronPdf.Rendering
Imports System
Imports System.IO
Module Program
Sub Main()
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape
renderer.RenderingOptions.MarginTop = 50
renderer.RenderingOptions.MarginBottom = 50
Dim htmlFile As String = Path.Combine(Directory.GetCurrentDirectory(), "input.html")
Dim pdf = renderer.RenderHtmlFileAsPdf(htmlFile)
pdf.SaveAs("custom.pdf")
Console.WriteLine("Custom PDF created")
End Sub
End Module
WebView2 requiere un Task.Delay de 3 segundos (una suposición poco confiable), creando configuraciones de impresión a través del entorno y usando PrintToPdfAsync con una transmisión.IronPDFproporciona propiedades directas RenderingOptions con nombres claros y utiliza milímetros para mediciones más precisas.
Opciones avanzadas de PDF con el protocolo DevTools
Las configuraciones complejas de WebView2requieren la interacción con el protocolo DevTools.
Enfoque de WebView2:
// NuGet: Install-Package Microsoft.Web.WebView2.WinForms
using System;
using System.IO;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;
class Program
{
static async Task Main()
{
var webView = new WebView2();
await webView.EnsureCoreWebView2Async();
var htmlPath = Path.GetFullPath("document.html");
var tcs = new TaskCompletionSource<bool>();
webView.CoreWebView2.NavigationCompleted += (s, e) => tcs.SetResult(true);
webView.CoreWebView2.Navigate($"file:///{htmlPath}");
await tcs.Task;
await Task.Delay(1000);
var options = new
{
landscape = false,
printBackground = true,
paperWidth = 8.5,
paperHeight = 11,
marginTop = 0.4,
marginBottom = 0.4,
marginLeft = 0.4,
marginRight = 0.4
};
var result = await webView.CoreWebView2.CallDevToolsProtocolMethodAsync(
"Page.printToPDF",
JsonSerializer.Serialize(options)
);
var base64 = JsonDocument.Parse(result).RootElement.GetProperty("data").GetString();
File.WriteAllBytes("output.pdf", Convert.FromBase64String(base64));
}
}
// NuGet: Install-Package Microsoft.Web.WebView2.WinForms
using System;
using System.IO;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;
class Program
{
static async Task Main()
{
var webView = new WebView2();
await webView.EnsureCoreWebView2Async();
var htmlPath = Path.GetFullPath("document.html");
var tcs = new TaskCompletionSource<bool>();
webView.CoreWebView2.NavigationCompleted += (s, e) => tcs.SetResult(true);
webView.CoreWebView2.Navigate($"file:///{htmlPath}");
await tcs.Task;
await Task.Delay(1000);
var options = new
{
landscape = false,
printBackground = true,
paperWidth = 8.5,
paperHeight = 11,
marginTop = 0.4,
marginBottom = 0.4,
marginLeft = 0.4,
marginRight = 0.4
};
var result = await webView.CoreWebView2.CallDevToolsProtocolMethodAsync(
"Page.printToPDF",
JsonSerializer.Serialize(options)
);
var base64 = JsonDocument.Parse(result).RootElement.GetProperty("data").GetString();
File.WriteAllBytes("output.pdf", Convert.FromBase64String(base64));
}
}
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports System.Text.Json
Imports Microsoft.Web.WebView2.WinForms
Imports Microsoft.Web.WebView2.Core
Module Program
Async Function Main() As Task
Dim webView As New WebView2()
Await webView.EnsureCoreWebView2Async()
Dim htmlPath As String = Path.GetFullPath("document.html")
Dim tcs As New TaskCompletionSource(Of Boolean)()
AddHandler webView.CoreWebView2.NavigationCompleted, Sub(s, e) tcs.SetResult(True)
webView.CoreWebView2.Navigate($"file:///{htmlPath}")
Await tcs.Task
Await Task.Delay(1000)
Dim options = New With {
.landscape = False,
.printBackground = True,
.paperWidth = 8.5,
.paperHeight = 11,
.marginTop = 0.4,
.marginBottom = 0.4,
.marginLeft = 0.4,
.marginRight = 0.4
}
Dim result As String = Await webView.CoreWebView2.CallDevToolsProtocolMethodAsync(
"Page.printToPDF",
JsonSerializer.Serialize(options)
)
Dim base64 As String = JsonDocument.Parse(result).RootElement.GetProperty("data").GetString()
File.WriteAllBytes("output.pdf", Convert.FromBase64String(base64))
End Function
End Module
Enfoque IronPDF:
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 40;
renderer.RenderingOptions.MarginBottom = 40;
renderer.RenderingOptions.MarginLeft = 40;
renderer.RenderingOptions.MarginRight = 40;
renderer.RenderingOptions.PrintHtmlBackgrounds = true;
var pdf = renderer.RenderHtmlFileAsPdf("document.html");
pdf.SaveAs("output.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 40;
renderer.RenderingOptions.MarginBottom = 40;
renderer.RenderingOptions.MarginLeft = 40;
renderer.RenderingOptions.MarginRight = 40;
renderer.RenderingOptions.PrintHtmlBackgrounds = true;
var pdf = renderer.RenderHtmlFileAsPdf("document.html");
pdf.SaveAs("output.pdf");
}
}
Imports IronPdf
Imports IronPdf.Rendering
Class Program
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter
renderer.RenderingOptions.MarginTop = 40
renderer.RenderingOptions.MarginBottom = 40
renderer.RenderingOptions.MarginLeft = 40
renderer.RenderingOptions.MarginRight = 40
renderer.RenderingOptions.PrintHtmlBackgrounds = True
Dim pdf = renderer.RenderHtmlFileAsPdf("document.html")
pdf.SaveAs("output.pdf")
End Sub
End Class
WebView2 requiere la construcción de objetos anónimos, la serialización a JSON, la llamada a métodos del protocolo DevTools, el análisis de respuestas JSON y la decodificación manual de base64.IronPDFproporciona propiedades tipificadas con nombres claros y valores de enumeración como PdfPaperSize.Letter.
Referencia de la API de WebView2a IronPDF
Este mapeo acelera la migración al mostrar los equivalentes directos de las API:
| API de WebView2 | Equivalente de IronPDF |
|---|---|
new WebView2() |
new ChromePdfRenderer() |
EnsureCoreWebView2Async() |
N/A |
NavigateToString(html) + PrintToPdfAsync() |
RenderHtmlAsPdf(html) |
Navigate(url) + PrintToPdfAsync() |
RenderUrlAsPdf(url) |
PrintSettings.PageWidth |
RenderingOptions.PaperSize |
PrintSettings.PageHeight |
RenderingOptions.PaperSize |
PrintSettings.MarginTop |
RenderingOptions.MarginTop |
PrintSettings.Orientation |
RenderingOptions.PaperOrientation |
ExecuteScriptAsync() |
JavaScript en HTML |
AddScriptToExecuteOnDocumentCreatedAsync() |
Etiquetas HTML <script> |
| Eventos de navegación | WaitFor.JavaScript() |
CallDevToolsProtocolMethodAsync("Page.printToPDF") |
RenderHtmlAsPdf() |
Problemas comunes de migración y soluciones
Tema 1: Fugas de memoria
Problema de WebView2: La memoria no se libera completamente cuando se eliminan las instancias de WebView2. Los procesos de larga duración acumulan memoria hasta que se bloquean.
Solución IronPDF: Recogida de basura adecuada sin fugas:
//IronPDF- clean memory management
using (var pdf = renderer.RenderHtmlAsPdf(html))
{
pdf.SaveAs("output.pdf");
} // Properly disposed
//IronPDF- clean memory management
using (var pdf = renderer.RenderHtmlAsPdf(html))
{
pdf.SaveAs("output.pdf");
} // Properly disposed
Imports IronPdf
Using pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
End Using
Tema 2: Sin hilo de interfaz de usuario en aplicaciones web
Problema de WebView2: Requiere hilo STA con bomba de mensajes. Los controladores ASP.NET Core no pueden crear instancias WebView2.
Solución IronPDF: Funciona en cualquier hilo:
// ASP.NET Core - just works
public async Task<IActionResult> GetPdf()
{
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return File(pdf.BinaryData, "application/pdf");
}
// ASP.NET Core - just works
public async Task<IActionResult> GetPdf()
{
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return File(pdf.BinaryData, "application/pdf");
}
Imports System.Threading.Tasks
Imports Microsoft.AspNetCore.Mvc
Public Class YourController
Inherits Controller
Public Async Function GetPdf() As Task(Of IActionResult)
Dim pdf = Await renderer.RenderHtmlAsPdfAsync(html)
Return File(pdf.BinaryData, "application/pdf")
End Function
End Class
Tema 3: Complejidad de los eventos de navegación
Problema de WebView2: debe manejar eventos de navegación asincrónica, devoluciones de llamadas de finalización y condiciones de carrera con TaskCompletionSource.
Solución IronPDF: Llamada a método único síncrono o asíncrono:
// Simple and predictable
var pdf = renderer.RenderHtmlAsPdf(html);
// or
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
// Simple and predictable
var pdf = renderer.RenderHtmlAsPdf(html);
// or
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
Número 4: Unidades de medida
WebView2 utiliza pulgadas para las dimensiones (8,5 x 11 para Carta). IronPDF utiliza milímetros para medidas más precisas.
Enfoque de conversión:
// WebView2: PageWidth = 8.27 (inches for A4)
// IronPDF: Use enum
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
// Or custom size in mm
renderer.RenderingOptions.SetCustomPaperSizeInMillimeters(210, 297);
// WebView2: PageWidth = 8.27 (inches for A4)
// IronPDF: Use enum
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
// Or custom size in mm
renderer.RenderingOptions.SetCustomPaperSizeInMillimeters(210, 297);
' WebView2: PageWidth = 8.27 (inches for A4)
' IronPDF: Use enum
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4
' Or custom size in mm
renderer.RenderingOptions.SetCustomPaperSizeInMillimeters(210, 297)
Cumplimiento normativo en España: VeriFactu, TicketBAI y LOPDGDD — WebView2 no puede satisfacer los requisitos legales
Para los desarrolladores .NET en España, la elección de la biblioteca PDF no es solo una decisión técnica, sino una obligación legal. WebView2 carece por completo de las capacidades necesarias para cumplir con la normativa de facturación española.
VeriFactu (Real Decreto-Ley 15/2025): leyenda VERI*FACTU y datos peninsulares
El sistema VeriFactu de la AEAT obliga al software de facturación a incluir en cada PDF la leyenda VERI\*FACTU e incrustar la cadena de huella encadenada que vincula cada factura con la anterior, garantizando la trazabilidad ante la sede electrónica de la AEAT (factura verificable). Dado que WebView2 requiere un hilo STA con bomba de mensajes y no puede ejecutarse en servicios en segundo plano, es incapaz de generar estos documentos de manera fiable en producción. Los proveedores de software que incumplan el RDL 15/2025 se exponen a sanciones de hasta 150.000 € anuales.
IronPDF genera estas facturas en cualquier contexto (ASP.NET Core, servicios de Windows, Docker) con datos en formato peninsular:
using IronPdf;
using System.Security.Cryptography;
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginBottom = 25;
// Factura VeriFactu con NIF español y formato EUR es-ES
string facturaHtml = @"
<html>
<body style='font-family: Arial; margin: 40px;'>
<h1>FACTURA Nº FAC-2026-0201</h1>
<p>Emisor: Sistemas Informáticos Iberia S.L. | NIF: B-33.445.566</p>
<p>Receptor: Comercial Peninsular S.A. | CIF: A-77.889.900</p>
<table border='1' cellpadding='6' style='width:100%;'>
<tr><th>Concepto</th><th>Base (€)</th><th>IVA 21%</th><th>Total (€)</th></tr>
<tr><td>Consultoría tecnológica</td>
<td>3.500,00 €</td><td>735,00 €</td><td>4.235,00 €</td></tr>
</table>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(facturaHtml);
// Huella SHA-256 para VeriFactu
string hash = Convert.ToHexString(SHA256.HashData(pdf.BinaryData));
// Pie de página con leyenda VERI*FACTU obligatoria
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
HtmlFragment = $@"
<div style='font-size:9px; text-align:center; border-top:1px solid #ccc; padding-top:4px;'>
VERI*FACTU | Factura verificable en la sede electrónica de la AEAT |
SHA-256: {hash[..12]}... | Página {{page}} de {{total-pages}}
</div>"
};
pdf.SaveAs("factura-verifactu-FAC2026-0201.pdf");
using IronPdf;
using System.Security.Cryptography;
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginBottom = 25;
// Factura VeriFactu con NIF español y formato EUR es-ES
string facturaHtml = @"
<html>
<body style='font-family: Arial; margin: 40px;'>
<h1>FACTURA Nº FAC-2026-0201</h1>
<p>Emisor: Sistemas Informáticos Iberia S.L. | NIF: B-33.445.566</p>
<p>Receptor: Comercial Peninsular S.A. | CIF: A-77.889.900</p>
<table border='1' cellpadding='6' style='width:100%;'>
<tr><th>Concepto</th><th>Base (€)</th><th>IVA 21%</th><th>Total (€)</th></tr>
<tr><td>Consultoría tecnológica</td>
<td>3.500,00 €</td><td>735,00 €</td><td>4.235,00 €</td></tr>
</table>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(facturaHtml);
// Huella SHA-256 para VeriFactu
string hash = Convert.ToHexString(SHA256.HashData(pdf.BinaryData));
// Pie de página con leyenda VERI*FACTU obligatoria
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
HtmlFragment = $@"
<div style='font-size:9px; text-align:center; border-top:1px solid #ccc; padding-top:4px;'>
VERI*FACTU | Factura verificable en la sede electrónica de la AEAT |
SHA-256: {hash[..12]}... | Página {{page}} de {{total-pages}}
</div>"
};
pdf.SaveAs("factura-verifactu-FAC2026-0201.pdf");
Imports IronPdf
Imports System.Security.Cryptography
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.MarginBottom = 25
' Factura VeriFactu con NIF español y formato EUR es-ES
Dim facturaHtml As String = "
<html>
<body style='font-family: Arial; margin: 40px;'>
<h1>FACTURA Nº FAC-2026-0201</h1>
<p>Emisor: Sistemas Informáticos Iberia S.L. | NIF: B-33.445.566</p>
<p>Receptor: Comercial Peninsular S.A. | CIF: A-77.889.900</p>
<table border='1' cellpadding='6' style='width:100%;'>
<tr><th>Concepto</th><th>Base (€)</th><th>IVA 21%</th><th>Total (€)</th></tr>
<tr><td>Consultoría tecnológica</td>
<td>3.500,00 €</td><td>735,00 €</td><td>4.235,00 €</td></tr>
</table>
</body>
</html>"
Dim pdf = renderer.RenderHtmlAsPdf(facturaHtml)
' Huella SHA-256 para VeriFactu
Dim hash As String = Convert.ToHexString(SHA256.HashData(pdf.BinaryData))
' Pie de página con leyenda VERI*FACTU obligatoria
renderer.RenderingOptions.HtmlFooter = New HtmlHeaderFooter With {
.HtmlFragment = $"
<div style='font-size:9px; text-align:center; border-top:1px solid #ccc; padding-top:4px;'>
VERI*FACTU | Factura verificable en la sede electrónica de la AEAT |
SHA-256: {hash.Substring(0, 12)}... | Página {{page}} de {{total-pages}}
</div>"
}
pdf.SaveAs("factura-verifactu-FAC2026-0201.pdf")
TicketBAI (Bizkaia, Gipuzkoa y Araba)
En el País Vasco, TicketBAI exige firmar cada factura con XAdES y adjuntar el documento PDF al registro fiscal. La firma requiere certificados digitales (FNMT u otros reconocidos) y una biblioteca capaz de aplicar firmas PAdES conformes a eIDAS. WebView2 no implementa ningún mecanismo de firma digital, por lo que resulta incompatible con este régimen.
Crea y Crece y Facturae XML
La Ley Crea y Crece obliga a la factura electrónica B2B estructurada en formato Facturae XML, compatible con el estándar europeo EN 16931 y el perfil CIUS-ES. Este XML debe poderse incrustar en documentos PDF/A-3 para su envío a través de FACe o el SII de la AEAT. IronPDF permite generar documentos PDF/A-3 y embeber archivos adjuntos XML, cubriendo este requisito de forma nativa.
LOPDGDD y procesamiento on-premise
La Ley Orgánica de Protección de Datos (LOPDGDD) y la supervisión de la AEPD exigen que los datos personales de las facturas no se transfieran a servidores externos sin base legal. IronPDF procesa todos los documentos de forma local (on-premise), sin enviar contenido a servicios de terceros, lo que simplifica el cumplimiento.
Lista de comprobación de la migración a WebView2
Tareas previas a la migración
Documente todo el código de generación de PDF de WebView2en su base de código. Identificar dónde WebView2está causando problemas (fugas de memoria, bloqueos, problemas de despliegue). Revise la documentación de IronPDF para familiarizarse con las funciones.
Tareas de actualización de código
- Eliminar el paquete NuGet Microsoft.Web.WebView2
- Instalación del paquete IronPdf NuGet
- Eliminar las dependencias de WinForms/WPF si sólo se utiliza para la generación de PDF
- Reemplace el código WebView2con
ChromePdfRenderer - Eliminar los requisitos de hilo STA
- Eliminar los controladores de eventos de navegación y los patrones
TaskCompletionSource - Eliminar los hacks
Task.Delay - Añadir la inicialización de la licenciaIronPDFal inicio
Pruebas posteriores a la migración
Tras la migración, verifique estos aspectos:
- Pruebas en el entorno de destino (ASP.NET, Docker, Linux, si procede)
- Verificación de que la calidad del PDF se ajusta a las expectativas
- Compruebe que las páginas con mucho JavaScript se visualizan correctamente
- Compruebe que los encabezados y pies de página funcionan con las funciones HTML de IronPDF
- Prueba de carga para comprobar la estabilidad de la memoria en operaciones prolongadas
- Probar escenarios de larga duración sin acumulación de memoria
Actualizaciones de despliegue
- Actualizar las imágenes Docker si procede (eliminar Edge WebView2Runtime)
- Eliminar la dependencia de Edge WebView2Runtime de los requisitos del servidor
- Actualización de la documentación sobre requisitos del servidor
- Verificar que la implementación multiplataforma funciona en las plataformas de destino
Beneficios clave de migrar a IronPDF
El paso de WebView2aIronPDFofrece varias ventajas fundamentales:
Compatibilidad multiplataforma: a diferencia de la limitación exclusiva de WebView2para Windows,IronPDFfunciona en Windows, Linux, macOS y Docker. Esta flexibilidad permite el despliegue en Azure, AWS, GCP y cualquier entorno en la nube sin limitaciones de plataforma.
Sin dependencias de UI:IronPDFno requiere subprocesos STA, bombas de mensajes o contextos WinForms/WPF. Funciona en aplicaciones de consola, API web, servicios de Windows y trabajadores en segundo plano.
Estabilidad de la memoria: la recolección de basura adecuada elimina las fugas de memoria que afectan a WebView2en procesos de ejecución prolongada. Los servidores de producción permanecen estables.
API simple: las llamadas de método único reemplazan eventos de navegación complejos, devoluciones de llamadas de finalización, interacciones del protocolo DevTools y decodificación base64.
Funciones ampliadas de PDF: encabezados, pies de página, marcas de agua, fusión/división, firmas digitales, protección con contraseña y compatibilidad con PDF/A: funciones que WebView2no puede proporcionar.
Desarrollo activo: a medida que aumenta la adopción de .NET 10 y C# 14 hasta 2026, las actualizaciones periódicas deIronPDFgarantizan la compatibilidad con las versiones actuales y futuras de .NET .
Preguntas Frecuentes
¿Por qué WebView2 no puede generar facturas VeriFactu conformes en España?
WebView2 requiere un hilo STA con bomba de mensajes y no puede ejecutarse en servicios en segundo plano. VeriFactu (RDL 15/2025) exige la leyenda VERI*FACTU, el QR de verificación de la AEAT y la cadena de huella SHA-256 encadenada — operaciones que WebView2 no puede realizar de forma fiable en producción. Las sanciones por incumplimiento alcanzan 150.000 € anuales.
¿Cómo genera IronPDF facturas VeriFactu con NIF español y formato EUR?
IronPDF renderiza HTML con datos peninsulares (NIF: B-33.445.566, importe en formato 3.500,00 €, IVA 21%) y añade la leyenda obligatoria VERI*FACTU mediante HtmlHeaderFooter. La huella SHA-256 se calcula sobre los bytes PDF y se incrusta en el pie de página. Funciona en ASP.NET Core, Docker y servicios Windows — sin hilo STA requerido.
¿Soporta IronPDF TicketBAI con firma XAdES y PAdES en el País Vasco?
Sí. TicketBAI es obligatorio en Bizkaia, Gipuzkoa y Araba, cada una con especificaciones propias. IronPDF genera el PDF de factura con firma PAdES conforme a eIDAS usando certificados FNMT-RCM; la firma XAdES de los XMLs se gestiona en la capa del ISV.
¿Cómo cubre IronPDF los requisitos de Facturae XML, FACe, Crea y Crece y LOPDGDD?
IronPDF genera documentos PDF/A-3 con adjuntos Facturae XML para envío a FACe bajo el estándar EN 16931 / CIUS-ES. La Ley Crea y Crece exige factura electrónica B2B desde 2027-2028. El procesamiento on-premise facilita el cumplimiento del LOPDGDD supervisado por la AEPD. El SII (Suministro Inmediato de Información) puede beneficiarse de la generación asíncrona nativa de IronPDF.
¿Cuáles son las principales razones técnicas para migrar de WebView2 a IronPDF?
WebView2 tiene fugas de memoria documentadas en procesos de larga duración, solo funciona en Windows (sin Linux/Docker/cloud), requiere hilo STA con bomba de mensajes, y no tiene soporte para encabezados/pies de página PDF, marcas de agua, fusión/división ni firmas digitales. IronPDF es multiplataforma, estable en memoria y proporciona una API sencilla para todas estas operaciones con soporte para la normativa española de facturación.

