Szybsze nagłówki i stopki w dużych plikach PDF
Dodawanie nagłówków i stopek do bardzo dużego pliku PDF w jednym przebiegu jest powolne. Przetwarzaj dokument w stałych rozmiarów porcjach stron, renderuj każdą partię, a następnie scal części z powrotem, aby skrócić całkowity czas renderowania, zachowując ciągłość numeracji stron w całym dokumencie.
Rozwiązanie
1. Podziel indeksy stron na partie
Wybierz rozmiar porcjowania i pogrupuj indeksy stron dokumentu w stałych rozmiarów partie, na przykład po 300 stron każda. Mniejsze porcje renderują się szybciej indywidualnie, ale dodają więcej pracy przy scalaniu na końcu.
2. Skopiuj każdą partię do osobnego dokumentu
Wyodrębnij strony dla bieżącej partii za pomocą CopyPages, uzyskując samodzielny dokument do renderowania.
3. Renderuj nagłówki i stopki z ciągłą numeracją
Ustaw FirstPageNumber na stronę początkową porcjowania, aby numeracja przenosiła się przez cały dokument zamiast zaczynać się od 1 w każdej partii.
4. Zapisz każdą porcję i zutylizuj ją
Zapisz każdą porcję do pliku tymczasowego i zutylizuj ją. Zwolnienie porcji przed rozpoczęciem kolejnej partii zapobiega wzrostowi pamięci podczas działania.
5. Scal porcje do ostatecznego pliku PDF
Ponownie otwórz pliki tymczasowe, dołącz je w kolejności za pomocą AppendPdf, zapisz wynik, a następnie usuń pliki tymczasowe.
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 jest kluczowy: jest ustawiony na każdą globalną stronę początkową porcji, więc stopka jak Page {page} of {total-pages} pozostaje spójna od pierwszej do ostatniej porcji.
Wskazówki dotyczące debugowania
- Dopasuj rozmiar porcji: wartość powyżej (300) jest regulowana, a moment, kiedy porcjowanie zaczyna przynosić korzyści, zależy od Twojego obciążenia.
- Uwzględnij scalanie: całkowity czas to renderowanie każdej porcji plus ostateczne scalanie, więc uwzględnij ten dodatkowy przebieg w każdej porównywaniu.
- Utrzymaj kontrolę nad pamięcią w bardzo dużych zadaniach: okresowe wymuszanie zbierania śmieci podczas scalania oraz dostosowywanie puli przeglądarki za pomocą
BrowserPool.MaxIdleTabsiMaxDynamicHFPagesPerBatchmoże pomóc.

