该页面可以包含自动翻译的文本。
在 C# 和 VB.NET 中合并 PDF 文档
业务中经常会合并 PDF 文件用于文档归档。虽然 PDF 合并看起来像一项简单的任务,但这里有许多陷阱。你应该正确合并表单字段、书签、图层和其他 PDF 对象。你还应避免重复对象,以获得紧凑的输出文件。
Docotic.Pdf 库 处理了所有合并细节。它允许你仅用几行 C# 或 VB.NET 代码合并 PDF 文档。

Docotic.Pdf 提供免费和付费许可证。请在 下载 C# .NET PDF 库 页面获取该库和免费的限时许可证密钥。
PDF 合并基础
PdfDocument.Append 方法允许你从文件、流或字节数组中追加 PDF 文档。对于追加受保护文件和合并表单字段,也有相应选项。
合并两个 PDF 文件
此示例代码演示如何在 C# 中合并 PDF 文件:
using var pdf = new PdfDocument("first.pdf");
pdf.Append("second.pdf");
pdf.Save("merged.pdf");
请尝试 GitHub 上的 合并两个 PDF 文档 代码示例。
合并 PDF 流
很容易将前面的示例改为使用流而不是文件路径。以下是用于合并流的辅助方法:
void Merge(Stream first, Stream second, Stream result)
{
using var pdf = new PdfDocument(first);
pdf.Append(second);
pdf.Save(result);
}
合并多个 PDF 文件
你可以重复调用 Append 方法来追加多个 PDF 文件:
string[] filesToMerge = ..;
using var pdf = new PdfDocument();
foreach (string file in filesToMerge)
pdf.Append(file);
// 删除由 PdfDocument() 调用添加的空白页
pdf.RemovePage(0);
pdf.Save(pathToFile);
合并加密的 PDF 文件
有用于合并加密文档的 Append 重载:
using var pdf = new PdfDocument();
pdf.Append("encrypted.pdf", new PdfStandardDecryptionHandler("password"));
pdf.Save("merged.pdf");
你可以在在 C# 和 VB.NET 中解密 PDF 文档一文中找到更多信息。
合并 PDF 表单
PDF 文档中的每个表单字段都必须具有唯一名称。当要合并的文档包含同名字段时,这可能会导致问题。Docotic.Pdf 为冲突的表单控件提供以下合并策略:
- 当附加控件与现有控件冲突时重命名附加的控件
- 将附加控件合并到现有控件
- 展平附加控件
- 不追加任何控件
- 按原样追加控件
默认情况下,库会在冲突时重命名附加的控件。你可以使用 PdfMergingOptions 类选择其他策略:
using var pdf = new PdfDocument("form.pdf");
var decryptionHandler = new PdfStandardDecryptionHandler(string.Empty);
var mergingOptions = new PdfMergingOptions()
{
ControlMergingMode = PdfControlMergingMode.CopyAsKids
};
pdf.Append("form.pdf", decryptionHandler, mergingOptions);
pdf.Save("merged.pdf");
在 CopyAsKids 模式下,库会合并并同步发生冲突的控件。即:当你更改一个控件时,第二个控件将具有相同的值。
减小合并后的 PDF 文件
PDF 文档可能包含相同的对象,例如字体或图像。当你合并这类文档时,生成的文档会包含相同对象的副本。使用 PdfDocument.ReplaceDuplicateObjects() 方法优化合并结果:
using var pdf = new PdfDocument("2024-05-28.pdf");
pdf.Append("2024-05-29.pdf");
pdf.ReplaceDuplicateObjects();
pdf.Save("merged.pdf");
你还可以进一步减小输出文件大小。例如,你可以删除未使用的字体字形或压缩图像。有关支持的压缩选项,请参阅 在 C# 和 VB.NET 中压缩 PDF 文档 一文。
自定义 PDF 合并
Docotic.Pdf 提供用于提取、重新排序或删除 PDF 页面的方法。你可以将它们与 Append 方法结合使用,实现自定义 PDF 合并任务。
追加指定的 PDF 页面
Docotic.Pdf 也允许你合并 PDF 文档的一部分。实现方式有多种。例如,你可以拆分添加的 PDF 文档并追加提取的页面。以下 C# 辅助方法会将所选页面追加到 PdfDocument:
private static void AppendPart(PdfDocument pdf, string filePath, params int[] pagesToAppend)
{
using var streamToAppend = new MemoryStream();
using var other = new PdfDocument(filePath);
using var extracted = other.CopyPages(pagesToAppend);
var options = new PdfSaveOptions
{
UseObjectStreams = false
};
extracted.Save(streamToAppend, options);
pdf.Append(streamToAppend);
}
或者,你也可以追加整个 PDF 文档并删除不必要的页面。以下代码示例会追加 second.pdf 的前两页:
using var pdf = new PdfDocument(@"first.pdf");
int pageCountBefore = pdf.PageCount;
pdf.Append(@"second.pdf");
pdf.RemovePages(pageCountBefore + 2);
pdf.Save(pathToFile);
另一种解决方案与 PDF 拼版有关。你可以在相应部分中阅读相关内容。
前置 PDF
Append 方法总是将页面追加到当前文档末尾。如何以不同顺序合并 PDF 文件?有时你可以调整 Append 调用的顺序。即,使用
pdf.Append("first.pdf");
pdf.Append("second.pdf");
而不是
pdf.Append("second.pdf");
pdf.Append("first.pdf");
或者,你也可以在合并后重新排序页面。以下 C# 代码将追加的 PDF 文档移动到开头:
using var pdf = new PdfDocument(@"second.pdf");
int pageCountBefore = pdf.PageCount;
pdf.Append(@"first.pdf");
pdf.MovePages(pageCountBefore, pdf.PageCount - pageCountBefore, 0);
pdf.Save(pathToFile);
有关重新排序 PDF 页面的更多信息,请参阅:
PDF 拼版
Docotic.Pdf 允许你在单个页面上组合多个 PDF 页面。使用 PdfDocument.CreateXObject(PdfPage) 方法基于现有页面创建 PdfXObject 对象。然后,以所需缩放比例绘制该对象。示例代码:
using var src = new PdfDocument(@"src.pdf");
using var dest = new PdfDocument();
PdfXObject firstXObject = dest.CreateXObject(src.Pages[0]);
PdfXObject secondXObject = dest.CreateXObject(src.Pages[1]);
PdfPage page = dest.Pages[0];
page.Orientation = PdfPaperOrientation.Landscape;
double halfOfPage = page.Width / 2;
page.Canvas.DrawXObject(firstXObject, 0, 0, halfOfPage, page.Height, 0);
page.Canvas.DrawXObject(secondXObject, halfOfPage, 0, halfOfPage, page.Height, 0);
dest.Save("result.pdf");
请在 GitHub 上测试相关的 从页面创建 XObject 示例项目。
作为附件合并
有时,你可能需要将一个 PDF 文件作为附件嵌入到另一个文件中。这也是可以的。你还可以在 PDF 页面上为嵌入的文件添加链接:
using var pdf = new PdfDocument();
PdfFileSpecification first = pdf.CreateFileAttachment("first.pdf");
pdf.SharedAttachments.Add(first);
var bounds = new PdfRectangle(20, 70, 100, 100);
PdfFileSpecification fs = pdf.CreateFileAttachment("second.pdf");
pdf.Pages[0].AddFileAnnotation(bounds, fs);
pdf.Save("attachments.pdf");
你可以在 GitHub 上的 PDF 附件 组中找到相关代码示例。
在并行线程中合并
当合并许多 PDF 文件时,可以将代码并行化。PdfDocument 类不是线程安全的。因此,我们需要在线程间使用独立的 PdfDocument 对象。有关更多细节,请查看 在并行线程中合并 PDF 文档 代码示例。
以下代码展示如何并行合并 PDF 流:
Stream[] documentsToMerge = ..;
int rangeSize = 50;
while (documentsToMerge.Length > rangeSize)
{
int partitionCount = (int)Math.Ceiling(documentsToMerge.Length / (double)rangeSize);
var result = new Stream[partitionCount];
var partitioner = Partitioner.Create(0, documentsToMerge.Length, rangeSize);
Parallel.ForEach(partitioner, range =>
{
int startIndex = range.Item1;
int count = range.Item2 - range.Item1;
result[startIndex / rangeSize] = MergeToStream(documentsToMerge, startIndex, count);
});
documentsToMerge = result;
}
using PdfDocument final = GetMergedDocument(documentsToMerge, 0, documentsToMerge.Length);
final.Save("merged.pdf");
private static Stream MergeToStream(Stream[] streams, int startIndex, int count)
{
using PdfDocument pdf = GetMergedDocument(streams, startIndex, count);
var result = new MemoryStream();
var options = new PdfSaveOptions
{
UseObjectStreams = false // 加速写入中间文档
};
pdf.Save(result, options);
return result;
}
private static PdfDocument GetMergedDocument(Stream[] streams, int startIndex, int count)
{
var pdf = new PdfDocument();
try
{
for (int i = 0; i < count; ++i)
{
var s = streams[startIndex + i];
pdf.Append(s);
s.Dispose();
}
pdf.RemovePage(0);
pdf.ReplaceDuplicateObjects();
return pdf;
}
catch
{
pdf.Dispose();
throw;
}
}
上面的代码将输入文档按 rangeSize 大小分组。然后,代码并行将每组合并为中间文档。该过程持续进行,直到输入文档数量足够少,可以进行简单合并。
并行方案不一定比单线程版本更快。结果可能会因输入文档的数量及其大小而异。在示例代码中,rangeSize 参数的最佳值可能更大也可能更小。你应该对应用程序进行基准测试,以找到最有效的实现。
结论
你可以使用 Docotic.Pdf 库 在 C# 和 VB.NET 中合并 PDF 文档。它允许你合并文件、流或字节数组。你可以合并加密文件、PDF 表单和特定 PDF 页面。Docotic.Pdf 还能帮助你压缩生成的文件并节省磁盘空间。
请尝试 GitHub 上 Docotic.Pdf 示例仓库 中的代码示例。你可以在 Docotic.Pdf 下载页面获取评估版许可证密钥并下载该库。