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

.NET PDF 生成器

借助 Docotic.Pdf,你可以通过组合页、容器、文本片段、图像、链接、页眉、页脚、表格、列表等结构元素来生成 PDF 文档。这些元素可通过由免费的 Layout 附加组件为 Docotic.Pdf 提供的高层 Layout API 使用。该 API 还支持可复用的自定义组件。

Docotic.Pdf 如何生成 PDF 的示意图:你组织图像、表格和文本等元素,库会根据该布局生成 PDF

Layout API 让你可以使用流式方式,完全通过 C# 或 VB.NET 代码定义文档。根据这种描述,附加组件提供的 PDF 生成器可以生成布局任意复杂的文档,从简单页面到高度结构化的 PDF 报告都可以。

PDF 生成基础

要使用 Layout API 生成 PDF 文档,需要免费的 Layout 附加组件。使用核心库和附加组件还需要许可证密钥。你可以使用免费的试用密钥,也可以使用已购买的密钥。

安装附加组件

推荐的方式是从 NuGet 安装附加组件。

Install-Package BitMiracle.Docotic.Pdf.Layout

包管理器会自动处理依赖项。

如果你想手动安装附加组件,请先下载包含 Docotic.Pdf 二进制文件的 ZIP 压缩包。解压后,为以下 DLL 添加引用:

  • BitMiracle.Docotic.Pdf.dll
  • 来自 Layout add-on 子文件夹的 BitMiracle.Docotic.Pdf.Layout.dll

获取许可证密钥

要试用该库,请填写 Docotic.Pdf 下载页上的表单,申请一个免费的限时许可证密钥。如果你已经购买了许可证,请使用购买后提供给你的代码。

Layout 附加组件是免费的,不需要额外许可证。你可以直接使用现有的 Docotic.Pdf 许可证来使用 Layout API。

使用 Layout API 体验 Hello, world!

下面是使用该 API 生成包含经典“Hello, world!”短语的 PDF 的示例代码:

BitMiracle.Docotic.LicenseManager.AddLicenseData("PUT-LICENSE-HERE");

PdfDocumentBuilder.Create().Generate("hello.pdf", doc => doc.Pages(pages =>
{
    pages.Content().Text("Hello, world!");
}));

这段代码会生成一个单页 PDF,文本位于左上角。

理解代码示例

示例首先添加许可证密钥。没有许可证,Docotic.Pdf 库不会生成任何内容。生成 PDF 的代码从下一行开始。

代码通过调用静态 PdfDocumentBuilder.Create() 方法创建文档构建器实例。对 Generate 方法的调用启动 PDF 生成过程。该方法接受两个参数:要生成的文件名,以及类型为 Action<Document> 的委托。附加组件会将 hello.pdf 作为 Generate 调用的结果输出。

为了知道 PDF 应该采用什么布局,文档构建器会用 Document 实例调用该委托。委托代码负责组合文档内容。在这个示例中,委托使用 Pages 方法向构建器提供另一个委托,用于定义文档页面的布局。

构建器会使用 PageLayout 实例调用传递给 Pages 方法的委托。该实例表示文档中的一个或多个页面。具体页数取决于添加到其中的内容。

页面委托调用 Content 方法,访问用于页面主要内容的布局容器。继续调用容器的 Text 方法会向页面添加示例文本片段。有关其工作方式的详细说明,请参阅布局容器指南。

文档构建器会自动将内容分页,精确创建容纳主要内容布局容器中全部数据所需的页数。在这个示例中,单页即可满足,因此输出中只有一页。

基础示例之外的常见任务

示例代码先创建 PdfDocumentBuilder 实例,然后调用 Generate 方法生成 PDF。你可以在其开始生成 PDF 之前对构建器进行配置。

例如,你可以通过向构建器提供加密处理程序来生成加密 PDF。你还可以为构建器指定元数据,以便将其包含到生成的文档中。有关如何自定义构建器的更多详细信息,请参阅单独的文章。

示例代码只使用了主要内容槽的布局容器,但其他内容槽也可用。以下是用于访问内容槽的 PageLayout 方法完整列表:

  • Background() - 返回背景层,它会被其他内容覆盖。
  • Header() - 返回所有页面共享的页眉。
  • Content() - 返回页面主要内容槽。
  • Footer() - 返回所有页面共享的页脚。
  • Foreground() - 返回前景层,它显示在其他内容之上。

只有主要内容槽中的内容会影响生成的 PDF 页数。文档构建器会在每一页重复除 Content() 提供的槽之外的所有槽。有关内容槽的更多信息,请参阅页面布局文章。

许多文档在不同页面使用不同布局。例如,第一页可能采用封面式设计,而后续页面使用更简单的布局。有些文档会为表格使用特殊页面,或者根据章节应用不同背景。要使用 Docotic.Pdf 和 Layout 附加组件生成此类文档,请多次调用 Document.Pages 方法。你可以在我们的示例仓库中找到一个可运行示例

组织代码

对于示例中的短代码,链式调用和嵌套 lambda 是可以接受的。当你构建更大的内容时,将代码拆分为独立方法可能更方便。这会让代码更易读,也更易维护。

下面是将 Hello, world! 示例代码拆分为方法后的样子。

public void GeneratePdf()
{
    BitMiracle.Docotic.LicenseManager.AddLicenseData("PUT-LICENSE-HERE");

    PdfDocumentBuilder.Create().Generate("hello.pdf", BuildDocument);
}

private void BuildDocument(Document doc)
{
    doc.Pages(BuildPages);
}

private void BuildPages(PageLayout pages)
{
    pages.Content().Text("Hello, world!");
}

为什么使用 Docotic.Pdf Layout API 生成 PDF

Layout API 是一种对开发者友好、现代的 PDF 生成方式,能够从可组合的构建块生成 PDF,而无需了解 PDF 格式内部结构。它以高性能且确定性、可预测的方式排版 PDF。你可以在高吞吐量文档生成场景中使用该 API。

在将 Docotic.Pdf 与免费的 Layout 附加组件一起使用时,可以获得该 API。Docotic.Pdf 和 Layout 附加组件都是 100% 托管代码 DLL,不包含 unsafe 代码块。Layout API 的实现不依赖浏览器或 Skia 二进制文件等任何第三方外部依赖,因此是轻量、低开销且易于部署和维护的解决方案。

API 概览

Layout API 是一个流式、易用的 API。你可以完全通过代码使用灵活的布局元素来描述文档布局,而生成器会流动处理你的内容,自动分页,并将结果渲染为 PDF。

Layout 附加组件使用声明式、基于流的布局系统。其布局元素包括列表、列、行、表格、图像、文本片段、页眉和页脚、容器等。你不是手动在精确坐标放置元素,而是描述元素应如何行为。文档构建器随后计算最终布局并创建 PDF。

基于流的布局系统可确保布局适应页面大小和内容。得益于该系统,附加组件在复杂、结构化、基于规则的布局方面表现出色。你可以嵌套不同类型的容器,而不会牺牲可读性,从而构建复杂结构。

在使用 Layout API 时,你可以将大多数调用链式组合。这比传统 API 生成的代码更紧凑,也更具表达力。链中调用的顺序很重要。所有内容都是强类型的,提供编译时安全性,并使代码更易于重构。你还可以对使用 Layout API 的代码进行单元测试。

为了让布局实现更加简洁,你可以使用自己的方法扩展该 API,并围绕它构建一个干净、富有表现力的 DSL。

有关定位和 DSL 创建的更多信息,请参阅解释如何控制容器大小、位置、对齐和渲染行为的文章。

.NET 版本和平台支持

你可以在面向 .NET Standard 2.1 及更新框架的项目中使用 Layout API。换句话说,Layout 附加组件兼容 .NET 5 到 .NET 10。此外,还支持 .NET Core 3.0+。

你可以在 ASP.NET Core、MAUI 应用、Unity、Xamarin 和控制台应用程序中使用 Layout API 生成 PDF。带有 Layout 附加组件的 Docotic.Pdf 可以在 Windows、macOS 和 Linux 上生成 PDF。

云平台和 Docker 镜像

带有 Layout 附加组件的 Docotic.Pdf 可以运行在 Azure 和 AWS 云环境中,包括无服务器部署。该库和附加组件完全支持动态硬件变更、自动扩缩容以及其他云原生运行时特性。

在大多数云场景中,需要无绑定许可证。License FAQ 说明如何选择适用于云应用的许可证

Layout API 在 Docker 容器中可直接使用。将库和 Layout 附加组件运行在容器内时,无需任何特殊配置即可生成 PDF。

向 PDF 文件添加文本

文本是任何 PDF 文档的基础组成部分。你可以使用 LayoutContainer 类的 Text 方法向内容槽添加文本。

围绕文档页面的文本样式示意图,例如 Title、Caption、Plain、Hyperlink、Strong 和 Footnote

文本片段

在 Hello, world 示例中,我使用了 LayoutContainer.Text(string) 重载向页面的主要内容添加文本。现在我们来看 Text 方法的另一个重载。

public static void GenerateTextPdf()
{
    PdfDocumentBuilder.Create().Generate("text-spans.pdf", doc => doc.Pages(pages =>
    {
        pages.Content().Text(AddTextSpans);
    }));
}

private static void AddTextSpans(TextContainer text)
{
    text.Line("About VB.NET")
        .Style(t => t.Strong);

    text.Span("VB.NET is a multi-paradigm, object-oriented language ");
    text.Span("that runs on .NET, Mono, and the ");
    text.Hyperlink(
        ".NET Framework",
        new Uri("https://dotnet.microsoft.com/download/dotnet-framework"));
    text.Line(".");

    text.Span("Released by Microsoft in 2002, ");
    text.Line("it continues the lineage of the original Visual Basic language.");
}

这段代码使用 TextContainer 类的 SpanLine 方法向当前行添加文本。Line 方法还会结束当前行。示例代码还使用 Hyperlink 方法为特定文本片段附加外部资源链接。

注意示例代码如何使用 Style 方法为第一行应用加粗格式。下面我们更详细地了解文本样式的概念。

文本样式

文本样式允许你自定义文本外观。TextStyle 类提供了更改字体大小、字距、颜色和其他文本属性的方法。TextStyle 对象是不可变的,因此每次方法调用都会生成一个新的样式实例。你可以在不同布局层级应用文本样式。

PdfDocumentBuilder.Create().Generate("text-styles.pdf", doc =>
{
    doc.Pages(pages =>
    {
        pages.TextStyle(TextStyle.Parent.FontSize(30));

        pages.Content()
            .TextStyle(TextStyle.Parent.FontColor(new PdfRgbColor(0, 0, 255)))
            .Text(text =>
            {
                text.Span("Hello,");

                text.Span("World!")
                    .Style(TextStyle.Parent.Underline());
            });
    });
});

TextStyle.Parent 属性返回一个特殊样式,其中所有文本属性都未定义。在上面的示例中,代码使用 30 磅字号,以蓝色绘制 “Hello, World!”,并给第二个单词加下划线。

这是因为代码:

  • 在页面级别将字号设置为 30 磅,
  • 然后为主要内容槽将文本颜色设置为蓝色,
  • 再对最后一个文本片段应用下划线样式。

每个后续样式都会通过 TextStyle.Parent 属性继承前一个样式。

使用 TextStyle 类的方法,你还可以把文本方向改为从右到左。请参阅我们示例仓库中另一个使用文本样式继承的示例。

排版

TextStyle 类支持自定义除关联字体之外的所有文本属性。要更改默认字体,请使用 Document.TextStyleWithFont 方法基于特定字体创建文本样式。你可以使用操作系统中安装的字体,也可以从文件或流中加载字体。

创建基于字体的样式后,你可以应用其他可选属性,然后在文本片段上使用结果样式。下面的 C# 示例演示如何使用系统字体。

PdfDocumentBuilder.Create().Generate("text-style-with-font.pdf", doc =>
{
    var font = SystemFont.Family("Calibri");
    var style = doc.TextStyleWithFont(font).FontSize(30);

    doc.Pages(pages =>
    {
        pages.Content().Text(text =>
        {
            text.Span("Hello,");

            text.Span("World!")
                .Style(style);
        });
    });
});

