Merge branch 'master' of https://github.com/ztane/python-escpos into ztane-master

Conflicts:
	.gitignore
	escpos/constants.py
	escpos/escpos.py

The branch by DavisGoglin contains a fix for issue 10, fixes #10
This commit is contained in:
Patrick Kanzler 2015-12-30 10:35:00 +01:00
commit 302e6e8917
7 changed files with 126 additions and 145 deletions

4
.gitignore vendored
View File

@ -1,3 +1,7 @@
*.py[cod] *.py[cod]
.DS_Store .DS_Store
build build
escpos.egg-info
MANIFEST
dist
.idea

BIN
doge.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

View File

@ -1,92 +1,93 @@
""" ESC/POS Commands (Constants) """ """ ESC/POS Commands (Constants) """
import binascii as ba
#{ Control characters #{ Control characters
# as labelled in http://www.novopos.ch/client/EPSON/TM-T20/TM-T20_eng_qr.pdf # as labelled in http://www.novopos.ch/client/EPSON/TM-T20/TM-T20_eng_qr.pdf
NUL = '\x00' NUL = ba.hexlify(b'\x00')
EOT = '\x04' EOT = ba.hexlify(b'\x04')
ENQ = '\x05' ENQ = ba.hexlify(b'\x05')
DLE = '\x10' DLE = ba.hexlify(b'\x10')
DC4 = '\x14' DC4 = ba.hexlify(b'\x14')
CAN = '\x18' CAN = ba.hexlify(b'\x18')
ESC = '\x1b' ESC = ba.hexlify(b'\x1b')
FS = '\x1c' FS = ba.hexlify(b'\x1c')
GS = '\x1d' GS = ba.hexlify(b'\x1d')
#{ Feed control sequences #{ Feed control sequences
CTL_LF = '\n' # Print and line feed CTL_LF = ba.hexlify(b'\n') # Print and line feed
CTL_FF = '\f' # Form feed CTL_FF = ba.hexlify(b'\f') # Form feed
CTL_CR = '\r' # Carriage return CTL_CR = ba.hexlify(b'\r') # Carriage return
CTL_HT = '\t' # Horizontal tab CTL_HT = ba.hexlify(b'\t') # Horizontal tab
CTL_VT = '\v' # Vertical tab CTL_VT = ba.hexlify(b'\v') # Vertical tab
#{ Printer hardware #{ Printer hardware
HW_INIT = ESC + '@' # Clear data in buffer and reset modes HW_INIT = ESC + ba.hexlify(b'@') # Clear data in buffer and reset modes
HW_SELECT = ESC + '=\x01' # Printer select HW_SELECT = ESC + ba.hexlify(b'=\x01') # Printer select
HW_RESET = ESC + ba.hexlify(b'\x3f\x0a\x00') # Reset printer hardware
HW_RESET = ESC + '\x3f\x0a\x00' # Reset printer hardware
# (TODO: Where is this specified?) # (TODO: Where is this specified?)
#{ Cash Drawer (ESC p <pin> <on time: 2*ms> <off time: 2*ms>) #{ Cash Drawer (ESC p <pin> <on time: 2*ms> <off time: 2*ms>)
_CASH_DRAWER = lambda m, t1='', t2='': ESC + 'p' + m + chr(t1) + chr(t2) _CASH_DRAWER = lambda m, t1='', t2='': ESC + ba.hexlify(b'p' + m + chr(t1) + chr(t2))
CD_KICK_2 = _CASH_DRAWER('\x00', 50, 50) # Sends a pulse to pin 2 [] CD_KICK_2 = _CASH_DRAWER(b'\x00', 50, 50) # Sends a pulse to pin 2 []
CD_KICK_5 = _CASH_DRAWER('\x01', 50, 50) # Sends a pulse to pin 5 [] CD_KICK_5 = _CASH_DRAWER(b'\x01', 50, 50) # Sends a pulse to pin 5 []
#{ Paper Cutter #{ Paper Cutter
_CUT_PAPER = lambda m: GS + 'V' + m _CUT_PAPER = lambda m: GS + ba.hexlify(b'V' + m)
PAPER_FULL_CUT = _CUT_PAPER('\x00') # Full cut paper PAPER_FULL_CUT = _CUT_PAPER(b'\x00') # Full cut paper
PAPER_PART_CUT = _CUT_PAPER('\x01') # Partial cut paper PAPER_PART_CUT = _CUT_PAPER(b'\x01') # Partial cut paper
#{ Text format #{ Text format
# TODO: Acquire the "ESC/POS Application Programming Guide for Paper Roll # TODO: Acquire the "ESC/POS Application Programming Guide for Paper Roll
# Printers" and tidy up this stuff too. # Printers" and tidy up this stuff too.
TXT_NORMAL = ESC + '!\x00' # Normal text TXT_NORMAL = ESC + ba.hexlify(b'!\x00') # Normal text
TXT_2HEIGHT = ESC + '!\x10' # Double height text TXT_2HEIGHT = ESC + ba.hexlify(b'!\x10') # Double height text
TXT_2WIDTH = ESC + '!\x20' # Double width text TXT_2WIDTH = ESC + ba.hexlify(b'!\x20') # Double width text
TXT_4SQUARE = ESC + '!\x30' # Quad area text TXT_4SQUARE = ESC + ba.hexlify(b'!\x30') # Quad area text
TXT_UNDERL_OFF = ESC + '\x2d\x00' # Underline font OFF TXT_UNDERL_OFF = ESC + ba.hexlify(b'\x2d\x00') # Underline font OFF
TXT_UNDERL_ON = ESC + '\x2d\x01' # Underline font 1-dot ON TXT_UNDERL_ON = ESC + ba.hexlify(b'\x2d\x01') # Underline font 1-dot ON
TXT_UNDERL2_ON = ESC + '\x2d\x02' # Underline font 2-dot ON TXT_UNDERL2_ON = ESC + ba.hexlify(b'\x2d\x02') # Underline font 2-dot ON
TXT_BOLD_OFF = ESC + '\x45\x00' # Bold font OFF TXT_BOLD_OFF = ESC + ba.hexlify(b'\x45\x00') # Bold font OFF
TXT_BOLD_ON = ESC + '\x45\x01' # Bold font ON TXT_BOLD_ON = ESC + ba.hexlify(b'\x45\x01') # Bold font ON
TXT_FONT_A = ESC + '\x4d\x00' # Font type A TXT_FONT_A = ESC + ba.hexlify(b'\x4d\x00') # Font type A
TXT_FONT_B = ESC + '\x4d\x01' # Font type B TXT_FONT_B = ESC + ba.hexlify(b'\x4d\x01') # Font type B
TXT_ALIGN_LT = ESC + '\x61\x00' # Left justification TXT_ALIGN_LT = ESC + ba.hexlify(b'\x61\x00') # Left justification
TXT_ALIGN_CT = ESC + '\x61\x01' # Centering TXT_ALIGN_CT = ESC + ba.hexlify(b'\x61\x01') # Centering
TXT_ALIGN_RT = ESC + '\x61\x02' # Right justification TXT_ALIGN_RT = ESC + ba.hexlify(b'\x61\x02') # Right justification
#{ Barcode format #{ Barcode format
_SET_BARCODE_TXT_POS = lambda n: GS + 'H' + n _SET_BARCODE_TXT_POS = lambda n: GS + ba.hexlify(b'H' + n)
BARCODE_TXT_OFF = _SET_BARCODE_TXT_POS('\x00') # HRI barcode chars OFF BARCODE_TXT_OFF = _SET_BARCODE_TXT_POS(b'\x00') # HRI barcode chars OFF
BARCODE_TXT_ABV = _SET_BARCODE_TXT_POS('\x01') # HRI barcode chars above BARCODE_TXT_ABV = _SET_BARCODE_TXT_POS(b'\x01') # HRI barcode chars above
BARCODE_TXT_BLW = _SET_BARCODE_TXT_POS('\x02') # HRI barcode chars below BARCODE_TXT_BLW = _SET_BARCODE_TXT_POS(b'\x02') # HRI barcode chars below
BARCODE_TXT_BTH = _SET_BARCODE_TXT_POS('\x03') # HRI both above and below BARCODE_TXT_BTH = _SET_BARCODE_TXT_POS(b'\x03') # HRI both above and below
_SET_HRI_FONT = lambda n: GS + 'f' + n _SET_HRI_FONT = lambda n: GS + b'f' + n
BARCODE_FONT_A = _SET_HRI_FONT('\x00') # Font type A for HRI barcode chars BARCODE_FONT_A = _SET_HRI_FONT(b'\x00') # Font type A for HRI barcode chars
BARCODE_FONT_B = _SET_HRI_FONT('\x01') # Font type B for HRI barcode chars BARCODE_FONT_B = _SET_HRI_FONT(b'\x01') # Font type B for HRI barcode chars
BARCODE_HEIGHT = GS + 'h' # Barcode Height [1-255] BARCODE_HEIGHT = GS + ba.hexlify(b'h') # Barcode Height [1-255]
BARCODE_WIDTH = GS + 'w' # Barcode Width [2-6] BARCODE_WIDTH = GS + ba.hexlify(b'w') # Barcode Width [2-6]
#NOTE: This isn't actually an ESC/POS command. It's the common prefix to the #NOTE: This isn't actually an ESC/POS command. It's the common prefix to the
# two "print bar code" commands: # two "print bar code" commands:
# - "GS k <type as integer> <data> NUL" # - "GS k <type as integer> <data> NUL"
# - "GS k <type as letter> <data length> <data>" # - "GS k <type as letter> <data length> <data>"
# The latter command supports more barcode types # The latter command supports more barcode types
_SET_BARCODE_TYPE = lambda m: GS + 'k' + m _SET_BARCODE_TYPE = lambda m: GS + ba.hexlify(b'k' + m)
BARCODE_UPC_A = _SET_BARCODE_TYPE('\x00') # Barcode type UPC-A BARCODE_UPC_A = _SET_BARCODE_TYPE(b'\x00') # Barcode type UPC-A
BARCODE_UPC_E = _SET_BARCODE_TYPE('\x01') # Barcode type UPC-E BARCODE_UPC_E = _SET_BARCODE_TYPE(b'\x01') # Barcode type UPC-E
BARCODE_EAN13 = _SET_BARCODE_TYPE('\x02') # Barcode type EAN13 BARCODE_EAN13 = _SET_BARCODE_TYPE(b'\x02') # Barcode type EAN13
BARCODE_EAN8 = _SET_BARCODE_TYPE('\x03') # Barcode type EAN8 BARCODE_EAN8 = _SET_BARCODE_TYPE(b'\x03') # Barcode type EAN8
BARCODE_CODE39 = _SET_BARCODE_TYPE('\x04') # Barcode type CODE39 BARCODE_CODE39 = _SET_BARCODE_TYPE(b'\x04') # Barcode type CODE39
BARCODE_ITF = _SET_BARCODE_TYPE('\x05') # Barcode type ITF BARCODE_ITF = _SET_BARCODE_TYPE(b'\x05') # Barcode type ITF
BARCODE_NW7 = _SET_BARCODE_TYPE('\x06') # Barcode type NW7 BARCODE_NW7 = _SET_BARCODE_TYPE(b'\x06') # Barcode type NW7
#{ Image format #{ Image format
# NOTE: _PRINT_RASTER_IMG is the obsolete ESC/POS "print raster bit image" # NOTE: _PRINT_RASTER_IMG is the obsolete ESC/POS "print raster bit image"
# command. The constants include a fragment of the data's header. # command. The constants include a fragment of the data's header.
_PRINT_RASTER_IMG = lambda data: GS + 'v0' + data _PRINT_RASTER_IMG = lambda data: GS + b'v0' + data
S_RASTER_N = _PRINT_RASTER_IMG('\x00') # Set raster image normal size S_RASTER_N = _PRINT_RASTER_IMG(b'\x00') # Set raster image normal size
S_RASTER_2W = _PRINT_RASTER_IMG('\x01') # Set raster image double width S_RASTER_2W = _PRINT_RASTER_IMG(b'\x01') # Set raster image double width
S_RASTER_2H = _PRINT_RASTER_IMG('\x02') # Set raster image double height S_RASTER_2H = _PRINT_RASTER_IMG(b'\x02') # Set raster image double height
S_RASTER_Q = _PRINT_RASTER_IMG('\x03') # Set raster image quadruple S_RASTER_Q = _PRINT_RASTER_IMG(b'\x03') # Set raster image quadruple

