Объединение, разбивка и сборка 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.