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

在 C# 中编辑 PDF

使用 Docotic.Pdf 修改你的 PDF 文档。它是适用于 C# 和 VB.NET 的 PDF 编辑库,将强大功能与直观 API 结合在一起。

PDF 编辑器图标

Docotic.Pdf 提供了大量编辑 PDF 文档的方法。以下是该库用于 PDF 编辑的一些关键功能:

  • 将多个 PDF 合并为一个,或将单个 PDF 拆分为多个文件。
  • 减小 PDF 文件大小。
  • 重新排列、删除、旋转或提取页面。
  • 读取、更改或删除 PDF 元数据。
  • 使用数字签名对 PDF 进行签名。
  • 加密文档或从 PDF 中移除密码。
  • 添加、编辑或删除注释和附件。
  • 填写 AcroForms,添加或删除表单控件。
  • 展平表单字段和注释,使其成为 PDF 内容的一部分。
  • 在 PDF 中添加、删除或替换文本。
  • 插入、替换和调整图像大小。
  • 添加水印和背景。
  • 将扫描文档转换为可编辑、可搜索的文本。

在接下来的章节中,我将更详细地介绍 PDF 编辑功能。这些章节还包含代码片段和相关资源链接。

合并和拆分 PDF

本节介绍两个目标相反的功能。

在 C# 和 VB.NET 中拆分和合并 PDF

合并 PDF

当你整合 PDF 文件时,会创建一个单独的 PDF 文档。合并后的 PDF 通常包含来自多个现有 PDF 文件的相关信息。

下面的代码片段演示如何使用 Docotic.Pdf 合并 PDF 文件。

using var pdf = new PdfDocument("first.pdf");
pdf.Append("second.pdf");
pdf.Save("merged.pdf");

这段代码非常简单,因为它展示的是最基本的情况。我们有一篇文章介绍更复杂的合并场景。例如,它展示了如何合并加密文档。

拆分 PDF

拆分是指从原始 PDF 中提取选定页面,以创建一个或多个新的 PDF 文件。当你只想共享文档的一部分时,这个过程很有用。

下面的代码片段演示如何从 PDF 的每一页创建一个新文档。

using var pdf = new PdfDocument("compound.pdf");
for (int i = 0; i < pdf.PageCount; ++i)
{
    using PdfDocument copy = pdf.CopyPages(i, 1);

    // 在复制的页面引用未使用资源(如字体、图像、图案)时,
    // 这有助于减小文件大小。
    copy.RemoveUnusedResources();
    copy.Save(i + ".pdf");
}

请阅读专门文章,了解其他实现 PDF 拆分器的方法。

PDF 压缩

压缩 PDF 时主要有两种方法。第一种只应用不会改变文件内容、仅改变文件形式的操作。第二种还包括可能导致细节丢失或文档质量下降、但压缩效果更好的更改。

无损 PDF 压缩

默认情况下,库会保存 PDF 对象,使其占用更少字节。为此,它会排除未使用的对象,以无格式方式写入对象,并在可能时使用更短的表示形式。

为了进一步提高压缩效果,Docotic.Pdf 还会在输出的 PDF 中生成对象流。这是另一种以最紧凑表示形式写入对象的方法。对象流会使用 Flate 算法压缩。

你可以使用保存选项来影响库保存对象的方式。

某些文档包含重复的字体、图像、颜色配置文件和其他对象。增量更新的文档以及通过合并多个文档创建的文件通常都会出现这种情况。使用 PdfDocument.ReplaceDuplicateObjects 方法去重这些对象,可以显著减小输出大小。

文档的页面可能引用未使用的资源。例如,页面上曾经可见但现在不再使用的图像。使用 PdfDocument.RemoveUnusedResources 方法可移除此类资源。

对 PDF 执行这些操作都可以在不损失质量的情况下减小文件大小。

有损转换

对于包含图像的文件,缩小 PDF 大小最有效的方法之一是更改图像的压缩方案。例如,对图像使用 JPEG 之类的有损压缩会减小体积。作为副作用,图像上可能会出现压缩伪影和细节损失。

如果 PDF 中的图像比需要的更大,你可以调整其大小。这通常能带来更好的压缩效果。另一种选择是将图像转换为黑白(双值)图像。

你可以展平表单字段和注释来节省一些空间。当你不再需要文档中的可编辑注释和表单字段时,这种方法很有意义。

字体会在文档中占用大量空间。PdfDocument.RemoveUnusedFontGlyphs 方法可以从字体中移除未使用的字形,以优化输出大小。字体子集化是这个过程的另一种叫法。你甚至可以完全从文档中移除字体字节(取消字体嵌入)。当文档包含 Arial 之类常用字体的字节时,这可能很有意义。

其他转换会直接移除文档中的信息。你可以移除元数据、结构信息以及私有应用程序数据。这些数据虽然不可见,但只有在对文档用户不重要时才应移除。

如需了解更多细节并查看代码示例,请阅读如何减小 PDF 文件大小一文。

调整 PDF 中页面顺序

在 PDF 中更改页面顺序有很多理由。例如,你可能需要将相关信息分组,或者通过确保文档按逻辑流转来提高可读性。