TextStyleWithFont 方法包含一个可选参数,用于指定字体应如何嵌入到生成的文档中。默认情况下,库:

  • 为 TrueType/OpenType 字体嵌入已使用的字形,
  • 为 Type1 和 CFF 字体嵌入所有字形,
  • 不为内置 PDF 字体(Base14 字体)嵌入字形。

由于这些默认设置,即使使用较大的字体,使用 TrueType 和 OpenType 字体的文档大小仍可能保持较小。你还可以指定自定义字体加载器、回退字体以及缺失字形处理程序。有关在 PDF 文档中管理字体的详细信息,请参阅我们示例仓库中的示例代码。

Layout API 提供了一组预定义样式,你可以通过 Typography 类访问并修改它们。

PdfDocumentBuilder.Create().Generate("typography.pdf", doc =>
{
    doc.Typography(t =>
    {
        var fontsPath = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
        var arialFont = new FileInfo(Path.Combine(fontsPath, "arial.ttf"));

        t.Document = doc.TextStyleWithFont(arialFont);
        t.Header = t.Parent.FontSize(20).FontColor(new PdfGrayColor(20));
        t.Footer = t.Footnote;
    });

    doc.Pages(pages =>
    {
        pages.Header().AlignCenter().Text("Header");

        pages.Content().Text(t =>
        {
            t.Line("Title").Style(t => t.Title);
            t.Line("Heading 1").Style(t => t.Heading1);
            t.Line("Regular");
        });

        pages.Footer()
            .Height(20)
            .AlignCenter()
            .Text(t => t.CurrentPageNumber());
    });
});

我建议通过为整个文档配置排版来覆盖预定义样式,尽管这不是必需的,你仍然可以在文本片段级别应用文本样式。

使用 Typography 类时,你不需要把文本样式引用存储在变量中。相反,你可以使用 Document.Typography 方法注册所需样式,然后通过 Typography 对象的属性访问它们。请参阅 Typography 示例,了解在生成 PDF 文档时如何使用预定义和自定义文本样式

许多真实世界的 PDF 文档,例如报告或发票,都包含页眉和页脚。本节演示如何为 PDF 添加这两部分。

public static void GeneratePdfWithHeaderAndFooter()
{
    PdfDocumentBuilder.Create()
        .Generate("header-footer.pdf", doc => doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6).Margin(10);

        BuildPagesHeader(pages.Header());
        BuildPagesFooter(pages.Footer());

        pages.Content().Text("Hello, world!");
    }));
}

public static void BuildPagesHeader(LayoutContainer header)
{
    header.TextStyle(TextStyle.Parent.FontSize(8))
        .AlignRight()
        .Text(t =>
        {
            t.Line($"Created by: {Environment.UserName}");
            t.Line($"Date: {DateTime.Now}");
        });
}

public static void BuildPagesFooter(LayoutContainer footer)
{
    footer.AlignRight().Text(text =>
    {
        text.Style(t => t.Parent.FontColor(new PdfRgbColor(255, 0, 0)));

        text.CurrentPageNumber();
        text.Span(" / ");
        text.PageCount();
    });
}

BuildPagesHeader 方法为页眉文本设置了更小的字号。它使用两行显示页眉内容:一行是当前用户名,另一行是当前日期。文本采用右对齐。

注意,这段代码没有为页眉指定任何显式尺寸。页眉占据页面宽度减去左右边距后的全部宽度,其高度取决于文本行的高度。

BuildPagesFooter 方法演示如何在 PDF 页脚中放置当前页码。库会自动计算当前页码和总页数。你可以通过 TextContainer 对象的 CurrentPageNumberPageCount 方法访问这些值。

Layout API 还提供了页码格式化方式。例如,你可以像这样绘制十六进制页码:

text.CurrentPageNumber().Format(p => "0x" + p?.ToString("x2"));

我们的示例仓库中还有另一个添加页眉和页脚到 PDF 的示例。该示例将页码格式化为罗马数字。

插入图像

俗话说,一图胜千言。在报价单或收据中,你通常会包含公司的标志或其他重要图片。在这个示例中,我将使用一张简单且美观的图片。

