Python PDF Merger Project

Python project 17

Python PDF Merger Project

Build a command-line PDF merger with pypdf. The tool preserves the order provided by the user, rejects missing, duplicate, encrypted, or conflicting input paths, creates an output directory when needed, and reports the final page count.

Learn Python with mentor-guided projectsView all project ideas

What you will build

The program accepts two or more PDF filenames and an optional output path. Each document is appended with an outline label based on its filename, then written as one combined PDF.

Skills practised

  • Command-line arguments
  • Path validation
  • PDF reading and writing
  • Exceptions and cleanup

Requirements

  • Python 3.10 or later
  • pypdf: pip install pypdf
  • Two or more unencrypted PDFs
  • About 30 minutes to build

Full Python code

Save this code as pdf_merger.py, then follow the run instructions below.

"""Merge PDF files in a chosen order with pypdf."""

from __future__ import annotations

import argparse
from pathlib import Path

from pypdf import PdfReader, PdfWriter
from pypdf.errors import PdfReadError


def validate_paths(inputs: list[Path], output: Path) -> None:
    if len(inputs) < 2:
        raise ValueError("Choose at least two input PDF files.")

    resolved_output = output.resolve()
    seen: set[Path] = set()
    for source in inputs:
        resolved_source = source.resolve()
        if source.suffix.lower() != ".pdf" or not source.is_file():
            raise ValueError(f"Not a readable PDF file: {source}")
        if resolved_source == resolved_output:
            raise ValueError("The output file cannot also be an input file.")
        if resolved_source in seen:
            raise ValueError(f"The same input was listed twice: {source}")
        seen.add(resolved_source)


def merge_pdfs(inputs: list[Path], output: Path) -> int:
    """Merge inputs, write output, and return the number of pages written."""
    validate_paths(inputs, output)
    output.parent.mkdir(parents=True, exist_ok=True)
    writer = PdfWriter()
    total_pages = 0

    try:
        for source in inputs:
            reader = PdfReader(source)
            if reader.is_encrypted:
                raise ValueError(f"Encrypted PDF requires a password and was skipped: {source}")
            total_pages += len(reader.pages)
            writer.append(reader, outline_item=source.stem)
        with output.open("wb") as output_file:
            writer.write(output_file)
    except PdfReadError as error:
        raise ValueError(f"One input is not a valid readable PDF: {error}") from error
    finally:
        writer.close()

    return total_pages


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Merge PDF files in the order supplied.")
    parser.add_argument("inputs", nargs="+", type=Path, help="Two or more source PDF files")
    parser.add_argument("-o", "--output", type=Path, default=Path("merged.pdf"))
    return parser.parse_args()


def main() -> None:
    args = parse_args()
    try:
        pages = merge_pdfs(args.inputs, args.output)
    except (ValueError, OSError) as error:
        raise SystemExit(f"Merge failed: {error}") from error
    print(f"Created {args.output} with {pages} page(s).")


if __name__ == "__main__":
    main()

How the project works

validate_paths() checks every source before output is created. It prevents the output from also being an input and rejects duplicate sources, avoiding confusing or destructive results.

PdfWriter.append() copies each complete document in the supplied order. The writer is closed in a finally block so resources are released even when one source cannot be read. Encrypted PDFs are rejected because a complete password workflow is outside this beginner project.

Run the project

  1. Install pypdf with python -m pip install pypdf.
  2. Place two practice PDFs in the same folder.
  3. Run python pdf_merger.py first.pdf second.pdf --output combined.pdf.
  4. Open combined.pdf and verify the page order.

Accuracy and safety notes

  • Order: files merge in exactly the order typed.
  • Originals: source PDFs are read, not overwritten.
  • Encryption: password-protected documents require a separate authorised workflow.

Ways to extend it

Add drag-and-drop ordering, selected page ranges, password prompts, file-size reporting, or a desktop interface. See the official pypdf merging guide for advanced options.

Continue learning Python

Build the next project, return to the Softenant project library, or explore the Python programming course in Vizag for structured lessons, mentor feedback, and portfolio practice.