Python win32print

python 调用win32print

背景

POS机打印小票含有中文, 英文, 高棉语、图片等信息,高棉语的无法正常输出。原因是大多嵌入式打印小票打印机只提供了英文字库,国产的做了中文字库的装载。而类似小国家的非常的库没有厂商愿做。

解决办法(Windows 平台)

  • 适配字库, 然后调用DLL;
  • 生成word docx、txt、rtf文件, 调用print 命令打印;
  • win32 api把打印合成图片, 使用win32print以图片bmp格式打印;

前提是已配置好打印机,可以在word或记事本里能正常打印


HORZRES = 8
VERTRES = 10
LOGPIXELSX = 88
LOGPIXELSY = 90
PHYSICALWIDTH = 110
PHYSICALHEIGHT = 111

from PIL import ImageWin
import win32gui, win32ui, win32print, win32con, win32api

scale_factor = 20

pr_dict = dict()

paper_sizes = {
    "letter": 1,
    "lettersmall": 2,
    "tabloid": 3,
    "ledger": 4,
    "legal": 5,
    "statement": 6,
    "executive": 7,
    "a3": 8,
    "a4": 9,
    "envelope9": 19,
    "envelope10": 20,
    "envelope11": 21,
    "envelope12": 22,
    "envelope14": 23,
    "fanfold": 39,
}

orientations = {
    "portrait": 1,
    "landscape": 2,
}

duplexes = {
    "normal": 1,
    "none": 1,
    "long": 2,
    "short": 3,
}


class Document:

    def __init__(self, printer=None, paper_size=None, orientation=None, duplex=None):
        self.dc = None
        self.font = None
        self.printer = printer
        self.paper_size = paper_size
        self.orientation = orientation
        self.page = 0
        self.duplex = duplex
        self.pen = None
        self.hdc = None
        self.h_printer = None

    def scale_pos(self, pos):
        rc, _ = list(), self
        for i in range(len(pos)):
            p = pos[i]
            if i % 2:
                p *= -1
            rc.append(int(p * scale_factor))
        return tuple(rc)

    def begin_document(self, name="DMallPOS print job"):
        # open the printer
        if self.printer is None:
            self.printer = win32print.GetDefaultPrinter()
        self.h_printer = win32print.OpenPrinter(self.printer)

        # load default settings
        dev_mode = win32print.GetPrinter(self.h_printer, 8)["pDevMode"]

        # change paper size and orientation
        if self.paper_size is not None:
            if type(self.paper_size) is int:
                dev_mode.PaperSize = self.paper_size
            else:
                dev_mode.PaperSize = paper_sizes[self.paper_size]
        if self.orientation is not None:
            dev_mode.Orientation = orientations[self.orientation]
        if self.duplex is not None:
            dev_mode.Duplex = duplexes[self.duplex]

        # create dc using new settings
        self.hdc = win32gui.CreateDC("WINSPOOL", self.printer, dev_mode)
        self.dc = win32ui.CreateDCFromHandle(self.hdc)

        # self.dc = win32ui.CreateDC()
        # if self.printer is not None:
        #     self.dc.CreatePrinterDC(self.printer)
        # else:
        #     self.dc.CreatePrinterDC()

        self.dc.SetMapMode(win32con.MM_TWIPS)  # hundredths of inches
        self.dc.StartDoc(name)
        self.dc.SetBkMode(win32con.TRANSPARENT)
        self.pen = win32ui.CreatePen(0, int(scale_factor), 0)
        self.dc.SelectObject(self.pen)
        win32gui.SetBkMode(self.hdc, 1)  # transparent
        self.page = 1

    def end_document(self):
        if self.page == 0:
            return  # document was never started
        self.dc.EndDoc()
        del self.dc

    def end_page(self):
        if self.page == 0:
            return  # nothing on the page
        # end page gets stupid if the page is completely blank
        self.text((1, 1), " ")
        self.dc.EndPage()
        self.page += 1

    def getsize(self):
        if not self.page:
            self.begin_document()
        # returns printable (width, height) in points
        width = float(self.dc.GetDeviceCaps(HORZRES)) * (72.0 / self.dc.GetDeviceCaps(LOGPIXELSX))
        height = float(self.dc.GetDeviceCaps(VERTRES)) * (72.0 / self.dc.GetDeviceCaps(LOGPIXELSY))
        return width, height

    def line(self, from_, to):
        if not self.page:
            self.begin_document()
        self.dc.MoveTo(self.scale_pos(from_))
        self.dc.LineTo(self.scale_pos(to))

    def rectangle(self, box):
        if not self.page:
            self.begin_document()
        self.dc.MoveTo(self.scale_pos((box[0], box[1])))
        self.dc.LineTo(self.scale_pos((box[2], box[1])))
        self.dc.LineTo(self.scale_pos((box[2], box[3])))
        self.dc.LineTo(self.scale_pos((box[0], box[3])))
        self.dc.LineTo(self.scale_pos((box[0], box[1])))

    def text(self, position, text):
        if self.page == 0:
            self.begin_document()
        self.dc.TextOut(int(scale_factor * position[0]),
                        int(-1 * scale_factor * position[1]), text)

    def set_font(self, name, size, bold=None, italic=None):
        if not self.page:
            self.begin_document()
        wt = 400
        if bold:
            wt = 700
        if italic:
            italic = 1
        else:
            italic = 0
        self.font = get_font(name, size, wt, italic)
        self.dc.SelectObject(self.font)

    def image(self, position, image, size):
        """print PIL image at position with given size"""
        if ImageWin is None:
            raise Exception()
        if self.page == 0:
            self.begin_document()
        dib = ImageWin.Dib(image)
        end_pos = (position[0] + size[0], position[1] + size[1])
        dst = (position[0] * scale_factor, -1 * position[1] * scale_factor,
               end_pos[0] * scale_factor, -1 * end_pos[1] * scale_factor)
        dib.draw(self.hdc, dst)

    def set_ink(self, ink):
        win32gui.SetTextColor(self.hdc, win32api.RGB(*ink))

    def set_fill(self, on_off):
        pass


