Как работать с PDF-файлом на Python

Оглавление

Portable Document Format, или PDF, - это формат файлов, который можно использовать для надежного представления и обмена документами в разных операционных системах. Хотя PDF был изначально изобретен компанией Adobe, в настоящее время он является открытым стандартом, который поддерживается Международной организацией по стандартизации (ISO). Вы можете работать с уже существующим PDF в Python, используя пакет PyPDF2.

PyPDF2 - это пакет pure-Python, который вы можете использовать для множества различных типов операций с PDF.

К концу этой статьи вы будете знать, как делать следующее:

  • Извлечение информации о документе из PDF на Python
  • Поворот страниц
  • Слияние PDF-файлов
  • Разделить PDF
  • Добавлять водяные знаки
  • Зашифровать PDF

Давайте начнем!

История pyPdf, PyPDF2 и PyPDF4

Оригинальный пакет pyPdf был выпущен в далеком 2005 году. Последний официальный релиз pyPdf состоялся в 2010 году. После примерно годичного перерыва компания под названием Phasit спонсировала форк pyPdf под названием PyPDF2. Код был написан так, чтобы быть обратно совместимым с оригиналом, и работал довольно хорошо в течение нескольких лет, последний релиз состоялся в 2016 году.

Была короткая серия выпусков пакета под названием PyPDF3, а затем проект был переименован в PyPDF4. Все эти проекты делают практически одно и то же, но самое большое различие между pyPdf и PyPDF2+ заключается в том, что в последних версиях добавлена поддержка Python 3. Существует форк оригинальной версии pyPdf для Python 3, но он не поддерживается уже много лет.

В то время как PyPDF2 был заброшен в 2016 году, он был возрожден в 2022 году и в настоящее время активно поддерживается. Новый PyPDF4 не имеет полной обратной совместимости с PyPDF2. Большинство примеров в этой статье будут прекрасно работать с PyPDF4, но есть и такие, которые не смогут, поэтому PyPDF4 не представлен в этой статье более подробно. Не стесняйтесь заменить импорт PyPDF2 на PyPDF4 и посмотрите, как это работает для вас.

pdfrw: Альтернатива

Патрик Мопин (Patrick Maupin) создал пакет pdfrw, который может делать многое из того, что делает PyPDF2. Вы можете использовать pdfrw для всех тех же задач, которые вы научитесь выполнять в этой статье для PyPDF2, за исключением шифрования.

Самое большое отличие pdfrw в том, что он интегрируется с пакетом ReportLab, так что вы можете взять уже существующий PDF и создать новый с помощью ReportLab, используя часть или весь уже существующий PDF.

Установка

Установка PyPDF2 может быть выполнена с помощью pip или conda, если вы используете Anaconda вместо обычного Python.

Вот как вы установите PyPDF2 с pip:

$ pip install pypdf2

Установка происходит довольно быстро, поскольку PyPDF2 не имеет никаких зависимостей. Скорее всего, вы потратите столько же времени на загрузку пакета, сколько и на его установку.

Теперь давайте продолжим и узнаем, как извлечь некоторую информацию из PDF.

Как извлечь информацию о документе из PDF в Python

Вы можете использовать PyPDF2 для извлечения метаданных и некоторого текста из PDF. Это может быть полезно, когда вы выполняете определенные виды автоматизации на уже существующих PDF-файлах.

Вот текущие типы данных, которые можно извлечь:

  • Автор
  • Создатель
  • Продюсер
  • Субъект
  • Титул
  • Количество страниц

Вам нужно найти PDF-файл, который можно использовать для этого примера. Вы можете использовать любой PDF, который есть у вас под рукой. Чтобы упростить задачу, я зашел на сайт Leanpub и взял образец одной из моих книг для этого упражнения. Образец, который вы хотите скачать, называется reportlab-sample.pdf.

Давайте напишем код, используя этот PDF, и узнаем, как можно получить доступ к этим атрибутам:

# extract_doc_info.py

from PyPDF2 import PdfFileReader

def extract_information(pdf_path):
    with open(pdf_path, 'rb') as f:
        pdf = PdfFileReader(f)
        information = pdf.getDocumentInfo()
        number_of_pages = pdf.getNumPages()

    txt = f"""
    Information about {pdf_path}: 

    Author: {information.author}
    Creator: {information.creator}
    Producer: {information.producer}
    Subject: {information.subject}
    Title: {information.title}
    Number of pages: {number_of_pages}
    """

    print(txt)
    return information

