George Sp
George Sp

Reputation: 572

python3 print landscape image/file with specified printer

I want to print a pdf file (or image) that I create on my script with a specified printer but the file is landscape orientation. I have tried the Tim Golden's python print but it is printed wrong and most of the image is not printed or I get an error message that the specified file is not found. This is the error: "pywintypes.error: (2, 'ShellExecute', 'The system cannot find the file specified.')" and the command is this: win32api.ShellExecute (0, "print", filename, '/d:"%s"' % printer_name, ".", 0). Of course filename and printer are strings and printer name is taken from win32print.EnumPrinters(2,None,1)

Here is my printing function:

def programA_printer():
    global name
    global printer_name
    global event2
    # time.sleep(3)
    i=0
    while True:
        if not event2.is_set():
            try:
                img = Image.open("Program_A_graph.png", 'r')
                if (time.time()-(os.path.getmtime("Program_A_graph.png")) < 1.75):
                    break
            except OSError as identifier:
                i = i+1
                print(identifier)
                time.sleep(1)
                if i>5:
                    print("Υπήρξε πρόβλημα, δεν εκτυπώνω και συνεχίζω στο επόμενο σετ!")
                    return


    serial_number_message = int(time.time())

    # img.show(title="Final Result")
    img.convert('RGB').save('./εκτυπώσεις/'+str(serial_number_message)+'.pdf', format="PDF", resolution=100.0)

#win32api.ShellExecute (0, "print", './εκτυπώσεις/'+str(serial_number_message)+'.pdf', '/d:"%s"' % printer_name, ".",0)
#win32api.ShellExecute (0, "print", './εκτυπώσεις/'+str(serial_number_message)+'.pdf', '/d:"%s"' % printer_name, "./εκτυπώσεις",0)
    HORZRES = 10
    VERTRES = 10

    PHYSICALWIDTH = 110
    PHYSICALHEIGHT = 111

    PHYSICALOFFSETX = 112
    PHYSICALOFFSETY = 113

    hDC = win32ui.CreateDC()
    hDC.CreatePrinterDC(printer_name)
    printable_area = hDC.GetDeviceCaps(HORZRES), hDC.GetDeviceCaps(VERTRES)
    printer_size = hDC.GetDeviceCaps(PHYSICALWIDTH), hDC.GetDeviceCaps(PHYSICALHEIGHT)
    printer_margins = hDC.GetDeviceCaps(PHYSICALOFFSETX), hDC.GetDeviceCaps(PHYSICALOFFSETY)

    bmp = img
    if bmp.size[0] > bmp.size[1]:
        bmp = bmp.rotate(90)

    ratios = [1.0 * printable_area[0] / bmp.size[0], 1.0 * printable_area[1] / bmp.size[1]]
    scale = min(ratios)

    hDC.StartDoc("Result")
    hDC.StartPage()

    dib = ImageWin.Dib(bmp)
    scaled_width, scaled_height = [int(scale * i) for i in bmp.size]
    x1 = int((printer_size[0] - scaled_width) / 2)
    y1 = int((printer_size[1] - scaled_height) / 2)
    x2 = x1 + scaled_width
    y2 = y1 + scaled_height
    dib.draw(hDC.GetHandleOutput(), (x1, y1, x2, y2))

    hDC.EndPage()
    hDC.EndDoc()
    hDC.DeleteDC()

I don't know what else to try. Is there a way to achieve this?

Upvotes: 4

Views: 3698

Answers (2)

Iorek
Iorek

Reputation: 581

@Barmak Shemirani above deserves full credit for most of the code below. I simply added a section which allows you to choose the page size and/or force the orientation. You can also use the devmode in the win32con easily now to push the image around as needed.

