該頁面可以包含自動翻譯的文字。

在 C# 和 VB.NET 中合併 PDF 文件

企業常為文件歸檔合併 PDF 檔案。雖然 PDF 合併看似簡單,但這裡有許多陷阱。您應正確合併表單欄位、書籤、圖層及其他 PDF 物件。您也應避免重複物件,以取得精簡的輸出檔案。

Docotic.Pdf library 可處理所有合併細節。它讓您只需幾行 C# 或 VB.NET 程式碼即可合併 PDF 文件。

合併 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 文件的一部分。方法有很多。例如,您可以 split an added PDF document 並附加擷取的頁面。以下 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 檔案中。這也是可以的。您也可以在 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 library 在 C# 和 VB.NET 中合併 PDF 文件。它可讓您合併檔案、串流或位元組陣列。您可以合併加密檔案、PDF 表單及特定 PDF 頁面。Docotic.Pdf 也可協助您壓縮結果檔案並節省磁碟空間。

請試用 GitHub 上 Docotic.Pdf samples repository 的程式範例。您可以在 Docotic.Pdf 下載頁面取得評估授權金鑰並下載程式庫。