Recursive AI

Merge PDFs

All onepdf recipes

Merge PDF files in C# with OnePDF, keeping bookmarks, links and form fields, merge PDFs held in memory, or append chosen pages to a file.

C#
using OnePdf;
 
string[] files = ["chapter-1.pdf", "chapter-2.pdf"];
List<PdfDocument> sources = files.Select(f => PdfDocument.Open(f)).ToList();
try
{
    using PdfDocument merged = PdfDocument.Merge(sources);
    merged.SetInfo("Title", "Collected chapters");     // Merge copies no metadata
    merged.Save("merged.pdf");
 
    Console.WriteLine($"{merged.PageCount} pages");
    foreach (PdfOutlineItem bookmark in merged.Outline)
        Console.WriteLine($"  bookmark: {bookmark.Title}");
}
finally
{
    foreach (PdfDocument source in sources)
        source.Dispose();
}
 
// PDFs held in memory: open the bytes, merge, and get bytes back.
byte[] first = File.ReadAllBytes("chapter-1.pdf");
byte[] second = File.ReadAllBytes("chapter-2.pdf");
using (PdfDocument a = PdfDocument.Open(first))
using (PdfDocument b = PdfDocument.Open(second))
using (PdfDocument both = PdfDocument.Merge([a, b]))
{
    var buffer = new MemoryStream();
    both.Save(buffer);
    Console.WriteLine($"{buffer.Length:N0} bytes in memory");
}
 
// Append only the first page of another file to an existing document.
using (PdfDocument target = PdfDocument.Open("chapter-1.pdf", new PdfOpenOptions { AllowEditing = true }))
using (PdfDocument other = PdfDocument.Open("chapter-2.pdf"))
{
    target.ImportPages(other, [0]);
    target.Save("chapter-1-plus.pdf");
    Console.WriteLine($"chapter-1-plus.pdf: {target.PageCount} pages");
}
Text
4 pages
  bookmark: Chapter 1, part 1
  bookmark: Chapter 1, part 2
  bookmark: Chapter 2, part 1
  bookmark: Chapter 2, part 2
32,147 bytes in memory
chapter-1-plus.pdf: 3 pages

What comes along

  • Bookmarks that lead to copied pages are appended after the existing ones.
  • Links between copied pages keep working. A link to a page that was left behind keeps its rectangle and loses its target.
  • Form fields come with the field tree above them. A field name already used in the target gets a _2, _3 suffix, so fields of the same name are renamed, not merged.
  • Tagged structure, page labels and optional content of the copied pages.
  • Each shared font or image is copied once, however many pages use it.

Not carried: the sources' metadata, signatures, document-level JavaScript and permissions. Merge writes no title, so set one with SetInfo.

Good to know

  • Merge and ImportPages take any open document, including encrypted ones. An encrypted source imports as plain content, so a merged result is unencrypted unless you save it with PdfSaveOptions.Encryption = PdfEncryptionAction.Set.
  • The merged document lives in memory and doesn’t refer to its sources, which you can dispose as soon as Merge returns.
  • ImportPages(source, indexes, insertAt) inserts at a position instead of appending.

To go the other way, see Split a PDF. Every rule for copied pages is in Page operations.

A longer walk-through with bookmarks, links, form fields and an ASP.NET Core endpoint: Merge PDF files in C#.