要使用图像,必须先将其添加到文档中。库可以从文件或流中加载图像。仅支持栅格格式:PNG、JPEG、JPEG 2000、BMP、GIF 和 TIFF。

一旦你获得 Image 对象,就可以通过调用容器的 Image 方法,将其设置为一个或多个布局容器的内容。你可以像处理其他内容一样,对图像进行缩放、旋转、添加内边距以及排列图像

PdfDocumentBuilder.Create().Generate("image-with-text.pdf", doc =>
{
    var imageFile = new FileInfo("red-flowers-at-butterfly-world.jpg");
    var image = doc.Image(imageFile);

    doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6);
        pages.Content().Column(c =>
        {
            c.Spacing(20);

            c.Item()
                .AlignCenter()
                .Text("Hello, world!")
                .FontSize(20);

            c.Item()
                .AlignCenter()
                .MaxWidth(200)
                .Image(image);
        });
    });
});

示例代码在主要内容槽中同时使用文本和图像。然而,布局容器只能包含文本或只包含图像。要在页面主要内容槽中同时包含两者,你需要使用复合容器

我使用 Column 容器将文本和图像垂直放置,按顺序排列。列容器的 Item 方法会提供一个子容器。一次 Item 调用创建一个文本容器,另一次创建一个图像容器。包含所有子容器的复合容器就成为主要内容。

要运行该示例,请从我们的示例仓库下载花朵图片,并将其放到应用程序的工作目录中。

在线图像

如果你只有图像 URL 而不是文件,可以先将图像下载到内存流,再从该流创建 Image。下面的示例展示如何在 Layout API 中使用在线图像。

public static async Task AddImageWithTextFromUrl()
{
    using var http = new HttpClient();
    using var stream = await http.GetStreamAsync("url/to/image");

    var memoryStream = new MemoryStream();
    await stream.CopyToAsync(memoryStream);
    memoryStream.Position = 0;

    PdfDocumentBuilder.Create().Generate("online-image-with-text.pdf", doc =>
    {
        var image = doc.Image(memoryStream);

        doc.Pages(pages =>
        {
            pages.Size(PdfPaperSize.A6);
            pages.Content().Column(c =>
            {
                c.Spacing(20);

                c.Item()
                    .AlignCenter()
                    .Text("Hello, world!")
                    .FontSize(20);

                c.Item()
                    .AlignCenter()
                    .MaxWidth(200)
                    .Image(image);
            });
        });
    });
}

由于该方法执行异步工作(从 URL 下载图像),因此它被声明为 async Task 而不是 void。调用方必须 await 它,以确保 PDF 生成正确完成。在你自己的代码中,类似的方法也可能返回一个值,此时它将被声明为 async Task<TResult>

创建列表

什么是列表?你可以把它理解为按顺序逐行排列的编号项组。Layout API 没有为列表提供专门的容器类型,但可以很容易地使用其他复合容器实现。

var dayNames = DateTimeFormatInfo.InvariantInfo.DayNames;
var dayNamesSpain = DateTimeFormatInfo.GetInstance(new CultureInfo("es-ES")).DayNames;

PdfDocumentBuilder.Create().Generate("list.pdf", doc => doc.Pages(pages =>
{
    pages.Size(PdfPaperSize.A6);
    pages.Content().Column(column =>
    {
        for (int i = 0; i < dayNames.Length; i++)
        {
            column.Item().Row(row =>
            {
                row.Spacing(5);
                row.AutoItem().Text($"{i + 1}.");
                row.RelativeItem().Text(t =>
                {
                    t.Line(dayNames[i]);
                    t.Line($"In Spain they call it {dayNamesSpain[i]}");
                });
            });
        }
    });
}));

示例代码将列表创建为一列行。每一行包含两个项:第一个(左侧)放置项编号,第二个(右侧)放置项文本。代码显式设置了每一行中项之间的间距。

要排列项,Row 容器必须显式指定每个项的大小,或者自行计算。在这个示例中,使用了 AutoItemRelativeItem 方法。因此,行容器会先计算第一个项所需的宽度,然后将剩余可用宽度用于第二个项。