View File

@ -5,21 +5,21 @@
@copyright: Copyright (c) 2012 Bashlinux @copyright: Copyright (c) 2012 Bashlinux
@license: GPL @license: GPL
''' '''
import struct
import os import os
import time import time
import qrcode import qrcode
import operator import operator
from PIL import Image from PIL import Image, ImageOps
from constants import * from escpos.utils import *
from exceptions import * from escpos.constants import *
from escpos.exceptions import *
class Escpos: class Escpos(object):
""" ESC/POS Printer object """ """ ESC/POS Printer object """
device = None device = None
def _check_image_size(self, size): def _check_image_size(self, size):
""" Check and fix the size of the image to 32 bits """ """ Check and fix the size of the image to 32 bits """
if size % 32 == 0: if size % 32 == 0:
@ -27,31 +27,20 @@ class Escpos:
else: else:
image_border = 32 - (size % 32) image_border = 32 - (size % 32)
if (image_border % 2) == 0: if (image_border % 2) == 0:
return (image_border / 2, image_border / 2) return (image_border // 2, image_border // 2)
else: else:
return (image_border / 2, (image_border / 2) + 1) return (image_border // 2, image_border // 2 + 1)
def _print_image(self, imagedata, n_rows, col_bytes):
def _print_image(self, line, size):
""" Print formatted image """ """ Print formatted image """
i = 0 i = 0
cont = 0 cont = 0
buffer = "" buffer = ""
self._raw(S_RASTER_N) self._raw(S_RASTER_N)
buffer = "%02X%02X%02X%02X" % (((size[0]/size[1])/8), 0, size[1], 0) buffer = struct.pack('<HH', col_bytes, n_rows)
self._raw(buffer.decode('hex')) self._raw(buffer)
buffer = "" self._raw(imagedata)
while i < len(line):
hex_string = int(line[i:i+8],2)
buffer += "%02X" % hex_string
i += 8
cont += 1
if cont % 4 == 0:
self._raw(buffer.decode("hex"))
buffer = ""
cont = 0
def fullimage(self, img, max_height=860, width=512, histeq=True, bandsize=255): def fullimage(self, img, max_height=860, width=512, histeq=True, bandsize=255):
""" Resizes and prints an arbitrarily sized image """ """ Resizes and prints an arbitrarily sized image """
@ -66,11 +55,11 @@ class Escpos:
lut = [] lut = []
for b in range(0, len(h), 256): for b in range(0, len(h), 256):
# step size # step size
step = reduce(operator.add, h[b:b+256]) / 255 step = reduce(operator.add, h[b:b+256]) // 255
# create equalization lookup table # create equalization lookup table
n = 0 n = 0
for i in range(256): for i in range(256):
lut.append(n / step) lut.append(n // step)
n = n + h[i+b] n = n + h[i+b]
im = im.point(lut) im = im.point(lut)
@ -91,8 +80,7 @@ class Escpos:
min(im.size[1], current + bandsize)))) min(im.size[1], current + bandsize))))
current += bandsize current += bandsize
def image(self, im):
def image(self, img):
""" Parse image and prepare it to a printable format """ """ Parse image and prepare it to a printable format """
pixels = [] pixels = []
pix_line = "" pix_line = ""
@ -101,47 +89,25 @@ class Escpos:
switch = 0 switch = 0
img_size = [ 0, 0 ] img_size = [ 0, 0 ]
if isinstance(img, Image.Image): if not isinstance(im, Image.Image):
im = img.convert("RGB") im = Image.open(im)
else:
im = Image.open(img).convert("RGB")
if im.size[0] > 512: im = im.convert("L")
print "WARNING: Image is wider than 512 and could be truncated at print time " im = ImageOps.invert(im)
if im.size[1] > 255: im = im.convert("1")
if im.size[0] > 640:
print("WARNING: Image is wider than 640 and could be truncated at print time ")
if im.size[1] > 640:
raise ImageSizeError() raise ImageSizeError()
im_border = self._check_image_size(im.size[0]) orig_width, height = im.size
for i in range(im_border[0]): width = ((orig_width + 31) // 32) * 32
im_left += "0" new_image = Image.new("1", (width, height))
for i in range(im_border[1]): new_image.paste(im, (0, 0, orig_width, height))
im_right += "0"
for y in range(im.size[1]): the_bytes = new_image.tobytes()
img_size[1] += 1 self._print_image(the_bytes, n_rows=height, col_bytes=width//8)
pix_line += im_left
img_size[0] += im_border[0]
for x in range(im.size[0]):
img_size[0] += 1
RGB = im.getpixel((x, y))
im_color = (RGB[0] + RGB[1] + RGB[2])
im_pattern = "1X0"
pattern_len = len(im_pattern)
switch = (switch - 1 ) * (-1)
for x in range(pattern_len):
if im_color <= (255 * 3 / pattern_len * (x+1)):
if im_pattern[x] == "X":
pix_line += "%d" % switch
else:
pix_line += im_pattern[x]
break
elif im_color > (255 * 3 / pattern_len * pattern_len) and im_color <= (255 * 3):
pix_line += im_pattern[-1]
break
pix_line += im_right
img_size[0] += im_border[1]
self._print_image(pix_line, img_size)
def qr(self, text): def qr(self, text):
""" Print QR Code for the provided string """ """ Print QR Code for the provided string """
@ -150,9 +116,14 @@ class Escpos:
qr_code.make(fit=True) qr_code.make(fit=True)
qr_img = qr_code.make_image() qr_img = qr_code.make_image()
# Convert the RGB image in printable image # Convert the RGB image in printable image
im = qr_img._img.convert("RGB") im = qr_img._img.convert("1")
self.text('\n') width = im.size[0]
self.set(align='center') height = im.size[1]
while width * 2 <= 640:
width *= 2
height *= 2
im = im.resize((width, height))
self.image(im) self.image(im)
self.text('\n') self.text('\n')
@ -207,7 +178,6 @@ class Escpos:
else: else:
raise exception.BarcodeCodeError() raise exception.BarcodeCodeError()
def text(self, txt): def text(self, txt):
""" Print alpha-numeric text """ """ Print alpha-numeric text """
if txt: if txt:
@ -215,7 +185,6 @@ class Escpos:
else: else:
raise TextError() raise TextError()
def set(self, align='left', font='a', type='normal', width=1, height=1): def set(self, align='left', font='a', type='normal', width=1, height=1):
""" Set text properties """ """ Set text properties """
# Width # Width
@ -273,7 +242,6 @@ class Escpos:
else: # DEFAULT MODE: FULL CUT else: # DEFAULT MODE: FULL CUT
self._raw(PAPER_FULL_CUT) self._raw(PAPER_FULL_CUT)
def cashdraw(self, pin): def cashdraw(self, pin):
""" Send pulse to kick the cash drawer """ """ Send pulse to kick the cash drawer """
if pin == 2: if pin == 2:
@ -283,7 +251,6 @@ class Escpos:
else: else:
raise CashDrawerError() raise CashDrawerError()
def hw(self, hw): def hw(self, hw):
""" Hardware operations """ """ Hardware operations """
if hw.upper() == "INIT": if hw.upper() == "INIT":
@ -295,7 +262,6 @@ class Escpos:
else: # DEFAULT: DOES NOTHING else: # DEFAULT: DOES NOTHING
pass pass
def control(self, ctl): def control(self, ctl):
""" Feed control sequences """ """ Feed control sequences """
if ctl.upper() == "LF": if ctl.upper() == "LF":

View File

@ -11,9 +11,9 @@ import usb.util
import serial import serial
import socket import socket
from escpos import * from escpos.escpos import *
from constants import * from escpos.constants import *
from exceptions import * from escpos.exceptions import *
class Usb(Escpos): class Usb(Escpos):
""" Define USB printer """ """ Define USB printer """
@ -44,13 +44,13 @@ class Usb(Escpos):
try: try:
self.device.detach_kernel_driver(0) self.device.detach_kernel_driver(0)
except usb.core.USBError as e: except usb.core.USBError as e:
print "Could not detatch kernel driver: %s" % str(e) print("Could not detatch kernel driver: %s" % str(e))
try: try:
self.device.set_configuration() self.device.set_configuration()
self.device.reset() self.device.reset()
except usb.core.USBError as e: except usb.core.USBError as e:
print "Could not set configuration: %s" % str(e) print("Could not set configuration: %s" % str(e))
def _raw(self, msg): def _raw(self, msg):
@ -88,9 +88,9 @@ class Serial(Escpos):
self.device = serial.Serial(port=self.devfile, baudrate=self.baudrate, bytesize=self.bytesize, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=self.timeout, dsrdtr=True) self.device = serial.Serial(port=self.devfile, baudrate=self.baudrate, bytesize=self.bytesize, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=self.timeout, dsrdtr=True)
if self.device is not None: if self.device is not None:
print "Serial printer enabled" print("Serial printer enabled")
else: else:
print "Unable to open serial printer on: %s" % self.devfile print("Unable to open serial printer on: %s" % self.devfile)
def _raw(self, msg): def _raw(self, msg):
@ -124,7 +124,7 @@ class Network(Escpos):
self.device.connect((self.host, self.port)) self.device.connect((self.host, self.port))
if self.device is None: if self.device is None:
print "Could not open socket for %s" % self.host print("Could not open socket for %s" % self.host)
def _raw(self, msg): def _raw(self, msg):
@ -153,7 +153,7 @@ class File(Escpos):
self.device = open(self.devfile, "wb") self.device = open(self.devfile, "wb")
if self.device is None: if self.device is None:
print "Could not open the specified file %s" % self.devfile print("Could not open the specified file %s" % self.devfile)
def _raw(self, msg): def _raw(self, msg):

9
escpos/utils.py Normal file
View File

@ -0,0 +1,9 @@
try:
bytes.fromhex
def hex2bytes(hex_string):
return bytes.fromhex(hex_string)
except:
def hex2bytes(hex_string):
return hex_string.decode('hex')

View File

@ -1,5 +1,6 @@
#!/usr/bin/python #!/usr/bin/python
from setuptools import setup
from distutils.core import setup from distutils.core import setup
setup( setup(