Containers and their content

A document is a set of pages. Each page, in its own turn, is a set of content pieces. As you might already know, PageLayout provides containers for main and additional content. But what to do with these containers?

Containers for page content

Containers help organize content on pages. Everything you put on a page must go into a container. If the content is large enough, a container can occupy space on more than one page.

You can put some text or an image into a container. For alignment and positioning purposes, you can put a container inside another container. There are containers that provide room for more than one piece of content.

Containers on a page are represented by objects of LayoutContainer class. Use this class to set up size, position, content and rendering process of your containers.

This article is part of a series about Layout API for PDF generation. If you are new to the API, read the Getting Started with Layout API part first.

Docotic.Pdf library 9.4.17342 Layout add-on 9.4.17342
Regression tests 14,727 passed Total NuGet downloads 4,260,602

How containers work

You start by obtaining a container. For example, by calling PageLayout.Content method. This method will return a container that occupies no space and has no content or properties set. Use methods of LayoutContainer class to set up the container.

The very important thing to understand is that all LayoutContainer methods put content in the container. When you add some text to the container, it is expected. But what becomes the content when you set an indent or a width?

When you set a container property, the library creates a new container with the specified property. The new container becomes the content of the original container. The library returns the nested container as the result of the method.

Properties of nested containers affect parent containers and vice versa. For example, the width of a nested container affects parent containers size. The default text style of a parent container affects text in nested containers. Layout add-on uses the hierarchy of containers and their properties to build up the resulting layout.

The result of the following code might surprise you, but please spend some time analysing it. The code illustrates how nested containers work.

PdfDocumentBuilder.Create().Generate("containers-how.pdf", doc => doc.Pages(page =>
{
    page.Size(150, 150);
    page.Content()
        .Background(new PdfRgbColor(235, 64, 52)) // red

        .PaddingTop(50)
        .Background(new PdfRgbColor(187, 237, 237)) // blue

        .PaddingRight(50)
        .Background(new PdfRgbColor(15, 130, 9)) // green

        .PaddingBottom(50)
        .Background(new PdfRgbColor(250, 123, 5)) // orange

        .PaddingLeft(50)
        .Background(new PdfRgbColor(204, 204, 204)); // gray
}));

Decoration

Document design involves more than arranging text and images. Properly designed document creates not only effective but also a visually appealing communication.

With Layout API, you can apply a background color to any container. This helps to establish a hierarchy within your content. You can also define boundaries by applying borders. Use vertical and horizontal lines to separate content elements.

PdfDocumentBuilder.Create().Generate("containers-decor.pdf", doc => doc.Pages(page =>
{
    page.Size(150, 150);
    page.Content()
        .Background(new PdfRgbColor(250, 123, 5))
        .Border(b =>
        {
            b.Color(new PdfGrayColor(0), 50);
            b.Thickness(15);
        })
        .PaddingTop(74)
        .LineHorizontal(2)
            .Color(new PdfCmykColor(73, 45, 0, 4))
            .DashPattern(new PdfDashPattern(new double[] { 8, 2 }));
}));

The API supports opaque and semi-translucent colors in Gray, RGB, and CMYK color spaces. Besides solid lines, Layout API provides support for lines that use dash patterns.

The result of the above code is in containers-decor.pdf.

Content

Let's see what LayoutContainer class provides for organizing content.

Text

To add some text to a container, use one of the Text methods.

If all the text uses the same style, use the simple short-hand version of the method. That version accepts a string and returns a TextSpan object. You can use that object to set up text style for the span.

There is another version of Text method that accepts a delegate of type Action<TextContainer>. Use that version to have spans with different styles within one block of text. TextContainer provides methods to insert images and other elements between text spans. There are other advanced features, like the ability to set a distance between paragraphs.

PdfDocumentBuilder.Create().Generate("containers-text.pdf", doc => doc.Pages(page =>
{
    page.Header().Text("This is a simple text span");

    page.Content().Text(t =>
    {
        t.Span("This line contains ");
        t.Span("some underlined text").Style(TextStyle.Parent.Underline());
    });
}));

You can see the result of the code in containers-text.pdf.

Image

Layout API provides methods for creating Image objects from image data in a file or a stream. Any Image object can serve as the content of a container. You can use the same Image object in multiple containers. There are different content modes that affect how the image looks like inside the container.

The library can load images in raster formats only: PNG, JPEG, JPEG 2000, BMP, GIF, and TIFF.

PdfDocumentBuilder.Create().Generate("containers-image.pdf", doc =>
{
    var imageFile = new FileInfo(@"path-to-image.jpg");
    var image = doc.Image(imageFile);

    doc.Pages(pages =>
    {
        pages.Size(image.Width, image.Height);
        pages.Content().Image(image, ImageContentMode.FitArea);
    });
});

Column

Columns provide space for an unlimited number of items placed vertically one after another. You can use items of any type inside a column. For example, a column can contain image and text items. Each item's width is equal to column width. The height of each item depends on the item's content and properties.

PdfDocumentBuilder.Create().Generate("containers-column.pdf", doc => doc.Pages(page =>
{
    page.Content().Column(c =>
    {
        for (int i = 0; i < 10; i++)
        {
            PdfColor color = i % 2 == 0
                ? new PdfRgbColor(187, 237, 237)
                : new PdfGrayColor(66);

            c.Item().Background(color).Height(10 + i * 3);
        }
    });
}));

You can see the result of the code in containers-column.pdf.

Row

Row containers help organize an unlimited number of items horizontally. Each item in a row is a container. Because of this, you can put content of different types in a row.

PdfDocumentBuilder.Create().Generate("containers-row.pdf", doc => doc.Pages(page =>
{
    var rowItems = new[] { "three", "two", "one" };

    page.Content().Row(row =>
    {
        for (int index = 0; index < rowItems.Length; index++)
        {
            row.AutoItem().Text(rowItems[index]);

            if (index != rowItems.Length - 1)
                row.AutoItem().PaddingHorizontal(10).LineVertical(0.5);
        }
    });
}));

You can see the result of the code in containers-row.pdf.

Table

Use Table containers to layout your most complex data. Start by defining at least one column and then fill columns and rows by calling Cell method multiple times.

Tables can have a header and a footer. Cells in a table can span more than one column and/or row. Here is a code that adds a simple table.

PdfDocumentBuilder.Create().Generate("containers-table.pdf", doc => doc.Pages(page =>
{
    page.Content().Table(t =>
    {
        t.Columns(c =>
        {
            for (int i = 0; i < 4; ++i)
                c.ConstantColumn(50);
        });

        for (int i = 0; i < 16; i++)
        {
            t.Cell()
                .Border(b => b.Thickness(0.5))
                .PaddingHorizontal(10)
                .Text($"{i + 1}");
        }
    });
}));

The result of the code is in containers-table.pdf.

Inlined

Inlined container provides a convenient way to fill an area with items from a collection of containers. You just add items one after another and the library puts them in a row one after another. If there is no space to put an item, the library starts a new row.

Layers

There are cases when it's best to put content on several layers. Layers container serves exactly this purpose. You must define exactly one primary layer and any number of non-primary ones. Layout API will compose layers in the same order as you create them.

Element

This is a special kind of content. You can create an element dynamically and put the result in a container.

Dynamically created elements can provide layout that depends on page number, size and other properties.