采用这种方式并按需调整行的外观后,你可以创建最适合文档布局和样式的列表。

构建表格

许多 PDF 文档包含表格,这不足为奇,因为表格能提升数据的清晰度和组织性。本节展示如何使用 Layout API 在 PDF 中创建表格。

public static void AddTable()
{
    PdfDocumentBuilder.Create().Generate("table.pdf", doc => doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6);

        var color = new PdfGrayColor(75);
        pages.Content().Padding(20).Table(t =>
        {
            t.Columns(c =>
            {
                c.RelativeColumn(4);
                c.RelativeColumn(1);
                c.RelativeColumn(4);
            });

            t.Header(h =>
            {
                h.Cell().Background(color).Text("Month");
                h.Cell().Background(color).Text("Days");
                h.Cell().Background(color).Text("First Day");
            });

            var year = DateTime.Now.Year;
            for (int yearDiff = 0; yearDiff < 4; yearDiff++)
            {
                for (int i = 11; i >= 0; i--)
                {
                    var stats = GetMonthStats(year - yearDiff, i);

                    t.Cell().Text(stats.Item1);
                    t.Cell().Text(stats.Item2);
                    t.Cell().Text(stats.Item3);
                }
            }
        });
    }));
}

private static (string, string, string) GetMonthStats(int year, int monthIndex)
{
    return (
        $"{DateTimeFormatInfo.InvariantInfo.MonthNames[monthIndex]} {year}",
        DateTime.DaysInMonth(year, monthIndex + 1).ToString(),
        new DateTime(year, monthIndex + 1, 1).DayOfWeek.ToString()
    );
}

示例代码创建了一个简单表格,显示当前年份和前三年的月份基本信息。代码定义了三列相对宽度。最左列和最右列的宽度是中间列的四倍。

代码还通过添加表头单元格并为每个单元格指定文本和背景色来定义表格表头。当表格无法放入单页时,表头会在表格占用的每一页上重复。你可以在示例代码生成的 PDF 中看到这种行为。

表格行由两个简单循环构建。外层循环遍历年份,内层循环按相反顺序遍历月份。内层循环会获取每个月的信息,然后添加三个单元格组成一行。

有关更多细节,你可以阅读深入解释 Table container 功能的文章。

PDF 文档支持内部链接,允许读者跳转到同一文件中的其他位置。在 PDF 查看器中,这些链接显示为可点击的文本或图像元素。它们的工作方式类似超链接,但不是指向外部网站,而是在文档内部导航。

PDF 中内部链接的示意图,显示了通过链接和目标图标连接的文档部分

许多 PDF 文档会使用内部链接作为书签或目录,帮助读者在章节之间快速移动。下面是如何使用 Layout API 为文档章节添加链接:

PdfDocumentBuilder.Create().Generate("link.pdf", doc =>
{
    doc.Pages(pages =>
    {
        pages.Content().Column(c =>
        {
            const string SectionName = "Chapter 1";
            c.Item().SectionLink(SectionName).Text("Link");

            c.Item().PageBreak();

            c.Item().Section(SectionName).Text("Target");
        });
    });
});

示例代码使第一页上的文本链接到指定名称的章节。它通过在包含文本的布局容器上调用 SectionLink 方法来实现。该章节此时可能已经存在,也可能尚不存在。文本会在 PDF 查看器中变为可点击。

随后,代码将第二页上的文本标记为同名章节的起始位置。其外观和行为不会改变,但它会成为第一页链接的目标。这是通过在对应的布局容器上调用 Section 方法完成的。

在这个示例中,链接和目标都是文本片段,但你可以在任何布局容器上创建章节和链接。它可以是包含图像的容器、表格容器,或你自己构造的容器。

请查看我们示例仓库中如何创建 PDF 目录的示例。

设计复杂的 PDF 布局

Layout API 提供了多种布局容器,你可以组合它们来生成任意复杂的 PDF 文档。你还可以用自己的自定义组件扩展该 API。

本页示例故意保持简单,旨在创建直接明了的文档,这样你可以专注于核心概念,而不会陷入细节。如果你想了解如何组合不同容器来生成更复杂的 PDF,请查看 GitHub 上我们示例仓库中的对应示例。