重新排列 PDF 页面

除了合并和拆分页面之外,Docotic.Pdf 还提供了一组功能丰富的其他方法来重新排列 PDF 中的页面。我将使用 C# 代码片段和 Docotic.Pdf API 演示如何组织 PDF 页面。

你可以在示例代码的页面与导航组中找到本节示例的完整测试项目。我在代码片段中使用了 ten-pages.pdf。这是一个很简单的测试文档,每一页都有一个 Page N 标题。

移动 PDF 页面

下面的代码片段演示如何移动连续范围的页面。代码会将前一半页面移动到文档末尾。

using var pdf = new PdfDocument("ten-pages.pdf");

pdf.MovePages(0, 5, pdf.PageCount);

pdf.Save("continuous-move.pdf");

你也可以移动任意集合的页面。下面的代码会将奇数页移动到文档末尾。

using var pdf = new PdfDocument("ten-pages.pdf");

int[] indexes = [0, 2, 4, 6, 8];
pdf.MovePages(indexes, pdf.PageCount);

pdf.Save("arbitrary-move.pdf");

若只移动一页,请使用 PdfDocument.MovePage 方法。

交换 PDF 页面

要交换两页,请使用如下代码片段。

using var pdf = new PdfDocument("ten-pages.pdf");

pdf.SwapPages(9, 0);
pdf.SwapPages(8, 1);

pdf.Save("swapped.pdf");

PdfDocument.SwapPages 方法接受两个页面的索引,这两个页面将互换位置。当页数不等于两页时,请使用移动页面方法之一。

添加和插入页面

任何 PdfDocument 都至少包含一页。当你创建新文档时,库会隐式添加一页。

以下示例演示如何使用 Docotic.Pdf API 在 PDF 中插入空白页。你可以在索引从 0PageCount(包含两端)的任意位置插入页面。

using var pdf = new PdfDocument();

var newPage = pdf.InsertPage(0);
newPage.Canvas.DrawString("This is the new first page");

pdf.Save("two-pages.pdf");

若要向 PDF 添加空白页,请使用 PdfDocument.AddPage 方法。该方法会将新页面添加到文档末尾。它与调用 pdf.InsertPage(pdf.PageCount) 的效果相同。

若要从其他文档添加或插入页面,请结合使用类似预先追加 PDF部分中所述的调用组合。你可以使用这些调用组合为 PDF 添加封面页。

复制 PDF 页面

使用该库 API,你可以将复制操作拆分为两个连续操作。第一步是复制页面。请参见拆分 PDF部分中的代码示例。

第二步是粘贴页面。对于该操作,请使用 PdfDocument.Append 方法。然后将追加的页面移动到所需位置。

提取页面

下面的代码片段演示如何从 PDF 中提取页面。它会从源文档中提取前 3 页。

using var pdf = new PdfDocument("ten-pages.pdf");

using (var extracted = pdf.ExtractPages(0, 3))
{
    extracted.RemoveUnusedResources();
    extracted.Save("three-pages.pdf");
}

pdf.Save("seven-pages.pdf");

ExtractPages 方法会从文档中移除页面。因此,源文档中只剩下 7 页。库不允许从仅包含一页的文档中提取页面。

我们建议对包含提取页面的文档移除未使用的资源。

从 PDF 中删除页面

请查看下面的代码片段,它演示如何删除 PDF 文档中的一页。它实际上使用 RemovePage 方法的两个不同重载删除了两页。第一个重载接受页面索引作为参数。第二个重载接受页面对象。

using var pdf = new PdfDocument("ten-pages.pdf");
pdf.RemovePage(0);
pdf.RemovePage(pdf.Pages[0]);
pdf.Save("without-first-two-pages.pdf");

若要一次删除多页,请使用 PdfDocument.RemovePages 方法。该方法的重载支持页面索引数组或页面对象数组。

数字签名

Docotic.Pdf 实现了 PDF 数字签名的许多操作,可帮助你维护 PDF 文档的可信性和法律有效性。以下是该库可以执行的一些示例:

使用证书对 PDF 签名。在 PDF 中添加数字签名是确认签署人身份并确保签名后无人篡改文档的方式。

认证签名。若要为 PDF 添加额外限制,可以使用认证签名对其签名。你可以在签名后完全锁定 PDF,或允许少数类型的更改。

验证 PDF 中的签名。检查数字签名的有效性,以确认文档的已签名部分未被更改。

允许多个签名。合同、协议和表单通常需要多个参与方对同一文档签名。要在 PDF 中添加多个签名,需要以增量方式保存文档。

嵌入签名时间戳。可以在签名选项中指定时间戳授权机构 URL,并可选地提供其凭据。库会将收到的时间戳嵌入签名中。

嵌入证书。库会自动将签名证书嵌入数字签名中。

数字签名页面包含示例代码以及每个操作的更多信息。

保护 PDF

你可以使用三项功能来确保 PDF 安全。Docotic.Pdf 可以双向处理它们:既可以保护 PDF,也可以解锁受保护的 PDF。

