Saltar al pie de página
USANDO IRONPDF

C# Imprimir formulario a PDF -- Guía completa para desarrolladores

El problema con las plantillas PDF paralelas

Página principal de IronPDF Las vistas Razor ya están construidas. La página de detalle de la factura renderiza los elementos de línea, calcula los totales y aplica la hoja de estilos de la empresa. La página de estado del proyecto muestra desgloses de tareas con secciones condicionales que solo aparecen cuando los hitos están atrasados. La vista de recibo de pago formatea ganancias, deducciones y las cifras de YTD en una tabla que el equipo de RRHH pasó dos semanas ajustando. Todo ese trabajo está hecho, y luego un interesado pide un botón "Descargar como PDF".

La respuesta estándar es construir una segunda plantilla: una cadena HTML o definición de informe que reproduzca el mismo diseño para la ruta del PDF. Esa segunda plantilla comienza siendo una copia de la primera, luego inmediatamente comienza a divergir. El diseñador de UI actualiza el estilo de la tabla de la vista de factura en la Sprint 14. Nadie actualiza la plantilla PDF hasta que un usuario informa que la factura descargada se ve diferente de la pantalla. Ahora hay dos fuentes de verdad, y una de ellas siempre es ligeramente incorrecta.

Las bibliotecas PDF de JavaScript del lado del cliente evitan la duplicación pero pierden los datos renderizados por el servidor, los datos autenticados, los totales calculados por el servidor y las secciones condicionales impulsadas por el ViewModel no sobreviven al traspaso a un renderer del lado del navegador. La automatización de navegadores sin cabeza desde el servidor es frágil, añade sobrecarga de infraestructura y falla de manera impredecible en entornos containerizados. La impresión de navegador a PDF funciona para un usuario imprimiendo manualmente; no es un botón "Descargar PDF" en una aplicación de producción.

Los escenarios reales revelan el costo real: un administrador de comercio electrónico descargando una página de detalle del pedido para el cumplimiento, un cliente exportando una página de estado del proyecto desde una herramienta de gestión de proyectos, un empleado descargando su recibo de pago, un despachador imprimiendo un resumen de ruta. Todos esperan que el PDF se vea exactamente como lo que ven en la pantalla.

La solución: Renderizar la vista existente, no una copia de ella

IronPDF permite que las aplicaciones ASP.NET Core rendericen una vista Razor existente, la misma que se sirve al navegador, directamente en un PDF. La acción del controlador PDF renderiza la vista Razor a una cadena HTML usando el motor de vista estándar, pasa esa cadena a ChromePdfRenderer.RenderHtmlAsPdf() y devuelve el resultado como una descarga de archivo.

Una vista, dos salidas. Cuando la vista Razor cambia, la salida PDF cambia con ella, automáticamente, sin requerir coordinación. No existen plantillas paralelas que mantener, no hay soluciones del lado del cliente que depurar, y no hay procesos de navegador sin cabeza que mantener vivos. El renderizado se ejecuta dentro de la aplicación .NET existente como un único paquete NuGet.

Cómo funciona en la práctica

1. La vista ya existe: La acción PDF es lo que es nuevo

Una página de detalle de factura en /invoices/{id} renderiza el mismo modelo de datos, ya sea que esté sirviendo a un navegador o produciendo un PDF. El modelo incluye elementos de línea, totales, detalles del cliente y branding de la empresa, todos los datos que la vista necesita. El InvoicesController existente tiene una acción Details que llena ese modelo. La acción PDF es un hermano de esta, no un reemplazo.

Cuando el usuario hace clic en "Descargar PDF", la solicitud llega a /invoices/{id}/pdf. La acción PDF obtiene el mismo ViewModel usando la misma llamada de servicio, el modelo es idéntico. Lo que difiere es lo que ocurre a continuación.

2. Vista Razor renderizada a cadena HTML

