Эта страница может содержать автоматически переведенный текст.

Контейнеры и их содержимое

Документ — это набор страниц. Каждая страница, в свою очередь, представляет собой набор фрагментов содержимого. Как вы, возможно, уже знаете, класс PageLayout предоставляет контейнеры для основного и дополнительного содержимого. Но что делать с этими контейнерами?

Контейнеры для содержимого страницы

Контейнеры помогают организовать содержимое на страницах. Все, что вы размещаете на странице, должно попадать в контейнер. Если содержимое достаточно велико, контейнер может занимать место на более чем одной странице.

В контейнер можно поместить текст или изображение. Для выравнивания и позиционирования можно поместить один контейнер внутрь другого. Есть контейнеры, которые предоставляют место для более чем одного фрагмента содержимого.

Контейнеры на странице представлены объектами класса LayoutContainer. Используйте этот класс для задания размера, положения, содержимого и процесса рендеринга контейнеров.

Эта статья входит в серию о Layout API для генерации PDF. Если вы впервые работаете с API, сначала прочитайте раздел Начало работы с Layout API.

Как работают контейнеры

Начните с получения контейнера. Например, вызвав метод PageLayout.Content. Этот метод возвращает контейнер, который не занимает места и не имеет заданных содержимого или свойств. Используйте методы класса LayoutContainer для настройки контейнера.

Очень важно понимать, что все методы LayoutContainer помещают содержимое в контейнер. Когда вы добавляете в контейнер текст, это ожидаемо. Но что становится содержимым, когда вы задаете отступ или ширину?

Когда вы задаете свойство контейнера, библиотека создает новый контейнер с указанным свойством. Новый контейнер становится содержимым исходного контейнера. Библиотека возвращает вложенный контейнер как результат метода.

Свойства вложенных контейнеров влияют на родительские контейнеры и наоборот. Например, ширина вложенного контейнера влияет на размер родительского контейнера. Стиль текста по умолчанию у родительского контейнера влияет на текст во вложенных контейнерах. Дополнение Layout использует иерархию контейнеров и их свойства для построения итоговой разметки.

Результат следующего кода может вас удивить, но потратьте немного времени на его анализ. Код показывает, как работают вложенные контейнеры.

PdfDocumentBuilder.Create().Generate("containers-how.pdf", doc => doc.Pages(page =>
{
    page.Size(150, 150);
    page.Content()
        .Background(new PdfRgbColor(235, 64, 52)) // красный

        .PaddingTop(50)
        .Background(new PdfRgbColor(187, 237, 237)) // синий

        .PaddingRight(50)
        .Background(new PdfRgbColor(15, 130, 9)) // зеленый

        .PaddingBottom(50)
        .Background(new PdfRgbColor(250, 123, 5)) // оранжевый

        .PaddingLeft(50)
        .Background(new PdfRgbColor(204, 204, 204)); // серый
}));

Оформление

Оформление документа — это не только размещение текста и изображений. Грамотно оформленный документ обеспечивает не только эффективность, но и визуально привлекательную коммуникацию.

С помощью Layout API можно задать цвет фона любому контейнеру. Это помогает выстроить иерархию содержимого. Можно также задать границы с помощью рамок. Используйте вертикальные и горизонтальные линии для разделения элементов содержимого.

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 }));
}));

API поддерживает непрозрачные и полупрозрачные цвета в Gray, RGB и CMYK цветовых пространствах. Помимо сплошных линий, Layout API поддерживает линии с шаблонами штрихов.

Результат приведенного выше кода находится в containers-decor.pdf.

Содержимое

Посмотрим, что класс LayoutContainer предоставляет для организации содержимого.

Text

Чтобы добавить текст в контейнер, используйте один из методов Text.

Если весь текст использует один и тот же стиль, используйте простую сокращенную версию метода. Эта версия принимает строку и возвращает объект TextSpan. Вы можете использовать этот объект для настройки стиля текста для фрагмента.

Есть и другая версия метода Text, которая принимает делегат типа Action<TextContainer>. Используйте эту версию, чтобы иметь фрагменты с разными стилями внутри одного блока текста. TextContainer предоставляет методы для вставки изображений и других элементов между фрагментами текста. Есть и другие расширенные возможности, например задание расстояния между абзацами.

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());
    });
}));

Результат кода можно увидеть в containers-text.pdf.

Image

Layout API предоставляет методы для создания объектов Image из данных изображения в файле или потоке. Любой объект Image может служить содержимым контейнера. Один и тот же объект Image можно использовать в нескольких контейнерах. Существуют разные режимы отображения, которые влияют на то, как изображение выглядит внутри контейнера.

Библиотека может загружать изображения только в растровых форматах: PNG, JPEG, JPEG 2000, BMP, GIF и 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

Колонки предоставляют место для неограниченного числа элементов, расположенных вертикально один за другим. В колонке можно использовать элементы любого типа. Например, колонка может содержать элементы изображения и текста. Ширина каждого элемента равна ширине колонки. Высота каждого элемента зависит от содержимого и свойств элемента.

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);
        }
    });
}));

Результат кода можно увидеть в containers-column.pdf.

Подробную информацию о возможностях контейнера Column см. в статье Составные контейнеры.

Row

Контейнеры Row помогают организовать неограниченное число элементов по горизонтали. Каждый элемент в строке является контейнером. Благодаря этому в строку можно помещать содержимое разных типов.

var rowItems = new[] { "three", "two", "one" };

PdfDocumentBuilder.Create().Generate("containers-row.pdf", doc => doc.Pages(page =>
{
    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);
        }
    });
}));

Результат кода можно увидеть в containers-row.pdf.

Подробная информация о контейнерах Row содержится в статье Составные контейнеры.

Table

Используйте контейнеры Table для компоновки наиболее сложных данных. Сначала определите хотя бы один столбец, а затем заполните столбцы и строки, многократно вызывая метод Cell.

У таблиц могут быть заголовок и нижний колонтитул. Ячейки в таблице могут занимать более одного столбца и/или строки. Вот код, который добавляет простую таблицу.

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}");
        }
    });
}));

Результат кода находится в containers-table.pdf.

Ознакомьтесь со всеми возможностями контейнера Table в статье Контейнер таблицы.

Inlined

Тип контейнера InlineContainer предоставляет удобный способ заполнить область элементами из коллекции контейнеров. Вы просто добавляете элементы один за другим, и библиотека размещает их в строку один за другим. Если для элемента не хватает места, библиотека начинает новую строку.

Дополнительная информация о контейнерах InlineContainer содержится в статье Составные контейнеры.

Layers

Есть случаи, когда лучше размещать содержимое на нескольких слоях. Тип контейнера LayerContainer предназначен именно для этого. Необходимо определить ровно один основной слой и любое количество неосновных. Layout API будет компоновывать слои в том же порядке, в котором вы их создаете.

Подробнее о типе LayerContainer см. в Составные контейнеры.

Element

Это особый вид содержимого. Вы можете динамически создать элемент и поместить результат в контейнер.

Динамически созданные элементы могут формировать разметку, зависящую от номера страницы, размера и других свойств.