除了使用内置容器外,你还可以定义并使用自定义布局组件。当你希望单个类同时封装数据和布局逻辑时,这些组件特别有用。把所有内容放在一起会让组件更容易理解、修改和复用,同时也为处理复杂布局提供了灵活方式。

要创建自定义布局组件,请在类中实现 ILayoutComponent 接口。生成 PDF 时,库会调用该接口的 Compose 方法,并提供一个 LayoutContext 对象。你的代码可以使用该对象创建布局元素,并访问正在生成的文档信息。

要将自定义布局组件添加到布局中,请调用 LayoutContainer 类的 Component 方法。在尺寸和定位方面,自定义组件的行为与文本或图像完全相同。

有关使用 ILayoutComponent 接口实现自定义组件的示例,请参阅布局组件示例。

生成 PDF/A 文档

你可以将 Layout API 与免费的 Conformance 附加组件一起使用,以生成 PDF/A 文档:

using var ms = new MemoryStream();
PdfDocumentBuilder.Create().Generate(ms, doc => doc.Pages(_ => { }));

using var pdf = new PdfDocument(ms);
pdf.SaveAsPdfa("pdfa-4.pdf", PdfaConformanceLevel.Pdfa4);

支持所有 PDF/A 一致性级别。你还可以创建 Factur-X/ZUGFeRD 发票。请在 将 PDF 转换为 PDF/A 文章中了解更多信息。

创建 PDF 的其他方式

Docotic.Pdf 提供多种创建 PDF 的方式,每种方式适用于不同场景。本节解释何时应依赖 Layout API,以及何时其他方法可能更适合你。

何时优先使用 Layout API

Docotic.Pdf Layout API 是一种强大的方式,可从结构化布局生成 PDF。它允许你将文本、图像、容器和表格组合成任意复杂的嵌套结构来构建文档。生成过程快速、内存占用合理,并且行为可预测。

Docotic.Pdf 与 Layout 附加组件组成了一个轻量、完全自包含的集合。该 API 不需要外部库或浏览器即可生成 PDF。这使它成为 .NET 微服务、云应用(尤其是无服务器应用)以及其他对体积敏感环境的可靠选择。

由于文档布局是用代码定义的,因此你可以对其任何部分进行单元测试。布局逻辑可以像其他代码一样复用,你还可以在自定义组件中封装数据和布局行为。

Layout API 的替代方案

一篇专门的文章详细比较了使用 Docotic.Pdf 创建 PDF 的所有方式。下面是一些最常用的替代方案。

  • HTML 转 PDF 转换
    重用现有的 HTML/CSS 模板。当你的团队已经用 HTML/CSS 生成文档,并且需要这些文档的 PDF 版本时,选择HTML 转 PDF 方法

  • 低级 PDF 生成
    提供库中对 PDF 结构和内容的最大控制。当你需要像素级定位或复杂矢量图形时,选择这种方式。对于性能关键场景或需要尽可能小的体积时,推荐使用此方法。

  • 基于模板的 PDF 生成
    提供一种快速、可预测的方式来填充文档,而无需设计或排列其元素。当你拥有预定义的 PDF 结构,例如已批准或受合规控制的模板,只需要更改文本字段、替换占位符、附加相关文档并执行类似任务时,选择这种方式。

  • PDF 合并与组合
    允许你从现有片段创建 PDF,而不是从头构建。当你有图像、扫描页面或其他 PDF 需要合并为单个文件时,选择这种方式。

与其他 PDF 生成解决方案的比较

本节包含两个比较表:一个是关键结论,另一个是详细的结构化信息,用于说明带有 Layout 附加组件的 Docotic.Pdf 与其他流行 PDF 生成方案的对比。

关键比较结论

尽管有一些强大的 PDF 生成选项,包括免费方案,但签名、加密或文档合并等能力各不相同,这会限制哪些工具真正适合你的需求。

