该页面可以包含自动翻译的文本。

复合容器

复杂容器在构建和组织内容方面起着至关重要的作用。通过使用合适的容器,您可以轻松地以用户友好的方式呈 现文本和图像。

仅使用简单的文本和图像容器构建文档绝对是可能的。不过,有些要求很难或不可能仅使用简单容器来实现。复 合容器在这种情况下会有所帮助。此外,复杂容器有助于用更少的代码实现您的目标。

复合容器

使用 LayoutContainer 类的方法将复杂的容器(如 RowColumn)添加 到文档页面。使用这些容器,您可以实现其他容器(如网格和列表)。对于不太常见但仍然重要的情况,还有 InlinedLayers 方法。

本文是有关用于 PDF 生成的 Layout API 的系列文章的一部分。 如果您是 API 新手,请先阅读 Layout API入 门 部分。

Docotic.Pdf 库 9.5.17615-dev 布局附加组件 9.5.17615-dev
回归测试 14,813 通过 NuGet 总下载量 4,924,084

Row

Row 容器为水平排列在一行中的项目提供空间。一行中的每个项目都 是一个容器。这意味着您可以将不同类型的内容放在一行中。您可以使用 Spacing 方法指定项目之间的间距。

一行中的所有项目都具有相同的高度。库使用最高项目的高度作为行的高度。有三种方法可以指定项目的宽度。 创建项目时必须选择一种。一行可以包含以不同方式创建的项目。

Row.AutoItem 方法会创建一个没有明确指定宽度的项目。 对于此类项目,库会计算其内容的固有大小。计算出的内容宽度就是项目的宽度。请注意,使用 AutoItem 创 建的项目不会换行。

使用 ConstantItem 方法创建一个宽度等于精确点数的 项目。

如果您不知道行中项目的确切宽度,并且不想使用固有大小,那么 RelativeItem 就很方便。相反,您可以为行中的项目 指定相对宽度。该方法接受项目应占据的部分数量。部分总数是此行中所有 RelativeItem 调用中的所有数字 的总和。

例如,如果有一个 RelativeItem 调用,则数字并不重要。该项目将占据所有可用宽度。对于两个或更多项 目,数字定义比例。

row.RelativeItem(2)
row.RelativeItem(3)
row.RelativeItem(1)

使用上述代码创建的所有项目都包含 6 个部分(2 + 3 + 1 = 6)。单个项目分别占 6 个部分中的 2 个、6 个部分中的 3 个和 6 个部分中的 1 个。

Layout API 使用以下公式来计算一个部分的宽度:

PartWidth = (RowWidth - AutoWidth - ConstantWidth) / TotalParts

其中:
RowWidth = 行容器的宽度
AutoWidth = 使用 AutoItem 方法创建的所有项目的宽度
ConstantWidth = 使用 ConstantItem 方法创建的所有项目的宽度

下面是一个创建包含所有三种项目的行的示例。

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

您可以在compounds-row.pdf中看到代码的结果。

Column

要垂直排列项目,请使用 Column 容器。列中的每个项目都是一个 容器。因此,您可以将不同类型的内容放在一列中。

每个项目的宽度等于列宽。每个项目的高度取决于项目的内容和属性。Column 容器支持分页,因此 Layout 插 件可以在多个页面上呈现同一列的项目。

默认情况下,Column 容器没有页眉或页脚内容。使用方法 HeaderFooter 访问和设置相应的容器。当列项占用多页时,库会 在每页上重复页眉和页脚。

使用 Spacing 方法在列项之间添加一些垂直空间。请注 意,该库不会在页眉和第一项之间应用间距。该库也不会在页脚之前添加空间。

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

您可以在compounds-column.pdf中看到代码的结果。

网格

网格布局将项目组织成列和行。在这方面,网格与表格类似。Layout API 没有为网格提供特殊的容器类型。您可 以使用 ColumnRow 容器实现网格布局。

可以将网格视为一列,其中每个项目为一行。ColumnRow 容器都提供了设置项目间距的功能。如果您愿 意,可以设置页眉和页脚。

每行可以有独立的布局。每行可以有不同数量的项目。项目可以有不同的宽度和高度。可以在行中的项目之前、 之后或之间添加额外的空间。为此,请使用没有内容和装饰的项目。

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

您可以在compounds-grid.pdf中看到代码的结果。

列表

列表通过将信息分解为简洁的要点来提高可读性。列表项中的文本旁边可以有数字、项目符号和其他符号。您可 以使用 ColumnRow 容器轻松实现列表布局。Layout API 不提供用于列表的特殊容器类型。

查看创建按季节划分的月份列表的示例代码。请注意,列表有一个标题。如果项目包含可以换行到下一行的文 本,请对项目的文本部分使用 RelativeItemConstantItem 方法。

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

您可以在compounds-list.pdf中看到代码的结果。

Table

表格布局将项目组织成列和行。Table 容器类型提供了一组广泛的 功能,可以帮助您处理最复杂的情​​况。阅读​​ Table 容器 文章中有关所有功能的信息。

InlineContainer

您可以使用其他容器的集合来填充容器。首先调用 LayoutContainer.Inlined 方法。然后调用提供的 InlineContainerItem 方法来添加子容器。

Layout 插件将容器一个接一个地排成一行。如果没有空间放置项目,则库会开始新的一行。使用 Spacing/Horizo​​ntalSpacing/VerticalSpacing 方法在项目之间添加一些空间。

您可以使用对齐方法来影响容器中项目的位置。AlignTop/AlignMiddle/AlignBottom 方法可垂直对齐项目。对 于水平方向,请使用 AlignLeft/AlignCenter/AlignRight/AlignJustify 方法。

有一个特殊情况。AlignSpaceAround 方法水平对齐项目。它还会在第一个项目之前和最后一个项目之后添加额外的间距。

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

您可以在compounds-inlined.pdf中看到代码的结果。

LayerContainer

您可能需要将一些内容放在主页内容的下方和/或上方。明显的用例是在 PDF 页面顶部添加水印。

首先,通过调用 LayoutContainer.Layers 方法 获取 LayerContainer 对象。给定该对象,您可以开始添 加图层。调用 Layer 方法可添加次要图层。调用 PrimaryLayer 方法可添加主要内容图层。 您必须添加一个主图层。

Layout API 将按照您创建的顺序组合图层。在主要图层之前添加的图层将移至背景。在主要图层之后添加的所有 图层将位于主要内容上方。容器会在主要内容占据的所有页面上重复次要图层。

这是如何向 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);
        });
}));

您可以在compounds-layers.pdf中看到代码的结果。

水印

常见的需求之一是在 PDF 中添加水印。此需求可能出于多种原因。您可以向 PDF 添加水印来识别所有权或指出 PDF 中信息的机密性或敏感性。

为 PDF 添加水印的一种方法是使用图层。请参阅上一节中的示例。让我向您展示另一种方法。

我将使用页面背景和前景容器来为 PDF 加水印。Layout 插件会在下一页重复这些容器。每个包含主要文档内容 的页面也将包含背景和前景容器。这使得它们适合这项任务。

我首先将图像添加到 背景容器。您可以用同样的 方式将徽标添加到 PDF。图像将出现在页面内容后面。文档中有多少页使用背景图像并不重要。API 只会将图像 字节的一个副本添加到生成的 PDF。

文档的主要内容可以是任何内容。对于此示例代码,我使用了著名的 Lorem Ipsum 文本。

文本水印进入 前景容器。文本本身可以是任何内 容,您可以使用任何颜色或字体。我使用了旋转文本,并用较大尺寸的半透明红色字母绘制。

示例代码会从我们的示例代码库中异步下载图像和文本。当然,您可以使用本地图像和/或从文件中读取文本。

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

您可以在compounds-watermarks.pdf中看到代码的 结果。

示例代码

我们有一些示例应用程序,其中详细介绍了上述功能。请花点时间查看它们。