Headers e Rodapés Mais Rápidos em PDFs Grandes
Adicionar cabeçalhos e rodapés a um PDF muito grande em uma única passagem é lento. Processar o documento em pedaços de páginas de tamanho fixo, renderizar cada lote e depois mesclar as partes novamente para reduzir o tempo total de renderização enquanto mantém a numeração das páginas contínua em todo o documento.
Solução
1. Divida os índices de página em lotes
Escolha um tamanho de pedaço e agrupe os índices de páginas do documento em lotes de tamanho fixo, por exemplo, 300 páginas cada. Pedaços menores são renderizados mais rapidamente individualmente, mas adicionam mais trabalho de mesclagem no final.
2. Copie cada lote em seu próprio documento
Extraia as páginas para o lote atual com CopyPages, fornecendo um documento independente para renderizar.
3. Renderize cabeçalhos e rodapés com numeração contínua
Defina FirstPageNumber para a página inicial do pedaço, para que a numeração cubra o documento inteiro em vez de reiniciar no 1 em cada pedaço.
4. Salve cada pedaço e descarte-o
Escreva cada pedaço em um arquivo temporário e descarte-o. Liberar o pedaço antes do próximo lote impede que a memória aumente durante a execução.
5. Mescle os pedaços no PDF final
Reabra os arquivos temporários, anexe-os em ordem com AppendPdf, salve o resultado e, em seguida, exclua os arquivos temporários.
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
FirstPageNumber é a chave: ele é configurado para a página inicial global de cada pedaço, de modo que um rodapé como Page {page} of {total-pages} fique consistente do primeiro ao último pedaço.
Dicas de Depuração
- Ajuste o tamanho do pedaço: o valor acima (300) é ajustável, e o ponto em que a divisão começa a compensar depende da sua carga de trabalho.
- Considere a passagem de mesclagem: o tempo total é a renderização por pedaço mais a mesclagem final, então considere essa passagem extra em qualquer comparação.
- Mantenha a memória sob controle em trabalhos muito grandes: forçar a coleta de lixo periodicamente durante a mesclagem e ajustar o pool do navegador com
BrowserPool.MaxIdleTabseMaxDynamicHFPagesPerBatchpode ajudar.

