Python печать файла на принтере

Автоматизируем печать документов с помощью Python

Каждый день, я готовлю однообразные документы, в которых нужно печатать страницы — одинаково (однообразно):
1 (ую) и 2 (ую) страницы, двойной печатью по длинному краю;
3 (ью) и 4 (ую) по короткому краю (эти листы горизонтальные);
5 (ую) страницу отдельно (только 1 лист).

Каждый день, из раза в раз, нужно было настраивать диапазон для печатати. И в один момент (спустя 3 дня) мне это надоело и я решил написать программу, с помощью которой можно будет распечатать этот документ — одним нажатием мыши.

Спойлер — мне удалось. Но пришлось поискать информацию, а информации на русском не очень много, поэтому искал преимущественно в английских источниках.

Задача

В начале у меня были Word документы и их нужно распечатать максимально экономно и этично, чтобы не просто печатать весь документ в разнобой, а используя свои подходы на печать.

Решение

Из Word в PDF

Для начала преобразуем Word документы в отдельные PDF файлы, для этого устанавливаем следящую библиотеку:

И пишем код для конвертации. Для этого собираем все «.docx» файлы в папке, преобразуем их в PDF и нам требуется условие на проверку файла.

Если «.docx» есть в названии файла, то конвертируем, иначе пропускаем файлы. Нужно для того, чтобы сами pdf файлы не конвертировались в pdf (вылезет ошибка).

from docx2pdf import convert import os def word_convert(arr): out = [] for file in arr: if '.docx' in file: out.append(file) return out indir = '\\input' #ваша директория, где находятся .docx файлы all_files = next(os.walk(indir))[2] # все файлы в вашей директории в массиве input_files = word_convert(all_files) # входные .dock файлы #проходимся циклом по директории и конвертируем for file in input_files: path_file = indir + file # путь до файла convert(path_file)

Систематизация страниц

Далее устанавливаем библиотеку для редактирования страниц в pdf:

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

def del_pages(pdf, arr, num_pages=16): max = arr[1] min = arr[0] - 1 del pdf.pages[max:num_pages] del pdf.pages[0:min] return pdf # -1 для того, чтобы вы писали с 1 страницы, а не с 0. #В начале это не мешает, но если страниц 20, то в конце можно запутаться. 

Сохранение новых файлов

# Теперь пишем функцию для чтения и систематизации: outdir = '\\output' def doc_crushing(doc_path, arr, outpdf): with pikepdf.open(doc_path) as pdf: # Смотрим сколько страниц в pdf num_pages = len(pdf.pages) # Удаляем страницы из pdf del_pages(pdf, arr, num_pages) pdf.save(outdir + outpdf) #Пример doc = 'Договор.pdf' # Название вашего файла (тут для примера без цикла) doc_path = indir + doc # Путь до файла #doc_crushing(входной файл, масив с нужными срезом страниц, новое название) doc_crushing(doc_path, [1,2], 'Титульный лист.pdf') doc_crushing(doc_path, [3,4], 'Экзаменационный лист с фото.pdf') doc_crushing(doc_path, [5,6], 'Заявление на поступление.pdf') 

Т.е из документа «doc_path», мы оставляем промежуток страниц с 1 по 2 и сохраняем с именем ‘Титульный лист.pdf’ и так для каждого файла

Читайте также:  Java lib for excel

Печать документов

Для работы с принтерами установим следующую библиотеку

Далее пишем функцию для печати т.к мы ее будем вызывать для каждого нужного файла

