使用Python操作ESCPOS协议热敏打印机

前期准备

硬件软件安装

硬件:

  • 芯烨(Xprinter) XP-58IIQ USB接口热敏打印机,兼容ESC/POS打印协议;

软件:

  • python-escpos==3.0a3,是一个alpha测试版本,接口在将来可能会变动。
pip install python-escpos
  • 如果在树莓派上使用,需要先安装 libjpeg8-dev
sudo apt-get install libjpeg8-dev

访问权限设置

插上USB打印机,执行dmesg命令,会有新USB设备连接的信息,成功的被识别为了打印机usblp0。

# dmesg
[100271.364954] usb 1-1.4: new full-speed USB device number 6 using dwc_otg
[100271.498310] usb 1-1.4: New USB device found, idVendor=0483, idProduct=070b
[100271.498327] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[100271.498338] usb 1-1.4: Product: USB Printing Support
[100271.498348] usb 1-1.4: Manufacturer: Printer-58
[100271.498377] usb 1-1.4: SerialNumber: ÿ
[100271.509441] usblp 1-1.4:1.0: usblp0: USB Bidirectional printer dev 6 if 0 alt 0 proto 2 vid 0x0483 pid 0x070B

执行ls /dev/usb,可以看到有一个lp0打印机设备

# ls /dev/usb
lp0

查看打印机设备权限,为660。拥有者是root,拥有组是lp,当前用户pi无法访问打印机

# stat /dev/usb/lp0
  File: /dev/usb/lp0
  Size: 0           Blocks: 0          IO Block: 4096   character special file
Device: 6h/6d   Inode: 48155       Links: 1     Device type: b4,0
Access: (0660/crw-rw----)  Uid: (    0/    root)   Gid: (    7/      lp)
Access: 2017-10-30 20:47:05.762879696 +0800
Modify: 2017-10-30 20:47:05.762879696 +0800
Change: 2017-10-30 20:47:05.762879696 +0800
 Birth: -

把当前用户加入到lp组,使其能够访问打印机

sudo usermod -a -G lp pi

测试,如果正常打印,则权限已设置好

echo "hello" >> /dev/usb/lp0

定义打印机实例

所有的打印机类都定义于escpos.printer文件中。

USB打印机

class escpos.printer.Usb(idVendor, idProduct, timeout=0, in_ep=130, out_ep=1, *args, **kwargs)

在创建打印机实例之前,你需要获取一些打印机的参数。

使用lsusb命令,在输出中得到VendorID和Product ID,它们的格式是xxxx:xxxx,位置在设备名之前。

# lsusb
Bus 001 Device 004: ID 148f:5370 Ralink Technology, Corp. RT5370 Wireless Adapter
Bus 001 Device 006: ID 0483:070b STMicroelectronics
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

根据Vendor ID和Product ID,可以得到“EndPoint”地址。

# lsusb -vvv -d 0483:070b | grep bEndpointAddress
        bEndpointAddress     0x81  EP 1 IN
        bEndpointAddress     0x02  EP 2 OUT

得到:“EndPoint”地址IN方向为0x81,OUT方向为0x02。

用这些参数可以新建一个Usb类实例。timeout参数表示等待USB接口超时时间,默认为0。

from escpos import printer
p = printer.Usb(0x1a2b, 0x1a2b, timeout=0, in_ep=0x81, out_ep=0x02)

该类使用pyusb和libusb与USB接口打印机通信,不适用于USB转串口适配器设备,只适用于原生USB驱动。

网络打印机

class escpos.printer.Network(host, port=9100, timeout=60, *args, **kwargs)

一般情况下只需要IP地址

from escpos import printer
p = printer.Network("192.168.1.99")

串口打印机

class escpos.printer.Serial(devfile=r'/dev/ttyS0', baudrate=9600, bytesize=8, timeout=1, parity='N', stopbits=1, xonxoff=False, dsrdtr=True, *args, **kwargs)

大多数串口打印机都使用默认设置,只要知道连接哪个串口就可以了。

