Esta página puede contener texto traducido automáticamente.

Guía de inicio rápido para la generación de PDF en C#

Lea acerca de cómo generar archivos PDF como informes, facturas y recibos en sus proyectos .NET. Usando C# o VB.NET, cree documentos PDF fácilmente componiendo elementos estructurales. Los elementos incluyen encabezados, pies de página, contenedores, tablas, párrafos, imágenes y similares. La API divide automáticamente el contenido en páginas.

Algunos otros métodos para generar archivos PDF se describen en el artículo Crear documentos PDF en C# y VB.NET.

Presentamos la biblioteca de generación de PDF

Ofrecemos la API de diseño como complemento gratuito para la biblioteca Docotic.Pdf. Tanto la biblioteca como el complemento Layout están disponibles en NuGet y en nuestro sitio. Para probar la biblioteca sin restricciones del modo de evaluación, obtenga la clave de licencia gratuita por tiempo limitado en la página de la biblioteca.

Biblioteca Docotic.Pdf 9.4.17469-dev Complemento de diseño 9.4.17469-dev
Pruebas de regresión 14,760 pasaron Descargas totales de NuGet 4,447,259

Instalación

Instale el paquete BitMiracle.Docotic.Pdf.Layout desde NuGet. Esta es la forma más fácil y conveniente de instalar el complemento Layout.

Como alternativa, puede descargar el ZIP con los binarios de la biblioteca en nuestro sitio. Para utilizar Layout API, agregue referencias a las siguientes DLL del paquete ZIP:

  • BitMiracle.Docotic.Pdf.dll
  • Layout add-on/BitMiracle.Docotic.Pdf.Layout.dll

El enfoque

El punto de entrada es la clase PdfDocumentBuilder. Para comenzar a generar un PDF, llame al método Generate de la clase. El método requiere un delegado de tipo Action<Document> como uno de sus parámetros. La biblioteca espera que usted diseñe el contenido del documento en el delegado.

PdfDocumentBuilder.Create().Generate("output.pdf", (Document doc) =>
{
    // crear contenidos del documento aquí
});

Dado un objeto Document, el delegado debe definir el diseño. El diseño completo consta de bloques de construcción más pequeños. Páginas, encabezados y pies de página, contenedores y bloques de texto son ejemplos de dichos bloques.

Muchas llamadas a métodos están encadenadas. El orden de las llamadas en una cadena es importante. A continuación se muestra un ejemplo que muestra cómo configurar el contenido de las páginas en algunas llamadas encadenadas.

PdfDocumentBuilder.Create()
    .Generate("output.pdf", doc => doc.Pages(pages =>
    {
        pages.Content().Padding(24).AlignCenter().Text("Some text");
    }));

Intellisense le ayudará a descubrir la API. Pruebe la API usted mismo. Espero que te resulte fácil de usar.

¡Hola Mundo!

Desarrollemos una aplicación sencilla que muestre los componentes básicos comunes en acción. La aplicación utilizará el enfoque mencionado anteriormente.

Puse el código completo de la aplicación en nuestro repositorio de códigos de muestra Docotic.Pdf. El código para este paso está en la clase HelloWorld de la aplicación de ejemplo.

public void CreatePdf()
{
    PdfDocumentBuilder.Create().Generate("hello.pdf", doc => doc.Pages(page =>
    {
        page.Content().Text("Hello, world!");
    }));
}

No hay mucho código, pero ¿qué está pasando aquí?

Explicación larga para el código corto.

El código crea una instancia del generador de documentos y le pide al generador que genere hello.pdf. El constructor delega el trabajo de diseño del contenido del documento a mi código.

En el código del delegado, le pido al documento que cree algunas páginas. Ahora el documento delega el trabajo de diseñar el contenido de las páginas en mi código.

En el delegado de las páginas, accedo al contenedor de diseño para el contenido principal de las páginas. Hago esto llamando al método Content. La llamada encadenada a Text agrega el famoso intervalo de texto al contenido de las páginas.

Ahora es el momento de que el constructor genere el documento con algunas páginas según el diseño proporcionado. El constructor crea la cantidad exacta de páginas necesarias para contener los datos que agregué al contenedor de diseño para el contenido principal. ¿Una página es suficiente en este caso? Bueno.