PDF 安全性

密码保护

此功能允许你设置密码以限制对 PDF 的访问。根据密码类型,打开或修改文档时,PDF 会要求输入正确的密码。

PDF 中有两种密码:

  • 打开密码(用户密码)。打开和查看 PDF 需要此类密码。没有正确密码时,符合规范的 PDF 查看器不会打开该文档。
  • 权限密码(所有者密码)。从 PDF 中移除权限需要此密码。使用所有者密码打开 PDF 后,即使权限限制了某些操作,例如打印、复制或编辑 PDF,仍允许执行所有操作。

你可以为同一个 PDF 文档同时设置这两种密码。阅读解密 PDF 文件可了解如何从 PDF 文档中移除密码。

加密

PDF 加密和 PDF 密码协同工作,以确保 PDF 中的敏感信息只对合法用户可见。只有拥有正确解密密钥或密码的用户才能查看内容。

Docotic.Pdf 可以使用 RC4 40 位、RC4 128 位、AES 128 位以及AES 256 位加密算法加密 PDF 文件。

权限和限制

你可以设置各种权限,例如限制打印、复制文本、编辑文档等。权限仅会影响使用用户密码打开 PDF 时的体验。若使用所有者密码打开 PDF,则不受这些限制影响。

若要从 PDF 中移除权限,首先需要移除 PDF 安全密码。请阅读如何使用 C# 和 Docotic.Pdf API 执行此操作的说明

若要了解如何在 PDF 安全性之外还确保PDF 完整性,请阅读有关数字签名的部分。

PDF 中的元数据

PDF 元数据是嵌入 PDF 文件中的信息,用于提供文档相关细节。元数据主要有两个来源:PDF 文档属性和 XMP 元数据。

文档属性通常也称为文档信息字典、文件信息、元数据字段、文档属性和文件属性。

XMP(可扩展元数据平台)元数据本质上是嵌入在 PDF 中的 XML 文件。XMP 使用灵活的数据模型,可存储任意元数据属性集合。此类元数据使用命名空间对相关属性进行分组。一些常见命名空间包括 XMP Core/XMP Basic 和 Dublin Core。

Docotic.Pdf 完全支持 XMP 元数据和文档属性。请注意,PDF 2.0 标准已将大多数文档信息字典属性标记为已弃用。唯一的例外是创建日期和修改日期。

你可以在示例代码的元数据组中找到本节示例的完整测试项目。

文档属性

了解如何使用 Docotic.Pdf 编辑文档属性。

using var pdf = new PdfDocument("file.pdf");
pdf.Info.Author = "An example code";
pdf.Info.Subject = "Showing how to access and change document metadata";
pdf.Info.Title = "Custom title goes here";
pdf.Info.Keywords = "pdf Docotic.Pdf";

pdf.Save("updated-file.pdf");

你可以更改每个属性的值,但请注意,默认情况下,库会在保存 PDF 前自动更新某些属性。你可以在保存选项中更改这一行为。

若要移除文档属性中指定的所有元数据,请使用 PdfInfo.Clear 方法。如果你只想移除自定义属性,也可以这样做。

XMP 元数据

下面的代码片段演示如何更改 PDF 文档中 XMP 元数据的属性。

using var pdf = new PdfDocument("file.pdf");

pdf.Metadata.DublinCore.Creators = new XmpArray(XmpArrayType.Ordered);
pdf.Metadata.DublinCore.Creators.Values.Add(new XmpString("me"));
pdf.Metadata.DublinCore.Creators.Values.Add(new XmpString("Docotic.Pdf"));
pdf.Metadata.DublinCore.Format = new XmpString("application/pdf");

pdf.Metadata.Pdf.Producer = new XmpString("me too!");

pdf.Save("updated-file.pdf");

这段代码修改了 Dublin Core 和 Adobe PDF 模式中的属性。请注意,由于默认保存选项,Producer 属性会被覆盖。

你可以使用 XmpMetatada.Save 方法之一提取原始 XMP 元数据。该方法会生成包含所有属性的 XML。

若要从文档中移除所有 XMP 元数据,请使用 XmpMetadata.Unembed 方法。

同步元数据

最好确保 XMP 元数据和 Document Info 属性中对应属性的值一致。如果你在同一个文件中同时编辑这两类元数据,这一点尤其重要。

使用 PdfDocument.SyncMetadata 方法来同步 XMP Metadata 和 Document Info 中的值。当某个属性在两个来源中都发生更改时,该方法会用另一个来源的值覆盖其中一个来源。更多细节请阅读该方法文档

PDF 书签

PDF 文档可以包含特殊快捷方式或链接,帮助读者快速导航到特定章节或页面。PDF 大纲是书签的另一种叫法。

PDF 大纲

查看器应用通常会像书籍目录一样显示书签,但它是可交互的。当读者点击书签时,查看器应用会跳转到文档中的指定部分。使用链接注释也可以实现类似行为。

以下是向 PDF 添加书签的 C# 代码片段:

using var pdf = new PdfDocument("ten-pages.pdf");