from escpos import printer
p = printer.Serial(r"/dev/tty0")

文件系统打印机

class escpos.printer.File(devfile=r'/dev/usb/lp0', auto_flush=True, *args, **kwargs)

有些打印机使用上述类不能正常初始化(通常是使用printcap的打印机),如果你知道设备名,可以直接尝试用设备节点名来定义打印机实例:

from escpos import printer
p = printer.File(r"/dev/usb/lp1")

虚拟打印机

class escpos.printer.Dummy(*args, **kwargs)

此虚拟打印机用于不需要向实际打印机发送命令的情况,可以将原生命令输出保存到变量。可以用于生成print jobs供以后使用,或者测试打印输出。使用该类的output参数获取原生命令。

ESC/POS API详解

所有的打印机类都继承自escpos.escpos.Escpos,该Escpos抽象基类定义了打印时的各种方法。

先来个demo吧:

from escpos.printer import Usb

p = Usb(0x0483, 0x070b, 0, 0x81, 0x02)
p.hw('INIT')
p.textln('Hello, world!')
p.image('mafengwo.png')
p.set(align='center')
p.qr('https://i.senguo.cc', size=7,)
p.barcode('9787111436737', 'EAN13')
p.close()

打印QR码

qr(content, ec=0, size=3, model=2, native=False, center=False)

根据传入的字符串打印QR码,一般情况下为先生成QR码图片然后传给打印机打印图片。

参数说明:

  • content - 二维码的内容,数字数据能够被更高效地压缩。
  • ec - 容错能力,取值如下:QR_ECLEVEL_L=0、QR_ECLEVEL_M=1、QR_ECLEVEL_Q=2或QR_ECLEVEL_H=3,默认值为0。相对而言,容错能力越高,QR码图形面积越大。
  • size - QR码大小等级,取值范围为1-16,默认值为3。实际打印大小取决于QR码的内容,实测打印内容为https://i.senguo.cc/时在58mm打印纸上最大等级为14。
  • model - QR码格式,取值如下:QR_MODEL_1=1、QR_MODEL_2 =2或QR_MICRO=3,默认值为2。不是所有的打印机都支持QR_MICRO格式。(XP-58IIQ只支持QR_MODEL_2)。
  • native - 原生打印模式,为True时给打印机传代码,False时给打印机传生成的QR码图片,默认值False
  • center - 是否居中,默认值False。(当前并不生效,还是要先用set方法设置居中)

打印图片

image(img_source, high_density_vertical=True, high_density_horizontal=True, impl='bitImageRaster', fragment_height=960, center=False)

你可以选择是否使用高密度模式打印,默认为高密度模式。如果以低密度模式打印,图像将被拉伸(低密度模式即是倍宽、倍高模式)。

ESC/POS提供了几个打印命令,本方法支持其中的三个。如果你打印图片有问题,可以尝试其他打印命令。

可用的打印实现有:

  • bitImageRaster:使用GS v 0命令打印;
  • graphics:使用GS ( L命令打印;
  • bitImageColumn:使用ESC *命令打印;

参数列表:

  • img_source - PIL image对象或者文件名字符串,支持jpg、gif、png和bmp格式图像。
  • high_density_vertical - 在垂直方向使用高密度打印模式,默认值True;
  • high_density_horizontal - 在水平方向使用高密度打印模式,默认值True;
  • impl - 打印实现模式,可选值如上所述,默认值为bitImageRaster
  • fragment_height - 图片高度大于该值的将会被分为多片段打印,默认值为960;
  • center - 是否居中,默认值False。(当前并不生效,还是要先用set方法设置居中)

打印条形码

barcode(code, bc, height=64, width=3, pos='BELOW', font=u'A', align_ct=True, function_type=None, check=True)

该方法用于打印条形码,条形码打印是大部分打印机原生支持的功能。默认情况下,该方法会检查传入的条形码文本是否正确,即ESCPOS协议支持的条形码的字符和长度。使用 check=False 参数可以禁用检查,但是不正确的条形码可能会导致打印机的未知行为。条形码功能有两种类型,默认使用类型A,但类型A条码较少,类型B有更多的选择。

使用参数 heightwidth 调整条码尺寸。请注意,条码如果在可打印区域外将不会被打印。(一般来讲这是不会出现的,因此这个信息可能在调试时更有用。)

如果你不想让条形码居中显示可以设置参数 align_ct=False,将会禁用自动居中。请注意,如果设置了条码居中显示,那么在此之后打印文字也将是居中的,你需要在打印完条码后手动设置对齐方式。

参数列表:

  • code - 要打印为条形码的字母数字数据;

  • bc - 条形码格式,type A的格式有:

  • UPC-A
  • UPC-E
  • EAN13
  • EAN8
  • CODE39
  • ITF
  • NW7

type B的格式有:

  • 所有Type A中的格式;
  • CODE93
  • CODE128
  • GS1-128
  • GS1 DataBar Omnidirectional
  • GS1 DataBar Truncated
  • GS1 DataBar Limited
  • GS1 DataBar Expanded

如果没有传该参数,会抛出BarcodeTypeError异常。

  • height - 条码高度,取值范围1-255,默认值64。
  • width - 条码宽度,取值范围2-6,默认值3。
  • pos - 文字相对于条形码的位置,默认值BELOW在条码下方,可取值:
  • ABOVE
  • BELOW
  • BOTH
  • OFF
  • font - 选择字体A或B,默认值A。
  • align_ct - 条码是否居中,默认True。如果为False,将不会发送对齐方式指令。
  • function_type - 根据打印机是否支持支持和所需条形码,在ESCPOS功能类型A或B之间进行选择。如果没有给出,打印机将尝试根据当前配置文件自动选择正确的功能。 默认值:A。
  • check - 如果设置为True,将会检查条形码格式,已确保其符合esc/pos文档中的要求。默认值True。更多信息见escpos.Escpos.check_barcode方法。
soft_barcode(barcode_type, data, impl='bitImageColumn', module_height=5, module_width=0.2, text_distance=1)

“软打印”条形码。类似打印QR码,该方法将数据先转化为图片,再发送给打印机打印。暂时没有文档。

打印文本

text(txt)

打印字母和数字文本,传入txt为Unicode编码格式。但是这样无法打印中文,会乱码。

ln(count=1)

打印n个换行,默认打印1个换行。count不能小于0。

textln(txt='')

打印字母数字文本并换行,调用text(txt)方法实现。

block_text(txt, font=None, columns=None)

打印文本到指定的列,即打印columns列后就自动换到下一行继续打印。注意在最后不会打印换行符,所以最后一行的文本打印不出来。

设置格式

set(align='left',
    font=u'a',
    bold=False,
    underline=0,
    width=1,
    height=1,
    density=9,
    invert=False,
    smooth=False,
    flip=False,
    double_width=False,
    double_height=False,
    custom_size=False
)

设置文本属性。

参数列表:

  • align (str) - 设置水平位置,可选值centerleftright。默认值left
  • font (str) - 设置字体,可以使用字体名,’a’和’b’,或者0和1。(貌似并没有用)
  • bold (bool) - 设置文本加粗,默认值False。
  • underline (int) - 设置文本下划线,取值范围0-2,0没有下划线,1和2有下划线,2时下划线颜色较深,仅英文有效,默认值0。
  • double_height (bool) - 设置文字倍高,默认值False。
  • double_width (bool) – 设置文字倍宽,默认值False。
  • custom_size (bool) – 使用由width和height参数定义的大小,此时double_height和double_width参数不生效。默认值False。
  • width (int) – 当custom_size为True时用于设定文本宽度,取值范围1-8,默认值1。
  • height (int) – 当custom_size为True时用于设定文本高度,取值范围1-8,默认值1。
  • density (int) – 打印密度,取值范围0-8,当前版本未生效
  • invert (bool) – 设置反色打印, 默认值: False
  • smooth (bool) – 开启字体平滑,只对4x4及以上大小的文本有效,默认值: False
  • flip (bool) – 开启倒置打印,当前版本无效,默认值:False
line_spacing(spacing=None, divisor=180)

设置行距,如果不传参数,行距会被设置为默认值。

有几种不同的命令可以设置行距,它们分别使用了不同的分母divisor:

  • ”+”: 一英寸的line_spacing/360,其中0 <= line_spacing <= 255;
  • “3”: 一英寸的line_spacing/180,其中0 <= line_spacing <= 255;
  • “A”: 一英寸的line_spacing/60,其中0 <= line_spacing <= 85。

有些打印机不支持上述所有类型,请使用最通常的方式,也就是180做分母的方式。(xp-58IIQ支持180模式)

硬件操作

切纸

cut(mode='FULL', feed=True)

默认不传任何参数时,切纸会完全切断。当mode传”PART”是,将会尝试部分切纸。不是所有的打印机都支持切纸和部分切纸。

参数列表:

  • mode - 可选”PART”或者”FULL”,默认值”FULL”;
  • feed - 在切纸前“打印并进纸”,默认为True。

打开钱箱

cashdraw(pin)

发送打开钱箱指令脉冲。

传入2或5或一个十进制数组成的list,表示脉冲序列。

其他

hw(hw)

硬件操作,可选值”INIT”,”SELECT”或”RESET”。

print_and_feed(n=1)

打印缓冲区数据,并进纸n行。n的取值范围为0-255,默认为1。

panel_buttons(enable=True)

是否使能控制面板按钮。默认True。该设置持续到打印机initialized, reset or power-cycled。

禁用按钮对有些打印机无效。

查询状态

is_online()

这个,不用解释了吧,返回True或者False。

paper_status()

查询打印纸状态。返回int型,2有纸,1有纸但纸快没了,0没纸。

query_status(mode)

查询打印机状态,返回一个数组。mode取值RT_STATUS_ONLINERT_STATUS_PAPER,分别查询打印机在线状态和打印纸状态,但返回数组意义不明。

打印中文兼容性

可以写一个自定义类,继承自Usb类(假如你是Usb打印机的话):

class MyUsb(Usb):
    """docstring for MyUsb"""
    def __init__(self, *args, **kwargs):
        super(MyUsb, self).__init__(*args, **kwargs)
        self.charcode('CP1252')

    def text(self, txt):
        txt = txt.encode('gb2312').decode('l1')
        txt = six.text_type(txt)
        self.magic.write(txt)

芯烨 XP-58IIQ

  1. 设置字符代码表为”CP1252”;
  2. 打印机默认使用GB2312编码,将中文文本用GB2312编码后使用latin1解码。
p.charcode('CP1252')
p.textln('中文'.encode('gb2312').decode('latin1'))

复坤 FK-POS58GP-2

  1. 设置字符代码表为”ISO_8859-2”;
  2. 打印机默认使用UTF-8编码,将中文文本用UTF-8编码后使用latin2解码。
p.charcode('ISO_8859-2')
p.textln('中文'.encode().decode('latin2')) # encode不传参数即编码为utf-8

复坤 FK-POS80GP-2

p.charcode('CP1252')
p.textln('中文'.encode('gb2312').decode('latin1'))

佳博 S-U804

p.charcode('ISO_8859-15')
p.textln('中文'.encode('gb2312').decode('latin2'))

写在最后

目前来看,escpos库的3.0版本将会很不错,但是发布时间并不确定,目前已经处于alpha测试10个月了= =只能先用着3.0a3测试版本,等正式版本出来后再更新笔记。

热敏打印机的应用,想到几个:

  • 打印开发任务上线的checklist;
  • 作为一个输出设备,用来输出一些需要对比历史数据、不重要的、看过就可以扔的输出;
  • 也许哪天SG要用Pyton操作打印机了也说不定呢😄(中了!)
目录