Prepárese para futuras actualizaciones

Voy a agregar algunas características más al código. Para que sea más conveniente seguir desarrollando la aplicación, cambié el código de esta manera:

public void CreatePdf()
{
    PdfDocumentBuilder.Create().Generate("hello.pdf", BuildDocument);
}

static void BuildDocument(Document doc)
{
    doc.Pages(BuildPages);
}

static void BuildPages(PageLayout pages)
{
    pages.Content().Text("Hello, world!");
}

Puede parecer innecesario, pero dividir el código en métodos al menos lo hace más legible.

El código de muestra para este paso está en la clase HelloWorld2 de la aplicación de ejemplo.

Consulte los siguientes artículos para obtener más información sobre documentos, páginas y contenedores.

Fuentes y colores

La fuente predeterminada es buena, pero mostraré cómo usar otra. La idea principal es crear un estilo de texto usando una fuente. Luego, si es necesario, aplique algunas propiedades opcionales al estilo y, finalmente, use el estilo en un segmento de texto.

Puede utilizar una fuente de la colección de fuentes instaladas en el sistema operativo o cargar una fuente desde un archivo o secuencia.

Recomiendo anular los estilos predefinidos configurando la tipografía para el documento. Pero eso no es necesario y puedes usar estilos de texto directamente.

El código de muestra para este paso está en la clase HelloWorld3 de la aplicación de ejemplo. A continuación se muestran las partes que cambiaron desde el paso anterior.

static void BuildDocument(Document doc)
{
    doc.Typography(t =>
    {
        t.Document = doc.TextStyleWithFont(SystemFont.Family("Calibri"));
    });
    doc.Pages(BuildPages);
}

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildTextContent(pages.Content());
}

static void BuildTextContent(LayoutContainer content)
{
    var colorAccented = new PdfRgbColor(56, 194, 10);
    var styleAccented = TextStyle.Parent.FontColor(colorAccented);

    content.Text(t =>
    {
        t.Span("Hello, ").Style(styleAccented);
        t.Span("world!").Style(styleAccented.Underline());
    });
}

Como puede ver, actualicé el código para BuildDocument y BuildPages. También agregué un nuevo método llamado BuildTextContent.

En BuildDocument, creo un estilo de texto a partir de la fuente del sistema llamado "Calibri". Luego configuro este estilo de texto como estilo de texto predeterminado para el documento.

El método BuildPages ahora contiene código para configurar el tamaño y el margen de todas las páginas. Además, el método llama a BuildTextContent y pasa el contenedor del contenido principal de las páginas como parámetro.

La forma en que construyo el contenido principal es diferente ahora. Utilizo dos tramos de texto y aplico diferentes estilos de texto a cada tramo. De hecho, ambos tramos utilizan el color de acento, pero el segundo tramo también tiene un subrayado.

El código produce un PDF con la fuente y el color del texto personalizado. Si su resultado contiene un mensaje de advertencia de prueba, obtenga una licencia gratuita por tiempo limitado en la página de la biblioteca Docotic.Pdf.

Muchos documentos PDF del mundo real, como informes o facturas, contienen encabezado y pie de página. Déjame mostrarte cómo agregar encabezado y pie de página a un PDF.

El código de muestra para este paso está en la clase HelloWorld4 de la aplicación de ejemplo. A continuación se muestran las partes que cambiaron desde el paso anterior.

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildPagesHeader(pages.Header());
    BuildPagesFooter(pages.Footer());

    BuildTextContent(pages.Content());
}

static void BuildPagesHeader(LayoutContainer header)
{
    header.TextStyle(TextStyle.Parent.FontSize(8))
        .AlignRight()
        .Text(t =>
        {
            t.Line($"Created by: {Environment.UserName}");
            t.Line($"Date: {DateTime.Now}");
        });
}

static void BuildPagesFooter(LayoutContainer footer)
{
    footer.Height(20)
        .Background(new PdfGrayColor(95))
        .AlignCenter()
        .Text(t => t.CurrentPageNumber());
}

Cambié el método BuildPages para llamar a BuildPagesHeader con el contenedor del encabezado como parámetro. El método también llama a BuildPagesFooter pasándole el contenedor de pie de página.