var root = pdf.OutlineRoot;
root.AddChild("Fifth page", 4);
root.AddChild("Seventh page", pdf.Pages[6]);

pdf.PageMode = PdfPageMode.UseOutlines;
pdf.Save("simple-bookmarks.pdf");

PDF 大纲可以包含主书签和子书签,这使结构化大型文档更容易。以下是如何在 PDF 中创建子书签:

using var pdf = new PdfDocument("ten-pages.pdf");

var root = pdf.OutlineRoot;
var evenPages = root.AddChild("Even pages");

evenPages.AddChild("Second page", 1);
evenPages.AddChild("Fourth page", 3);

pdf.PageMode = PdfPageMode.UseOutlines;
pdf.Save("even-pages-bookmarks.pdf");

你可以为书签项应用字体和颜色。请查看创建带样式的大纲的完整示例。

若要从 PDF 中删除书签,请使用 RemoveChildRemoveChildAt 方法。你可以在根节点上调用 RemoveAllChildren 方法来删除所有书签。

文件附件

PDF 附件是嵌入在 PDF 文档中的外部文件。人们也常将这些文件称为嵌入文件和文件附件。你可以附加任何文件:图像、音频/视频文件、另一个 PDF、Word 文档、Excel 电子表格或其他任何文件。

如果你想将多个 PDF 作为附件合并在一起,创建一个组合 PDF 文件,请查看关于合并 PDF 文档的文章。

以下是使用 Docotic.Pdf API 向 PDF 添加附件的 C# 代码。

using var pdf = new PdfDocument();

var excelFile = pdf.CreateFileAttachment("this-year-figures.xlsx");
pdf.SharedAttachments.Add(excelFile);

pdf.Save("shared-attachment.pdf");

上面的代码将文件作为共享附件添加。读者可以在其查看器的附件面板中找到附加文件。

也可以向 PDF 页面添加附件。这类附件会像其他注释一样显示在页面内容中。

using var pdf = new PdfDocument();

var page = pdf.Pages[0];
page.Canvas.DrawString(20, 100, "Here is this year's figures document:");

var bounds = PdfRectangle.FromLTRB(155, 100, 165, 110);
var excelFile = pdf.CreateFileAttachment("this-year-figures.xlsx");
pdf.Pages[0].AddFileAnnotation(bounds, excelFile);

pdf.Save("page-attachment.pdf");

请查看示例代码的附件组,以找到本节示例的完整测试项目。

若要从 PDF 中移除附件,你需要枚举共享附件和页面注释,并删除不需要的项。下面的枚举代码示例可供参考。若要移除所有共享附件,可以调用 pdf.SharedAttachments.Clear()

你还需要枚举集合,才能从 PDF 中提取嵌入文件。下面是一个示例代码:

using var pdf = new PdfDocument("file-with-attachments.pdf");

int i = 0;
foreach (var attachment in pdf.SharedAttachments)
{
    if (attachment?.Contents == null)
        continue;

    var fileName = attachment.Specification ?? $"attachment{i++}";
    attachment.Contents.Save(fileName);
}

foreach (var widget in pdf.GetWidgets())
{
    var attachment = (widget as PdfFileAttachmentAnnotation)?.File;
    if (attachment?.Contents == null)
        continue;

    var fileName = attachment.Specification ?? $"attachment{i++}";
    attachment.Contents.Save(fileName);
}

页面标签

PDF 页面标签是分配给 PDF 文档中页面的自定义名称或编号。与标准页码不同,页面标签可以包含字母、数字,甚至罗马数字。页面标签的其他名称还有页面标识符和页面名称。

以下是如何使用 Docotic.Pdf 向 PDF 添加页面标签:

using var pdf = new PdfDocument("ten-pages.pdf");

pdf.PageLabels.AddRange(0, 3, PdfPageNumberingStyle.LowercaseRoman);
pdf.PageLabels.AddRange(4, PdfPageNumberingStyle.DecimalArabic, string.Empty, 5);
pdf.PageLabels.AddRange(7, PdfPageNumberingStyle.DecimalArabic, "Appendix page ", 1);

pdf.Save("page-labels.pdf");

前四页的标签将是 iiiiiiiv。接下来的三个标签是 567。其余页面的标签将是 Appendix page 1Appendix page 2Appendix page 3

OCR PDF

某些 PDF 文档包含扫描页,在提取其中的文本之前需要进行光学字符识别(OCR)。OCR 的另一种用途是从使用自定义字形到 Unicode 映射的 PDF 中提取文本。

OCR 扫描 PDF 以提取文本

我们有一篇博文演示如何对扫描文档进行 OCR 处理。该文章包含一个不可搜索的 PDF 示例,并展示如何使用 Tesseract OCR、C# 代码和 Docotic.Pdf 来识别仅含图像的 PDF 中的文本。你还可以借助 Docotic.Pdf 为扫描的 PDF 文件添加 OCR 文本层。

编辑页面

本节讨论对现有 PDF 页面所做的更改,例如:

  • 如何旋转 PDF 页面
  • 如何更改页面大小
  • 在页面画布上使用矢量图形
  • 添加 HTML 内容

