Объединение, разбивка и сборка PDF

Часто PDF нужно не читать, а пересобирать: склеить десять счетов в один, вырезать нужные страницы договора, перевернуть скан. pypdf делает это через PdfWriter.
Суть: PdfReader читает страницы, PdfWriter собирает новый документ. Вы добавляете в writer нужные страницы и сохраняете — как сборку из деталей.

Сборка PDF — это конструктор: вы берёте страницы из существующих документов (через PdfReader) и складываете их в новый (через PdfWriter). На этом построены все операции: объединение — добавить все страницы нескольких файлов; разбивка — положить в writer только нужный диапазон; поворот — повернуть страницу перед добавлением.

Логику выбора диапазонов и нумерации страниц легко отработать в браузере на чистом Python — это арифметика индексов, на которой и строится разбивка.

Попробуй сам ▶

# Разбивка документа на части по N страниц
total_pages = 13
chunk = 5   # по 5 страниц в каждый файл

part = 1
for start in range(0, total_pages, chunk):
    end = min(start + chunk, total_pages)
    # страницы нумеруются с 0, людям показываем с 1
    pages = list(range(start, end))
    human = f'{start + 1}-{end}'
    print(f'part_{part}.pdf: страницы {human} (индексы {pages})')
    part += 1

print(f'Итого файлов: {part - 1}')

Здесь видна частая грабля: внутри pypdf страницы нумеруются с нуля, а человек думает «с первой». Поэтому в коде держат индексы (с 0), а в именах файлов и сообщениях показывают человеческую нумерацию (с 1). Реальная сборка через writer показана как нерабочая в браузере врезка.

from pypdf import PdfReader, PdfWriter

# ОБЪЕДИНЕНИЕ нескольких файлов в один
writer = PdfWriter()
for name in ['jan.pdf', 'feb.pdf', 'mar.pdf']:
    reader = PdfReader(name)
    for page in reader.pages:
        writer.add_page(page)
with open('quarter.pdf', 'wb') as f:
    writer.write(f)

# РАЗБИВКА: только первые 5 страниц
reader = PdfReader('big.pdf')
out = PdfWriter()
for page in reader.pages[:5]:
    out.add_page(page)
with open('first5.pdf', 'wb') as f:
    out.write(f)
СБОРКА PDF КАК КОНСТРУКТОР

  jan.pdf  --pages-->  +-----------+
  feb.pdf  --pages-->  | PdfWriter | --write--> quarter.pdf
  mar.pdf  --pages-->  +-----------+

  Reader -> страницы -> Writer -> новый файл
  (открывать на запись нужно в режиме 'wb')

Сборка PDF открывает и более тонкие сценарии, чем простая склейка. Можно, например, добавлять на каждую страницу водяной знак или нумерацию, накладывая один PDF поверх другого через merge_page. Можно собирать титульный лист на лету и приклеивать его к основному документу. Можно извлекать отдельные счета из одного большого файла-выписки, ориентируясь на страницы-разделители. Во всех случаях схема одна: читаем страницы из источников, при необходимости преобразуем, складываем в writer и сохраняем. Освоив базовую сборку, вы дальше лишь комбинируете эти кубики под конкретную задачу документооборота.

Как работает под капотом

Когда вы делаете writer.add_page(page), страница копируется в новый документ вместе со своим содержимым и шрифтами. Поэтому собранный PDF самодостаточен — он не зависит от исходных файлов. Поворот делается методом page.rotate(90), который меняет атрибут поворота в метаданных страницы, не перерисовывая её содержимое.

Запись PDF идёт в бинарном режиме ('wb'), а не текстовом: PDF — двоичный формат, и попытка открыть его в текстовом режиме испортит файл. Это отличает работу с PDF от работы с CSV или текстом, где режим текстовый.

Частые ошибки

  • Открывать на запись в текстовом режиме. PDF бинарен; нужен режим 'wb', иначе файл сломается.
  • Путать индексы и человеческую нумерацию. Страница 1 — это индекс 0; держите их раздельно.
  • Терять защиту/шифрование. Зашифрованный PDF нужно сначала расшифровать через decrypt.

Best practices

  • Собирайте новый документ в PdfWriter, не пытаясь «править» исходный на месте.
  • В именах файлов используйте человеческую нумерацию, в коде — индексы с нуля.
  • Всегда пишите в бинарном режиме и проверяйте результат, открыв файл.

Итоги. Сборка PDF — это Reader → страницы → Writer → новый файл в режиме wb. Объединение, разбивка и поворот — вариации одной схемы. Не путайте индексы с нумерацией. Дальше — генерация документов Word.

Проверьте себя
1. В каком режиме нужно открывать файл для записи PDF?
AВ текстовом режиме 'w'
BВ бинарном режиме 'wb'
CВ режиме добавления 'a'
DРежим не важен
2. Какой индекс у первой страницы PDF в pypdf?
A1
B0
C-1
DЗависит от документа