PDF. Именно в этом формате мы читаем электронные книги, делаем презентации и руководства. И он действительно удобен тем, что дает возможность, вне зависимости от платформы, легко и просто работать с документами. Изначально, а может быть даже в первую очередь, данный формат был предназначен для представления в электронном виде полиграфической продукции. С июля 2008 года данный формат стал открытым. А на сегодняшний день, можно сказать, что он стал стандартом обмена электронными документами полиграфического качества уже подготовленными для печати. Но, несмотря на открытый стандарт, работать с контентом представленным в таком виде, на самом деле сложно. Давайте посмотрим, сможет ли Python хоть немного облегчить задачу по работе с форматом PDF.
Кроме создания документов в формате PDF над ним можно проводить разнообразные операции. Давайте рассмотрим сегодня несколько из них, а именно: сохранение текста из документа в файл; сохранение изображений; разделение документа на страницы; сборка одного документа из нескольких.
Что потребуется?
Для выполнения нужных нам операций потребуется две библиотеки. На самом деле, можно было бы обойтись одной, но в данном случае каждая из библиотек делает какую-то операцию чуть лучше, чем ее аналог. Давайте установим библиотеки pymupdf , PyPDF2 и img2pdf. Для этого пишем в терминале:
После того, как библиотеки установились, нужно импортировать их в скрипт.
Так же, нам понадобиться os.path, для корректного указания путей для сохранения и для проверки на наличие файлов и каталогов.
Извлечение текста из документа
Создадим первую функцию, которая будет извлекать текст из PDF-документа, если он там, конечно же, есть. Назовем ее text_extract(path_doc). На вход она принимает путь к файлу PDF. Изначально нам нужно сделать проверки на существование файла и на то, является ли переданный файл PDF. Поэтому давайте сделаем два условия, в которых и будем делать это.
После того, как необходимые проверки сделаны, открываем документ, выводим на печать для пользователя количество страниц документа и получаем его название из переданного пути к файлу. Тут можно было бы получать название файла из метаданных документа. Но, не во всяком документе есть название, а потому добавиться лишняя проверка и обработки исключения отсутствия ключа. Потому, просто заберем название из пути.
Создадим список. В него мы будем добавлять полученный из документа текст постранично. Это нужно для того, чтобы не сохранять пустые документы. Дело в том, что не во всех PDF есть текстовый слой. К примеру, документ может быть собран из картинок. И, казалось бы, в этом случае считывать нечего и должно вызываться исключение. Но, этого не происходит, а сохраняется просто пустота. Потому, чтобы избежать лишнего создания пустых документов при отсутствии текстового слоя, будем делать проверку длины списка.
Затем в цикле перебираем документ постранично, загружаем текстовое содержимое страницы в переменную, после чего добавляем в список. После окончания цикла делаем проверку на длину списка. Если она больше нуля, создаем папку с именем документа для сохранения содержимого. В цикле пробегаемся по списку и сохраняем его содержимое в текстовый документ. После чего выводим сообщение об успешном сохранении и возвращаемся в функцию выбора действий. О ней немного позже.
Если же длина списка не больше нуля, выводим сообщение об отсутствии текстового слоя и также возвращаемся в функцию пользовательского выбора.
Извлечение картинок из PDF-документа
Следующая функция, которую мы создадим, это функция извлечения картинок из документа. Иногда это бывает очень полезно. Но, в некоторых документах присутствует некоторая каша из разнообразных заголовков, кусков больших картинок, а потому, при извлечении получается большая куча всякой всячины. Но, тем не менее, если в документе полноценные картинки, их извлечение может быть полезным. Создадим функцию image_extract(path_doc). На входе она также будет принимать путь к документу.
Первым делом проверим, есть ли документ, который передал пользователь и является ли документ pdf. Проверка аналогична той, что мы проводили в предыдущей функции. Изначально я хотел сделать одну функцию для проверки на наличие файла и его расширение, но после понял, что количество кода от этого не особо уменьшиться. Так как вместе с проверкой нужно было бы передавать пользовательский выбор, а, следовательно, после проверки на наличие файла, делать еще и проверку на выбор пользователя, чтобы запустить соответствующую функцию. Потому, от этой идеи я отказался.
Дальше открываем документ, получаем из него количество страниц и выводим пользователю для информации, а также забираем из пути к документу название файла.
На следующем этапе создаем папки, в которые будут сохраняться извлеченные картинки. И если папка с текстом этого же документа уже существует, создаем внутри нее только папку для картинок.
Устанавливаем счетчик. Он нужен будет для двух целей. Первая, это вывод информации о сохраненных картинках, то есть их количестве, для пользователя. А вторая, и основная, это сохранение файлов с картинками под разными номерами. Теперь создаем цикл с количеством страниц документа. Он будет нужен для того, чтобы открыть каждую страницу по отдельности. В нем запустим дополнительный цикл, в котором с помощью функции doc.get_page_images(i) будем получать все картинки с определенной страницы. Затем получаем номер ссылки на изображение и создаем пиксельную карту с помощью Pixmap. Затем данную пиксельную карту преобразовываем из CMYK в пиксельную карту RGB.
Увеличиваем счетчик на единицу и сохраняем изображение, о чем после сообщаем пользователю с помощью принта. Ну и после завершения всех циклов выводим принт об успешном сохранении всех изображений.
Разбивка PDF-файлов на страницы
Для разбивки документа на страницы создадим функцию pages_split(path_doc), которая на входе принимает путь к файлу. Делаем все те же проверки на наличие файла и его расширение:
Будем использовать библиотеку PyPDF2. Создаем объект PdfFileReader, в который считываем содержимое документа. Выводим на печать количество страниц для информации пользователю. Получаем имя документа из пути к нему.
Затем создаем необходимые папки для сохранения документа постранично.
В цикле пробегаемся по диапазону с количеством страниц документа. Создаем объект PdfFileWriter() для записи страницы. Открываем страницу и добавляем ее в объект для записи, после чего сохраняем страницу в файл и выводим сообщение об успешном сохранении и так до конца документа.
В завершении функции печатаем сообщение о завершении разделения документа и переходим в функцию пользовательского выбора.
Объединение отдельных страниц или PDF-документов в один
Следующая функция, это функция объединения документов. Назовем ее pages_merge(path_doc, name_file). На вход она получает путь к папке с документами, а также название объединенного документа.
Делаем проверки. Если в предыдущих функциях мы делали проверки на наличие файла и его расширение, то здесь мы делаем проверку уже на наличие папки. Если папка существует, делаем проверку на количество файлов в папке. Если она пустая или количество файлов в ней меньше или равно 1, то сообщаем об этом пользователю, так как, если файл один, следовательно, и объединять нечего, не говоря уже о полном отсутствии файлов. И выходим из функции в пользовательский выбор.
Затем создаем объект PdfFileMerger(). Устанавливаем счетчик, который в принципе не обязателен, но, чтобы пользователю не было скучно, будем выводить в терминал принты о каждом добавленном файле. Дальше пробегаемся по списку полученных файлов в цикле. Проверяем, является ли объект в списке файлом, если да, имеет ли он расширение pdf. И если да, то печатаем принт пользователю, считываем содержимое файла побайтово, после чего добавляем в объект PdfFileMerger. И увеличиваем счетчик на единицу.
После того, как все файлы из директории будут проверены и добавлены к объекту, сохраняем все полученное в один файл с ранее полученным от пользователя именем. Выводим принт пользователю, что объединение завершено и выходим в функцию пользовательского выбора.
Объединение изображений в PDF-документ
Следующая функция, это функция объединения изображений распространенных форматов (jpg, jpeg, gif, png, tiff). С ее помощью из изображений можно собрать документ буквально в несколько секунд. Создадим функцию merge_images(path_doc, title). На входе она принимает путь к папке с изображениями и название выходного документа pdf.
Затем проверяем, существует ли папка переданная пользователем. Так как изображений должно быть хотя бы одно, сканируем директорию на наличие в ней файлов. Проверяем, не является ли она пустой. Создаем счетчик, чтобы показать пользователю, что что-то обрабатывается, затем в цикле перебираем содержимое переданной пользователем директории, проверяем, является ли файл в списке файлом, затем проверяем его расширение, чтобы не добавлять в список документы word или excel. Ну и если все звезды сходятся в одном месте, добавляем изображение в список.
После того, как завершиться цикл, а значит, будут обработаны все файлы, проверяем, не является ли список файлов пустым. Так как в папке может не оказаться изображений, а следовательно в список ничего не добавиться и обрабатывать будет нечего. Затем, если список не нулевой, передаем его в функцию конвертации изображений в pdf, и сохраняем побайтово в файл с переданным пользователем именем. После завершения операции возвращаемся в функцию пользовательского выбора. Если же список является нулевым сообщаем, что в папке, которую он передал, нет изображений и выходим из функции в пользовательский выбор.
Здесь надо упомянуть то, что файлы должны называться правильно, если вы хотите объединить их в нужном порядке. То есть, желательно, пронумерованы. Нумерация должна начинаться не с 1, к примеру: image_1.png, а с 01, то есть: image_01.png. Иначе файл, который у вас должен быть в документе первым, будет идти где-то за 19-й страницей.
Функция пользовательского выбора
Ну и еще одна функция, это функция main(). В ней мы запрашиваем у пользователя нужное действие. Затем делаем проверку, какую цифру он ввел. И в соответствии с этим запускаем нужную функцию, передавая в нее путь к файлу, который тут же и запрашиваем у пользователя. Если же что-то пошло не так и пользователь ввел не то, что нужно, возвращаем его к самому началу.
На этом создание скрипта можно закончить. С помощью библиотек pymupdf и PyPDF2 работа с документами в pdf-формате становиться в принципе возможной. Конечно же, при извлечении текста из документов порою попадаются странные символы, но с этим в данном случае ничего особо не поделаешь. Так как с помощью данных библиотек только лишь извлекается текстовый слой, а не распознается полученный текст. Ну и, конечно же, структура полученного текстового документа оставляет желать лучшего. Конечно, она почти такая же, как и в оригинале, но за счет того, что в оригинале идет перенос на другую строку, предложения получаются несвязные. И если в тексте, который вы не будете дальше обрабатывать, а просто читать, это еще более-менее допустимо, то вот уже в, например, тексте на английском языке, если вы его будете переводить, строки придется объединять, так как иначе переводчик будет переводить построчно, а не по предложениям, из-за чего будет искажаться смысл.
В остальном же, библиотеки работают просто замечательно. И если под рукой нет какого-либо навороченного инструмента, то можно воспользоваться питоном и сделать то, что вам нужно с его помощью.
А на этом, пожалуй, все.
Спасибо за внимание. Надеюсь, что данная информация будет вам полезна
Кроме создания документов в формате PDF над ним можно проводить разнообразные операции. Давайте рассмотрим сегодня несколько из них, а именно: сохранение текста из документа в файл; сохранение изображений; разделение документа на страницы; сборка одного документа из нескольких.
Что потребуется?
Для выполнения нужных нам операций потребуется две библиотеки. На самом деле, можно было бы обойтись одной, но в данном случае каждая из библиотек делает какую-то операцию чуть лучше, чем ее аналог. Давайте установим библиотеки pymupdf , PyPDF2 и img2pdf. Для этого пишем в терминале:
pip install pymupdf PyPDF2 img2pdf
После того, как библиотеки установились, нужно импортировать их в скрипт.
Python:
import os.path
import fitz
import img2pdf
from PyPDF2 import PdfFileReader, PdfFileWriter, PdfFileMerger
Так же, нам понадобиться os.path, для корректного указания путей для сохранения и для проверки на наличие файлов и каталогов.
Извлечение текста из документа
Создадим первую функцию, которая будет извлекать текст из PDF-документа, если он там, конечно же, есть. Назовем ее text_extract(path_doc). На вход она принимает путь к файлу PDF. Изначально нам нужно сделать проверки на существование файла и на то, является ли переданный файл PDF. Поэтому давайте сделаем два условия, в которых и будем делать это.
Python:
if not os.path.isfile(path_doc):
print('[+] Файл не найден')
return
if not path_doc.endswith('.pdf'):
print('[+] Неверное расширение файла')
return
После того, как необходимые проверки сделаны, открываем документ, выводим на печать для пользователя количество страниц документа и получаем его название из переданного пути к файлу. Тут можно было бы получать название файла из метаданных документа. Но, не во всяком документе есть название, а потому добавиться лишняя проверка и обработки исключения отсутствия ключа. Потому, просто заберем название из пути.
Python:
doc = fitz.open(path_doc)
print(f'\n[+] Количество страниц документа: {doc.page_count}')
doc_name = os.path.split(path_doc)[-1].removesuffix('.pdf')
Создадим список. В него мы будем добавлять полученный из документа текст постранично. Это нужно для того, чтобы не сохранять пустые документы. Дело в том, что не во всех PDF есть текстовый слой. К примеру, документ может быть собран из картинок. И, казалось бы, в этом случае считывать нечего и должно вызываться исключение. Но, этого не происходит, а сохраняется просто пустота. Потому, чтобы избежать лишнего создания пустых документов при отсутствии текстового слоя, будем делать проверку длины списка.
Затем в цикле перебираем документ постранично, загружаем текстовое содержимое страницы в переменную, после чего добавляем в список. После окончания цикла делаем проверку на длину списка. Если она больше нуля, создаем папку с именем документа для сохранения содержимого. В цикле пробегаемся по списку и сохраняем его содержимое в текстовый документ. После чего выводим сообщение об успешном сохранении и возвращаемся в функцию выбора действий. О ней немного позже.
Python:
text_dict = []
for current_page in range(len(doc)):
print(f'\r[+] Считываю страницу: {current_page + 1}', end='')
if doc.load_page(current_page).get_text("text") != "":
text_dict.append(doc.load_page(current_page).get_text("text"))
if len(text_dict) > 0:
if not os.path.exists(os.path.join(os.getcwd(), doc_name)):
os.mkdir(os.path.join(os.getcwd(), doc_name))
for text in text_dict:
with open(os.path.join(os.getcwd(), doc_name, f"{doc_name}.txt"), 'a',
encoding='utf-8') as tex:
tex.write(f'{text}\n')
…
print(f'\n[+] Сохранение текста pdf-документа "{doc_name}" завершено.')
main()
return
Если же длина списка не больше нуля, выводим сообщение об отсутствии текстового слоя и также возвращаемся в функцию пользовательского выбора.
Python:
else:
print('\n[+] Документ не имеет текстового слоя')
main()
return
Python:
def text_extract(path_doc):
if not os.path.isfile(path_doc):
print('[+] Файл не найден')
return
if not path_doc.endswith('.pdf'):
print('[+] Неверное расширение файла')
return
doc = fitz.open(path_doc)
print(f'\n[+] Количество страниц документа: {doc.page_count}')
doc_name = os.path.split(path_doc)[-1].removesuffix('.pdf')
text_dict = []
for current_page in range(len(doc)):
print(f'\r[+] Считываю страницу: {current_page + 1}', end='')
if doc.load_page(current_page).get_text("text") != "":
text_dict.append(doc.load_page(current_page).get_text("text"))
if len(text_dict) > 0:
if not os.path.exists(os.path.join(os.getcwd(), doc_name)):
os.mkdir(os.path.join(os.getcwd(), doc_name))
for text in text_dict:
with open(os.path.join(os.getcwd(), doc_name, f"{doc_name}.txt"), 'a',
encoding='utf-8') as tex:
tex.write(f'{text}\n')
else:
print('\n[+] Документ не имеет текстового слоя')
main()
return
print(f'\n[+] Сохранение текста pdf-документа "{doc_name}" завершено.')
main()
return
Извлечение картинок из PDF-документа
Следующая функция, которую мы создадим, это функция извлечения картинок из документа. Иногда это бывает очень полезно. Но, в некоторых документах присутствует некоторая каша из разнообразных заголовков, кусков больших картинок, а потому, при извлечении получается большая куча всякой всячины. Но, тем не менее, если в документе полноценные картинки, их извлечение может быть полезным. Создадим функцию image_extract(path_doc). На входе она также будет принимать путь к документу.
Первым делом проверим, есть ли документ, который передал пользователь и является ли документ pdf. Проверка аналогична той, что мы проводили в предыдущей функции. Изначально я хотел сделать одну функцию для проверки на наличие файла и его расширение, но после понял, что количество кода от этого не особо уменьшиться. Так как вместе с проверкой нужно было бы передавать пользовательский выбор, а, следовательно, после проверки на наличие файла, делать еще и проверку на выбор пользователя, чтобы запустить соответствующую функцию. Потому, от этой идеи я отказался.
Python:
if not os.path.isfile(path_doc):
print('[+] Файл не найден')
main()
return
if not path_doc.endswith('.pdf'):
print('[+] Неверное расширение файла')
main()
return
Дальше открываем документ, получаем из него количество страниц и выводим пользователю для информации, а также забираем из пути к документу название файла.
Python:
doc = fitz.open(path_doc)
print(f'\n[+] Количество страниц документа: {doc.page_count}')
doc_name = os.path.split(path_doc)[-1].removesuffix('.pdf')
На следующем этапе создаем папки, в которые будут сохраняться извлеченные картинки. И если папка с текстом этого же документа уже существует, создаем внутри нее только папку для картинок.
Python:
if not os.path.exists(os.path.join(os.getcwd(), doc_name)):
os.mkdir(os.path.join(os.getcwd(), doc_name))
if not os.path.exists(os.path.join(os.getcwd(), doc_name, 'images')):
os.mkdir(os.path.join(os.getcwd(), doc_name, 'images'))
Устанавливаем счетчик. Он нужен будет для двух целей. Первая, это вывод информации о сохраненных картинках, то есть их количестве, для пользователя. А вторая, и основная, это сохранение файлов с картинками под разными номерами. Теперь создаем цикл с количеством страниц документа. Он будет нужен для того, чтобы открыть каждую страницу по отдельности. В нем запустим дополнительный цикл, в котором с помощью функции doc.get_page_images(i) будем получать все картинки с определенной страницы. Затем получаем номер ссылки на изображение и создаем пиксельную карту с помощью Pixmap. Затем данную пиксельную карту преобразовываем из CMYK в пиксельную карту RGB.
Python:
page_count = 0
for i in range(len(doc)):
for img in doc.get_page_images(i):
xref = img[0]
pix = fitz.Pixmap(doc, xref)
pix1 = fitz.Pixmap(fitz.csRGB, pix)
page_count += 1
pix1.save(os.path.join(os.getcwd(), doc_name, 'images', f'image_0{page_count}.png'))
print(f'\r[+] Изображение {page_count} сохранено', end='')
print(f'\n[+] Сохранение изображений pdf-документа "{doc_name}" завершено.')
main()
return
Увеличиваем счетчик на единицу и сохраняем изображение, о чем после сообщаем пользователю с помощью принта. Ну и после завершения всех циклов выводим принт об успешном сохранении всех изображений.
Python:
def image_extract(path_doc):
if not os.path.isfile(path_doc):
print('[+] Файл не найден')
main()
return
if not path_doc.endswith('.pdf'):
print('[+] Неверное расширение файла')
main()
return
doc = fitz.open(path_doc)
print(f'\n[+] Количество страниц документа: {doc.page_count}')
doc_name = os.path.split(path_doc)[-1].removesuffix('.pdf')
if not os.path.exists(os.path.join(os.getcwd(), doc_name)):
os.mkdir(os.path.join(os.getcwd(), doc_name))
if not os.path.exists(os.path.join(os.getcwd(), doc_name, 'images')):
os.mkdir(os.path.join(os.getcwd(), doc_name, 'images'))
page_count = 0
for i in range(len(doc)):
for img in doc.get_page_images(i):
xref = img[0]
pix = fitz.Pixmap(doc, xref)
pix1 = fitz.Pixmap(fitz.csRGB, pix)
page_count += 1
pix1.save(os.path.join(os.getcwd(), doc_name, 'images', f'image_0{page_count}.png'))
print(f'\r[+] Изображение {page_count} сохранено', end='')
print(f'\n[+] Сохранение изображений pdf-документа "{doc_name}" завершено.')
main()
return
Разбивка PDF-файлов на страницы
Для разбивки документа на страницы создадим функцию pages_split(path_doc), которая на входе принимает путь к файлу. Делаем все те же проверки на наличие файла и его расширение:
Python:
if not os.path.isfile(path_doc):
print('[+] Файл не найден')
main()
return
if not path_doc.endswith('.pdf'):
print('[+] Неверное расширение файла')
main()
return
Будем использовать библиотеку PyPDF2. Создаем объект PdfFileReader, в который считываем содержимое документа. Выводим на печать количество страниц для информации пользователю. Получаем имя документа из пути к нему.
Python:
pdf = PdfFileReader(path_doc)
print(f'\n[+] Количество страниц документа: {pdf.getNumPages()}\n')
doc_name = os.path.split(path_doc)[-1].removesuffix('.pdf')
Затем создаем необходимые папки для сохранения документа постранично.
Python:
if not os.path.exists(os.path.join(os.getcwd(), doc_name)):
os.mkdir(os.path.join(os.getcwd(), doc_name))
if not os.path.exists(os.path.join(os.getcwd(), doc_name, 'pages')):
os.mkdir(os.path.join(os.getcwd(), doc_name, 'pages'))
В цикле пробегаемся по диапазону с количеством страниц документа. Создаем объект PdfFileWriter() для записи страницы. Открываем страницу и добавляем ее в объект для записи, после чего сохраняем страницу в файл и выводим сообщение об успешном сохранении и так до конца документа.
Python:
for page in range(pdf.getNumPages()):
pdf_writer = PdfFileWriter()
current_page = pdf.getPage(page)
pdf_writer.addPage(current_page)
with open(os.path.join(os.getcwd(), doc_name, 'pages', f'{doc_name}_{page + 1}.pdf'), "wb") as out:
pdf_writer.write(out)
print(f'\r[+] Страница {page + 1} сохранена', end='')
В завершении функции печатаем сообщение о завершении разделения документа и переходим в функцию пользовательского выбора.
Python:
def pages_split(path_doc):
if not os.path.isfile(path_doc):
print('[+] Файл не найден')
main()
return
if not path_doc.endswith('.pdf'):
print('[+] Неверное расширение файла')
main()
return
pdf = PdfFileReader(path_doc)
print(f'\n[+] Количество страниц документа: {pdf.getNumPages()}\n')
doc_name = os.path.split(path_doc)[-1].removesuffix('.pdf')
if not os.path.exists(os.path.join(os.getcwd(), doc_name)):
os.mkdir(os.path.join(os.getcwd(), doc_name))
if not os.path.exists(os.path.join(os.getcwd(), doc_name, 'pages')):
os.mkdir(os.path.join(os.getcwd(), doc_name, 'pages'))
for page in range(pdf.getNumPages()):
pdf_writer = PdfFileWriter()
current_page = pdf.getPage(page)
pdf_writer.addPage(current_page)
with open(os.path.join(os.getcwd(), doc_name, 'pages', f'{doc_name}_{page + 1}.pdf'), "wb") as out:
pdf_writer.write(out)
print(f'\r[+] Страница {page + 1} сохранена', end='')
print(f'\n[+] Сохранение страниц pdf-документа "{doc_name}" завершено.')
main()
return
Объединение отдельных страниц или PDF-документов в один
Следующая функция, это функция объединения документов. Назовем ее pages_merge(path_doc, name_file). На вход она получает путь к папке с документами, а также название объединенного документа.
Делаем проверки. Если в предыдущих функциях мы делали проверки на наличие файла и его расширение, то здесь мы делаем проверку уже на наличие папки. Если папка существует, делаем проверку на количество файлов в папке. Если она пустая или количество файлов в ней меньше или равно 1, то сообщаем об этом пользователю, так как, если файл один, следовательно, и объединять нечего, не говоря уже о полном отсутствии файлов. И выходим из функции в пользовательский выбор.
Python:
if not os.path.isdir(path_doc):
print('[+] Папка не найдена')
main()
return
if len(os.listdir(path_doc)) <= 1:
print('[+] В папке нет файлов для объединения')
main()
return
Затем создаем объект PdfFileMerger(). Устанавливаем счетчик, который в принципе не обязателен, но, чтобы пользователю не было скучно, будем выводить в терминал принты о каждом добавленном файле. Дальше пробегаемся по списку полученных файлов в цикле. Проверяем, является ли объект в списке файлом, если да, имеет ли он расширение pdf. И если да, то печатаем принт пользователю, считываем содержимое файла побайтово, после чего добавляем в объект PdfFileMerger. И увеличиваем счетчик на единицу.
Python:
for file in os.listdir(path_doc):
if os.path.isfile(os.path.join(path_doc, file)):
if file.endswith('.pdf'):
print(f'\r[+] Объединяю файлы: {count} подождите...', end='')
with open(os.path.join(path_doc, file), 'rb') as pdf_file:
source_file = PdfFileReader(pdf_file)
merger.append(source_file)
count += 1
После того, как все файлы из директории будут проверены и добавлены к объекту, сохраняем все полученное в один файл с ранее полученным от пользователя именем. Выводим принт пользователю, что объединение завершено и выходим в функцию пользовательского выбора.
Python:
merger.write(f'{name_file}.pdf')
print(f'\n[+] Объединение страниц в pdf-документ "{name_file}.pdf" завершено.')
main()
return
Python:
def pages_merge(path_doc, name_file):
if not os.path.isdir(path_doc):
print('[+] Папка не найдена')
main()
return
if len(os.listdir(path_doc)) <= 1:
print('[+] В папке нет файлов для объединения')
main()
return
merger = PdfFileMerger()
count = 1
for file in os.listdir(path_doc):
if os.path.isfile(os.path.join(path_doc, file)):
if file.endswith('.pdf'):
print(f'\r[+] Объединяю файлы: {count} подождите...', end='')
with open(os.path.join(path_doc, file), 'rb') as pdf_file:
source_file = PdfFileReader(pdf_file)
merger.append(source_file)
count += 1
merger.write(f'{name_file}.pdf')
print(f'\n[+] Объединение страниц в pdf-документ "{name_file}.pdf" завершено.')
main()
return
Объединение изображений в PDF-документ
Следующая функция, это функция объединения изображений распространенных форматов (jpg, jpeg, gif, png, tiff). С ее помощью из изображений можно собрать документ буквально в несколько секунд. Создадим функцию merge_images(path_doc, title). На входе она принимает путь к папке с изображениями и название выходного документа pdf.
Затем проверяем, существует ли папка переданная пользователем. Так как изображений должно быть хотя бы одно, сканируем директорию на наличие в ней файлов. Проверяем, не является ли она пустой. Создаем счетчик, чтобы показать пользователю, что что-то обрабатывается, затем в цикле перебираем содержимое переданной пользователем директории, проверяем, является ли файл в списке файлом, затем проверяем его расширение, чтобы не добавлять в список документы word или excel. Ну и если все звезды сходятся в одном месте, добавляем изображение в список.
Python:
def merge_images(path_doc, title):
if not os.path.isdir(path_doc):
print('\n[+] Указанной папки не существует. Проверьте введенные данные')
main()
return
if len(os.listdir(path_doc)) > 0:
image_list = []
count = 1
for image in os.listdir(path_doc):
if os.path.isfile(os.path.join(path_doc, image)):
if image.split(".")[-1] in ['jpg', 'jpeg', 'bmp', 'png', 'tiff', 'gif']:
image_list.append(os.path.join(path_doc, image))
print(f'\r[+] Дабавлено {count}-е изображение', end='')
count += 1
if len(image_list) > 0:
print('\n[+] Обработка добавленных изображений...')
with open(f'{title}.pdf', 'wb') as file:
file.write(img2pdf.convert(image_list))
print(f'\n[+] Объединение изображений в pdf-документ "{title}.pdf" завершено.')
main()
return
else:
print('\n[+] В папке нет файлов изображений. Проверьте правильность пути к изображениям')
main()
return
После того, как завершиться цикл, а значит, будут обработаны все файлы, проверяем, не является ли список файлов пустым. Так как в папке может не оказаться изображений, а следовательно в список ничего не добавиться и обрабатывать будет нечего. Затем, если список не нулевой, передаем его в функцию конвертации изображений в pdf, и сохраняем побайтово в файл с переданным пользователем именем. После завершения операции возвращаемся в функцию пользовательского выбора. Если же список является нулевым сообщаем, что в папке, которую он передал, нет изображений и выходим из функции в пользовательский выбор.
Здесь надо упомянуть то, что файлы должны называться правильно, если вы хотите объединить их в нужном порядке. То есть, желательно, пронумерованы. Нумерация должна начинаться не с 1, к примеру: image_1.png, а с 01, то есть: image_01.png. Иначе файл, который у вас должен быть в документе первым, будет идти где-то за 19-й страницей.
Функция пользовательского выбора
Ну и еще одна функция, это функция main(). В ней мы запрашиваем у пользователя нужное действие. Затем делаем проверку, какую цифру он ввел. И в соответствии с этим запускаем нужную функцию, передавая в нее путь к файлу, который тут же и запрашиваем у пользователя. Если же что-то пошло не так и пользователь ввел не то, что нужно, возвращаем его к самому началу.
Python:
def main():
user_input = input('\n[+] Выберите действие:\n [1] Сохранить текст из PDF\n [2] Сохранить картинки из PDF\n'
' [3] Разделить документ PDF на страницы\n [4] Объединить PDF-документ из страниц\n '
'[5] Объединить картинки в PDF\n [6] Выход\n >>> ')
if user_input == "1":
text_extract(input('\n[+] Введите путь к файлу pdf >>> '))
elif user_input == "2":
image_extract(input('\n[+] Введите путь к файлу pdf >>> '))
elif user_input == "3":
pages_split(input('\n[+] Введите путь к файлу pdf >>> '))
elif user_input == "4":
pages_merge(input('\n[+] Введите путь к папке с файлами >>> '),
input('[+] Введите имя создаваемого pdf >>> '))
elif user_input == "5":
merge_images(input('\n[+] Введите путь к папке с картинками >>> '),
input('[+] Введите имя создаваемого pdf >>> '))
elif user_input == "6":
exit(0)
else:
print('\n[+] Неопознанный выбор. Попробуйте снова')
main()
return
На этом создание скрипта можно закончить. С помощью библиотек pymupdf и PyPDF2 работа с документами в pdf-формате становиться в принципе возможной. Конечно же, при извлечении текста из документов порою попадаются странные символы, но с этим в данном случае ничего особо не поделаешь. Так как с помощью данных библиотек только лишь извлекается текстовый слой, а не распознается полученный текст. Ну и, конечно же, структура полученного текстового документа оставляет желать лучшего. Конечно, она почти такая же, как и в оригинале, но за счет того, что в оригинале идет перенос на другую строку, предложения получаются несвязные. И если в тексте, который вы не будете дальше обрабатывать, а просто читать, это еще более-менее допустимо, то вот уже в, например, тексте на английском языке, если вы его будете переводить, строки придется объединять, так как иначе переводчик будет переводить построчно, а не по предложениям, из-за чего будет искажаться смысл.
В остальном же, библиотеки работают просто замечательно. И если под рукой нет какого-либо навороченного инструмента, то можно воспользоваться питоном и сделать то, что вам нужно с его помощью.
Python:
# pip install pymupdf
# pip install PyPDF2
# pip install img2pdf
import os.path
import fitz
import img2pdf
from PyPDF2 import PdfFileReader, PdfFileWriter, PdfFileMerger
# извлечение текста из PDF документа
def text_extract(path_doc):
if not os.path.isfile(path_doc):
print('[+] Файл не найден')
return
if not path_doc.endswith('.pdf'):
print('[+] Неверное расширение файла')
return
doc = fitz.open(path_doc)
print(f'\n[+] Количество страниц документа: {doc.page_count}')
doc_name = os.path.split(path_doc)[-1].removesuffix('.pdf')
text_dict = []
for current_page in range(len(doc)):
print(f'\r[+] Считываю страницу: {current_page + 1}', end='')
if doc.load_page(current_page).get_text("text") != "":
text_dict.append(doc.load_page(current_page).get_text("text"))
if len(text_dict) > 0:
if not os.path.exists(os.path.join(os.getcwd(), doc_name)):
os.mkdir(os.path.join(os.getcwd(), doc_name))
for text in text_dict:
with open(os.path.join(os.getcwd(), doc_name, f"{doc_name}.txt"), 'a',
encoding='utf-8') as tex:
tex.write(f'{text}\n')
else:
print('\n[+] Документ не имеет текстового слоя')
main()
return
print(f'\n[+] Сохранение текста pdf-документа "{doc_name}" завершено.')
main()
return
# извлечение изображений из PDF документа
def image_extract(path_doc):
if not os.path.isfile(path_doc):
print('[+] Файл не найден')
main()
return
if not path_doc.endswith('.pdf'):
print('[+] Неверное расширение файла')
main()
return
doc = fitz.open(path_doc)
print(f'\n[+] Количество страниц документа: {doc.page_count}')
doc_name = os.path.split(path_doc)[-1].removesuffix('.pdf')
if not os.path.exists(os.path.join(os.getcwd(), doc_name)):
os.mkdir(os.path.join(os.getcwd(), doc_name))
if not os.path.exists(os.path.join(os.getcwd(), doc_name, 'images')):
os.mkdir(os.path.join(os.getcwd(), doc_name, 'images'))
page_count = 0
for i in range(len(doc)):
for img in doc.get_page_images(i):
xref = img[0]
pix = fitz.Pixmap(doc, xref)
pix1 = fitz.Pixmap(fitz.csRGB, pix)
page_count += 1
pix1.save(os.path.join(os.getcwd(), doc_name, 'images', f'image_0{page_count}.png'))
print(f'\r[+] Изображение {page_count} сохранено', end='')
print(f'\n[+] Сохранение изображений pdf-документа "{doc_name}" завершено.')
main()
return
# разбивка PDF-файлов на страницы
def pages_split(path_doc):
if not os.path.isfile(path_doc):
print('[+] Файл не найден')
main()
return
if not path_doc.endswith('.pdf'):
print('[+] Неверное расширение файла')
main()
return
pdf = PdfFileReader(path_doc)
print(f'\n[+] Количество страниц документа: {pdf.getNumPages()}\n')
doc_name = os.path.split(path_doc)[-1].removesuffix('.pdf')
if not os.path.exists(os.path.join(os.getcwd(), doc_name)):
os.mkdir(os.path.join(os.getcwd(), doc_name))
if not os.path.exists(os.path.join(os.getcwd(), doc_name, 'pages')):
os.mkdir(os.path.join(os.getcwd(), doc_name, 'pages'))
for page in range(pdf.getNumPages()):
pdf_writer = PdfFileWriter()
current_page = pdf.getPage(page)
pdf_writer.addPage(current_page)
with open(os.path.join(os.getcwd(), doc_name, 'pages', f'{doc_name}_{page + 1}.pdf'), "wb") as out:
pdf_writer.write(out)
print(f'\r[+] Страница {page + 1} сохранена', end='')
print(f'\n[+] Сохранение страниц pdf-документа "{doc_name}" завершено.')
main()
return
# объединение документов PDF в один
def pages_merge(path_doc, name_file):
if not os.path.isdir(path_doc):
print('[+] Папка не найдена')
main()
return
if len(os.listdir(path_doc)) <= 1:
print('[+] В папке нет файлов для объединения')
main()
return
merger = PdfFileMerger()
count = 1
for file in os.listdir(path_doc):
if os.path.isfile(os.path.join(path_doc, file)):
if file.endswith('.pdf'):
print(f'\r[+] Объединяю файлы: {count} подождите...', end='')
with open(os.path.join(path_doc, file), 'rb') as pdf_file:
source_file = PdfFileReader(pdf_file)
merger.append(source_file)
count += 1
merger.write(f'{name_file}.pdf')
print(f'\n[+] Объединение страниц в pdf-документ "{name_file}.pdf" завершено.')
main()
return
# объединение картинок в pdf-документ
def merge_images(path_doc, title):
if not os.path.isdir(path_doc):
print('\n[+] Указанной папки не существует. Проверьте введенные данные')
main()
return
if len(os.listdir(path_doc)) > 0:
image_list = []
count = 1
for image in os.listdir(path_doc):
if os.path.isfile(os.path.join(path_doc, image)):
if image.split(".")[-1] in ['jpg', 'jpeg', 'bmp', 'png', 'tiff', 'gif']:
image_list.append(os.path.join(path_doc, image))
print(f'\r[+] Дабавлено {count}-е изображение', end='')
count += 1
if len(image_list) > 0:
print('\n[+] Обработка добавленных изображений...')
with open(f'{title}.pdf', 'wb') as file:
file.write(img2pdf.convert(image_list))
print(f'\n[+] Объединение изображений в pdf-документ "{title}.pdf" завершено.')
main()
return
else:
print('\n[+] В папке нет файлов изображений. Проверьте правильность пути к изображениям')
main()
return
# выбор пользователем нужных действий
def main():
user_input = input('\n[+] Выберите действие:\n [1] Сохранить текст из PDF\n [2] Сохранить картинки из PDF\n'
' [3] Разделить документ PDF на страницы\n [4] Объединить PDF-документ из страниц\n '
'[5] Объединить картинки в PDF\n [6] Выход\n >>> ')
if user_input == "1":
text_extract(input('\n[+] Введите путь к файлу pdf >>> '))
elif user_input == "2":
image_extract(input('\n[+] Введите путь к файлу pdf >>> '))
elif user_input == "3":
pages_split(input('\n[+] Введите путь к файлу pdf >>> '))
elif user_input == "4":
pages_merge(input('\n[+] Введите путь к папке с файлами >>> '),
input('[+] Введите имя создаваемого pdf >>> '))
elif user_input == "5":
merge_images(input('\n[+] Введите путь к папке с картинками >>> '),
input('[+] Введите имя создаваемого pdf >>> '))
elif user_input == "6":
exit(0)
else:
print('\n[+] Неопознанный выбор. Попробуйте снова')
main()
return
if __name__ == "__main__":
main()
А на этом, пожалуй, все.
Спасибо за внимание. Надеюсь, что данная информация будет вам полезна
Последнее редактирование: