Skip to content
English
  • There are no suggestions because the search field is empty.

IronPDF on All Platforms: Stamping Slows Down on Very Large PDFs (Split, Process, Merge)

Overview

Stamping every page of a very large PDF in one pass gets progressively slower as the job runs. In the reported 20,000-page case, the bottleneck was the method used to split the document, not the stamping operation itself. The fix is to recursively split the PDF into small chunks, stamp each chunk (optionally in parallel worker processes), and merge the results into one file.

Environment

  • Hosting: 16-core benchmark machine running 8 worker processes

Version Metadata

  • Version found: 2026.7
  • Version resolved: Unknown

Cause

Engineering traced the slowdown in the 20,000-page case to the document-splitting method rather than the stamping operation. The detailed mechanism has not been independently confirmed from developer notes.

Solution

1. Recommended: Split the source PDF recursively into chunks.
Keep splitting until each chunk is at or below a target page count. Save each chunk to disk so memory stays low and workers can pick up files independently. The file name stores the chunk’s first page, which keeps chunks in order and supports page-dependent stamps later.
   using IronPdf;
   using IronPdf.Editing;

   static void SplitToFiles(PdfDocument doc, int startPage, int targetPages,
                            string outDir, List<string> files)
   {
       if (doc.PageCount <= targetPages)
       {
           // File name carries the chunk's first page (1-based).
           string path = Path.Combine(outDir, $"chunk_{startPage:D6}.pdf");
           doc.SaveAs(path);
           files.Add(path);
           return;
       }

       int mid = doc.PageCount / 2;
       using (var left = doc.CopyPages(0, mid - 1))
           SplitToFiles(left, startPage, targetPages, outDir, files);
       using (var right = doc.CopyPages(mid, doc.PageCount - 1))
           SplitToFiles(right, startPage + mid, targetPages, outDir, files);
   }

   // Usage
   var chunkFiles = new List<string>();
   using (var source = PdfDocument.FromFile("input.pdf"))
       SplitToFiles(source, 1, 500, @"C:\temp\chunks", chunkFiles);

The chunk size of 500 is a starting point. Tune it for your documents and hardware.

2. Stamp each chunk.
Open each chunk, apply the stamp, and save the result to a new file.

   var stamper = new HtmlStamper("<div style='font-size:24px;color:red'>CONFIDENTIAL</div>")
   {
       VerticalAlignment = VerticalAlignment.Middle,
       HorizontalAlignment = HorizontalAlignment.Center,
       Opacity = 50
   };

   var stampedFiles = new List<string>();
   foreach (string file in chunkFiles)
   {
       string outPath = file.Replace(".pdf", "_stamped.pdf");
       using (var chunk = PdfDocument.FromFile(file))
       {
           chunk.ApplyStamp(stamper);
           chunk.SaveAs(outPath);
       }
       stampedFiles.Add(outPath);
   }

3. Optional: Process chunks in parallel worker processes.
Give each worker process its own slice of the chunk list. The benchmark used 8 worker processes on a 16-core machine. Tune the worker count for your hardware. 

   // In each worker process: workerIndex = 0..workerCount-1
   var mySlice = chunkFiles.Where((f, i) => i % workerCount == workerIndex);

4. Merge the stamped chunks in order.

   var parts = stampedFiles.OrderBy(f => f)
                           .Select(f => PdfDocument.FromFile(f))
                           .ToList();
   using (var merged = PdfDocument.Merge(parts))
   {
       merged.SaveAs("output.pdf");
   }
   parts.ForEach(p => p.Dispose());

Delete the temporary chunk files when you are done.

5. Expect these timings as a rough guide.
Results from the 20,000-page benchmark:
  • Splitting: about 6 minutes
  • Stamping and merging, single process: about 6 minutes
  • Stamping and merging, 8 processes: about 1.5 minutes
  • Total parallel run: about 8 minutes

Splitting is the largest cost. If you generate the PDF yourself, consider producing it in chunks from the start and skipping the split.

6. Reuse the same pattern for other page-level operations.
Split–process–merge works for any operation that treats each page independently. Good fit (split, process, merge):

  • Text, image, HTML, and barcode stamps, and watermarks
  • HTML headers and footers (set FirstPageNumber so numbering stays continuous; see References)
  • Redacting text across all pages
  • Find-and-replace text
  • Image compression and form flattening
  • Backgrounds, foregrounds, and page rotation

Page-dependent stamps such as Bates numbers or page labels need each chunk’s global start page. Read it from the chunk file name:

   int firstPage = int.Parse(Path.GetFileNameWithoutExtension(file).Split('_')[1]);
   for (int i = 0; i < chunk.PageCount; i++)
   {
       var bates = new TextStamper { Text = $"ABC{firstPage + i:D7}" };
       chunk.ApplyStamp(bates, i);
   }

Split only, no merge needed:

  • Extracting text for search indexing
  • Rasterizing pages to images
  • Preparing pages for OCR

Do after merging, not per chunk:

  • Digital signatures (merging changes the document and breaks per-chunk signatures)
  • Passwords, permissions, and encryption
  • Bookmarks, outlines, and tables of contents
  • Document metadata
  • PDF/A or PDF/UA conversion

Merged chunks may repeat shared resources such as fonts. Compare output size with the original and compress the final file if needed.

Typical use cases:

  • Bates numbering for legal discovery sets
  • CONFIDENTIAL or DRAFT watermarks on large archives
  • Batch statements or invoices with barcodes for mail sorting
  • Redacting personal data across exported records
  • Shrinking scanned archives before long-term storage