该页面可以包含自动翻译的文本。
复合容器
复杂容器在构建和组织内容方面起着至关重要的作用。通过使用合适的容器,您可以轻松地以用户友好的方式呈 现文本和图像。
仅使用简单的文本和图像容器构建文档绝对是可能的。不过,有些要求很难或不可能仅使用简单容器来实现。复 合容器在这种情况下会有所帮助。此外,复杂容器有助于用更少的代码实现您的目标。
使用 LayoutContainer 类的方法将复杂的容器(如
Row 和 Column)添加
到文档页面。使用这些容器,您可以实现其他容器(如网格和列表)。对于不太常见但仍然重要的情况,还有
Inlined
和 Layers
方法。
本文是有关用于 PDF 生成的 Layout API 的系列文章的一部分。 如果您是 API 新手,请先阅读 Layout API入 门 部分。
9.5.17548-dev 9.5.17548-dev14,726 通过 NuGet 总下载量 4,514,921
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
容器没有页眉或页脚内容。使用方法
Header 和
Footer 访问和设置相应的容器。当列项占用多页时,库会
在每页上重复页眉和页脚。
使用 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 没有为网格提供特殊的容器类型。您可 以使用 Column 和 Row 容器实现网格布局。
可以将网格视为一列,其中每个项目为一行。Column
和 Row
容器都提供了设置项目间距的功能。如果您愿
意,可以设置页眉和页脚。
每行可以有独立的布局。每行可以有不同数量的项目。项目可以有不同的宽度和高度。可以在行中的项目之前、 之后或之间添加额外的空间。为此,请使用没有内容和装饰的项目。
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中看到代码的结果。
列表
列表通过将信息分解为简洁的要点来提高可读性。列表项中的文本旁边可以有数字、项目符号和其他符号。您可 以使用 Column 和 Row 容器轻松实现列表布局。Layout API 不提供用于列表的特殊容器类型。
查看创建按季节划分的月份列表的示例代码。请注意,列表有一个标题。如果项目包含可以换行到下一行的文 本,请对项目的文本部分使用 RelativeItem 或 ConstantItem 方法。
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
方法。然后调用提供的
InlineContainer 的
Item 方法来添加子容器。
Layout 插件将容器一个接一个地排成一行。如果没有空间放置项目,则库会开始新的一行。使用
Spacing/HorizontalSpacing/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中看到代码的 结果。
示例代码
我们有一些示例应用程序,其中详细介绍了上述功能。请花点时间查看它们。