if __name__ == '__main__':
    path = 'reportlab-sample.pdf'
    extract_information(path)

Здесь вы импортируете PdfFileReader из пакета PyPDF2. PdfFileReader - это класс с несколькими методами для работы с файлами PDF. В этом примере вы вызываете .getDocumentInfo(), который возвращает экземпляр DocumentInformation. Он содержит большую часть интересующей вас информации. Вы также вызываете .getNumPages() на объекте reader, который возвращает количество страниц в документе.

Примечание: В последнем блоке кода используются новые f-строки Python 3 для форматирования строк. Если вы хотите узнать больше, вы можете ознакомиться с Python's F-String for String Interpolation and Formatting.

У information переменной есть несколько атрибутов экземпляра, которые вы можете использовать для получения остальных метаданных, которые вы хотите получить из документа. Вы распечатываете эту информацию, а также возвращаете ее для возможного использования в будущем.

Хотя у PyPDF2 есть .extractText(), который можно использовать в его страничных объектах (в этом примере не показано), он работает не очень хорошо. Некоторые PDF-файлы возвращают текст, а некоторые - пустую строку. Если вы хотите извлечь текст из PDF, то вместо этого вам стоит обратить внимание на проект PDFMiner. PDFMiner гораздо более надежен и был специально разработан для извлечения текста из PDF.

Теперь вы готовы узнать, как вращать страницы PDF.

Как перевернуть страницы

Иногда вы получаете PDF-файлы, содержащие страницы, которые находятся в режиме пейзажа вместо портретного режима. А возможно, они даже перевернуты вверх ногами. Это может произойти, когда кто-то сканирует документ в PDF или отправляет по электронной почте. Вы можете распечатать документ и прочитать бумажную версию, а можете использовать возможности Python, чтобы повернуть неработающие страницы.

Для этого примера вы можете выбрать статью Real Python и распечатать ее в PDF.

Давайте научимся вращать несколько страниц этой статьи с помощью PyPDF2:

# rotate_pages.py

from PyPDF2 import PdfFileReader, PdfFileWriter

def rotate_pages(pdf_path):
    pdf_writer = PdfFileWriter()
    pdf_reader = PdfFileReader(pdf_path)
    # Rotate page 90 degrees to the right
    page_1 = pdf_reader.getPage(0).rotateClockwise(90)
    pdf_writer.addPage(page_1)
    # Rotate page 90 degrees to the left
    page_2 = pdf_reader.getPage(1).rotateCounterClockwise(90)
    pdf_writer.addPage(page_2)
    # Add a page in normal orientation
    pdf_writer.addPage(pdf_reader.getPage(2))

    with open('rotate_pages.pdf', 'wb') as fh:
        pdf_writer.write(fh)

if __name__ == '__main__':
    path = 'Jupyter_Notebook_An_Introduction.pdf'
    rotate_pages(path)

В этом примере вам нужно импортировать PdfFileWriter в дополнение к PdfFileReader, потому что вам нужно будет записать новый PDF. rotate_pages() принимает путь к PDF, который вы хотите изменить. Внутри этой функции необходимо создать объект writer, который можно назвать pdf_writer, и объект reader, который называется pdf_reader.

Далее вы можете использовать .GetPage(), чтобы получить нужную страницу. Здесь вы берете нулевую страницу, которая является первой. Затем вы вызываете метод .rotateClockwise() объекта page и передаете 90 градусов. Затем для второй страницы вы вызываете метод .rotateCounterClockwise() и также передаете ему 90 градусов.

Примечание: Пакет PyPDF2 позволяет поворачивать страницу только на 90 градусов. В противном случае вы получите AssertionError.

После каждого вызова методов поворота вы вызываете .addPage(). Это добавит повернутую версию страницы в объект writer. Последней страницей, которую вы добавляете в объект writer, будет страница 3 без какого-либо вращения.

Наконец, вы записываете новый PDF с помощью .write(). В качестве параметра он принимает файлоподобный объект. Новый PDF будет содержать три страницы. Первые две будут повернуты в противоположные стороны друг от друга и будут иметь альбомную ориентацию, а третья страница будет обычной.

Теперь давайте узнаем, как можно объединить несколько PDF-файлов в один.

Как объединить PDF-файлы

Существует множество ситуаций, когда необходимо взять два или более PDF-файла и объединить их в один PDF. Например, у вас может быть стандартная титульная страница, которую нужно включить во многие типы отчетов. Вы можете использовать Python, чтобы помочь вам сделать это.

