Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python print pdf file with win32print

I'm trying to print a pdf file from Python with module win32print but the only way I can print success is a text.

hPrinter = win32print.OpenPrinter("\\\\Server\Printer")
filename = "test.pdf"
try:
    hJob = win32print.StartDocPrinter(hPrinter, 1, ('PrintJobName', None, 'RAW'))
    try:
        win32api.ShellExecute(0, "print", filename, None, ".", 0)
        win32print.StartPagePrinter(hPrinter)
        win32print.WritePrinter(hPrinter, "test")  # Instead of raw text is there a way to print PDF File ?
        win32print.EndPagePrinter(hPrinter)
    finally:
        win32print.EndDocPrinter(hPrinter)
finally:
    win32print.ClosePrinter(hPrinter)

So instead of printing a text I need to print the "test.pdf" file.

I also tried with win32api.ShellExecute(0, "print", filename, None, ".", 0) but it's not working, after some test like (getprinter, getdefault, setprinter, setdefaultprinter) it seems not to be attaching the printer. So at this way I can't get working.

This is the code I used !

win32print.SetDefaultPrinter(hPrinter)
win32api.ShellExecute(0, "print", filename, None,  ".",  0)
like image 808
ndAR Avatar asked Aug 31 '16 12:08

ndAR


3 Answers

I'd like a solution which is able to print about 100 pdf files daily every about 200 pages long. Files are stored in certain directory.

import win32api
import win32print
from glob import glob

# A List containing the system printers
all_printers = [printer[2] for printer in win32print.EnumPrinters(2)]
# Ask the user to select a printer
printer_num = int(input("Choose a printer:\n"+"\n".join([f"{n} {p}" for n, p in enumerate(all_printers)])+"\n"))
# set the default printer
win32print.SetDefaultPrinter(all_printers[printer_num])
pdf_dir = "D:/path/to/pdf_dir/**/*"
for f in glob(pdf_dir, recursive=True):
    win32api.ShellExecute(0, "print", f, None,  ".",  0)

input("press any key to exit")

Notes:

  1. You can change the printer attributes, such as color/black, scaling, quality, etc, by using the DevMode object (example on the GUI code).
  2. Provide the absolute paths of the filenames
  3. Give enough time to process the job before exiting the script.

While not direct answer to the question, I couldn't resist the opportunity to make a GUI using tkinter. It can be easily changed to select a dir - and all the files inside - instead of a single file.

import win32api
import win32print
import traceback

from tkinter.filedialog import askopenfilename
from tkinter import *
from tkinter import font # * doesn't import font or messagebox
from tkinter import messagebox

root = Tk()
root.title("Python Printer")
root.geometry("410x310")
root.resizable(False, False)
root.tk.call('encoding', 'system', 'utf-8')

def font_size(fs):
    return font.Font(family='Helvetica', size=fs, weight='bold')

# Add a grid
mainframe = Frame(root)
#mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )
mainframe.grid(column=0,row=0, sticky=(N) )
mainframe.columnconfigure(0, weight = 1)
mainframe.rowconfigure(0, weight = 1)
mainframe.pack(pady = 10, padx = 0)

# Create a _printer variable
_printer = StringVar(root)
# Create a _color variable
_color = StringVar(root)
_filename = ""

# on change dropdown value
def sel_printer(*args):
    print( _printer.get() )
# link function to change dropdown
_printer.trace('w', sel_printer)

def sel_color(*args):
    print( _color.get() )
# link function to change dropdown
_color.trace('w', sel_color)

def UploadAction(event=None):
    global _filename
    _filename = filedialog.askopenfilename()
    #print('Selected:', _filename)

def PrintAction(event=None):

    PRINTER_DEFAULTS = {"DesiredAccess":win32print.PRINTER_ALL_ACCESS} 
    pHandle = win32print.OpenPrinter(_printer.get(), PRINTER_DEFAULTS)
    properties = win32print.GetPrinter(pHandle, 2)
    properties['pDevMode'].Color = 1 if str(_color.get()) == "Color" else 2
    properties['pDevMode'].Copies = 1
    win32print.SetPrinter(pHandle, 2, properties, 0)

    if not _filename:
        messagebox.showerror("Error", "No File Selected")
        return
    elif not _printer.get():
        messagebox.showerror("Error", "No Printer Selected")
        return

    try:
        #win32print.SetDefaultPrinter(_printer.get())
        win32api.ShellExecute(0, "print", _filename, None,  ".",  0)
        win32print.ClosePrinter(pHandle)
    except:
        pass
        messagebox.showerror("Error", "There was an error printing the file :(")

choices = [printer[2] for printer in win32print.EnumPrinters(2)]
_printer.set(win32print.GetDefaultPrinter()) # set the default option

popupMenu = OptionMenu(mainframe, _printer, *choices)
popupMenu['font'] = font_size(12)
Label(mainframe, text="SELECT PRINTER").grid(row = 1, column = 1)
popupMenu.grid(row = 2, column =1)

# Dictionary with options
choices = ["COLOR", "MONOCHROME"]
_color.set("COLOR") # set the default option

popupMenu2 = OptionMenu(mainframe, _color, *choices)
popupMenu2['font'] = font_size(12)
Label(mainframe, text="COLOR MODE").grid(row = 3, column = 1)
popupMenu2.grid(row = 4, column =1)

Label(mainframe, text="SELECT FILE").grid(row = 5, column = 1)
button = Button(mainframe, text=u"\uD83D\uDCC1" ' BROWSE', command=UploadAction)
button['font'] = font_size(12)
button.grid(row = 6, column =1)


_copies = IntVar()
_copies.set(1)

def copies_increase(event=None):
    _copies.set(_copies.get() + 1)

def copies_decrease(event=None):
    _copies.set(_copies.get() - 1)
    if _copies.get() < 1 :
        _copies.set(1)

Label(mainframe, textvariable=_copies).grid(columnspan=2)
button_frame = Frame(mainframe)
button_frame.grid(columnspan=2)


dec_button = Button(button_frame, text=u"\u2212", command=copies_decrease, fg="dark green", bg = "white", height=1, width=3 )
dec_button['font'] = font_size(10)

inc_button = Button(button_frame, text=u"\uFF0B", command=copies_increase, fg="dark green", bg = "white", height=1, width=3 )
inc_button['font'] = font_size(10)

button_frame.columnconfigure(0, weight=1)
button_frame.columnconfigure(1, weight=1)

dec_button.grid(row=0, column=0, sticky=W+E)
inc_button.grid(row=0, column=1, sticky=W+E)

Label(mainframe).grid(row = 10, column = 1)
p_button = Button(mainframe, text=u'\uD83D\uDDB6' + " PRINT", command=PrintAction, fg="dark green", bg = "white")
p_button['font'] = font_size(18)
p_button.grid(row = 11, column =1)

root.mainloop()

GIST

enter image description here enter image description here


Sources:

  1. Python printing a pdf file on my Brother Laser printer (Duplex print on/off)
  2. Python print pdf file with win32print
  3. http://docs.activestate.com/activepython/3.1/pywin32/PyDEVMODE.html
  4. https://www.thecodingforums.com/threads/printing-using-python.502484/
  5. https://stackoverflow.com/a/37586129/797495
  6. https://stackoverflow.com/a/56638762/797495
  7. https://t.codebug.vip/questions-712908.htm
  8. How do I create a button in Python Tkinter to increase integer variable by 1 and display that variable?
  9. https://gregcaporale.wordpress.com/2012/01/18/powershell-to-print-files-automatically/ (this may also be a solution)

PS: I may not win the bounty, but I certainly enjoyed making the GUI. tkinter layout was the hardest part!

like image 110
Pedro Lobito Avatar answered Oct 20 '22 17:10

Pedro Lobito


This is the code I have used and it works correctly.

name = win32print.GetDefaultPrinter() # verify that it matches with the name of your printer
printdefaults = {"DesiredAccess": win32print.PRINTER_ALL_ACCESS} # Doesn't work with PRINTER_ACCESS_USE
handle = win32print.OpenPrinter(name, printdefaults)
level = 2
attributes = win32print.GetPrinter(handle, level)
#attributes['pDevMode'].Duplex = 1  #no flip
#attributes['pDevMode'].Duplex = 2  #flip up
attributes['pDevMode'].Duplex = 3   #flip over
win32print.SetPrinter(handle, level, attributes, 0)
win32print.GetPrinter(handle, level)['pDevMode'].Duplex
win32api.ShellExecute(0,'print','manual1.pdf','.','/manualstoprint',0)
like image 27
David González Blazman Avatar answered Oct 20 '22 16:10

David González Blazman


You can try

win32print.SetDefaultPrinter("\\\\Server\Printer")

This method accepts a String, not the printer object you tried to pass it.

like image 1
WJVDP Avatar answered Oct 20 '22 16:10

WJVDP