请阅读该库的 Layout API,了解如何使用页眉页脚、表格、图像、文本段落等构建块来创建 PDF 文档

查看其他章节以获取以下信息:

旋转页面

请查看以下 C# 代码片段,了解如何只旋转 PDF 中的一页:

using var pdf = new PdfDocument("existing.pdf");

pdf.Pages[0].Rotation = PdfRotation.Rotate180;

pdf.Save("rotated.pdf");

这段代码将第一页旋转 180 度。你也可以将 PDF 页面旋转 0、90 和 270 度。

更改页面大小

Docotic.Pdf 提供了多种更改 PDF 页面大小的方法。最简单的情况下,你可以使用 PdfPage 对象的 WidthHeight 属性来指定所需大小。对于现有文档,它不会调整页面内容大小,也不会移除任何内容。它只会隐藏指定大小矩形之外的所有页面内容。

类似的方法是裁剪页面。你可以像下面这样用 C# 代码更改页面的 CropBox

using var pdf = new PdfDocument("existing.pdf");

var page = pdf.Pages[0];
var cropBoxBefore = page.CropBox;
page.CropBox = new PdfBox(0, cropBoxBefore.Height - 256, 256, cropBoxBefore.Height);

pdf.Save("cropped.pdf");

如果你想将页面的一部分保存为图像,更改裁剪框就是合适的做法。

如果目标是在不同大小的页面上保持所有内容可见,那么请使用缩放方法。在下面的代码片段中,我从一页创建了一个 XObject。XObject 类似于矢量图像。你可以在多页上绘制同一个对象,并按需缩放和旋转它。

当 XObject 准备好后,我会清除之前的页面内容,调整页面大小,然后将对象绘制到调整后的页面上。

using var pdf = new PdfDocument("existing.pdf");

var page = pdf.Pages[0];
var pageXObject = pdf.CreateXObject(page);

page.Canvas.Clear();
page.Width /= 2;
page.Height /= 2;

page.Canvas.DrawXObject(pageXObject, 0, 0, page.Width, page.Height, 0);

pdf.Save("resized.pdf");

矢量图形

Docotic.Pdf 库可以向 PDF 文档中添加线条、曲线和形状等矢量图形。你可以从图形对象构造图形路径,然后使用来自不同颜色空间的颜色对路径进行填充或描边。

在示例代码的图形组中查找与图形相关功能的示例代码。

也可以从 PDF 中提取图形。先调用 GetObjects 方法,然后从 PdfPageObjectType.Path 类型的对象中提取信息。别忘了,XObject 也可能包含嵌套路径。

using var pdf = new PdfDocument("existing.pdf");

var options = new PdfObjectExtractionOptions();
var objects = pdf.Pages[0].GetObjects(options);
foreach (var obj in objects)
{
    if (obj.Type == PdfPageObjectType.Path)
    {
        var path = (PdfPath)obj;
        Console.WriteLine($"Found path {path}");
    }
    else if (obj.Type == PdfPageObjectType.XObject)
    {
        var paintedXObject = (PdfPaintedXObject)obj;
        var nestedObjects = paintedXObject.XObject.GetObjects(options);
        // ...
    }
}

向 PDF 页面添加 HTML

将 HTML 内容叠加到 PDF 文档上,对于添加图表或股票行情滚动等动态元素会很有用。

阅读在 PDF 中插入 HTML以了解更多细节并下载示例代码。

编辑 PDF 文本

本节介绍如何编辑 PDF 中的文本、如何更改 PDF 文本颜色,以及如何添加新文本。

PDF 编辑器更改文本

我们有一篇专门介绍如何从 PDF 中提取文本的文章。你可以查看以了解更多相关信息。

借助 Docotic.Pdf 还可以进行文本展平

查找和替换

要修改 PDF 中的文本,你需要先找到包含文本的区域,然后移除该区域中的文本。最后一步是在文档的同一位置添加新文本。

搜索 PDF 可能很棘手,因为文档内部的单词顺序可能任意,文本也可能经过旋转。幸运的是,我们有一个示例代码演示如何在 PDF 中搜索单词或短语

当你获得要移除文本的坐标后,就可以编辑该页面内容了。该库提供了枚举和复制页面对象的方法,因此在复制对象时可以省略某些文本。这样实际上就移除了文本。编辑 PDF 页面内容示例的代码展示了这一过程的全部细节。你需要更新 ShouldRemoveText 方法,以使用找到的坐标。

请阅读下一节,了解如何向文档添加新文本。

如果你创建文档时使用了占位文本,并且之后要用其他文本替换该占位文本,那么也可以改用文本框。

思路是:向文档中添加一个无边框、只读的文本框,并在其中放入占位文本。之后你可以打开文档,根据名称找到该文本框,并通过简单调用 box.Text = "new text"; 来替换占位文本。如果不希望再进行进一步更改,请在替换后展平该文本框。

添加新文本

要向文档添加文本,请使用 PdfCanvas 对象的 DrawStringDrawText 方法。这些方法使用当前画布字体。字体必须包含文本中所有字符的字形。使用 PdfFont.ContainsGlyphsForText 方法检查字体是否满足该要求。