En lugar de devolver un ViewResult, la acción PDF utiliza un servicio de renderización de vistas para invocar el motor Razor contra el archivo de vista y ViewModel, capturando la salida como una cadena. Este es un patrón común en ASP.NET Core, un IViewRenderService inyectado en el controlador que llama a ICompositeViewEngine, ejecuta la vista en un ActionContext falso y devuelve el HTML renderizado.

La cadena HTML renderizada está completa: todos los datos están poblados, todas las secciones condicionales están resueltas, todos los nombres de clase CSS están presentes. Es el mismo HTML que recibiría el navegador, capturado del lado del servidor.

3. ChromePdfRenderer convierte la cadena HTML a formato PDF

using IronPdf;

[HttpGet("{id}/pdf")]
public async Task<IActionResult> DownloadInvoicePdf(int id)
{
    var model = await _invoiceService.GetInvoiceViewModelAsync(id);

    // Render the existing Razor view to an HTML string
    string html = await _viewRenderer.RenderToStringAsync("Invoices/Details", model);

    var renderer = new ChromePdfRenderer();

    renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
    renderer.RenderingOptions.MarginTop = 15;
    renderer.RenderingOptions.MarginBottom = 15;

    PdfDocument pdf = renderer.RenderHtmlAsPdf(html);

    return File(pdf.BinaryData, "application/pdf"
        $"Invoice-{model.InvoiceNumber}.pdf");
}
using IronPdf;

[HttpGet("{id}/pdf")]
public async Task<IActionResult> DownloadInvoicePdf(int id)
{
    var model = await _invoiceService.GetInvoiceViewModelAsync(id);

    // Render the existing Razor view to an HTML string
    string html = await _viewRenderer.RenderToStringAsync("Invoices/Details", model);

    var renderer = new ChromePdfRenderer();

    renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
    renderer.RenderingOptions.MarginTop = 15;
    renderer.RenderingOptions.MarginBottom = 15;

    PdfDocument pdf = renderer.RenderHtmlAsPdf(html);

    return File(pdf.BinaryData, "application/pdf"
        $"Invoice-{model.InvoiceNumber}.pdf");
}
Imports IronPdf
Imports Microsoft.AspNetCore.Mvc

<HttpGet("{id}/pdf")>
Public Async Function DownloadInvoicePdf(id As Integer) As Task(Of IActionResult)
    Dim model = Await _invoiceService.GetInvoiceViewModelAsync(id)

    ' Render the existing Razor view to an HTML string
    Dim html As String = Await _viewRenderer.RenderToStringAsync("Invoices/Details", model)

    Dim renderer As New ChromePdfRenderer()

    renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print
    renderer.RenderingOptions.MarginTop = 15
    renderer.RenderingOptions.MarginBottom = 15

    Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(html)

    Return File(pdf.BinaryData, "application/pdf", $"Invoice-{model.InvoiceNumber}.pdf")
End Function
$vbLabelText   $csharpLabel

Documento PDF Generado

Ejemplo de salida PDF de IronPDF CssMediaType.Print aplica cualquier regla @media print ya presente en la hoja de estilos de la vista: ocultando la barra de navegación, suprimiendo botones de acción y aplicando espaciado específico de impresión, sin requerir cambios en la propia vista Razor.

ConsejosSi la vista Razor hace referencia a hojas de estilos o imágenes a través de rutas relativas, establezca un BaseUrlPath como el segundo parámetro en RenderHtmlAsPdf() para que IronPDF resuelva esos activos correctamente durante el renderizado. Sin él, las referencias de CSS e imágenes que funcionan en un contexto de navegador fallarán al cargarse en el renderer del lado del servidor.

4. Ajustes de salida PDF sin tocar la vista

Ajustes específicos de PDF como números de página, márgenes personalizados, encabezados con el título del documento, se configuran en el renderer, no en la vista Razor. Esto mantiene la lógica de impresión fuera de la plantilla:

var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = @"
        <div style='font-size:9px; color:#888; text-align:center; width:100%;'>
            Invoice — Page {page} of {total-pages}
        </div>",
    DrawDividerLine = true
};