import win32print import win32api #print_pdf (входной pdf, режим печати, какой принтер) #режим печати: 1 - односторонняя, 2 двойная по длинному краю, 3 - по короткому def print_pdf(input_pdf, mode=2, printer=1): # тут мои принтеры, для своихузнаем имя дефолтного принтера через метод win32print.GetDefaultPrinter() if int(printer) == 2: name = "\\\\buh\\BUH DCP-L5500DN series (копия 1)" #win32print.GetDefaultPrinter() elif int(printer) == 1: name = "Brother DCP-L2540DN series Printer" #win32print.GetDefaultPrinter() try: # Устанавливаем дефолтный принтер win32print.SetDefaultPrinterW(name) win32print.SetDefaultPrinter(name) finally: # Если не получилось или получилось -> устанавливаем этот принтер стандартом name = win32print.GetDefaultPrinter() # оставляем без изменений ## тут нужные права на использование принтеров printdefaults = ## начинаем работу с принтером ("открываем" его) handle = win32print.OpenPrinter(name, printdefaults) ## Если изменить level на другое число, то не сработает level = 2 ## Получаем значения принтера attributes = win32print.GetPrinter(handle, level) ## Настройка двухсторонней печати attributes['pDevMode'].Duplex = mode #flip over 3 - это короткий 2 - это длинный край ## Передаем нужные значения в принтер win32print.SetPrinter(handle, level, attributes, 0) win32print.GetPrinter(handle, level)['pDevMode'].Duplex ## Предупреждаем принтер о старте печати win32print.StartDocPrinter(handle, 1, [input_pdf, None, "raw"]) ## 2 в начале для открытия pdf и его сворачивания, для открытия без сворачивания поменяйте на 1 win32api.ShellExecute(2,'print', input_pdf,'.','/manualstoprint',0) ## "Закрываем" принтер win32print.ClosePrinter(handle) ## Меняем стандартный принтер на часто используемый win32print.SetDefaultPrinterW("Brother DCP-L2540DN series Printer") win32print.SetDefaultPrinter("Brother DCP-L2540DN series Printer") # Пример inputs_print = next(os.walk(outdir))[2] # Берем все файлы в папке outdir # Печатаем документы for input_print in inputs_print: # Путь до файла, который нужно расспечатать path_print = outdir + input_print # Если в названии файла есть 'Экзаменационный', то печатаем по короткому краю if 'Экзаменационный' in input_print: print_pdf(path_print, 3, num) else: print_pdf(path_print, 2, num)
from docx2pdf import convert import win32print import win32api import pikepdf import shutil import time import sys import os #Мой код запускается сразу с определными значениями if sys.argv[7]: lastname = sys.argv[1] name = sys.argv[2] fname = sys.argv[3] name1 = sys.argv[4] today = sys.argv[5] num = sys.argv[6] level_stydy = sys.argv[7] else: lastname = sys.argv[1] name = sys.argv[2] fname = sys.argv[3] today = sys.argv[4] num = sys.argv[5] level_stydy = sys.argv[6] #Если значения передавались, то изменяем директорию if lastname: dir = os.path.abspath(os.curdir) + "\\py\\" + f"  " else: dir = os.path.abspath(os.curdir) indir = dir + "\\input\\" outdir = dir + "\\output\\" #Получаем все файлы в дикетории -> выдаем docx файлы def tackword(arr): out = [] for file in arr: if '.docx' in file: out.append(file) return out #Функция для копирования сгенерированых файлов в мою директорию def copy_in_input(indir, name1): dir = os.path.abspath(os.curdir) + '\\iles\\' input_files = tackword(next(os.walk('.\\files'))[2]) for file in input_files: if name1 in file and today in file: src = dir + file ind = indir + file shutil.copyfile(src, ind) def del_pages(pdf, arr, num_pages=16): max = arr[1] min = arr[0] - 1 del pdf.pages[max:num_pages] del pdf.pages[0:min] return pdf def doc_crushing(doc_path, arr, outpdf): with pikepdf.open(doc_path) as pdf: num_pages = len(pdf.pages) #Удаляем страницы из pdf del_pages(pdf, arr, num_pages) pdf.save(outdir+outpdf) def print_pdf(input_pdf, mode=2, printer=1): if int(printer) == 2: name = "\\buh\BUH DCP-L5500DN series (копия 1)" #win32print.GetDefaultPrinter() elif int(printer) == 1: name = "Brother DCP-L2540DN series Printer" #win32print.GetDefaultPrinter() win32print.SetDefaultPrinterW(name) win32print.SetDefaultPrinter(name) name = win32print.GetDefaultPrinter() print(name) print("<bk>") drivers = win32print.EnumPrinterDrivers(None, None, 2) for drive in drivers: print(drive['Name']) printdefaults = handle = win32print.OpenPrinter(name, printdefaults) level = 2 attributes = win32print.GetPrinter(handle, level) attributes['pDevMode'].Duplex = mode #flip over 3 - это короткий 2 - это длинный край win32print.SetPrinter(handle, level, attributes, 0) win32print.GetPrinter(handle, level)['pDevMode'].Duplex r = win32print.StartDocPrinter(handle, 1, [input_pdf, None, "raw"]) print(r) win32api.ShellExecute(2,'print', input_pdf,'.','/manualstoprint',0) # win32print.ClosePrinter(handle) # win32print.SetDefaultPrinterW("Brother DCP-L2540DN series Printer") # win32print.SetDefaultPrinter("Brother DCP-L2540DN series Printer") def main(): # проверяем есть ли директория, если нету, то делаем if not os.path.exists(dir): os.mkdir(dir) os.mkdir(dir + '\\input') os.mkdir(dir + '\\output') # копируем из файлы имеющие имя в файле copy_in_input(indir, name1) # берем docx скопированные файлы input_files = tackword(next(os.walk(indir))[2]) for file in input_files: convert(indir+file) input_pds = next(os.walk(indir))[2] doc = '' rec = '' outs = [] # Если я хочу изменять pdf файл, то записываю его в переменную for file in input_pds: if '.pdf' in file: if 'Договор' in file: doc = file elif 'Реквизиты' in file: rec = file else: outs.append(file) # Открываем договор pdf, если он есть if doc != '': doc_path = indir + doc # 1 - БАК; 2 - МАГ; 3 - СПО if level_stydy == 1: doc_crushing(doc_path, [1,2], 'Титульный лист.pdf') doc_crushing(doc_path, [3,4], 'Экзаменационный лист с фото.pdf') doc_crushing(doc_path, [5,6], 'Заявление на поступление.pdf') doc_crushing(doc_path, [7,7], 'Согласие на зачисление.pdf') doc_crushing(doc_path, [8,9], 'Согласие на обработку персональных данных.pdf') doc_crushing(doc_path, [10,16], 'Договор.pdf') elif level_stydy == 2: doc_crushing(doc_path, [1,2], 'Титульный лист.pdf') doc_crushing(doc_path, [3,4], 'Экзаменационный лист с фото.pdf') doc_crushing(doc_path, [5,6], 'Заявление на поступление.pdf') doc_crushing(doc_path, [7,7], 'Согласие на зачисление.pdf') doc_crushing(doc_path, [8,9], 'Согласие на обработку персональных данных.pdf') doc_crushing(doc_path, [10,14], 'Договор.pdf') elif level_stydy == 3: doc_crushing(doc_path, [1,2], 'Титульный лист.pdf') doc_crushing(doc_path, [3,4], 'Заявление на поступление.pdf') doc_crushing(doc_path, [5,5], 'Согласие на зачисление.pdf') doc_crushing(doc_path, [6,7], 'Согласие на обработку персональных данных.pdf') doc_crushing(doc_path, [8,12], 'Договор.pdf') if rec != '': rec_path = indir + rec with pikepdf.open(rec_path) as pdf: num_pages = len(pdf.pages) if num_pages != 1: del_pages(pdf, [1,1], num_pages) pdf.save(outdir+'Реквизиты на оплату.pdf') if outs: for out in outs: with pikepdf.open(indir+out) as pdf: pdf.save(outdir+out) files_to_print = [] inputs_print = next(os.walk(outdir))[2] #Печатаем документы for input_print in inputs_print: path_print = outdir + input_print if 'Экзаменационный' in input_print: print_pdf(path_print, 3, num) else: print_pdf(path_print, 2, num) # #Удаляем файлы # time.sleep(100) # for file in os.scandir(indir): # if file.name.endswith(".pdf") or file.name.endswith(".docx"): # os.unlink(file.path) # for file in os.scandir(outdir): # if file.name.endswith(".pdf") or file.name.endswith(".docx"): # os.unlink(file.path) if name == 'main': main()

Вывод

Сразу про минусы:
принтеры не всегда меняются (т.е печатает, только, 1 принтер), разбираюсь почему.

Читайте также:  What is super method in python

Плюсы т.к мне не нужно постоянно вбивать диапазон страниц, освобождается куча времени, на саморазвитие)

Прощу, раскритиковать мой говнокод и поделится своим мнением о автоматизации печати (как вы это делали) или была ли моя статья полезно для вас

Это моя первая статья, поэтому давайте пожестче

P.S Документы генерирую на php форме с php сервера, на котором запускаю Python скрипт, с помощью php формы

Источник

Оцените статью