方案 适用场景 最适合
带有 Layout 附加组件的 Docotic.Pdf 当你需要一个高质量、高性能的布局引擎,能够生成经过优化的 PDF,并且在跨平台签名、加密和 PDF 编辑方面提供高级支持时 高质量生成发票、报告、对账单及类似文档,并提供出色的开发体验。适合需要企业级 PDF 处理和专业支持的场景
PDFsharp + MigraDoc 当你想要一个用于基础 PDF 生成的免费、MIT 许可库,并且不需要数字签名或现代加密算法时 开源项目或预算受限项目中的简单文档创建
QuestPDF 当你想要一个专用于 PDF 生成的现代布局引擎,并且不需要编辑、签名或加密时 在其他非生成类功能由别处处理的前提下,使用 MIT 或低成本商业许可证进行高质量 PDF 生成
iText 当你需要一个成熟、功能丰富的 PDF 工具包来同时生成和处理 PDF,并且你愿意根据 AGPL/GPLv3 许可证开源方案,或购买昂贵的商业许可证时 已经熟悉 iText API,因此不会受到其陡峭学习曲线影响的团队

详细比较

查看该表以了解更广泛的背景,并形成你自己的结论。

  带有 Layout 的 Docotic.Pdf PDFsharp + MigraDoc QuestPDF iText
PDF 功能 功能完整的 PDF 库 PDF 生成和有限编辑 仅 PDF 生成 广泛的 PDF 功能
PDF/A 支持 所有一致性级别 正在构建中 仅支持 PDF/A-2 和 PDF/A-3 所有一致性级别
渲染模型 现代、声明式、保留模式布局引擎 基于盒子的布局引擎 现代、声明式、保留模式布局引擎 基于盒子的布局引擎 + 渲染器树
API 类型 流式 API 命令式 API 流式 API 命令式 API
开发体验 出色。API 简洁、现代且直观 良好。API 设计符合常规 出色。API 简洁、现代且直观 一般。API 冗长且过于复杂
字体子集 支持;默认仅嵌入已使用的字形 不支持;可能导致 PDF 不必要地变大 支持;默认仅嵌入已使用的字形 支持;默认仅嵌入已使用的字形
从右到左(RTL)内容方向 支持 不支持 支持 支持
数字签名 支持,包括 LTV 和外部签名 不支持 不支持 支持,包括 LTV 和外部签名
加密 / 权限 完全支持 仅支持 RC4 加密,不支持 AES 或证书 不支持 完全支持
外部依赖项 SkiaSharp / 基于 Skia 的组件
支持 为潜在客户和现有客户提供专业支持;顶级许可证提供优先支持 社区支持;可单独购买专业支持 通过 GitHub 提供社区支持 AGPL 版本提供社区支持;商业许可证持有者提供专业支持
许可证 商业许可证,符合条件的用例可获得免费许可证 MIT 个人和小公司适用 MIT;更大的企业需要商业许可证 开源用途使用 AGPL;专有项目需要昂贵的商业许可证
开发者授权 所有许可证均支持无限开发者 所有许可证均支持无限开发者 MIT 许可支持无限开发者;Professional 支持 10 名开发者;Enterprise 支持无限开发者 AGPL 版本支持无限开发者;商业许可证按开发者授权

结论

带有 Layout 附加组件的 Docotic.Pdf 提供了一种现代、高性能、高质量的方式,可在 C# 和 VB.NET 中生成 PDF。该库可生成报告、对账单、发票及类似文档。其设计良好的流式 API 提供了出色的开发体验。你可以依赖 Bit Miracle 为 Docotic.Pdf 及其附加组件提供的专业支持。

与其他一些 PDF 生成方案不同,Docotic.Pdf 是一个功能完整的 PDF API。该库可以使用数字签名签名生成的 PDF,包括支持 LTV 的签名。Docotic.Pdf 还可以使用存储在安全硬件中的证书,例如 USB 令牌和智能卡。也支持基于云的硬件安全模块(HSM),例如 Microsoft Azure Key Vault 和 AWS Key Management Service(KMS)。

使用 Docotic.Pdf,你可以将支持文档附加到生成的 PDF 中,例如电子表格或语音笔记。要在网页或类似界面中显示文档,你可以从其一页或多页创建缩略图图像

后续步骤: