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

Contenedores compuestos

Los contenedores complejos desempeñan un papel crucial en la estructuración y organización del contenido. Al utilizar un contenedor adecuado, puede presentar texto e imágenes fácilmente de una manera fácil de usar.

Definitivamente es posible construir un documento utilizando sólo contenedores simples de texto e imágenes. Sin embargo, existen requisitos que son difíciles o imposibles de implementar únicamente con contenedores simples. Los contenedores compuestos ayudan en tales casos. Además, los contenedores complejos ayudan a lograr sus objetivos con menos código.

Contenedores compuestos

Utilice métodos de la clase LayoutContainer para agregar contenedores complejos como Row y Column a las páginas de documentos. Con estos contenedores, puede implementar otros contenedores como cuadrículas y listas. También existen los métodos Inlined y Layers para casos menos comunes pero aún importantes.

Este artículo es parte de una serie sobre la API Layout para la generación de PDF. Si es nuevo en la API, lea primero la parte Introducción a la API de Layout.

Biblioteca Docotic.Pdf 9.5.17615-dev Complemento de diseño 9.5.17615-dev
Pruebas de regresión 14,813 pasaron Descargas totales de NuGet 4,924,084

Row

Los contenedores Row brindan espacio para elementos dispuestos horizontalmente en una línea. Cada elemento de una fila es un contenedor. Esto significa que puedes poner contenido de diferentes tipos en una fila. Puede especificar el espacio entre elementos utilizando el método Spacing.

Todos los elementos de una fila tienen la misma altura. La biblioteca utiliza la altura del elemento más alto como altura de la fila. Hay tres formas de especificar el ancho de un elemento. Debes elegir uno al crear el artículo. Una fila puede contener elementos creados de diferentes maneras.

El método Row.AutoItem crea un elemento sin un ancho especificado explícitamente. Para dichos elementos, la biblioteca calcula el tamaño intrínseco de su contenido. El ancho del contenido calculado es el ancho del elemento. Tenga en cuenta que los elementos creados con AutoItem no envuelven líneas largas.

Utilice el método ConstantItem para crear un elemento con un ancho igual a un número exacto de puntos.

El RelativeItem es útil cuando no se conocen los anchos exactos de los elementos de la fila y no se desea utilizar tamaños intrínsecos. En su lugar, puede especificar anchos relativos para los elementos de la fila. El método acepta la cantidad de partes que debe ocupar el artículo. El número total de partes es la suma de todos los números en todas las llamadas a RelativeItem en esta fila.

Por ejemplo, si hay una llamada RelativeItem, entonces el número no es importante. El artículo ocupará todo el ancho disponible. Para dos o más elementos, los números definen la proporción.

row.RelativeItem(2)
row.RelativeItem(3)
row.RelativeItem(1)

Todos los elementos creados con el código anterior tienen 6 partes (2 + 3 + 1 = 6). Los elementos individuales ocupan 2 de 6, 3 de 6 y 1 de 6 partes, respectivamente.

Layout API utiliza la siguiente fórmula para calcular el ancho de una parte:

PartWidth = (RowWidth - AutoWidth - ConstantWidth) / TotalParts

dónde:
RowWidth = ancho del contenedor de filas
AutoWidth = ancho de todos los elementos creados con el método AutoItem
ConstantWidth = ancho de todos los elementos creados con el método ConstantItem

A continuación se muestra un ejemplo que crea una fila con elementos de los tres tipos.

var monthNames = DateTimeFormatInfo.InvariantInfo.MonthNames;
var groups = new[]
{
    string.Join(", ", monthNames.Take(4)),
    string.Join(", ", monthNames.Skip(4).Take(4)),
    string.Join(", ", monthNames.Skip(8).Take(4)),
};

