大規模PDFでのヘッダーとフッターの高速化

This article was translated from English: Does it need improvement?
Translated
View the article in English

非常に大きなPDFにヘッダーとフッターを一度に追加するのは遅いです。 ドキュメントを固定サイズのページチャンクに分割し、各バッチをレンダリングし、その後、パーツを統合して合計レンダリング時間を短縮しつつ、ページ番号を全ドキュメントにわたって連続させます。

解決策

1. ページインデックスをバッチに分割

チャンクサイズを選び、ドキュメントのページインデックスを固定サイズのバッチにグループ化します。例としては各300ページです。 小さなチャンクは個別に高速でレンダリングされますが、最後にマージ作業が増えます。

2. 各バッチを個別のドキュメントにコピー

現在のバッチのページをCopyPagesで抽出し、レンダリング用のスタンドアロンのドキュメントを取得します。

3. 継続番号でヘッダーとフッターをレンダリング

FirstPageNumberをチャンクの開始ページに設定し、番号が各チャンクで1から再スタートするのではなく、全ドキュメントを通して継続するようにします。

4. 各チャンクを保存し、処分

各チャンクを一時ファイルに書き込み、処分します。 次のバッチの前にチャンクを解放することで、メモリの増加を防ぎます。

5. チャンクを最終PDFにマージ

一時ファイルを再度開き、AppendPdfで順番に添付し、結果を保存してから一時ファイルを削除します。

using IronPdf;
var pdf = PdfDocument.FromFile("input.pdf");
string headerHtml = "<div style='font-size:10px;'>Header</div>";
string footerHtml = "<div style='text-align:right;font-size:10px;'>Page {page} of {total-pages}</div>";
int chunkSize = 300;
var pageChunks = Enumerable.Range(0, pdf.PageCount)
    .GroupBy(i => i / chunkSize)
    .Select(g => g.ToArray())
    .ToList();
var tempFiles = new List<string>();
foreach (var indexes in pageChunks)
{
    var chunkPdf = pdf.CopyPages(indexes);
    int globalStartPage = indexes.First() + 1;
    chunkPdf.AddHtmlHeadersAndFooters(new ChromePdfRenderOptions
    {
        FirstPageNumber = globalStartPage,          // keeps numbering continuous
        HtmlHeader = new HtmlHeaderFooter { HtmlFragment = headerHtml, MaxHeight = 40 },
        HtmlFooter = new HtmlHeaderFooter { HtmlFragment = footerHtml, MaxHeight = 40 }
    });
    string tempFile = Path.GetTempFileName() + ".pdf";
    chunkPdf.SaveAs(tempFile);
    chunkPdf.Dispose();
    tempFiles.Add(tempFile);
}
// Merge the chunks back into one document.
var finalPdf = new PdfDocument(tempFiles.First());
foreach (var file in tempFiles.Skip(1))
{
    using var part = new PdfDocument(file);
    finalPdf.AppendPdf(part);
}
finalPdf.SaveAs("output.pdf");
finalPdf.Dispose();
// Clean up temp files.
foreach (var file in tempFiles)
    File.Delete(file);
using IronPdf;
var pdf = PdfDocument.FromFile("input.pdf");
string headerHtml = "<div style='font-size:10px;'>Header</div>";
string footerHtml = "<div style='text-align:right;font-size:10px;'>Page {page} of {total-pages}</div>";
int chunkSize = 300;
var pageChunks = Enumerable.Range(0, pdf.PageCount)
    .GroupBy(i => i / chunkSize)
    .Select(g => g.ToArray())
    .ToList();