PdfDocument pdf = renderer.RenderHtmlAsPdf(html);
var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = @"
        <div style='font-size:9px; color:#888; text-align:center; width:100%;'>
            Invoice — Page {page} of {total-pages}
        </div>",
    DrawDividerLine = true
};

PdfDocument pdf = renderer.RenderHtmlAsPdf(html);
Imports IronPdf

Dim renderer As New ChromePdfRenderer()

renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4
renderer.RenderingOptions.MarginTop = 20
renderer.RenderingOptions.MarginBottom = 20
renderer.RenderingOptions.HtmlFooter = New HtmlHeaderFooter With {
    .HtmlFragment = "
        <div style='font-size:9px; color:#888; text-align:center; width:100%;'>
            Invoice — Page {page} of {total-pages}
        </div>",
    .DrawDividerLine = True
}

Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(html)
$vbLabelText   $csharpLabel

Archivo PDF de salida

Archivo PDF con ajustes personalizados de salida La vista Razor nunca necesita saber si está renderizando en un navegador o en un PDF. La acción del controlador posee la configuración específica de PDF, y la vista permanece como una plantilla de visualización pura.

Beneficios reales

Duplicación de plantillas cero. La vista Razor es la única fuente de verdad para el diseño y contenido del documento. El navegador y el PDF se renderizan desde el mismo archivo, no hay una segunda plantilla que mantener y no hay desajuste que corregir.

Adopción instantánea. Si la vista ya existe, la exportación PDF está a una acción de controlador de distancia. No hay que rediseñar diseños, reconstruir plantillas o portar lógica condicional a un sistema de renderizado diferente.

Salida con precisión de píxel. El renderizado basado en Chromium significa que la cuadrícula CSS, flexbox, fuentes web y consultas de medios funcionan en el PDF. La salida coincide con lo que produce el navegador, no una aproximación degradada.

Estilo específico de impresión. Las reglas @media print ya presentes en la hoja de estilos de la vista controlan qué aparece en el PDF: esconder la navegación, ajustar anchos de columna para papel o reorganizar contenido. No hay plantilla separada, no hay estilos de impresión en línea para gestionar por separado.

Mantenibilidad. Actualiza la vista Razor y tanto la salida del navegador como la salida PDF reflejan el cambio. No hay un segundo sistema para coordinar actualizaciones, no hay riesgo de que un cambio del diseñador llegue al navegador pero no al PDF.

Sin costos por documento. La renderización se ejecuta en el proceso dentro de la aplicación web. No hay llamadas a API externas, no hay medición de uso y no hay un modelo de costos que escale según el volumen de descargas.

Cierre

Si la vista Razor ya está construida, la exportación PDF no es una nueva función, es un nuevo camino de entrega para el trabajo existente. El mismo modelo, la misma vista, el mismo estilo: la única adición es una acción del controlador que captura la salida HTML de la vista y la pasa a un renderer antes de devolverla como archivo.

Esa arquitectura mantiene la base de código limpia y la salida PDF permanentemente sincronizada con el navegador. IronPDF maneja todo el ciclo de vida de la generación de PDF en C# en ironpdf.com, desde renderizar HTML hasta guardar, transmitir y manipular documentos. Si está listo para agregar exportación PDF a sus vistas Razor existentes, comience su prueba gratuita de 30 días y valide la salida contra su renderizado actual del navegador antes de lanzar la función.

Curtis Chau
Escritor Técnico

Curtis Chau tiene una licenciatura en Ciencias de la Computación (Carleton University) y se especializa en el desarrollo front-end con experiencia en Node.js, TypeScript, JavaScript y React. Apasionado por crear interfaces de usuario intuitivas y estéticamente agradables, disfruta trabajando con frameworks modernos y creando manuales bien ...

Leer más

Equipo de soporte de Iron

Estamos disponibles online las 24 horas, 5 días a la semana.
Chat
Email
Llámame