var canvas = pdf.Pages[0].Canvas;
canvas.Font = pdf.CreateFont("NSimSun");
canvas.DrawString(10, 50, "Olá. 你好. Hello. This is some new text");

你可以使用 Type1、TrueType 和 OpenType 字体绘制 Unicode 文本。库可以使用系统上已安装的字体、14 种内置 Type1 字体,或者从文件中加载所需字体。

更改文本颜色

要更改 PDF 中文本的颜色,请使用与移除文本相同的方法。你至少需要修改示例代码中的 ReplaceColor 方法。

图像

Docotic.Pdf 提供了编辑 PDF 图像所需的一切。下面是最常见操作的 C# 代码片段。

示例代码中的图像组包含本节示例的完整测试项目。

向 PDF 添加图像

该库可以导入 GIF/TIFF/PNG/BMP/JPEG 格式的图像。你也可以从 System.Drawing.Image 对象添加图像。

var canvas = pdf.Pages[0].Canvas;
var image = pdf.CreateImage("image.jpg");
canvas.DrawImage(image, 10, 50);

你可以使用 DrawImage 方法的重载指定旋转角度和输出大小。若要在多个页面上绘制同一图像,只需添加一次图像,并在多次调用 DrawImage 方法时使用同一个 PdfImage 对象。

将图像合并到 PDF

下面的 C# 代码演示如何将多个图像合并到一个 PDF 中。

using var pdf = new PdfDocument();

var imagePaths = new string[] { "image.jpg", "another-image.png" };
foreach (var path in imagePaths)
{
    var image = pdf.CreateImage(path);

    var page = pdf.AddPage();
    page.Width = image.Width;
    page.Height = image.Height;

    page.Canvas.DrawImage(image, 0, 0);
}

pdf.RemovePage(0);
pdf.Save("combined-images.pdf");

这段代码会将多个图像添加到 PDF 中,并将每个页面大小调整为对应图像的大小。在保存结果之前,代码会移除最初隐式添加的空白页。

提取 PDF 图像

我们设计 Docotic.Pdf 的目的之一,就是在不影响图像质量的前提下从 PDF 文件中提取图像。库不会更改图像大小或压缩方式。你得到的图像质量与 PDF 中一致。

using var pdf = new PdfDocument("file-with-images.pdf");
int i = 0;
foreach (PdfImage image in pdf.GetImages())
{
    var path = image.Save($"image{i++}");
    Console.WriteLine($"Saved to {path}");
}

删除和替换图像

使用 PdfPage.RemovePaintedImages 方法可从 PDF 页面中移除全部或指定图像。你可以按位置、大小、变换或其他参数筛选图像。

using var pdf = new PdfDocument("file-with-images.pdf");
pdf.Pages[0].RemovePaintedImages(
    image =>
    {
        return image.Size.Width > 100;
    }
);

pdf.RemoveUnusedResources();
pdf.Save("no-wide-images.pdf");

上面的 C# 代码演示了如何借助 Docotic.Pdf 移除图像。我建议在更改或移除图像后,移除未使用的资源。

使用 PdfImage.ReplaceWith 方法可以替换 PDF 文档中该图像的所有出现位置。

using var pdf = new PdfDocument("file-with-images.pdf");
var firstImage = pdf.GetImages(false).FirstOrDefault()
    ?? throw new ArgumentException("No images found");

firstImage.ReplaceWith("another-image.png");

pdf.RemoveUnusedResources();
pdf.Save("replaced-image.pdf");

更改压缩方案

Docotic.Pdf 提供了更改 PDF 图像压缩方式的方法。可以使用 JPEG、CCITT Group 3 和 4(传真)、JPEG 2000 以及 zip/deflate 压缩算法重新打包图像。

根据初始压缩和新压缩方式的不同,更改可能会导致图像细节或质量损失。不过,有损转换通常有助于减小文档大小

firstImage.RecompressWithJpeg2000(25);

还有其他方法可用于重新打包图像。请查看以 RecompressWith 开头的 PdfImage 方法。你可以使用 Uncompress 方法移除图像上的任何压缩。

调整图像大小

如果 PDF 文档中的某些图像比需要的更大,库可以为你调整或缩小它们。

firstImage.Scale(0.5, PdfImageCompression.Jpeg2000, 25);

上面的代码会在两个方向上将第一张图像缩小一半。库会为结果图像使用 JPEG 2000 压缩。

你可以使用 ResizeTo 方法之一指定结果宽度和高度的精确值。

调整图像大小通常比更改压缩方式更能减小 PDF 文件大小(见上文),但这是一个有损过程。

水印与背景

PDF 上的水印

为 PDF 添加水印涉及以下步骤:

  • 创建 XObject,作为水印内容的容器
  • 用文本、图像和矢量图形填充该对象
  • 使用该对象为 PDF 页面盖章

下面的 C# 代码会向 PDF 添加“Confidential”水印:

using var pdf = new PdfDocument("existing.pdf");

var watermark = pdf.CreateXObject();
watermark.DrawOnBackground = true;

