Esta página puede contener texto traducido automáticamente.
Contenedores compuestos
Los contenedores compuestos desempeñan un papel crucial en la estructuración y organización del contenido. Con un contenedor adecuado, puede presentar texto e imágenes de forma fácil de usar.
Sin duda, es posible construir un documento usando solo contenedores simples de texto e imagen. Sin embargo, hay requisitos que son difíciles o imposibles de implementar solo con contenedores simples. Los contenedores compuestos ayudan en esos casos. Además, los contenedores compuestos ayudan a lograr sus objetivos con menos código.

Use los métodos de la clase LayoutContainer para agregar contenedores compuestos como Row y Column a las páginas del documento. 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 igualmente importantes.
Este artículo forma parte de una serie sobre Layout API para la generación de PDF. Si es nuevo en la API, lea primero la sección Introducción a Layout API.
Row
Los contenedores Row ofrecen espacio para elementos dispuestos horizontalmente en una sola línea. Cada elemento de una fila es un contenedor. Esto significa que puede colocar contenido de distintos tipos en una sola fila. Puede especificar el espaciado entre elementos usando el método Spacing.
Todos los elementos de una fila tienen la misma altura. La biblioteca usa la altura del elemento más alto como altura de la fila. Hay tres formas de especificar el ancho de un elemento. Debe elegir una al crear el elemento. Una fila puede contener elementos creados de distintas maneras.
El método Row.AutoItem crea un elemento sin ancho especificado explícitamente. Para esos elementos, la biblioteca calcula el tamaño intrínseco de su contenido. El ancho de contenido calculado es el ancho del elemento. Tenga en cuenta que los elementos creados con AutoItem no ajustan líneas largas.
Use el método ConstantItem para crear un elemento con un ancho igual a un número exacto de puntos.
RelativeItem es útil cuando no conoce los anchos exactos de los elementos de la fila y no desea usar 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 elemento. 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 sola llamada a RelativeItem, entonces el número no importa. El elemento 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 toman 6 partes (2 + 3 + 1 = 6). Los elementos individuales ocupan 2 de 6, 3 de 6 y 1 de 6 partes, respectivamente.
Layout API usa la siguiente fórmula para calcular el ancho de una parte:
PartWidth = (RowWidth - AutoWidth - ConstantWidth) / TotalParts
dónde:
RowWidth = width of the row container
AutoWidth = width of all items created with the AutoItem method
ConstantWidth = width of all items created with the ConstantItem method
Aquí hay 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 disponer elementos verticalmente, uno después de otro, use un contenedor Column. Cada elemento de una columna es un contenedor. Por ello, puede colocar contenido de distintos tipos en una sola 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. Use 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 en cada página.
Use el método Spacing para agregar algo de espacio vertical entre los elementos de la columna. Tenga en cuenta que la biblioteca no aplica espaciado entre el encabezado y el primer elemento. La biblioteca tampoco agrega espacio antes del pie.
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. En este aspecto, las cuadrículas son similares a las tablas. Layout API no proporciona un tipo de contenedor especial para cuadrículas. Puede implementar un diseño de cuadrícula usando los contenedores Column y Row.
Ayuda pensar en una cuadrícula como una columna, donde cada uno de sus elementos es una fila. Tanto el contenedor Column como el contenedor Row ofrecen la posibilidad de establecer espaciado entre elementos. Si lo desea, puede tener encabezado y pie.
Cada fila puede tener un diseño independiente. Puede haber una cantidad distinta de elementos en cada fila. Los elementos pueden tener ancho y alto diferentes. Es posible agregar espacio adicional antes, después o entre los elementos de una fila. Use elementos sin contenido ni decoración para ello.
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.
Listas
Las listas mejoran la legibilidad al desglosar la información en puntos concisos. Los elementos de lista pueden tener números, viñetas y otros símbolos junto al texto. Puede implementar fácilmente un diseño de lista usando los contenedores Column y Row. Layout API no proporciona un tipo de contenedor especial para listas.
Revise el código de ejemplo que crea una lista de meses por estaciones. Tenga en cuenta que la lista tiene un encabezado. Si los elementos contienen texto que puede ajustarse a la línea siguiente, use 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 tabla organizan elementos en columnas y filas. El tipo de contenedor Table proporciona un conjunto amplio de características y puede ayudarle con los casos más sofisticados. Lea sobre todas las características en el artículo Contenedor de tabla.
InlineContainer
Puede rellenar 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. Use los métodos Spacing/HorizontalSpacing/VerticalSpacing para agregar algo de espacio entre elementos.
Puede afectar la posición de los elementos en el contenedor usando métodos de alineación. Los métodos AlignTop/AlignMiddle/AlignBottom alinean los elementos verticalmente. Para la dirección horizontal, use los métodos AlignLeft/AlignCenter/AlignRight/AlignJustify.
Hay un caso especial. El método AlignSpaceAround alinea los elementos horizontalmente. También agrega espaciado 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
Puede necesitar colocar contenido por debajo y/o por encima del contenido principal de la página. 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. Con ese objeto, puede 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. Debe agregar exactamente una capa primaria.
Layout API compondrá las capas en el mismo orden en que las cree. Las capas agregadas antes de la capa primaria irán al fondo. Todas las capas agregadas después de la capa primaria irán por encima del contenido principal. El contenedor repite las capas secundarias en todas las páginas ocupadas por el contenido principal.
Aquí hay un código de ejemplo para 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);
});
}));
Puede 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 diversos motivos. Puede agregar una marca de agua al PDF para identificar la propiedad o para señalar la confidencialidad o sensibilidad de la información en el PDF.
Una forma de aplicar marcas de agua a los PDF es usar capas. Vea el ejemplo en la sección anterior. Permítame mostrarle otro enfoque.
Usaré los contenedores de fondo y primer plano de la página para aplicar marcas de agua a los PDF. El complemento Layout repite estos contenedores en las páginas siguientes. Cada página con contenido principal del documento 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 al 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 usen la imagen de fondo. La API agregará solo una copia de los bytes de la imagen al PDF generado.
El contenido principal del documento puede ser cualquier cosa. Para este código de ejemplo, uso el famoso texto Lorem Ipsum.
La marca de agua de texto va al contenedor de primer plano. El texto en sí puede ser cualquiera y puede usar cualquier color o fuente. Usé texto rotado dibujado con letras rojas semitransparentes de mayor tamaño.
El código de ejemplo descarga de forma asincrónica tanto la imagen como el texto desde nuestro repositorio de código de ejemplo. Por supuesto, puede usar una imagen local y/o leer el texto desde 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 ejemplo que cubren las características mencionadas con más detalle. Dedique algo de tiempo a revisarlas.