El PDF con encabezado y pie de página resultante definitivamente se parece más a un documento PDF profesional.

El método BuildPagesHeader establece un tamaño de fuente más pequeño para el texto. Utilizo dos líneas para el contenido del encabezado: una con el nombre del usuario actual y la segunda con la fecha actual. El texto está alineado a la derecha.

Tenga en cuenta que no especifico ningún tamaño explícito para el encabezado. Ocupará todo el ancho de la página menos los márgenes izquierdo y derecho. La altura dependerá de la altura de las líneas de texto.

El pie de página es similar, excepto que su altura se especifica explícitamente. Y se le aplica un color de fondo. La parte interesante es que para poner el número de página actual en el pie de página del PDF, puedo usar el método CurrentPageNumber. Todos los cálculos se realizan dentro de la biblioteca.

Imágenes

Como dicen, puedes ahorrar muchas palabras usando una imagen. En una cotización o recibo, probablemente utilizará un logotipo de su empresa o alguna otra imagen importante. Para este código de ejemplo, usaré uno bonito.

El archivo está en nuestro sitio, para que veas cómo se ve el PDF con la hermosa imagen resultante.

Para utilizar una imagen, primero deberá agregarla al documento. La biblioteca puede leer la imagen de un archivo o una secuencia. Cambié el método BuildDocument para mostrar cómo agregar una imagen al documento.

El código de muestra para este paso está en la clase HelloWorld5 de la aplicación de ejemplo. A continuación se muestran las partes que cambiaron desde el paso anterior.

static void BuildDocument(Document doc)
{
    doc.Typography(t =>
    {
        t.Document = doc.TextStyleWithFont(SystemFont.Family("Calibri"));
    });

    var imageFile = new FileInfo("red-flowers-at-butterfly-world.jpg");
    var image = doc.Image(imageFile);

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
        });
    });
}

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildPagesHeader(pages.Header());
    BuildPagesFooter(pages.Footer());
}

static void BuildImageContent(LayoutContainer content, Image image)
{
    content.AlignCenter()
        .PaddingVertical(20)
        .Image(image);
}

Como puede ver, hay más cambios en el método BuildDocument. Antes, el código usaba el método Text para establecer algo de texto como contenido principal de las páginas. Esto todavía sucede en el método BuildTextContent. Pero ahora también quiero una imagen en el contenido.

Para tener tanto el texto como la imagen en el contenido principal de las páginas, necesito un contenedor. Utilizo el contenedor Column para agregar el texto y la imagen verticalmente uno tras otro. El método Item del contenedor de columnas proporciona un subcontenedor. Utilizo una llamada al método Item para obtener un contenedor para el texto y otra llamada para obtener un contenedor para la imagen.

Como puede ver, no cambié ni un poco BuildTextContent. Pero, por supuesto, tuve que eliminar una llamada a BuildTextContent del método BuildPages.

BuildImageContent does an important job in three lines. It adds the image with some padding at the top and at the bottom. And it also centers the image on the page.

Tenemos más información sobre el contenedor Column y las imágenes en los siguientes artículos.

Listas

¿Qué es una lista? Pensemos en ello como un grupo de elementos numerados escritos uno debajo del otro. Esta idea sugiere una forma de implementar una lista.

El código de muestra para este paso está en la clase HelloWorld6 de la aplicación de ejemplo. A continuación se muestran las partes que cambiaron desde el paso anterior.

static void BuildDocument(Document doc)
{
    ....

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
            BuildListContent(c.Item());
        });
    });
}

static void BuildListContent(LayoutContainer content)
{
    var dayNames = DateTimeFormatInfo.InvariantInfo.DayNames;
    var dayNamesSpain = DateTimeFormatInfo.GetInstance(new CultureInfo("es-ES")).DayNames;

    content.Column(column =>
    {
        for (int i = 0; i < dayNames.Length; i++)
        {
            column.Item().Row(row =>
            {
                row.Spacing(5);
                row.AutoItem().Text($"{i + 1}.");
                row.RelativeItem().Text(t => {
                    t.Line(dayNames[i]);
                    t.Line($"In Spain they call it {dayNamesSpain[i]}");
                });
            });
        }
    });
}

