このページには自動翻訳されたテキストを含めることができます。

C# と VB.NET で PDF ドキュメントを結合する

業務では、文書アーカイブのために PDF ファイルを結合することがよくあります。PDF の結合は単純な作業に見えますが、ここには多くの落とし穴があります。フォーム フィールド、ブックマーク、レイヤー、その他の PDF オブジェクトを適切に統合する必要があります。また、重複オブジェクトを避けて、コンパクトな出力ファイルにする必要もあります。

Docotic.Pdf library は、結合に関するあらゆる нюансに対応します。C# または VB.NET のコードをわずか数行書くだけで PDF ドキュメントを結合できます。

PDF ドキュメントを結合する

Docotic.Pdf には 無料ライセンスと有料ライセンス があります。ライブラリと、期間限定の無料ライセンス キーは C# .NET PDF ライブラリをダウンロード ページで入手できます。

PDF 結合の基本

PdfDocument.Append メソッドを使うと、ファイル、ストリーム、またはバイト配列から PDF ドキュメントを追加できます。保護されたファイルの追加やフォーム フィールドの結合に関するオプションもあります。

2 つの PDF ファイルを結合する

このサンプルコードは、C# で PDF ファイルを結合する方法を示します:

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

GitHub の 2 つの 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 モードでは、ライブラリは競合したコントロールを結合し、同期します。つまり、1 つのコントロールを変更すると、もう 1 つも同じ値になります。

結合された 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 の最初の 2 ページを追加します:

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 ページを 1 枚のページにまとめられます。既存のページを基に PdfXObject オブジェクトを作成するには、PdfDocument.CreateXObject(PdfPage) メソッドを使います。次に、このオブジェクトを目的のスケーリングで描画します。サンプルコード:

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");

関連する ページから XObject を作成する サンプル プロジェクトを GitHub で試してください。

添付ファイルとして結合する

場合によっては、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 attachments グループにあります。

並列スレッドで結合する

多数の 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 download page で取得して、ライブラリをダウンロードできます。