var canvas = watermark.Canvas;
canvas.FontSize = 72;
canvas.Brush.Color = new PdfRgbColor(222, 35, 35);
canvas.Brush.Opacity = 45;
canvas.Pen.Color = canvas.Brush.Color;
canvas.Pen.Opacity = canvas.Brush.Opacity;
canvas.Pen.Width = 5;

var padding = 10;
var text = "CONFIDENTIAL";
canvas.DrawString(padding, padding, text);

var textSize = canvas.MeasureText(text);
var watermarkRect = new PdfRectangle(
    padding, padding, textSize.Width, textSize.Height);
canvas.DrawRoundedRectangle(watermarkRect, new PdfSize(padding, padding));

foreach (var page in pdf.Pages)
{
    page.Canvas.DrawXObject(
        watermark,
        (page.Width - watermarkRect.Width) / 2,
        (page.Height - watermarkRect.Height) / 2);
}

pdf.Save("watermarked.pdf");

这段代码设置了水印画布的画刷和画笔属性。画刷用于绘制文本。为了确定文本大小,代码会测量文本。然后它会在文本周围绘制一个圆角矩形。画笔用于描边该矩形。

水印内容准备好后,代码会将其绘制在每一页的中心。

PDF 背景与水印非常相似,至少创建方式几乎一样。若要向 PDF 添加背景,请按上面的代码执行,但在 CreateXObject 调用后添加 watermark.DrawOnBackground = true;。请注意,图像之类的不透明内容可能会遮挡背景。

注释

Docotic.Pdf 为 PDF 注释提供了丰富的 API。你可以创建、编辑和删除 PDF 文档中的注释。也可以展平注释

若要为文本添加注释,可使用:

  • 便签或文本注释。请参见 PdfPage 类的 AddTextAnnotation 方法。
  • 高亮。请参见 AddHighlightAnnotation 方法。
  • 删除线。请参见 AddStrikeoutAnnotation 方法。
  • 下划线。请参见 AddJaggedUnderlineAnnotationAddUnderlineAnnotation 方法。

使用链接在页面之间跳转,或跳转到外部资源。你可以使用墨迹注释在 PDF 页面上自由绘图。还有用于标记文档中待删除部分的遮盖注释。你也可以嵌入音频、视频或 3D 内容。

高亮文本

以下演示如何在 PDF 文档中高亮文本:

using var pdf = new PdfDocument();

var page = pdf.Pages[0];
var canvas = page.Canvas;
canvas.FontSize = 30;

var text = "Highlighted text.";
var position = new PdfPoint(10, 50);
canvas.DrawString(position, text);
canvas.DrawString(" Not highlighted.");

var size = canvas.MeasureText(text);
var bounds = new PdfRectangle(position, size);

var color = new PdfRgbColor(145, 209, 227);
var annotationText = "Please pay attention to this part.";
page.AddHighlightAnnotation(annotationText, bounds, color);

pdf.Save("highlighted.pdf");

若要链接到 PDF 中的特定页面,请使用如下代码:

using var pdf = new PdfDocument();
var secondPage = pdf.AddPage();
secondPage.Canvas.DrawString(10, 50, "Welcome to the second page.");

var firstPage = pdf.Pages[0];
var canvas = firstPage.Canvas;
var linkRect = new PdfRectangle(10, 50, 100, 60);
canvas.DrawRectangle(linkRect, PdfDrawMode.Stroke);

var options = new PdfTextDrawingOptions(linkRect)
{
    HorizontalAlignment = PdfTextAlign.Center,
    VerticalAlignment = PdfVerticalAlign.Center
};
canvas.DrawText("Go to 2nd page", options);

firstPage.AddLinkToPage(linkRect, 1);

pdf.Save("linked.pdf");

在代码中,该动作区域注释充当内部超链接。这样的区域也可以导航到外部资源,并执行非导航操作。

删除注释

若要从 PDF 中删除注释:

  1. 使用 PdfPage.Widgets 属性或 PdfDocument.GetWidgets 方法访问控件集合。
  2. 检查类型、属性,或以其他方式决定哪些注释已不再需要。
  3. 使用 PdfDocument.RemoveWidget 方法或 PdfWidgetCollection 对象的方法删除该注释。

若要从 PDF 中删除附件,你需要同时删除文件注释和共享附件

遮盖 PDF

作为 PDF 遮盖库,Docotic.Pdf 提供了永久移除或快速遮黑 PDF 文档中敏感信息的方法。

遮黑 PDF 中的信息

遮盖文本

下面演示如何仅使用 C# 和 Docotic.Pdf,在不使用遮盖工具的情况下遮黑 PDF 中的文本。

int i = 0;
foreach (var page in pdf.Pages)
{
    foreach (var word in page.GetWords())
    {
        if (i % 3 == 0)
        {
            page.Canvas.AppendRectangle(word.Bounds);
            page.Canvas.FillPath(PdfFillMode.Winding);
        }

        i++;
    }
}

这段代码会在文档中每三个单词上方绘制一个黑色矩形。请注意,矩形后面的文本仍然保留在文档中,之后仍然可以提取。若要永久移除文本,请使用替换文本部分中的方法。