PdfDocumentBuilder.Create().Generate("compounds-row.pdf", doc => doc.Pages(page =>
{
    page.Content()
        .Padding(20)
        .MinimalBox()
        .Row(row =>
        {
            row.ConstantItem(100)
                .Background(new PdfRgbColor(187, 237, 237))
                .Text("100 points wide");

            for (int i = 0; i < groups.Length; i++)
            {
                row.AutoItem().LineVertical(0.1);

                var numberOfParts = groups.Length - i + 1;
                row.RelativeItem(numberOfParts)
                    .PaddingHorizontal(5)
                    .Text(t =>
                    {
                        t.Line($"{numberOfParts} parts wide");
                        t.Line();
                        t.Line(groups[i]);
                    });
            }
        });
}));

Puede ver el resultado del código en compounds-row.pdf.

Column

Para organizar elementos verticalmente, uno tras otro, utilice un contenedor Column. Cada elemento de una columna es un contenedor. Debido a esto, puedes colocar contenido de diferentes tipos en una columna.

El ancho de cada elemento es igual al ancho de la columna. La altura de cada elemento depende del contenido y las propiedades del elemento. Los contenedores Column admiten paginación, por lo que el complemento Layout puede representar elementos de una columna en más de una página.

De forma predeterminada, un contenedor Column no tiene contenido de encabezado ni pie de página. Utilice los métodos Header y Footer para acceder y configurar los contenedores correspondientes. Cuando los elementos de la columna ocupan más de una página, la biblioteca repite tanto los encabezados como los pies de página en cada página.

Utilice el método Spacing para agregar algo de espacio vertical entre los elementos de la columna. Tenga en cuenta que la biblioteca no aplica espacios entre el encabezado y el primer elemento. La biblioteca tampoco agrega espacio antes del pie de página.

PdfDocumentBuilder.Create().Generate("compounds-column.pdf", doc => doc.Pages(page =>
{
    page.Size(PdfPaperSize.A6);

    page.Content()
        .Padding(20)
        .Column(column =>
        {
            column.Header()
                .Background(new PdfRgbColor(187, 237, 237))
                .Padding(5)
                .Text("Month names");

            for (int i = 0; i < 12; i++)
            {
                column.Item().Background(
                    new PdfGrayColor(i % 2 == 0 ? 90 : 100))
                    .Padding(5)
                    .Text(DateTimeFormatInfo.InvariantInfo.MonthNames[i]);
            }

            column.Footer().LineHorizontal(1);
        });
}));

Puede ver el resultado del código en compounds-column.pdf.

Cuadrícula

Los diseños de cuadrícula organizan elementos en columnas y filas. Las cuadrículas son similares a las tablas en este aspecto. Layout API no proporciona un tipo de contenedor especial para cuadrículas. Puede implementar un diseño de cuadrícula utilizando los contenedores Column y Row.

Es útil pensar en una cuadrícula como una columna, en la que cada uno de sus elementos es una fila. Tanto el contenedor Column como el Row brindan la posibilidad de establecer el espacio entre elementos. Puedes tener un encabezado y un pie de página, si lo deseas.

Cada fila puede tener un diseño independiente. Puede haber una cantidad diferente de elementos en cada fila. Los artículos pueden tener diferentes anchos y altos. Es posible agregar espacio adicional antes, después o entre elementos de una fila. Utilice para ello elementos sin contenido ni decoración.

var blue = new PdfRgbColor(187, 237, 237);
var darkerBlue = blue.Darken(50);
PdfDocumentBuilder.Create().Generate("compounds-grid.pdf", doc => doc.Pages(page =>
{
    page.Size(300, 200);

    page.Content().Padding(15).Column(column =>
    {
        column.Spacing(10);

        column.Item().Row(row =>
        {
            row.Spacing(10);

            row.ConstantItem(100).Background(darkerBlue).Height(40);
            row.RelativeItem(4).Background(blue);
        });

        column.Item().Row(row =>
        {
            row.Spacing(10);

            row.RelativeItem(2).Background(blue).Height(60);
            row.RelativeItem(1);
            row.RelativeItem(2).Background(blue);
        });

        column.Item().Row(row =>
        {
            row.Spacing(10);

            row.RelativeItem(1).Background(blue).Height(50);
            row.RelativeItem(3).Background(blue);
            row.ConstantItem(50).Background(darkerBlue);
        });
    });
}));

Puede ver el resultado del código en compounds-grid.pdf.

Liza

Las listas mejoran la legibilidad al dividir la información en puntos concisos. Los elementos de la lista pueden tener números, viñetas y otros símbolos junto al texto. Puede implementar fácilmente un diseño de lista utilizando los contenedores Column y Row. Layout API no proporciona un tipo de contenedor especial para listas.

Consulte el código de ejemplo que crea una lista de meses por temporadas. Tenga en cuenta que la lista tiene un encabezado. Si los elementos contienen texto que puede pasar a la siguiente línea, utilice el método RelativeItem o ConstantItem para la parte de texto del elemento.

var monthNames = DateTimeFormatInfo.InvariantInfo.MonthNames.ToList();
monthNames.Insert(0, monthNames[11]);

PdfDocumentBuilder.Create().Generate("compounds-list.pdf", doc => doc.Pages(page =>
{
    page.Size(150, 200);

    page.Content().Padding(5).Column(column =>
    {
        column.Header()
            .Text("Months by seasons:")
            .Style(TextStyle.Parent.Underline());

        for (int i = 0; i < 4; i++)
        {
            var season = string.Join(", ", monthNames.Skip(i * 3).Take(3));
            column.Item().Row(row =>
            {
                row.Spacing(2);

                row.AutoItem().Text("•");
                row.RelativeItem().Text(season);
            });
        }
    });
}));

Puede ver el resultado del código en compounds-list.pdf.

Table

Los diseños de tablas organizan los elementos en columnas y filas. El tipo de contenedor Table proporciona un amplio conjunto de características y puede ayudarle con los casos más sofisticados. Lea acerca de todas las funciones en el artículo Contenedor Table.

InlineContainer

Puede llenar un contenedor con una colección de otros contenedores. Comience llamando al método LayoutContainer.Inlined. Luego llame al método Item del InlineContainer proporcionado para agregar contenedores secundarios.

El complemento Layout coloca los contenedores en una fila, uno tras otro. Si no hay espacio para colocar un elemento, la biblioteca comienza una nueva fila. Utilice los métodos Spacing/HorizontalSpacing/VerticalSpacing para agregar algo de espacio entre elementos.

Puede afectar la posición de los elementos en el contenedor utilizando métodos de alineación. Los métodos AlignTop/AlignMiddle/AlignBottom alinean los elementos verticalmente. Para la dirección horizontal, utilice los métodos AlignLeft/AlignCenter/AlignRight/AlignJustify.

Hay un caso especial. El método AlignSpaceAround que alinea elementos horizontalmente. También agrega espacio adicional antes del primer elemento y después del último.

var orange = new PdfRgbColor(250, 123, 5);
var brown = orange.Darken(50);
var itemProps = new (int Width, PdfColor Color)[] {
    (20, orange), (30, orange), (50, brown), (50, orange), (50, orange),
    (30, orange), (20, brown), (30, orange), (50, brown), (10, brown)
};

PdfDocumentBuilder.Create().Generate("compounds-inlined.pdf", doc => doc.Pages(page =>
{
    page.Size(150, 120);

    page.Content().Inlined(c =>
    {
        c.Spacing(5);

        foreach (var (Width, Color) in itemProps)
            c.Item().Height(30).Width(Width).Background(Color);
    });
}));

Puede ver el resultado del código en compounds-inlined.pdf.

LayerContainer

Es posible que necesites colocar algún contenido debajo y/o encima del contenido de la página principal. El caso de uso obvio es agregar una marca de agua encima de las páginas PDF.

Primero, obtenga un objeto LayerContainer llamando al método LayoutContainer.Layers. Dado el objeto, puedes comenzar a agregar capas. Llamar al método Layer agrega una capa secundaria. Llame al método PrimaryLayer para agregar la capa de contenido principal. Debes agregar exactamente una capa primaria.

