How to layout PDF pages

PdfDocumentBuilder.Generate method provides an object of Document type to its delegate. Use Pages method of that object to construct your document pages. You must provide a delegate that accepts a parameter of 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 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 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.3.17105-dev Layout add-on 9.3.17105-dev
Regression tests 14,681 passed Total NuGet downloads 4,234,061

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: Content, Header, and Footer. And two slots for additional content: Background and 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 Header and 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 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. Header slot sticks to the top of the page. Content slot starts immediately after the Header. Footer slot sticks to the bottom.

Additional content

Background and Foreground provide containers usable for watermarks, overlays, and backgrounds. All content in Background slot goes below the header, the footer, and the main content of a page. The content in 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 Foreground and 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 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 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 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 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 Margin method. Use MarginVertical and 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 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 Document.Typography method. Each property in Typography class defines a style for a use case. These styles override the style specified by PageLayout.TextStyle method. For example, Body property overrides the default style for text in 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 CurrentPageNumber and 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 TextPageNumber 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.