escpos是一個Python的庫,可以讓用戶通過python無驅調用熱敏打印機打印文字、圖片、二維碼以及條形碼等以下是該庫的主頁。
https://python-escpos.readthedocs.io/en/latest/index.html
看完主頁之後可以發現文檔寫的相當的爛,要什麼沒什麼。
這個庫對win的支持相當的不友好,會報“usb.core.NoBackendError: No backend available”之類的錯誤。原因是程序要通過lsusb命令訪問系統的usb信息。
當然也是有解決辦法的,可以參見libusb-win32
由於本人在做一個嵌入式的課程設計,很自然還是在linux下完成,這樣就避免了這個問題。
迅速照著文檔安裝完依賴包以及通過lsusb找到設備的VID和PID之後,參見一下官網的例程。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from escpos import * """ Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """ Epson = printer.Usb(0x04b8,0x0202) # Print text Epson.text("Hello World\n") # Print image Epson.image("logo.gif") # Print QR Code Epson.qr("You can readme from your smartphone") # Print barcode Epson.barcode('1324354657687','EAN13',64,2,'','') # Cut paper Epson.cut() |
這個太強了,我從來都沒試過第一句就不能過的:”TypeError: Item in “from list” not a string”
本人才疏學淺不知道是為什麼,但是定睛一看,整個程序只用了printer類,於是只把printer類import進來好了。
1 |
from escpos import printer |
這段程序更強的是第二句也運行不了。由於文檔太爛,於是只能去看源碼,源碼里的注釋還是比較清晰的。因此去找源碼安裝的位置。
位置在這:
1 |
~/.local/lib/python2.7/site-packages/escpos |
於是編輯查看一下printer.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class Usb(Escpos): """ USB printer This class describes a printer that natively speaks USB. inheritance: .. inheritance-diagram:: escpos.printer.Usb :parts: 1 """ def __init__(self, idVendor, idProduct, timeout=0, in_ep=0x82, out_ep=0x01, *args, **kwargs): """ :param idVendor: Vendor ID :param idProduct: Product ID :param timeout: Is the time limit of the USB operation. Default without timeout. :param in_ep: Input end point :param out_ep: Output end point """ Escpos.__init__(self, *args, **kwargs) self.idVendor = idVendor self.idProduct = idProduct self.timeout = timeout self.in_ep = in_ep self.out_ep = out_ep self.open() |
最主要的問題在於out_ep的值作者是亂定的。所以得寫個程序獲得這個東西。下面這個程序來自樹莓派實驗室 http://shumeipai.nxez.com/2015/04/14/raspberry-pi-usb-printer-driver.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import usb.core import usb.util import sys dev = usb.core.find(idVendor= 0x0483, idProduct= 0x070b) cfg = dev.get_active_configuration() intf = cfg[(0,0)] ep = usb.util.find_descriptor( intf, # match the first OUT endpoint custom_match = \ lambda e: \ usb.util.endpoint_direction(e.bEndpointAddress) == \ usb.util.ENDPOINT_OUT ) print(ep) dev.reset() |
查詢到out_ep之後基本上就能實現打印的功能了。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from escpos import printer """ Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """ Epson = printer.Usb(0x04b8,0x0202,0,out_ep=0x02) # Print text Epson.text("Hello World\n") # Print image Epson.image("logo.gif") # Print QR Code Epson.qr("You can readme from your smartphone") # Print barcode Epson.barcode('1324354657687','EAN13',64,2,'','') # Cut paper Epson.cut() |
看了自己打印出來的東西,你可能會有點不舒服。然後你可能會思考以下問題:
1. 居中怎麼調?
2. 圖怎麼那麼小?
3. 二維碼怎麼那麼小?
4. 條形碼怎麼那麼短?
5. 怎麼打中文?
這些問題得一個個解決。
1. 居中怎麼調?看看escpos.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
def set(self, align='left', font='a', text_type='normal', width=1, height=1, density=9, invert=False, smooth=False, flip=False): """ Set text properties by sending them to the printer :param align: horizontal position for text, possible values are: * CENTER * LEFT * RIGHT *default*: LEFT :param font: font type, possible values are A or B, *default*: A :param text_type: text type, possible values are: * B for bold * U for underlined * U2 for underlined, version 2 * BU for bold and underlined * BU2 for bold and underlined, version 2 * NORMAL for normal text *default*: NORMAL :param width: text width multiplier, decimal range 1-8, *default*: 1 :param height: text height multiplier, decimal range 1-8, *default*: 1 :param density: print density, value from 0-8, if something else is supplied the density remains unchanged :param invert: True enables white on black printing, *default*: False :param smooth: True enables text smoothing. Effective on 4x4 size text and larger, *default*: False :param flip: True enables upside-down printing, *default*: False :type invert: bool """ # Width if height == 2 and width == 2: self._raw(TXT_NORMAL) self._raw(TXT_4SQUARE) elif height == 2 and width == 1: self._raw(TXT_NORMAL) self._raw(TXT_2HEIGHT) elif width == 2 and height == 1: self._raw(TXT_NORMAL) self._raw(TXT_2WIDTH) elif width == 1 and height == 1: self._raw(TXT_NORMAL) elif 1 <= width <= 8 and 1 <= height <= 8 and isinstance(width, int) and isinstance(height, int): self._raw(TXT_SIZE + six.int2byte(TXT_WIDTH[width] + TXT_HEIGHT[height])) else: raise SetVariableError() # Upside down if flip: self._raw(TXT_FLIP_ON) else: self._raw(TXT_FLIP_OFF) # Smoothing if smooth: self._raw(TXT_SMOOTH_ON) else: self._raw(TXT_SMOOTH_OFF) # Type if text_type.upper() == "B": self._raw(TXT_BOLD_ON) self._raw(TXT_UNDERL_OFF) elif text_type.upper() == "U": self._raw(TXT_BOLD_OFF) self._raw(TXT_UNDERL_ON) elif text_type.upper() == "U2": self._raw(TXT_BOLD_OFF) self._raw(TXT_UNDERL2_ON) elif text_type.upper() == "BU": self._raw(TXT_BOLD_ON) self._raw(TXT_UNDERL_ON) elif text_type.upper() == "BU2": self._raw(TXT_BOLD_ON) self._raw(TXT_UNDERL2_ON) elif text_type.upper() == "NORMAL": self._raw(TXT_BOLD_OFF) self._raw(TXT_UNDERL_OFF) # Font if font.upper() == "B": self._raw(TXT_FONT_B) else: # DEFAULT FONT: A self._raw(TXT_FONT_A) # Align if align.upper() == "CENTER": self._raw(TXT_ALIGN_CT) elif align.upper() == "RIGHT": self._raw(TXT_ALIGN_RT) elif align.upper() == "LEFT": self._raw(TXT_ALIGN_LT) # Density if density == 0: self._raw(PD_N50) elif density == 1: self._raw(PD_N37) elif density == 2: self._raw(PD_N25) elif density == 3: self._raw(PD_N12) elif density == 4: self._raw(PD_0) elif density == 5: self._raw(PD_P12) elif density == 6: self._raw(PD_P25) elif density == 7: self._raw(PD_P37) elif density == 8: self._raw(PD_P50) else: # DEFAULT: DOES NOTHING pass # Invert Printing if invert: self._raw(TXT_INVERT_ON) else: self._raw(TXT_INVERT_OFF) |
這時候看到”align”了,一切都很明朗。說明只要調用以下函數即可。
1 |
Epson.set(align='center') |
2. 圖怎麼那麼小?看看image(…)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
def image(self, img_source, high_density_vertical=True, high_density_horizontal=True, impl="bitImageRaster", fragment_height=1024): """ Print an image You can select whether the printer should print in high density or not. The default value is high density. When printing in low density, the image will be stretched. Esc/Pos supplies several commands for printing. This function supports three of them. Please try to vary the implementations if you have any problems. For example the printer `IT80-002` will have trouble aligning images that are not printed in Column-mode. The available printing implementations are: * `bitImageRaster`: prints with the `GS v 0`-command * `graphics`: prints with the `GS ( L`-command * `bitImageColumn`: prints with the `ESC *`-command :param img_source: PIL image or filename to load: `jpg`, `gif`, `png` or `bmp` :param high_density_vertical: print in high density in vertical direction *default:* True :param high_density_horizontal: print in high density in horizontal direction *default:* True :param impl: choose image printing mode between `bitImageRaster`, `graphics` or `bitImageColumn` :param fragment_height: Images larger than this will be split into multiple fragments *default:* 1024 """ |
這裡提供一種解決方法。利用PIL (Python Imaging Library)庫。
1 |
sudo pip install Pillow |
然後import
1 2 3 4 5 |
from PIL import Image#要是名字不對就去安裝庫的路徑找一下 im=Image.open('panda.jpg') im.thumbnail((800,400)) im.save('./panda_thumbnail.jpg','JPEG') Epson.image("panda_thumbnail.jpg") |
代碼中的thumbnail((width, height))可以調節大小,這樣就能控制自己打印圖片的大小啦。
3. 二維碼怎麼那麼小?看qr(…)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2, native=False): """ Print QR Code for the provided string :param content: The content of the code. Numeric data will be more efficiently compacted. :param ec: Error-correction level to use. One of QR_ECLEVEL_L (default), QR_ECLEVEL_M, QR_ECLEVEL_Q or QR_ECLEVEL_H. Higher error correction results in a less compact code. :param size: Pixel size to use. Must be 1-16 (default 3) :param model: QR code model to use. Must be one of QR_MODEL_1, QR_MODEL_2 (default) or QR_MICRO (not supported by all printers). :param native: True to render the code on the printer, False to render the code as an image and send it to the printer (Default) """ |
可以看到,傳參的時候把size改一下就好了。這裡ps一下,想要微信掃二維碼直接進入自己的主頁,要網址前面加上 http:// 哦。
4. 條形碼怎麼那麼短?看barcode(…)。同樣的道理改width就行。height的默認是64,這個值感覺挺合適的。
5. 怎麼打中文?
(1)修改程序的编码方式
(2)修改系统默认编码方式
为什么要改两次?
字符串保存的编码格式由 #coding:XXX 决定,即文件的保存格式确定。py文件默认是ASCII编码,程序中出现中文当然要用utf-8或者gbk。
使用print输出时,python会做一个utf-8/gbk到系统默认编码的转换。对于英文系统来说,默认的是ASCII,隐式转换时,是从代码文件编码格式转换成ASCII,默认是utf-8->ASCII,这样当然打不出中文。因此需要将系统默认编码方式改为gbk
1 2 3 4 |
# coding: utf-8 import sys reload(sys) sys.setdefaultencoding('gbk') |
至于这里为什么要reload, 你猜?
據說上面這種方式是最壞的方式。於是我想用encode/decode的方法嘗試。同樣地,我看到了樹莓派實驗室 http://shumeipai.nxez.com/2015/04/14/raspberry-pi-usb-printer-driver.html 的代碼:
1 2 3 4 5 6 7 8 |
from escpos import * usb = printer.Usb(0x0fe6, 0x811e, 0, out_ep=0x03) usb.text(u"终于可以愉快的打印啦\n\n\n\n\n\n\n\n".encode('gbk')) usb.image(‘image path’)#打印图片(黑白2值) usb.qr(‘值’)#打印二维码 usb.set(codepage=None, align=‘center’)#设置页面居中 usb.cut()#切纸 usb.close()#关闭连接 |
然後我encode/decode試了半天程序全部報錯。你真是在kidding我。於是我只好又去看源碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
def text(self, txt): """ Print alpha-numeric text The text has to be encoded in the currently selected codepage. The input text has to be encoded in unicode. :param txt: text to be printed :raises: :py:exc:`~escpos.exceptions.TextError` """ if txt: if self.codepage: self._raw(txt.encode(self.codepage)) else: self._raw(txt.encode()) else: # TODO: why is it problematic to print an empty string? raise TextError() |
原來作者修改了程序,把編碼方式加到了自己的屬性 (self.codepage) 裡面,然後如果設置了self.codepage,就自己內部encode(self.codepage)。當然,輸入字符串 “has to be encoded in unicode.”
那解決方法又很明朗了。
1 2 3 4 |
Epson.codepage='gbk' # Print text pt=u"这是一只大熊猫" Epson.text(pt) |
全部綜合看看結果:
全部代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# coding: utf-8 from escpos import printer from PIL import Image """ Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """ Epson = printer.Usb(0x0483,0x070b,0,out_ep=0x02) Epson.codepage='gbk' Epson.set(align='center') # Print text Epson.text(u"这是一只大熊猫") #Epson.codepage=None Epson.text("\n") # Print image im=Image.open('panda.jpg') im.thumbnail((400,200)) im.save('./panda_thumbnail.jpg','JPEG') Epson.image("panda_thumbnail.jpg") # Print QR Code Epson.text("\n") Epson.qr("http://www.chiange.com/?p=135", size=8) # Print barcode Epson.text("\n") Epson.barcode('1234567891011','EAN13',64,4) # Cut paper Epson.cut() |
最後ps,一定要記得打完中文之後打一個回車,要不然後面打圖片二維碼什麼的全部會變成亂碼。血和淚的教訓。
最最後,不好意思,那是兩隻大熊貓。
Then I’m so impressed that u stand out to be this kind of prominent 🤦♀️👋🤙🤙🤙
为什么我的一维码打印不出来,中文都可以打印了。。。。。
为什么我的一维码打印不出来,二维码和图片是乱码,用的树莓派
條形碼輸入的數字如果位數不符合的話也會不顯示的。
二維碼和圖片是亂碼的情況是否是因為你打印文字之後沒有打印一個回車導致的?
条形码打印出来是 @1324354657687 这样的
回车打了
条形码的情况是原本应该出现图形的地方现在只有一个@符号,二维码图片确认打了回车。
有可能是我打印机M129C/TM-T70的问题吗,但是我看商家是可以打印图片的。
一個月都在忙,你解決之前的問題了嗎?
多謝啦!哇嘎哩嗊,在打印輸出中文的問題上糾結了好久。終於順利解決了。
嘻嘻嘻ヽ(*^ー^)人(^ー^*)ノ