Python Central HubSign in

Automation and Scripting / Phase 2: Office & Productivity Automation

PDF Manipulation - Merging and Splitting

Merge and split PDF files using pypdf (or PyPDF2-compatible APIs).

Library choice

A common modern option is pypdf (successor of PyPDF2). Older tutorials use PdfMerger, which newer versions of pypdf have removed. Use PdfWriter as below.

Merge PDFs

Python
from pypdf import PdfWriter
 
writer = PdfWriter()
writer.append("a.pdf")
writer.append("b.pdf")
 
with open("merged.pdf", "wb") as f:
    writer.write(f)
writer.close()

Split a PDF

Python
from pypdf import PdfReader, PdfWriter
 
reader = PdfReader("input.pdf")
 
for i, page in enumerate(reader.pages, start=1):
    writer = PdfWriter()
    writer.add_page(page)
    with open(f"page_{i}.pdf", "wb") as f:
        writer.write(f)

Notes

  • encrypted PDFs need extra handling
  • layout/text extraction is a different problem (next page)

Merging with the current API

PdfMerger was deprecated and removed in recent pypdf releases, and PdfWriter now does everything. This version works on current pypdf, and lets you pick pages.

Python
from pathlib import Path
from pypdf import PdfWriter
 
writer = PdfWriter()
for name in ["a.pdf", "b.pdf"]:
    writer.append(name)                 # adds every page
 
writer.append("c.pdf", pages=(0, 2))    # only pages 1 and 2 of c.pdf
 
with Path("merged.pdf").open("wb") as f:
    writer.write(f)
writer.close()

pages=(0, 2) is a range of zero-based page numbers, where the end is not included, like range. So (0, 2) means the first two pages. If your installed pypdf is old and lacks PdfWriter.append, upgrade it with pip install -U pypdf.

Splitting by page range

The split example writes one file per page. Often you want a section instead, such as pages 3 to 5.

Python
from pypdf import PdfReader, PdfWriter
 
reader = PdfReader("input.pdf")
writer = PdfWriter()
 
for index in range(2, 5):               # pages 3, 4, 5
    writer.add_page(reader.pages[index])
 
with open("section.pdf", "wb") as f:
    writer.write(f)

Check len(reader.pages) first. Asking for a page that does not exist raises IndexError.

Other useful operations

  • Rotate. page.rotate(90) turns a page clockwise before you add it to the writer.
  • Metadata. reader.metadata gives the title and author, and writer.add_metadata({...}) sets them.
  • Bookmarks. writer.add_outline_item("Chapter 1", page_number=0) adds a navigation entry.
  • Encrypted files. Call reader.decrypt("password") before reading pages.

Common mistakes

  • Merging in the wrong order. Files sorted with sorted(Path(".").glob("*.pdf")) go in name order. 10.pdf sorts before 2.pdf, so use zero-padded names.
  • Writing the output into the folder you are globbing. The next run merges the old merged file into the new one.
  • Confusing page numbers. Humans count from 1, pypdf from 0.
  • Not closing the file. Use with for the output file.
  • Overwriting an original. Always write to a new name.

Adapting it

Merge every PDF in a folder into one report, split a scanned book into chapters, or extract the first page of many invoices into a summary file. Combine it with the previous lesson to extract text from each part.

Check your understanding

quizCheck yourself
  1. You want the first three pages of a PDF. What page indexes do you use with pypdf?

    Show answer

    B — 0, 1, 2 — pypdf uses zero-based indexes, so the first three pages are reader.pages[0], [1] and [2], the same as range(0, 3).

  2. Files named 1.pdf, 2.pdf and 10.pdf are merged in sorted string order. What is the result order?

    Show answer

    B — 1, 10, 2 — Strings compare character by character, so 10.pdf comes before 2.pdf. Use names like 01.pdf, 02.pdf, 10.pdf, or sort by the number.

  3. Why write the merged file into a different folder from the one you glob for inputs?

    Show answer

    B — The next run would pick up the merged file as an input and include it again — A glob for *.pdf also matches your own output. Keep inputs and outputs apart, or exclude the output name.

Was this page helpful?

Let us know how we did