def build_dict():
    global pr_dict
    lst = win32print.EnumPrinters(
        win32print.PRINTER_ENUM_CONNECTIONS
        + win32print.PRINTER_ENUM_LOCAL)
    pr_dict = {}
    for flags, description, name, comment in lst:
        pr_dict[name] = {}
        pr_dict[name]["flags"] = flags
        pr_dict[name]["description"] = description
        pr_dict[name]["comment"] = comment


def list_printers():
    dft = win32print.GetDefaultPrinter()
    if pr_dict is None:
        build_dict()
    keys = pr_dict.keys()
    keys.sort()
    rc = [dft]
    for k in keys:
        if k != dft:
            rc.append(k)
    return rc


def desc(name):
    not pr_dict and list_printers()
    return pr_dict[name]


def get_font(name, size, weight=400, italic=0):
    if italic:
        return win32ui.CreateFont({"name": name, "height": scale_factor * size, "weight": weight, "italic": italic,})
    else:
        return win32ui.CreateFont({"name": name, "height": scale_factor * size, "weight": weight, })


if __name__ == "__main__":
    doc = Document(orientation="portrait")
    doc.begin_document()
    doc.set_font("Khmer UI", 12, bold=True)
    doc.text((0, 0), "ញ្ចូលមាតិកាជាភាសាខ្មែរដែលអ្នកចង់បកប្រែខាងក្រោម")
    doc.set_font("Khmer UI", 12, bold=False)
    doc.text((0, 48), "ញ្ចូលមាតិកាជាភាសាខ្មែរដែលអ្នកចង់បកប្រែខាងក្រោម")

    doc.set_font("宋体", 12)
    doc.text((0, 48 + 48), "羅拔臣什味啫喱粉")
    # doc.rectangle((72, 72, 72*6, 72*3))
    # doc.line((72, 72), (72*6, 72*3))
    from PIL import Image

    img = Image.open("a.bmp", )
    doc.image((0, 100), img, img.size)
    doc.end_document()

    from win32com import client
    c = client.Dispatch("OPOS.CashDrawer")
    c.Open("StanderU")
    c.ClaimDevice(1000)
    c.DeviceEnabled = True
    c.OpenDrawer()
    c.DeviceEnabled = False
    c.Release()
    c.Close()

参考

代码
字体

posted @ 2020-10-30 19:20  Onsunsl  阅读(1312)  评论(0编辑  收藏  举报