How to layout PDF pages

The PdfDocumentBuilder.Generate method provides an object of the Document type to its delegate. Use the Pages method of that object to construct your document pages. You must provide a delegate that accepts a parameter of the PageLayout type to the method.

Laying out PDF pages

One call to the method is enough if all pages in your PDF document have the same layout. In case you have different layouts in your document, use more than one call to the Pages method. For example, you can call the method once to layout a cover page. Then call the method again to describe the report body.

Each call to the Pages creates at least one page. The created page can be empty if there is no content provided for it.

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.5.17615-dev Layout add-on 9.5.17615-dev
Regression tests 14,813 passed Total NuGet downloads 4,924,084

Content slots

To describe the layout of pages, use pre-defined containers. I also call them content slots. You can access these containers by calling methods of a PageLayout object.

There are three main slots: the Content, the Header, and the Footer. And two slots for additional content: the Background and the Foreground. By default, all five containers are empty and occupy no page space. You distribute your page content between the slots according to your requirements.

Read Containers and their content article to know how to layout your pages using containers.

You won't be surprised to know that the header and footer content goes into the Header and the Footer slots, respectively. The API repeats these slots above and below the main content on each generated page. Layout API never splits header or footer content between pages. You will get a LayoutException if a header or a footer does not fit on a page.

Main content

The main page content, like images, tables, and text, goes into the Content slot. Layout API automatically splits that content into pages.

The following code assigns simple text content to all main content slots. The code also sets background colors for the slots.

PdfDocumentBuilder.Create().Generate("pages-main-slots.pdf", doc => doc.Pages(pages => {
    pages.Header()
        .Text("This text goes to the header")
        .BackgroundColor(new PdfRgbColor(66, 135, 245));

    pages.Content()
        .Text("The main content goes in this slot")
        .BackgroundColor(new PdfRgbColor(242, 233, 206));

    pages.Footer()
        .Text("This is the footer contents")
        .BackgroundColor(new PdfRgbColor(194, 192, 188));
}));

Check the result of the code in pages-main-slots.pdf.

As you can see, each slot occupies only part of the page. The exact area depends on the content inside the slot. The Header slot sticks to the top of the page. The Content slot starts immediately after the Header. The Footer slot sticks to the bottom.

Additional content

The Background and the Foreground provide containers usable for watermarks, overlays, and backgrounds. All content in the Background slot goes below the header, the footer, and the main content of a page. The content in the Foreground slot covers everything added to the page.

These containers occupy the whole page. This is the unique feature of these containers. The API repeats their contents on each generated page. Exactly like it does for header and footer containers.

I added some lines to the above code to show how to use the Foreground and the Background containers.

PdfDocumentBuilder.Create().Generate("pages-all-slots.pdf", doc => doc.Pages(pages => {
    // ... 

    pages.Background()
        .Background(new PdfRgbColor(208, 227, 204));

    pages.Foreground()
        .Rotate(45)
        .Text(new string(' ', 30) + "Your watermark could go here, in the foreground");
}));

For the Background container, I do not provide any text or something. I only specify background color. Everything below the header, the main content, and the footer will show that shade of green.

I rotate the content in the Foreground container and add some text to it. Because of the leading spaces, the text does not cover the header or the footer contents. Content in all containers is visible on the page.

You can see the result of the code in pages-all-slots.pdf.

Settings

So far, all code snippets were focusing on containers that make up the pages. It's time to look at how you can customize the pages themselves, rather than their content slots.

To set up pages, use methods of the PageLayout class. Remember that a PageLayout object can describe more than one page. Methods calls will affect all the pages you describe.

Size

Probably, the most basic setting is the page size. You can use the Size method to specify one of the predefined sizes for your pages. There are all the usual sizes like A4, Ledger, or Monarch Envelope.

Optionally, you can specify an orientation for the pages. It's possible to set a custom pages size by providing its width and height in points.

Margins

Page margins may contribute to readability, aesthetics, and overall composition of your pages.

Set all margins to the same value in points using the Margin method. Use the MarginVertical and the MarginHorizontal methods to set only vertical or horizontal margins. Use MarginLeft/Top/Right/Bottom methods to specify each margin independently.

Text style

Layout API provides the TextStyle class for creating text styles. You create and apply styles to text to achieve the desired appearance.

There are cases when a large part of a text on your pages uses the same style. You can set that style as the default text style for the pages. The default style affects all the text in the main content slots. But you can override the default style for certain elements. Just apply another style to pieces of text that should look different.

PdfDocumentBuilder.Create().Generate("pages-text-styles.pdf", doc =>
{
    var defaultStyle = TextStyle.Parent.FontSize(30);
    var tightSpacing = TextStyle.Parent.LetterSpacing(-0.1);

    doc.Pages(pages =>
    {
        pages.TextStyle(defaultStyle);

        pages.Content().Text(t =>
        {
            t.Line("This line uses the default text style.");
            t.Line("This line uses a tight letter spacing.").Style(tightSpacing);
            t.Line("This line uses the default text style, again.");
        });
    });
});

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

Please note that you can set up document-wide text styles using the Document.Typography method. Each property in the Typography class defines a style for a use case. These styles override the style specified by the PageLayout.TextStyle method. For example, the Typography.Body property overrides the default style for text in the Content containers.

Content direction

There are languages written from right to left. Layout API handles text in these languages just fine. But you would need to specify the text direction explicitly.

If most of the text on your pages is in an RTL language, you can set the right-to-left as the default content direction for the pages. You'll be able to specify different direction for any container in your pages.

PdfDocumentBuilder.Create().Generate("pages-content-direction.pdf", doc =>
{
    var defaultTextStyle = doc.TextStyleWithFont(SystemFont.Family("Calibri"));

    doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6).TextStyle(defaultTextStyle);

        pages.ContentFromRightToLeft();

        pages.Content().Column(column =>
        {
            column.Item()
                .ContentFromLeftToRight()
                .Text("There are languages written from right to left.");

            column.Item()
                .Text("هناك لغات تكتب من اليمين إلى اليسار.");

            column.Item()
                .Text("יש שפות שנכתבות מימין לשמאל.");
        });
    });
});

In the code above, I set right-to-left as the default content direction. For the container with the English version of the phrase, I change the direction to left-to-right. You can see the result of the code in pages-content-direction.pdf.

Page numbers

When generating PDF, Layout API automatically calculates the current page number. It also calculate the total number of pages in the document. You can get the numbers by calling the CurrentPageNumber and the PageCount methods of any text container. No matter if the container is in the header, footer or main content slot.

PdfDocumentBuilder.Create().Generate("pages-page-numbers.pdf", doc => doc.Pages(pages =>
{
    pages.Content().Text(t =>
    {
        t.Span("This line is on page ");
        t.CurrentPageNumber();
        t.Line();
        t.Line("Check the footer.");
    });

    pages.Footer().Row(r =>
    {
        r.AutoItem().Text("Created with Docotic.Pdf Layout API");

        r.RelativeItem(2).Text(t =>
        {
            t.AlignRight();

            t.Span("Page ");
            t.CurrentPageNumber();
            t.Span(" of ");
            t.PageCount();
        });
    });
}));

The result of the code is in pages-page-numbers.pdf.

Both methods return a TextPageNumber instance that you can use to format the numbers.

Add header and footer to PDF documents example shows how to apply a custom formatting to page numbers.

Sample code

We have some sample apps that cover the aforementioned features in greater detail. Please spend some time checking them out.