win32ui insert an image to CreateDC()

1.1k Views Asked by At

I'm trying to print a receipt using a thermal printer, this is the best solution I got so far using win32ui but I need to insert a logo or QR code

import win32ui

doc = open("1.txt", 'r').readlines()
f = 50
dc = win32ui.CreateDC()
dc.CreatePrinterDC()
dc.StartDoc('Test')
dc.StartPage()


for i, text in enumerate(doc):
    print(i, text)
    dc.TextOut(0,i*f, text)
    dc.MoveTo(0, i*f)

dc.EndPage()
1

There are 1 best solutions below

4
CristiFati On

PyWin32 homepage: [GitHub]: mhammond/pywin32 - pywin32.

After "a bit" of researching:

I was able to come up with a dummy example (it also uses [PyPI]: Pillow - I wanted to use it just for reading the images (also tried OpenCV), but in the end it does almost all the work).

code00.py:

#!/usr/bin/env python

import os
import sys

import win32con as wcon
import win32print as wprn
import win32ui as wui
from PIL import Image as pil_image, ImageWin as pil_image_win


def add_txt(hdc, file_name, new_page=False):
    if new_page:
        hdc.StartPage()
    pixel_scale = 84
    with open(file_name, mode="r") as fin:
        for idx, line in enumerate(fin):
            print("Text line {:d}: {:s}".format(idx, line))
            hdc.TextOut(5, idx * pixel_scale, line)
    if new_page:
        hdc.EndPage()


def draw_img(hdc, dib, maxh, maxw):
    w, h = dib.size
    print("Image HW: ({:d}, {:d}), Max HW: ({:d}, {:d})".format(h, w, maxh, maxw))
    h = min(h, maxh)
    w = min(w, maxw)
    l = (maxw - w) // 2
    t = (maxh - h) // 2
    dib.draw(hdc, (l, t, l + w, t + h))


def add_img(hdc, file_name, new_page=False):
    if new_page:
        hdc.StartPage()
    maxw = hdc.GetDeviceCaps(wcon.HORZRES)
    maxh = hdc.GetDeviceCaps(wcon.VERTRES)
    img = pil_image.open(file_name)
    dib = pil_image_win.Dib(img)
    draw_img(hdc.GetHandleOutput(), dib, maxh, maxw)
    if new_page:
        hdc.EndPage()


def main(*argv):
    printer_name = "Microsoft Print to PDF"  #wprn.GetDefaultPrinter()
    out_file = ".\\test.pdf"
    #out_file = None  # Send to printer directly
    hdc = wui.CreateDC()
    hdc.CreatePrinterDC(printer_name)
    if out_file and os.path.isfile(out_file):
        os.unlink(out_file)
    hdc.StartDoc("Test", out_file)
    add_txt(hdc, "in.txt")
    add_img(hdc, "qr.jpg")
    hdc.EndDoc()
    hdc.DeleteDC()


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

Notes:

  • To avoid printing each time I ran the program (and there were lots of them), I "printed" to a .pdf

  • I centered the image in the .pdf page (in a dummy manner) only considering printable area (ignoring printer physical properties, V / H scaling, ...). A robust (production ready) implementation should take all into account, fact that will make it waay more complex

    • The above also applies to text (and other possible elements that might be added in the future). For example, a long line should be truncated in order for it not to go outside printable area

Output:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q073175487]> dir /b *.pdf
File Not Found

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q073175487]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" code00.py
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32

Text line 0: 1st dummy line

Text line 1: 2nd dummy longer line - The quick brown fox jumps over the lazy dog - The quick brown fox jumps over the lazy dog - The quick brown fox jumps over the lazy dog

Text line 2: 3rd dummy line

Image HW: (1200, 1200), Max HW: (7016, 4961)

Done.

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q073175487]> dir /b *.pdf
test.pdf

The generated test.pdf:

Img0

And a page from my (Epson) printer (with out_file = None). Colors are a bit off as black is very low and it attempted to synthetize it from CMY (and also it's late night and the phone that I took the picture with is old):

Img1


You could check [SO]: Square in Python GDI (with PatBLT) (@CristiFati's answer) for another GDI example from Python (and PyWin32).