遮盖图像

你也可以使用黑色矩形覆盖图像。但更简单的方法是将图像替换为黑色 1×1 像素图像。这不仅会在视觉上遮盖该图像,还会移除原始图像数据。

请查看删除和替换图像部分中的代码示例。我还建议在替换后调用 PdfDocument.ReplaceDuplicateObjects 方法。

PDF 表单

Docotic.Pdf 可以使用各种交互元素创建 Acroforms(这是 PDF 表单的另一种叫法),例如按钮、复选框、下拉列表、列表框、单选按钮和文本字段。

通常只需几行代码就能添加并设置一个表单字段。例如,你可以只需调用 PdfPage.AddTextBox 方法就向 PDF 添加可编辑字段。表单与注释组中的示例代码提供了有关创建和使用表单的更多信息。

如何填写 PDF 表单

使用 PdfDocument.TryGetControl 方法可通过控件的完整名称或部分名称检索 PDF 控件。或者,你也可以使用 GetControls 方法枚举文档中的所有控件。无论哪种方式,都需要将结果转换为预期的字段类型。

using var pdf = new PdfDocument(@"example-form.pdf");

if (pdf.TryGetControl("txt-name", out var c) && c is PdfTextBox nameTextBox)
    nameTextBox.Text = "Bit Miracle team";

if (pdf.TryGetControl("txt-email", out c) && c is PdfTextBox emailTextBox)
    emailTextBox.Text = "support@bitmiracle.com";

if (pdf.TryGetControl("check-agree", out c) && c is PdfCheckBox agreeCheckBox)
    agreeCheckBox.Checked = true;

pdf.Save("filled-form.pdf");

这段代码使用了这个PDF 表单示例。在代码中,我为两个文本字段设置了值,并勾选了复选框。

当你完成表单填写后,可以展平其所有字段

在表单中使用 JavaScript

你可以为控件事件添加操作。PdfControl 类提供了对一组预定义事件的访问。这些事件名称都以 "On" 开头(例如 OnMouseDown)。

下面是一个在 PDF 表单中使用 JavaScript 的示例:

using var pdf = new PdfDocument(@"example-form.pdf");
foreach (var field in pdf.GetControls())
    field.OnChange = pdf.CreateJavaScriptAction($"app.alert('{field.Name} changed!',3)");

pdf.Save("javascript-events.pdf");

表单数据格式

电子填充 PDF 还有另一种方式。使用该库的 FDF 转 PDF 功能,可以从数据库或其他来源自动填充 PDF 表单。

using var pdf = new PdfDocument(@"example-form.pdf");
pdf.ImportFdf("form-data.fdf");
pdf.Save("auto-populated.pdf");

这段代码使用此 FDF 文件一次性填充所有表单字段。

展平 PDF

本节介绍如何展平 PDF。

PDF 展平

在展平 PDF 时,你会将表单和注释等交互元素转换为静态内容,以防止进一步编辑。展平后的 PDF 在外观相同的情况下,字节数可能显著减少。

展平表单和注释

若要展平可填写 PDF,请使用 PdfDocument.FlattenControls 方法。该方法会将所有表单字段和其他控件绘制到其父页面上,并从文档中移除源控件。

当你展平 PDF 表单时,也应考虑展平注释。使用 PdfDocument.FlattenWidgets 方法可同时展平控件和注释。

如果你只想将某些控件和/或注释转换为其视觉表示,那么请使用 PdfWidget.Flatten 方法。你需要先找到所需控件或注释。

展平文本

借助 Docotic.Pdf,你可以将 PDF 文本转换为轮廓。这样做通常是为了实现与字体无关。无论字体是否安装,展平后的文本在任何设备上看起来都相同。

但是,一旦将文本转换为轮廓,就不能再将其作为文本编辑。此外,在展平过程中,库会将文本转换为矢量图形。这可能会增加文件大小。

若要展平 PDF 文本,你需要先将文本提取为矢量路径,然后将其复制到新页面或同一页面上。这里有一个示例代码

保存选项

在上面的代码片段中,我使用了不带额外参数的 PdfDocument.Save 方法。在这种情况下,库会使用默认保存选项。我们精心挑选了这些默认值,以便在通常情况下它们表现良好。

不过,在某些情况下,你需要覆盖默认选项。为此,请创建一个 PdfSaveOptions 对象,设置相关选项,然后将其提供给某个保存方法。下面我将介绍这些情况。

若要使用密码或证书保护 PDF,请创建一个加密处理程序,并将其设置到 EncryptionHandler 属性。

当你想要多次签名同一个 PDF时,请通过将 WriteIncrementally 属性设置为 true 来开启增量更新模式。当你保存一个之前已签名的文件并添加新的注释或表单数据时,也应这样做。

Linearize 属性设置为 true 可生成线性化(或针对 Fast Web View 优化的)PDF 文件。识别这种优化的查看器可以更快地显示此类文件。

若要防止在保存时更改某些元数据字段,请将 UpdateProducerUpdateModifiedDate 属性设置为 false