This script allows you to split a PDF file into a specified number of parts. It takes an input PDF file and the desired number of parts, calculates the page range for each part, and generates separate output files for each part.
Large PDFs are awkward for document-ingestion pipelines: upload limits, LLM context windows, OCR timeouts and parallel processing all work better on smaller files. This script splits one PDF into a chosen number of roughly equal parts using pdftk, a long-standing command-line PDF toolkit. It does not re-render or recompress pages; pdftk cat copies page objects into new files, so text, images and fonts are preserved exactly and the split is fast.
Before running the script, make sure to install pdftk on Ubuntu by running the following commands:
sudo apt-get update
sudo apt-get install -y pdftk
To run the script, use the following command:
./split-pdf.sh input_file number_of_parts
Replace input_file with the path to your PDF file, and number_of_parts with the number of parts you want to split the file into. For example:
./split-pdf.sh document.pdf 3
This command will split document.pdf into 3 separate PDF files.
The script performs the following steps:
pdftk to determine the total number of pages in the PDF.document-1.pdf, document-2.pdf).#!/bin/bash
# Installation Requirements on Ubuntu:
# sudo apt-get update
# sudo apt-get install -y pdftk
# Usage: ./split-pdf.sh input_file number_of_parts
# Input Arguments
input_file="$1"
number_of_parts="$2"
# Check if correct arguments are passed
if [[ -z "$input_file" || -z "$number_of_parts" ]]; then
echo "Usage: $0 input_file number_of_parts"
exit 1
fi
# Extract base name from input file (remove path and extension)
base_name=$(basename "$input_file" .pdf)
# Find the number of pages in the PDF
total_pages=$(pdftk "$input_file" dump_data | grep NumberOfPages | awk '{print $2}')
if [[ -z "$total_pages" ]]; then
echo "Error: Could not determine the number of pages in the PDF."
exit 1
fi
# Calculate the number of pages per part
pages_per_part=$((total_pages / number_of_parts))
remainder=$((total_pages % number_of_parts))
start_page=1
# Split the PDF file into the specified number of parts
for ((i=1; i<=number_of_parts; i++)); do
if [[ $i -eq $number_of_parts ]]; then
end_page=$total_pages
else
end_page=$((start_page + pages_per_part - 1))
fi
output_file="${base_name}-${i}.pdf"
pdftk "$input_file" cat ${start_page}-${end_page} output "$output_file"
if [[ $? -eq 0 ]]; then
echo "Generated: $output_file"
else
echo "Error creating: $output_file"
exit 1
fi
start_page=$((end_page + 1))
done
The script uses Bash integer arithmetic. $((total_pages / number_of_parts)) truncates, so with 10 pages and 3 parts each part gets 3 pages and one page is left over. Rather than distributing the remainder, the loop simply lets the final part end at total_pages. The remainder variable is computed but not otherwise used. The variable start_page advances to end_page + 1 after each part, so ranges never overlap and no page is skipped.
[[ -z ... ]] exits with a usage message when either argument is missing..pdf suffix so output files are written to the current directory as name-1.pdf, name-2.pdf, and so on.NumberOfPages: N; grep and awk extract N. An empty result means the file is not a readable PDF.pdftk "$input_file" cat START-END output FILE for each part, checking $? after every call.chmod +x split-pdf.sh
# Split a 300-page manual into 6 files of 50 pages
./split-pdf.sh manual.pdf 6
# Split every PDF in a folder into 4 parts each
for f in reports/*.pdf; do ./split-pdf.sh "$f" 4; done
# Check the result
for f in manual-*.pdf; do
echo "$f: $(pdftk "$f" dump_data | awk '/NumberOfPages/ {print $2}') pages"
done
If pdftk is unavailable, qpdf does the same job: qpdf in.pdf --pages . 1-50 -- part-1.pdf.
Inside a Python ingestion pipeline it is often simpler to split with the pypdf library and distribute the remainder evenly:
from pathlib import Path
from pypdf import PdfReader, PdfWriter # pip install pypdf
def split_pdf(path, parts):
reader = PdfReader(path)
n = len(reader.pages)
base, extra = divmod(n, parts)
start, stem = 0, Path(path).stem
for i in range(parts):
size = base + (1 if i < extra else 0) # spread the remainder
writer = PdfWriter()
for p in range(start, start + size):
writer.add_page(reader.pages[p])
out = f"{stem}-{i + 1}.pdf"
with open(out, "wb") as fh:
writer.write(fh)
print("Generated:", out, f"pages {start + 1}-{start + size}")
start += size
split_pdf("document.pdf", 3) # 4, 3, 3 pages
number_of_parts exceeds the page count, pages_per_part becomes 0 and pdftk receives invalid ranges; validate that parts is between 1 and the page count.input_pw for pdftk.In document-ingestion work the split is rarely the goal in itself; it is a preparation step that makes later stages manageable. Typical reasons are:
Because the script names parts sequentially and keeps original page order, the parts can be merged back with pdftk name-*.pdf cat output merged.pdf (with care for shell sort order when there are ten or more parts; use zero-padded numbers in that case). When the output feeds a retrieval system, record the original page offsets for each part so that answers can cite the page number in the source document, not in the fragment.