Пишем конвертер word файлов в разные форматы - JPEG, HTML, PDF и прочие

Кирилл

Команда форума
Администратор
Ассоциация VN
Сообщения
14,069
Реакции
5,784
Всем привет.
Поступила вот такая интересная задача - "сделать программу что бы удобно было файлы конвертировать и точно без вирусов".
Это и есть основная программа.

Далее потребовалось, что бы все это запускалось по расписанию, само решало какие файлы надо конвертировать и - внимание - отправлять в нужные чаты в аське, ой, mail.agent.
Зачем? Да лучше не спрашивайте))

Это стало, собственно, второй задачей и второй программой. Для этой второй задачи я написал бота для mail.agent и он там уже должен успешно рулить процессом, в общем.
Болталку к нему не прикручивал - хотя теперь стало интересно и болталку сделать.
Вернемся к изначальной теме теперь.

Вот так конвертер по итогу у меня сейчас выглядит:

1583597291464.png


Неказисто, но практично)
Кому лень или не хочется читать о том, как это сделано - внизу темы исходники. Можно пользоваться сразу - кому надо, могут сразу упаковать в EXE, я это делаю вот этой штукой:
Python exe packer - программа для сборки скриптов Python в exe файлы 1.1.0

Итак, для работы программы нам потребуются следующие библиотеки:

  • tkinter, ttk
  • os
  • win32com
  • pdf2image
  • poppler

Что то идет из коробки, что то надо установить. Тем, кто пользуется Anaconda будет полегче.

Вот, собственно, и сам код:

Word format converter:
# -*- coding: utf-8 -*-
"""
word file converter
url: safezone.cc
@author: Кирилл
"""

from tkinter import ttk
from tkinter import filedialog as fd
import os
import win32com.client
from pdf2image import convert_from_path

'''
Для работы программы требуются библиотеки:
    tkinter, ttk
    os, win32com
    pdf2image, poppler
list format files:
https://docs.microsoft.com/ru-ru/office/vba/api/word.wdsaveformat

              {'wdFormatDOSText':4, #    Microsoft DOS text format.
               'wdFormatEncodedText':7, #    Encoded text format.
               'wdFormatHTML':8, #      Standard HTML format
               'wdFormatUnicodeText':7, #    Unicode text format
               'wdFormatPDF':17, #    PDF format
               }
'''

format_file = {'HTML': 8, 'PDF': 17, 'JPG': 'JPG', 'text_unicode': 7}


class FormConvert(ttk.Frame):

    def __init__(self, root):
        self.root = root
        self.root.geometry('600x500')
        self.default_path = 'Выберите каталог или файлы...'
        self.entry_text_input = ttk.tkinter.StringVar()
        self.entry_text_output = ttk.tkinter.StringVar()
        self.log_text = ttk.tkinter.StringVar()
        self.input_folder = ''

    def navigation(self):
        '''
            Метод для создания элементов управления на форме и
            обращения к ним
        '''

        self.lb = ttk.Label(root, text='Что будете конвертировать?')
        self.lb.place(x=10, y=25)

        ###################################################################

        # Радиокнопка выбора файл / папка с файлами
        self.combo_menu = ttk.Combobox(root, values=[
                                       u"Все файлы в каталоге",
                                       u"Один или несколько файлов"],
                                       height=3, width=30,
                                       state='readonly')

        self.combo_menu.set(u"Все файлы в каталоге")
        self.combo_menu.place(x=10, y=50)
        self.combo_menu.bind('<<ComboboxSelected>>', self.change_combo)

        ###################################################################

        # Текстовое поле с указанием пути к исходным файлам
        self.text_path_input = ttk.Entry(root,
                                         textvariable=self.entry_text_input,
                                         state='disabled',)
        self.text_path_input.place(x=10, y=80, width=300, height=25)
        # Пишем каталог по умолчанию в текстовое поле
        self.entry_text_input.set(self.default_path)

        # Кнопка выбора с указанием пути к исходным файлам
        self.bt_dialog_input = ttk.Button(text='Выбрать ...')
        self.bt_dialog_input.place(x=310, y=80, height=25,)
        self.bt_dialog_input.bind('<Button-1>', self.file_dialog_input)

        ###################################################################

        # Текстовое поле с указанием пути сохранения файла
        self.text_path_output = ttk.Entry(root,
                                          textvariable=self.entry_text_output,
                                          state='disabled',)
        self.text_path_output.place(x=10, y=110, width=300, height=25)
        # Пишем каталог по умолчанию в текстовое поле
        self.entry_text_output.set('Укажите, где сохранить файлы...')

        # Кнопка выбора каталога или файла
        self.bt_dialog_output = ttk.Button(text='Выбрать ...')
        self.bt_dialog_output.place(x=310, y=110, height=25,)
        self.bt_dialog_output.bind('<Button-1>', self.file_dialog_output)

        ###################################################################

        # Радиокнопка выбора типа файла на выходе
        self.combo_type = ttk.Combobox(root, values=[
                                       u"Конвертировать в JPG",
                                       u"Конвертировать в PDF",
                                       u"Конвертировать в HTML", ],
                                       height=3, width=30,
                                       state='readonly')

        self.combo_type.set(u"Конвертировать в JPG")
        self.combo_type.place(x=10, y=140)
        self.combo_type.bind('<<ComboboxSelected>>', self.change_combo)

        ###################################################################

        # Текстовое поле общего назначения
        self.text_log = ttk.tkinter.Text(root)
        self.text_log.place(x=10, y=180,
                            relwidth=0.95, relheight=0.7)

        ###################################################################

        # Кнопка выполнить
        self.bt_run_ = ttk.Button(text='Выполнить')
        self.bt_run_.place(x=505, y=140, height=40,)
        self.bt_run_.bind('<Button-1>', run_all)

    def file_dialog_input(self, event):
        # Кнопка "Выбрать..." - исходные файлы
        if self.combo_menu.get() == u"Все файлы в каталоге":
            self.input_folder = fd.askdirectory()
        else:
            self.input_folder = fd.askopenfilenames(defaultextension='.docx',
                                                    filetypes=[
                                                     ('Word files',
                                                      ('*.docx', '*.doc')), ],
                                                       multiple=True)
        self.entry_text_input.set(self.input_folder)

    def file_dialog_output(self, event):
        # Кнопка "Выбрать..." - папка для сохранения файлов
        self.output_folder = fd.askdirectory()
        self.entry_text_output.set('Save to: ' + self.output_folder)

    def change_combo(self, event):
        print(event.widget.get())
        return event.widget.get()