Layout API compondrá capas en el mismo orden en que las crea. Las capas agregadas antes de la capa principal pasarán a un segundo plano. Todas las capas agregadas después de la capa principal irán por encima del contenido principal. Container repite las capas secundarias en todas las páginas ocupadas por el contenido principal.

Aquí hay un código de muestra sobre cómo agregar marcas de agua a páginas PDF.

PdfDocumentBuilder.Create().Generate("compounds-layers.pdf", doc => doc.Pages(page =>
{
    var largeRedText = TextStyle.Parent.FontSize(48)
        .FontColor(new PdfRgbColor(235, 64, 52));

    page.Size(400, 250);

    page.Content()
        .Padding(25)
        .Layers(layers =>
        {
            layers.Layer()
                .AlignCenter()
                .Text(text => text.CurrentPageNumber().Style(largeRedText));

            layers.PrimaryLayer()
                .Background(new PdfGrayColor(85), 65)
                .Padding(25)
                .Text(new string('_', 790));

            layers.Layer()
                .AlignCenter()
                .AlignMiddle()
                .Text("Watermark")
                .Style(largeRedText);
        });
}));

Puedes ver el resultado del código en compounds-layers.pdf.

Marcas de agua

Uno de los requisitos comunes es agregar una marca de agua al PDF. El requisito puede existir por diversas razones. Puede agregar una marca de agua al PDF para identificar la propiedad o señalar la confidencialidad o sensibilidad de la información en el PDF.

Un método para poner marcas de agua en archivos PDF es utilizar capas. Vea el ejemplo en la sección anterior. Déjame mostrarte otro enfoque.

Usaré contenedores de fondo y primer plano de la página para marcar archivos PDF con marcas de agua. El complemento Layout repite estos contenedores en las páginas siguientes. Cada página con contenido de documento principal también contendrá contenedores de fondo y primer plano. Esto los hace adecuados para la tarea.

Empiezo agregando una imagen al contenedor de fondo. Puede agregar su logotipo a PDF de la misma manera. La imagen aparecerá detrás del contenido de la página. No importa cuántas páginas de su documento utilicen la imagen de fondo. La API agregará solo una copia de los bytes de la imagen al PDF generado.

El contenido del documento principal puede ser cualquier cosa. Para este código de muestra, utilizo el famoso texto de Lorem Ipsum.

La marca de agua del texto va al contenedor de primer plano. El texto en sí puede ser cualquier cosa y puedes usar cualquier color o fuente. Utilicé texto rotado dibujado con letras rojas semitransparentes de mayor tamaño.

El código de muestra descarga de forma asincrónica tanto la imagen como el texto de nuestro repositorio de código de muestra. Por supuesto, puedes usar una imagen local y/o leer el texto de un archivo.

var urlPrefix =
    "https://raw.githubusercontent.com/BitMiracle/Docotic.Pdf.Samples/master/Samples/Sample%20Data/";

using var client = new HttpClient();
using var imageResponse = await client.GetAsync(urlPrefix + "watermark-background.png");
using var imageStream = await imageResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);

var loremIpsum = string.Empty;
using (var textResponse = await client.GetAsync(urlPrefix + "lorem-ipsum.txt"))
     loremIpsum = await textResponse.Content.ReadAsStringAsync().ConfigureAwait(false);

PdfDocumentBuilder.Create().Generate("compounds-watermarks.pdf", doc =>
{
    var image = doc.Image(imageStream);

    doc.Pages(page =>
    {
        page.Size(400, 250);

        page.Background()
            .AlignMiddle()
            .Image(image);

        page.Content()
            .Padding(25)
            .Text(loremIpsum);

        var largeRedText = TextStyle.Parent.FontSize(48)
            .FontColor(new PdfRgbColor(235, 64, 52), 50);
        page.Foreground()
            .Rotate(-30)
            .Translate(-50, 180)
            .Text("DO NOT COPY")
            .Style(largeRedText);
    });
});

Puede ver el resultado del código en compounds-watermarks.pdf.

Código de muestra

Tenemos algunas aplicaciones de muestra que cubren con mayor detalle las funciones antes mencionadas. Dedique algún tiempo a revisarlos.