var tempFiles = new List<string>();
foreach (var indexes in pageChunks)
{
    var chunkPdf = pdf.CopyPages(indexes);
    int globalStartPage = indexes.First() + 1;
    chunkPdf.AddHtmlHeadersAndFooters(new ChromePdfRenderOptions
    {
        FirstPageNumber = globalStartPage,          // keeps numbering continuous
        HtmlHeader = new HtmlHeaderFooter { HtmlFragment = headerHtml, MaxHeight = 40 },
        HtmlFooter = new HtmlHeaderFooter { HtmlFragment = footerHtml, MaxHeight = 40 }
    });
    string tempFile = Path.GetTempFileName() + ".pdf";
    chunkPdf.SaveAs(tempFile);
    chunkPdf.Dispose();
    tempFiles.Add(tempFile);
}
// Merge the chunks back into one document.
var finalPdf = new PdfDocument(tempFiles.First());
foreach (var file in tempFiles.Skip(1))
{
    using var part = new PdfDocument(file);
    finalPdf.AppendPdf(part);
}
finalPdf.SaveAs("output.pdf");
finalPdf.Dispose();
// Clean up temp files.
foreach (var file in tempFiles)
    File.Delete(file);
Imports IronPdf
Imports System.IO
Imports System.Linq

Dim pdf = PdfDocument.FromFile("input.pdf")
Dim headerHtml As String = "<div style='font-size:10px;'>Header</div>"
Dim footerHtml As String = "<div style='text-align:right;font-size:10px;'>Page {page} of {total-pages}</div>"
Dim chunkSize As Integer = 300
Dim pageChunks = Enumerable.Range(0, pdf.PageCount) _
    .GroupBy(Function(i) i \ chunkSize) _
    .Select(Function(g) g.ToArray()) _
    .ToList()
Dim tempFiles = New List(Of String)()

For Each indexes In pageChunks
    Dim chunkPdf = pdf.CopyPages(indexes)
    Dim globalStartPage As Integer = indexes.First() + 1
    chunkPdf.AddHtmlHeadersAndFooters(New ChromePdfRenderOptions With {
        .FirstPageNumber = globalStartPage,
        .HtmlHeader = New HtmlHeaderFooter With {.HtmlFragment = headerHtml, .MaxHeight = 40},
        .HtmlFooter = New HtmlHeaderFooter With {.HtmlFragment = footerHtml, .MaxHeight = 40}
    })
    Dim tempFile As String = Path.GetTempFileName() & ".pdf"
    chunkPdf.SaveAs(tempFile)
    chunkPdf.Dispose()
    tempFiles.Add(tempFile)
Next

' Merge the chunks back into one document.
Dim finalPdf = New PdfDocument(tempFiles.First())
For Each file In tempFiles.Skip(1)
    Using part = New PdfDocument(file)
        finalPdf.AppendPdf(part)
    End Using
Next
finalPdf.SaveAs("output.pdf")
finalPdf.Dispose()

' Clean up temp files.
For Each file In tempFiles
    File.Delete(file)
Next
$vbLabelText   $csharpLabel

Page {page} of {total-pages}のようなフッターが最初のチャンクから最後まで一貫します。

ご注意FirstPageNumberがなければ、各チャンクがページ1で番号を再スタートします。

デバッグのヒント

  • チャンクサイズを調整: 上記の値(300)は調整可能で、チャンク化が有効になるポイントはワークロードに依存します。
  • マージパスを考慮してください: 合計時間はチャンクごとのレンダリングに加えて最終マージですので、その追加のパスを比較に含めてください。
  • 非常に大きなジョブではメモリを抑える: マージ中に定期的にガベージコレクションを強制し、MaxDynamicHFPagesPerBatchでブラウザプールを調整することが役立ちます。
Curtis Chau
テクニカルライター

Curtis Chauは、カールトン大学でコンピュータサイエンスの学士号を取得し、Node.js、TypeScript、JavaScript、およびReactに精通したフロントエンド開発を専門としています。直感的で美しいユーザーインターフェースを作成することに情熱を持ち、Curtisは現代のフレームワークを用いた開発や、構造の良い視覚的に魅力的なマニュアルの作成を楽しんでいます。

開発以外にも、CurtisはIoT(Internet of Things)への強い関心を持ち、ハードウェアとソフトウェアの統合方法を模索しています。余暇には、ゲームをしたりDiscordボットを作成したりして、技術に対する愛情と創造性を組み合わせています。

準備はできましたか?
Nuget ダウンロード 20,296,129 | バージョン: 2026.7 リリースされたばかり
Still Scrolling Icon

まだスクロールしていますか?

すぐに証拠が欲しいですか? PM > Install-Package IronPdf
サンプルを実行するHTML が PDF に変換されるのを確認します。