El único cambio en BuildDocument es la nueva llamada al método. Agregué la llamada a BuildListContent después de la llamada BuildImageContent.

BuildListContent crea la lista como una columna de filas. En cada fila hay dos elementos. El primero (izquierdo) es para el número de artículo. Otro (derecha) es para el texto del elemento. El código establece explícitamente el espacio entre los elementos de la fila.

Para organizar los elementos, el contenedor Row necesita saber de antemano o calcular el tamaño de cada elemento. Utilizo los métodos AutoItem y RelativeItem en este ejemplo. Como resultado, el contenedor de filas calculará el ancho requerido para contener el primer elemento. Luego, el contenedor utilizará el ancho restante disponible para el segundo artículo.

Después de agregar la lista, el contenido del PDF resultante ya no cabe en una página. Layout API agrega automáticamente la segunda página y coloca la lista en ella. Puede ver que la API repitió el encabezado y el pie de página en la nueva página en el documento PDF con varias páginas.

Tenemos más información sobre listas en el artículo Contenedores compuestos.

Tablas

Muchos documentos PDF contienen tablas. No es de extrañar que las tablas mejoren la claridad y la organización de los datos. Permítanme mostrarles cómo crear una tabla en PDF usando Layout API.

El código de muestra para este paso está en la clase HelloWorld7 de la aplicación de ejemplo. A continuación se muestran las partes que cambiaron desde el paso anterior.

static void BuildDocument(Document doc)
{
    ....

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
            BuildListContent(c.Item());
            BuildTableContent(c.Item());
        });
    });
}

static void BuildTableContent(LayoutContainer content)
{
    var color = new PdfGrayColor(75);

    content.PaddingTop(20).Table(t =>
    {
        t.Columns(c =>
        {
            c.RelativeColumn(4);
            c.RelativeColumn(1);
            c.RelativeColumn(4);
        });

        t.Header(h =>
        {
            h.Cell().Background(color).Text("Month in 2024");
            h.Cell().Background(color).Text("Days");
            h.Cell().Background(color).Text("First Day");
        });

        for (int i = 0; i < 12; i++)
        {
            var stats = GetMonthStats(2024, i);

            t.Cell().Text(stats.Item1);
            t.Cell().Text(stats.Item2);
            t.Cell().Text(stats.Item3);
        }
    });
}

static (string, string, string) GetMonthStats(int year, int monthIndex)
{
    return (
        DateTimeFormatInfo.InvariantInfo.MonthNames[monthIndex],
        DateTime.DaysInMonth(year, monthIndex + 1).ToString(),
        new DateTime(year, monthIndex + 1, 1).DayOfWeek.ToString()
    );
}

La llamada a BuildTableContent es el único cambio en BuildDocument.

En BuildTableContent, creo una tabla simple. La tabla muestra información trivial sobre los meses del año 2024. Para empezar, defino tres columnas con anchos relativos. Las columnas de la izquierda y de la derecha serán 4 veces más anchas que la columna del medio.

El código también define el encabezado de la tabla. Lo hace agregando celdas de encabezado y especificando texto y color de fondo para cada celda. Cuando la tabla no cabe en una página, el encabezado se repite en cada página ocupada por la tabla. Puede ver esto en el documento PDF con tabla resultante. En el documento, la tabla comienza en la segunda página y continúa en la tercera.

Estoy usando el bucle simple para formar filas. El código del bucle comienza recuperando la información de cada mes. Luego agrega tres celdas que forman una fila de información.

El artículo Contenedor Table explica todas las características del contenedor Table con mayor detalle.

Código de muestra

Arriba, presenté algunas de las características más populares de la generación de PDF en C#. Te sugiero que sigas descubriendo la API leyendo el siguiente artículo. Pruebe también el código de muestra del repositorio Docotic.Pdf.Samples en GitHub. El código de muestra para Layout API se encuentra en carpeta de diseño del repositorio.

Puede utilizar el código de muestra como campo de juego de código. De esta manera podrás probar algunas ideas sin tener que empezar desde cero cada vez.

El mismo código de muestra se encuentra en la carpeta Samples del paquete ZIP. Hay dos archivos de solución. SamplesCSharp es para proyectos de muestra que utilizan el lenguaje C#. Y SamplesVB.NET es para las versiones de VB.NET.