前期准备
硬件软件安装
硬件:
- 芯烨 (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。
[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 有更多的选择。
使用参数 height 和 width 调整条码尺寸。请注意,条码如果在可打印区域外将不会被打印。(一般来讲这是不会出现的,因此这个信息可能在调试时更有用。)
如果你不想让条形码居中显示可以设置参数 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) - 设置水平位置,可选值
center
、left
和right
。默认值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_ONLINE
或 RT_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
- 设置字符代码表为 “CP1252”;
- 打印机默认使用 GB2312 编码,将中文文本用 GB2312 编码后使用 latin1 解码。
p.charcode('CP1252')
p.textln('中文'.encode('gb2312').decode('latin1'))
复坤 FK-POS58GP-2
- 设置字符代码表为 “ISO_8859-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'))