⟳ Automation

Merge, split and rotate PDF files with Python and pypdf

How to merge, split, rotate and extract pages from PDF files with Python and the pypdf library, with ready-made examples for invoices and scans.

In almost every office there comes a time when you need to combine ten PDFs into a single file, split a hundred-page document into many separate files or straighten a scan that was fed in upside down. There are online services that do this, but uploading contracts or identity documents to an unknown website is not a good idea. With Python and the pypdf library the same operations run on your own computer, in a repeatable way and with no limit on the number of files.

Installation and first steps

pypdf is a library written entirely in Python, with no external dependencies for the basic operations. You install it with:

pip install pypdf

There are two main objects: PdfReader, which opens an existing file and exposes its pages, and PdfWriter, which builds a new document. Almost every operation consists of reading pages from one or more readers and adding them to a writer.

from pypdf import PdfReader

reader = PdfReader("contract.pdf")
print(len(reader.pages), "pages")

Merging several PDFs into one file

The most common case: a folder of attachments that must be delivered as a single document. Order matters, so it is best to sort them explicitly, for example by name.

from pathlib import Path
from pypdf import PdfWriter

writer = PdfWriter()
for pdf in sorted(Path("attachments").glob("*.pdf")):
    writer.append(str(pdf))

with open("bundle.pdf", "wb") as f:
    writer.write(f)

The append method adds all the pages of the file and, in recent versions of the library, also keeps its bookmarks. If the files are numbered 1.pdf, 2.pdf, 10.pdf, alphabetical sorting will put 10 before 2: the fix is to use names with leading zeros (01, 02, 10) or to sort by the number extracted from the name.

Splitting a PDF into separate files

The reverse operation is needed, for instance, when the scanner produces a single PDF with all the month's invoices. To create one file per page:

from pypdf import PdfReader, PdfWriter

reader = PdfReader("monthly_scan.pdf")
for number, page in enumerate(reader.pages, start=1):
    writer = PdfWriter()
    writer.add_page(page)
    with open(f"page_{number:03d}.pdf", "wb") as f:
        writer.write(f)

Often, however, documents are longer than one page. In that case you define ranges: say pages 1-3 for the first document, 4-4 for the second, 5-9 for the third. A small list of tuples, perhaps read from a spreadsheet filled in by a colleague, makes the script reusable without touching the code.

ranges = [("order_A", 1, 3), ("order_B", 4, 4), ("order_C", 5, 9)]
for name, start, end in ranges:
    writer = PdfWriter()
    for i in range(start - 1, end):
        writer.add_page(reader.pages[i])
    with open(f"{name}.pdf", "wb") as f:
        writer.write(f)

Note the start - 1: people count pages from 1, Python from 0. It is the most frequent mistake in scripts like these.

Rotating crooked pages

Hurried scans often contain upside-down or sideways pages. Every page has a rotate method that accepts multiples of 90 degrees:

for i, page in enumerate(reader.pages):
    if i in {2, 5}:
        page.rotate(180)
    writer.add_page(page)

The rotation is an instruction to the viewer, not a reprocessing of the image: the file loses no quality and the operation is instantaneous even on heavy documents.

Extracting, reordering and deleting pages

With the same logic you can build a new document by picking only some pages or changing their order. Removing the blank final page that many scanners add amounts to copying every page except the last. Moving the cover to the end means adding the pages from the second onwards first and then the first one.

Operation Basic idea
Merge append several files into the same writer
Split A new writer for each range
Rotate rotate(90), rotate(180) on the page
Delete Copy every page except the unwanted ones
Reorder Add the pages in the desired order

Password-protected PDFs

Some PDFs are encrypted. PdfReader reports this through the is_encrypted attribute; if you know the password, you can unlock the file with reader.decrypt("password") and then carry on as usual. This applies to documents whose password you legitimately have, such as statements downloaded from your own bank: the library is not meant, and must not be used, to get around other people's protections. Likewise, PdfWriter can encrypt the output with the encrypt method, which is useful when the bundle contains personal data and has to travel by email.

Good practices

In my work I always keep originals and results apart: the script reads from an originals folder and writes to a processed folder, never overwriting the source files. Other useful precautions:

  • check that the final page count is what you expect by printing len(writer.pages);
  • give the output files names that contain a date and a description, so they are easy to find;
  • handle damaged files with a try block, otherwise they stop the whole run;
  • remember that pypdf manipulates pages, not text: to change the content of a page you need other tools.

In short

  • pypdf lets you merge, split, rotate and reorder PDFs locally, without uploading them anywhere.
  • PdfReader reads the pages, PdfWriter builds the new document.
  • Watch the numbering: people start from 1, Python from 0.
  • Rotation does not degrade quality and is instantaneous.
  • Keep originals and processed files apart and check the final page count.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. A computer science graduate, he combines rigorous technical analysis with attention to the people who use technology.

Read also