def print_test(filename, page_size="11x17", page_orientation="landscape", printer_name = win32print.GetDefaultPrinter()):

    try:
        img = Image.open(filename, 'r')
    except:
        print("error")
        return

    # open the printer.
    hprinter = win32print.OpenPrinter(printer_name)

    # retrieve default settings.  this code does not work on
    devmode = win32print.GetPrinter(hprinter, 2)["pDevMode"]

    # change paper size and orientation
    # all numbers can be found here -
    # https://github.com/SublimeText/Pywin32/blob/master/lib/x64/win32/lib/win32con.py
    if page_size == "letter":
        devmode.PaperSize = 1 # 11x17 = 17, letter = 1
    elif page_size == "11x17":
        devmode.PaperSize = 17 # 11x17 = 17, letter = 1

    # 1 = portrait, 2 = landscape
    if page_orientation == "portait":
        devmode.Orientation = 2
    elif page_orientation == "landscape":
        devmode.Orientation = 2

    # create dc using new settings
    # first get the integer hDC value - note that we need the name
    hdc = win32gui.CreateDC("WINSPOOL", printer_name, devmode)
    # next create a PyCDC from the hDC.
    hdc = win32ui.CreateDCFromHandle(hdc)


    horzres = hdc.GetDeviceCaps(win32con.HORZRES)
    vertres = hdc.GetDeviceCaps(win32con.VERTRES)

    landscape = horzres > vertres

    if landscape:
        if img.size[1] > img.size[0]:
            print('Landscape mode, tall image, rotate bitmap.')
            img = img.rotate(90, expand=True)
    else:
        if img.size[1] < img.size[0]:
            print('Portrait mode, wide image, rotate bitmap.')
            img = img.rotate(90, expand=True)

    img_width = img.size[0]
    img_height = img.size[1]

    if landscape:
        #we want image width to match page width
        ratio = vertres / horzres
        max_width = img_width
        max_height = (int)(img_width * ratio)
    else:
        #we want image height to match page height
        ratio = horzres / vertres
        max_height = img_height
        max_width = (int)(max_height * ratio)

    #map image size to page size
    hdc.SetMapMode(win32con.MM_ISOTROPIC)
    hdc.SetViewportExt((horzres, vertres));
    hdc.SetWindowExt((max_width, max_height))

    #offset image so it is centered horizontally
    offset_x = (int)((max_width - img_width)/2)
    offset_y = (int)((max_height - img_height)/2)
    hdc.SetWindowOrg((-offset_x, -offset_y)) 

    hdc.StartDoc('Result')
    hdc.StartPage()

    dib = ImageWin.Dib(img)
    dib.draw(hdc.GetHandleOutput(), (0, 0, img_width, img_height))

    hdc.EndPage()
    hdc.EndDoc()
    hdc.DeleteDC()

Upvotes: 0

Barmak Shemirani
Barmak Shemirani

Reputation: 31599

bmp = bmp.rotate(90)

This will crop the image. Use img.rotate(90, expand=True) to flip the image properly.

You can use SetViewportExt/SetWindowExt instead of manually calculating the bitmap ratio to printer resolution. You also need to account for printer's margin. See example below.

The system error for file not found error is separate. Use the debugger to find where it occurs.

import win32ui, win32con
from PIL import Image, ImageWin

def print_test(printer_name):

    try:
        filename = "Program_A_graph.png"
        img = Image.open(filename, 'r')
    except:
        print("error")
        return

    hdc = win32ui.CreateDC()
    hdc.CreatePrinterDC(printer_name)

    horzres = hdc.GetDeviceCaps(win32con.HORZRES)
    vertres = hdc.GetDeviceCaps(win32con.VERTRES)

    landscape = horzres > vertres

    if landscape:
        if img.size[1] > img.size[0]:
            print('Landscape mode, tall image, rotate bitmap.')
            img = img.rotate(90, expand=True)
    else:
        if img.size[1] < img.size[0]:
            print('Portrait mode, wide image, rotate bitmap.')
            img = img.rotate(90, expand=True)

    img_width = img.size[0]
    img_height = img.size[1]

    if landscape:
        #we want image width to match page width
        ratio = vertres / horzres
        max_width = img_width
        max_height = (int)(img_width * ratio)
    else:
        #we want image height to match page height
        ratio = horzres / vertres
        max_height = img_height
        max_width = (int)(max_height * ratio)

    #map image size to page size
    hdc.SetMapMode(win32con.MM_ISOTROPIC)
    hdc.SetViewportExt((horzres, vertres));
    hdc.SetWindowExt((max_width, max_height))

    #offset image so it is centered horizontally
    offset_x = (int)((max_width - img_width)/2)
    offset_y = (int)((max_height - img_height)/2)
    hdc.SetWindowOrg((-offset_x, -offset_y)) 

    hdc.StartDoc('Result')
    hdc.StartPage()

    dib = ImageWin.Dib(img)
    dib.draw(hdc.GetHandleOutput(), (0, 0, img_width, img_height))

    hdc.EndPage()
    hdc.EndDoc()
    hdc.DeleteDC()

    print( 'Debug info:' )
    print( 'Landscape: %d' % landscape )
    print( 'horzres: %d' % horzres )
    print( 'vertres: %d' % vertres )

    print( 'img_width: %d' % img_width )
    print( 'img_height: %d' % img_height )

    print( 'max_width: %d' % max_width )
    print( 'max_height: %d' % max_height )

    print( 'offset_x: %d' % offset_x )
    print( 'offset_y: %d' % offset_y )

Upvotes: 5

Related Questions