############################### END CLASS #####################################


def pdf_jpg(file):
    pages = convert_from_path(file, 500)
    for page in pages:
        try:
            page.save(file[:-8] + '.jpeg', 'JPEG')
        except:
            MyApp.text_log.insert('end', os.path.basename(file) + ' Отказ' + '\n')
    MyApp.text_log.insert('end', os.path.basename(file)[:-8] + '.jpeg' + ' -->  Успех' + '\n')
    os.remove(file)


def is_word(file):
    type_file = MyApp.combo_type.get().split()[-1]
    if file.lower().endswith('.docx'):
        return MyApp.output_folder + '/' + file[:-5] + '.' + type_file
    if file.lower().endswith('.doc'):
        return MyApp.output_folder + '/' + file[:-4] + '.' + type_file


def convert(subdir, file, save_file):
    '''
        Функция принимает в качестве параметров текущий каталог
        и имя исходного файла, путь и формат файла на выходе, а так же
        вычисляет соответствующий номер требуемого формата для
        конвертации файла.
    '''

    word = win32com.client.Dispatch('Word.Application')
    word.Visible = 0

    doc = word.Documents.Open(os.path.abspath(subdir + '//' + file))
    if format_file[MyApp.combo_type.get().split()[-1]] == 'JPG':
        doc.SaveAs(os.path.abspath(save_file[:-4]+'_tmp.pdf'), FileFormat=17)
        save_file = save_file[:-4]+'_tmp.pdf'
    else:
        try:
            doc.SaveAs(os.path.abspath(save_file), FileFormat=format_file[
                                    MyApp.combo_type.get().split()[-1]])
            MyApp.text_log.insert('end', file + ' --> ' + '\n --> ' + os.path.basename(save_file))
            MyApp.text_log.insert('end', ' : Успех' + '\n')
        except:
            MyApp.text_log.insert('end', os.path.basename(file) + 'Отказ' + '\n')

    doc.Close()
    word.Quit()
    if format_file[MyApp.combo_type.get().split()[-1]] == 'JPG':
        pdf_jpg(save_file)


def run_all(event):
    if not MyApp.text_path_input.get().endswith('...'):
        if not MyApp.text_path_output.get().endswith('...'):
            if MyApp.combo_menu.get() == 'Все файлы в каталоге':
                for subdir, dirs, files in os.walk(MyApp.text_path_input.get()):
                    for file in files:
                        if not file.startswith('~$'):
                            save_file = is_word(file)
                            if save_file:
                                convert(subdir, file, save_file)

            else:
                for file in MyApp.input_folder:
                    save_file = is_word(os.path.basename(file))
                    convert(os.path.dirname(file), os.path.basename(file),
                            save_file)


if __name__ == '__main__':
    root = ttk.tkinter.Tk()
    root.title('Word format converter')
    root.resizable(0, 0)
    MyApp = FormConvert(root)
    MyApp.navigation()
    root.mainloop()

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

Сам класс FormConvert выделен отдельным блоком и хорошо читается - но если у кого то возникнут вопросы, то без проблем отвечу на них.

В остальном же идея предельно проста: когда пользователь с помощью кнопок и комбобоксов выставил нужные параметры, то просто жмет кнопку "Выполнить".
Для бота я делал небольшие изменения - ему кнопка не нужна, так что он использует программу как импортируемый модуль.

Именно поэтому здесь так важно проверять, как именно запущена программа - за это отвечает код в строке:

if __name__ == '__main__':

Дословно - если программу запускает пользователь, то идет загрузка всего интерфейса и юзеру больше ни о чем думать не надо)

После нажатия заветной кнопки "Выполнить" мы стартуем функцию run_all, которая перебирает файлы по заданным параметрам и передает их в остальные функции для обработки.
Если выбран каталог - берутся все файлы с нужным расширением, если отдельные файлы - поочередно обрабатывается то, что выбрал пользователь.
Большинство преобразований можно было произвести с помощью win32com, но вот с картинками пришлось повозиться... и пилить отдельную функцию pdf_jpg.
Да, цифра в pages - это количество точек на выходе, так что вес файла можно отрегулировать под себя.
О результатах работы мы узнаем из информационного окна.

Это, собственно и все - если кому то еще, кроме меня это будет полезным, то я буду доволен))
 

Вложения

  • Word format converer.7z
    2.6 KB · Просмотры: 10
Последнее редактирование:
Назад
Сверху Снизу