Для этого примера можно открыть PDF-файл и распечатать одну страницу в виде отдельного PDF-файла. Затем сделайте это снова, но уже с другой страницей. Это даст вам несколько исходных данных, которые можно использовать для примера.

Давайте напишем код, который можно использовать для объединения PDF-файлов:

# pdf_merging.py

from PyPDF2 import PdfFileReader, PdfFileWriter

def merge_pdfs(paths, output):
    pdf_writer = PdfFileWriter()

    for path in paths:
        pdf_reader = PdfFileReader(path)
        for page in range(pdf_reader.getNumPages()):
            # Add each page to the writer object
            pdf_writer.addPage(pdf_reader.getPage(page))

    # Write out the merged PDF
    with open(output, 'wb') as out:
        pdf_writer.write(out)

if __name__ == '__main__':
    paths = ['document1.pdf', 'document2.pdf']
    merge_pdfs(paths, output='merged.pdf')

Вы можете использовать merge_pdfs(), когда у вас есть список PDF-файлов, которые вы хотите объединить. Вам также нужно будет знать, куда сохранить результат, поэтому эта функция принимает список входных путей и выходной путь.

Затем вы перебираете входы и создаете для каждого из них объект PDF Reader. Далее вы перебираете все страницы в PDF-файле и с помощью .addPage() добавляете каждую из них к себе.

После того как вы закончите итерацию по всем страницам всех PDF-файлов в вашем списке, выпишите результат в конце.

Один момент, на который я хотел бы обратить внимание, заключается в том, что вы можете немного усовершенствовать этот сценарий, добавив диапазон страниц, которые нужно добавить, если вы не хотите объединять все страницы каждого PDF. Если вы хотите потрудиться, вы также можете создать интерфейс командной строки для этой функции, используя модуль Python argparse.

Давайте узнаем, как сделать слияние наоборот!

Как разделить PDF-файл

Бывают случаи, когда PDF-файл необходимо разделить на несколько PDF-файлов. Это особенно актуально для PDF-файлов, содержащих большое количество отсканированного содержимого, но существует множество веских причин, по которым вы можете захотеть разделить PDF-файл.

Вот как вы можете использовать PyPDF2 для разделения PDF на несколько файлов:

# pdf_splitting.py

from PyPDF2 import PdfFileReader, PdfFileWriter

def split(path, name_of_split):
    pdf = PdfFileReader(path)
    for page in range(pdf.getNumPages()):
        pdf_writer = PdfFileWriter()
        pdf_writer.addPage(pdf.getPage(page))

        output = f'{name_of_split}{page}.pdf'
        with open(output, 'wb') as output_pdf:
            pdf_writer.write(output_pdf)

if __name__ == '__main__':
    path = 'Jupyter_Notebook_An_Introduction.pdf'
    split(path, 'jupyter_page')

В этом примере вы снова создаете объект PDF reader и перебираете его страницы. Для каждой страницы PDF вы создадите новый экземпляр PDF writer и добавите в него одну страницу. Затем вы запишете эту страницу в файл с уникальным именем. После завершения работы сценария каждая страница исходного PDF должна быть разбита на отдельные PDF-файлы.

Теперь давайте узнаем, как можно добавить водяной знак в PDF.

Как добавить водяные знаки

Водяные знаки - это идентифицирующие изображения или узоры на печатных и цифровых документах. Некоторые водяные знаки можно увидеть только при специальном освещении. Причина важности водяных знаков заключается в том, что они позволяют защитить вашу интеллектуальную собственность, например изображения или PDF-файлы. Другой термин для обозначения водяного знака - наложение.

Вы можете использовать Python и PyPDF2 для нанесения водяных знаков на документы. У вас должен быть PDF, содержащий только изображение или текст водяного знака.

Давайте узнаем, как добавить водяной знак:

# pdf_watermarker.py

from PyPDF2 import PdfFileWriter, PdfFileReader

def create_watermark(input_pdf, output, watermark):
    watermark_obj = PdfFileReader(watermark)
    watermark_page = watermark_obj.getPage(0)

    pdf_reader = PdfFileReader(input_pdf)
    pdf_writer = PdfFileWriter()

    # Watermark all the pages
    for page in range(pdf_reader.getNumPages()):
        page = pdf_reader.getPage(page)
        page.mergePage(watermark_page)
        pdf_writer.addPage(page)

    with open(output, 'wb') as out:
        pdf_writer.write(out)

if __name__ == '__main__':
    create_watermark(
        input_pdf='Jupyter_Notebook_An_Introduction.pdf', 
        output='watermarked_notebook.pdf',
        watermark='watermark.pdf')

create_watermark() принимает три аргумента:

  1. input_pdf: путь к файлу PDF, на который будет наложен водяной знак
  2. output: путь, по которому нужно сохранить версию PDF с водяными знаками
  3. watermark: PDF, содержащий изображение или текст водяного знака

В коде вы открываете PDF с водяным знаком и захватываете только первую страницу документа, поскольку именно на ней должен располагаться водяной знак. Затем вы создаете объект PDF Reader, используя input_pdf, и общий объект pdf_writer для записи PDF с водяным знаком.

Следующий шаг - итерация по страницам в input_pdf. Вот здесь и происходит волшебство. Вам нужно будет вызвать .mergePage() и передать ему watermark_page. Когда вы это сделаете, он наложит watermark_page поверх текущей страницы. Затем вы добавите эту новую объединенную страницу в ваш объект pdf_writer.

Наконец, вы записываете новый PDF с водяными знаками на диск, и все готово!

Последняя тема, о которой вы узнаете, - это то, как PyPDF2 обрабатывает шифрование.

Как зашифровать PDF

PyPDF2 в настоящее время поддерживает только добавление пароля пользователя и пароля владельца к уже существующему PDF. В стране PDF пароль владельца, по сути, дает вам права администратора PDF и позволяет устанавливать разрешения на документ. С другой стороны, пароль пользователя позволяет просто открыть документ.

Насколько я могу судить, PyPDF2 на самом деле не позволяет установить какие-либо разрешения на документ, хотя и позволяет установить пароль владельца.

Независимо от этого, вот как можно добавить пароль, который также будет по своей сути шифровать PDF:

# pdf_encrypt.py

from PyPDF2 import PdfFileWriter, PdfFileReader

def add_encryption(input_pdf, output_pdf, password):
    pdf_writer = PdfFileWriter()
    pdf_reader = PdfFileReader(input_pdf)

    for page in range(pdf_reader.getNumPages()):
        pdf_writer.addPage(pdf_reader.getPage(page))

    pdf_writer.encrypt(user_pwd=password, owner_pwd=None, 
                       use_128bit=True)

    with open(output_pdf, 'wb') as fh:
        pdf_writer.write(fh)

if __name__ == '__main__':
    add_encryption(input_pdf='reportlab-sample.pdf',
                   output_pdf='reportlab-encrypted.pdf',
                   password='twofish')

add_encryption() принимает пути входного и выходного PDF, а также пароль, который вы хотите добавить к PDF. Затем, как и раньше, открывается объект записи и чтения PDF. Поскольку вы хотите зашифровать весь входной PDF-файл, вам нужно будет перебрать все его страницы и добавить их в пишущий объект.

Завершающим шагом является вызов команды .encrypt(), которая принимает пароль пользователя, пароль владельца, а также информацию о том, следует ли включить 128-битное шифрование. По умолчанию 128-битное шифрование должно быть включено. Если вы установите значение False, то вместо него будет применяться 40-битное шифрование.

Примечание: Для шифрования PDF используется RC4 или AES (Advanced Encryption Standard) согласно pdflib.com.

Если вы зашифровали свой PDF-файл, это не значит, что он обязательно защищен. Существуют инструменты для удаления паролей из PDF-файлов. Если вы хотите узнать больше, в Университете Карнеги-Меллона есть интересная статья на эту тему.

Заключение

Пакет PyPDF2 весьма полезен и обычно работает довольно быстро. Вы можете использовать PyPDF2 для автоматизации больших заданий и использовать его возможности, чтобы помочь вам лучше выполнять свою работу!

В этом уроке вы научились делать следующее:

  • Извлечение метаданных из PDF
  • Поворот страниц
  • Слияние и разделение PDF-файлов
  • Добавляйте водяные знаки
  • Добавляйте шифрование

Также следите за более новым пакетом PyPDF4, поскольку он, вероятно, скоро заменит PyPDF2. Вы также можете ознакомиться с пакетом pdfrw, который может делать многое из того же, что и PyPDF2.

Дальнейшее чтение

Если вы хотите узнать больше о работе с PDF-файлами в Python, вам следует ознакомиться со следующими ресурсами:

Back to Top