From 4daece43e715b11e951cf74657ca32a6106eda0b Mon Sep 17 00:00:00 2001 From: Thomas van den Berg Date: Wed, 27 Feb 2013 04:27:04 -0800 Subject: [PATCH 1/8] Initial commit --- .gitignore | 35 +++++++++++++++++++++++++++++++++++ README.md | 4 ++++ 2 files changed, 39 insertions(+) create mode 100644 .gitignore create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d2d6f36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +*.py[cod] + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject diff --git a/README.md b/README.md new file mode 100644 index 0000000..1f4681e --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +python-escpos +============= + +Fork of the python-escpos project for printing to POS printers. \ No newline at end of file From a6c72adc584b69be3c30980989c9f489972a6de8 Mon Sep 17 00:00:00 2001 From: Thomas van den Berg Date: Wed, 27 Feb 2013 13:31:18 +0100 Subject: [PATCH 2/8] copy files from google code repo --- .gitignore | 35 +----- README.md | 91 ++++++++++++++- escpos/__init__.py | 1 + escpos/constants.py | 53 +++++++++ escpos/escpos.py | 255 +++++++++++++++++++++++++++++++++++++++++++ escpos/exceptions.py | 80 ++++++++++++++ escpos/printer.py | 136 +++++++++++++++++++++++ 7 files changed, 614 insertions(+), 37 deletions(-) create mode 100644 escpos/__init__.py create mode 100644 escpos/constants.py create mode 100644 escpos/escpos.py create mode 100644 escpos/exceptions.py create mode 100644 escpos/printer.py diff --git a/.gitignore b/.gitignore index d2d6f36..05ea79d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,2 @@ *.py[cod] - -# C extensions -*.so - -# Packages -*.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg -lib -lib64 - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox -nosetests.xml - -# Translations -*.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 1f4681e..a60323e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,89 @@ -python-escpos -============= +ESCPOS +====== + +Python library to manipulate ESC/POS Printers. + +------------------------------------------------------------------ +1. Dependencies + +In order to start getting access to your printer, you must ensure +you have previously installed the following python modules: + + * pyusb (python-usb) + * PIL (Python Image Library) + +------------------------------------------------------------------ +2. Description + +Python ESC/POS is a library which lets the user have access to all +those printers handled by ESC/POS commands, as defined by Epson, +from a Python application. + +The standard usage is send raw text to the printer, but in also +helps the user to enhance the experience with those printers by +facilitating the bar code printing in many different standards, +as well as manipulating images so they can be printed as brand +logo or any other usage images migh have. + +Text can be aligned/justified and fonts can be changed by size, +type and weight. + +Also, this module handles some hardware functionalities like, cut +paper, carrier return, printer reset and others concerned to the +carriage alignment. + +------------------------------------------------------------------ +3. Define your printer + +Before start create your Python ESC/POS printer instance, you must +see at your system for the printer parameters. This is done with +the 'lsusb' command. + +First run the command to look for the "Vendor ID" and "Product ID", +then write down the values, these values are displayed just before +the name of the device with the following format: + + xxxx:xxxx + +Example: + Bus 002 Device 001: ID 1a2b:1a2b Device name + +Write down the the values in question, then issue the following +command so you can get the "Interface" number and "End Point" + + lsusb -vvv -d xxxx:xxxx | grep iInterface + lsusb -vvv -d xxxx:xxxx | grep bEndpointAddress | grep OUT + +The first command will yields the "Interface" number that must +be handy to have and the second yields the "Output Endpoint" +address. + +By default the "Interface" number is "0" and the "Output Endpoint" +address is "0x82", if you have other values then you can define +with your instance. + +------------------------------------------------------------------ +4. Define your instance + +The following example shows how to initialize the Epson TM-TI88IV +*** NOTE: Always finish the sequence with Epson.cut() otherwise + you will endup with weird chars being printed. + + from escpos import * + + """ Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """ + Epson = escpos.Escpos(0x04b8,0x0202,0) + Epson.text("Hello World") + Epson.image("logo.gif") + Epson.barcode + Epson.barcode('1324354657687','EAN13',64,2,'','') + Epson.cut() + +------------------------------------------------------------------ +5. Links + +Please visit project homepage at: +http://repo.bashlinux.com/projects/escpos.html + +Manuel F Martinez -Fork of the python-escpos project for printing to POS printers. \ No newline at end of file diff --git a/escpos/__init__.py b/escpos/__init__.py new file mode 100644 index 0000000..22a5af6 --- /dev/null +++ b/escpos/__init__.py @@ -0,0 +1 @@ +__all__ = ["constants","escpos","exceptions","printer"] diff --git a/escpos/constants.py b/escpos/constants.py new file mode 100644 index 0000000..c39f6ce --- /dev/null +++ b/escpos/constants.py @@ -0,0 +1,53 @@ +""" ESC/POS Commands (Constants) """ + +# Feed control sequences +CTL_LF = '\x0a' # Print and line feed +CTL_FF = '\x0c' # Form feed +CTL_CR = '\x0d' # Carriage return +CTL_HT = '\x09' # Horizontal tab +CTL_VT = '\x0b' # Vertical tab +# Printer hardware +HW_INIT = '\x1b\x40' # Clear data in buffer and reset modes +HW_SELECT = '\x1b\x3d\x01' # Printer select +HW_RESET = '\x1b\x3f\x0a\x00' # Reset printer hardware +# Cash Drawer +CD_KICK_2 = '\x1b\x70\x00' # Sends a pulse to pin 2 [] +CD_KICK_5 = '\x1b\x70\x01' # Sends a pulse to pin 5 [] +# Paper +PAPER_FULL_CUT = '\x1d\x56\x00' # Full cut paper +PAPER_PART_CUT = '\x1d\x56\x01' # Partial cut paper +# Text format +TXT_NORMAL = '\x1b\x21\x00' # Normal text +TXT_2HEIGHT = '\x1b\x21\x10' # Double height text +TXT_2WIDTH = '\x1b\x21\x20' # Double width text +TXT_UNDERL_OFF = '\x1b\x2d\x00' # Underline font OFF +TXT_UNDERL_ON = '\x1b\x2d\x01' # Underline font 1-dot ON +TXT_UNDERL2_ON = '\x1b\x2d\x02' # Underline font 2-dot ON +TXT_BOLD_OFF = '\x1b\x45\x00' # Bold font OFF +TXT_BOLD_ON = '\x1b\x45\x01' # Bold font ON +TXT_FONT_A = '\x1b\x4d\x00' # Font type A +TXT_FONT_B = '\x1b\x4d\x01' # Font type B +TXT_ALIGN_LT = '\x1b\x61\x00' # Left justification +TXT_ALIGN_CT = '\x1b\x61\x01' # Centering +TXT_ALIGN_RT = '\x1b\x61\x02' # Right justification +# Barcode format +BARCODE_TXT_OFF = '\x1d\x48\x00' # HRI barcode chars OFF +BARCODE_TXT_ABV = '\x1d\x48\x01' # HRI barcode chars above +BARCODE_TXT_BLW = '\x1d\x48\x02' # HRI barcode chars below +BARCODE_TXT_BTH = '\x1d\x48\x03' # HRI barcode chars both above and below +BARCODE_FONT_A = '\x1d\x66\x00' # Font type A for HRI barcode chars +BARCODE_FONT_B = '\x1d\x66\x01' # Font type B for HRI barcode chars +BARCODE_HEIGHT = '\x1d\x68\x64' # Barcode Height [1-255] +BARCODE_WIDTH = '\x1d\x77\x03' # Barcode Width [2-6] +BARCODE_UPC_A = '\x1d\x6b\x00' # Barcode type UPC-A +BARCODE_UPC_E = '\x1d\x6b\x01' # Barcode type UPC-E +BARCODE_EAN13 = '\x1d\x6b\x02' # Barcode type EAN13 +BARCODE_EAN8 = '\x1d\x6b\x03' # Barcode type EAN8 +BARCODE_CODE39 = '\x1d\x6b\x04' # Barcode type CODE39 +BARCODE_ITF = '\x1d\x6b\x05' # Barcode type ITF +BARCODE_NW7 = '\x1d\x6b\x06' # Barcode type NW7 +# Image format +S_RASTER_N = '\x1d\x76\x30\x00' # Set raster image normal size +S_RASTER_2W = '\x1d\x76\x30\x01' # Set raster image double width +S_RASTER_2H = '\x1d\x76\x30\x02' # Set raster image double height +S_RASTER_Q = '\x1d\x76\x30\x03' # Set raster image quadruple diff --git a/escpos/escpos.py b/escpos/escpos.py new file mode 100644 index 0000000..3267125 --- /dev/null +++ b/escpos/escpos.py @@ -0,0 +1,255 @@ +#!/usr/bin/python +''' +@author: Manuel F Martinez +@organization: Bashlinux +@copyright: Copyright (c) 2012 Bashlinux +@license: GPL +''' + +import Image +import time + +from constants import * +from exceptions import * + +class Escpos: + """ ESC/POS Printer object """ + device = None + + + def _check_image_size(self, size): + """ Check and fix the size of the image to 32 bits """ + if size % 32 == 0: + return (0, 0) + else: + image_border = 32 - (size % 32) + if (image_border % 2) == 0: + return (image_border / 2, image_border / 2) + else: + return (image_border / 2, (image_border / 2) + 1) + + + def _print_image(self, line, size): + """ Print formatted image """ + i = 0 + cont = 0 + buffer = "" + + self._raw(S_RASTER_N) + buffer = "%02X%02X%02X%02X" % (((size[0]/size[1])/8), 0, size[1], 0) + self._raw(buffer.decode('hex')) + buffer = "" + + 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 image(self, img): + """ Parse image and prepare it to a printable format """ + pixels = [] + pix_line = "" + im_left = "" + im_right = "" + switch = 0 + img_size = [ 0, 0 ] + + im_open = Image.open(img) + im = im_open.convert("RGB") + + if im.size[0] > 512: + print "WARNING: Image is wider than 512 and could be truncated at print time " + if im.size[1] > 255: + raise ImageSizeError() + + im_border = self._check_image_size(im.size[0]) + for i in range(im_border[0]): + im_left += "0" + for i in range(im_border[1]): + im_right += "0" + + for y in range(im.size[1]): + img_size[1] += 1 + 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 barcode(self, code, bc, width, height, pos, font): + """ Print Barcode """ + # Align Bar Code() + self._raw(TXT_ALIGN_CT) + # Height + if height >=2 or height <=6: + self._raw(BARCODE_HEIGHT) + else: + raise BarcodeSizeError() + # Width + if width >= 1 or width <=255: + self._raw(BARCODE_WIDTH) + else: + raise BarcodeSizeError() + # Font + if font.upper() == "B": + self._raw(BARCODE_FONT_B) + else: # DEFAULT FONT: A + self._raw(BARCODE_FONT_A) + # Position + if pos.upper() == "OFF": + self._raw(BARCODE_TXT_OFF) + elif pos.upper() == "BOTH": + self._raw(BARCODE_TXT_BTH) + elif pos.upper() == "ABOVE": + self._raw(BARCODE_TXT_ABV) + else: # DEFAULT POSITION: BELOW + self._raw(BARCODE_TXT_BLW) + # Type + if bc.upper() == "UPC-A": + self._raw(BARCODE_UPC_A) + elif bc.upper() == "UPC-E": + self._raw(BARCODE_UPC_E) + elif bc.upper() == "EAN13": + self._raw(BARCODE_EAN13) + elif bc.upper() == "EAN8": + self._raw(BARCODE_EAN8) + elif bc.upper() == "CODE39": + self._raw(BARCODE_CODE39) + elif bc.upper() == "ITF": + self._raw(BARCODE_ITF) + elif bc.upper() == "NW7": + self._raw(BARCODE_NW7) + else: + raise BarcodeTypeError() + # Print Code + if code: + self._raw(code) + else: + raise exception.BarcodeCodeError() + + + def text(self, txt): + """ Print alpha-numeric text """ + if txt: + self._raw(txt) + else: + raise TextError() + + + def set(self, align='left', font='a', type='normal', width=1, height=1): + """ Set text properties """ + # 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) + # Font + if font.upper() == "B": + self._raw(TXT_FONT_B) + else: # DEFAULT FONT: A + self._raw(TXT_FONT_A) + # Type + if type.upper() == "B": + self._raw(TXT_BOLD_ON) + self._raw(TXT_UNDERL_OFF) + elif type.upper() == "U": + self._raw(TXT_BOLD_OFF) + self._raw(TXT_UNDERL_ON) + elif type.upper() == "U2": + self._raw(TXT_BOLD_OFF) + self._raw(TXT_UNDERL2_ON) + elif type.upper() == "BU": + self._raw(TXT_BOLD_ON) + self._raw(TXT_UNDERL_ON) + elif type.upper() == "BU2": + self._raw(TXT_BOLD_ON) + self._raw(TXT_UNDERL2_ON) + elif type.upper == "NORMAL": + self._raw(TXT_BOLD_OFF) + self._raw(TXT_UNDERL_OFF) + # Width + if width == 2 and height != 2: + self._raw(TXT_NORMAL) + self._raw(TXT_2WIDTH) + elif height == 2 and width != 2: + self._raw(TXT_NORMAL) + self._raw(TXT_2HEIGHT) + elif height == 2 and width == 2: + self._raw(TXT_2WIDTH) + self._raw(TXT_2HEIGHT) + else: # DEFAULT SIZE: NORMAL + self._raw(TXT_NORMAL) + + + def cut(self, mode=''): + """ Cut paper """ + # Fix the size between last line and cut + # TODO: handle this with a line feed + self._raw("\n\n\n\n\n\n") + if mode.upper() == "PART": + self._raw(PAPER_PART_CUT) + else: # DEFAULT MODE: FULL CUT + self._raw(PAPER_FULL_CUT) + + + def cashdraw(self, pin): + """ Send pulse to kick the cash drawer """ + if pin == 2: + self._raw(CD_KICK_2) + elif pin == 5: + self._raw(CD_KICK_5) + else: + raise CashDrawerError() + + + def hw(self, hw): + """ Hardware operations """ + if hw.upper() == "INIT": + self._raw(HW_INIT) + elif hw.upper() == "SELECT": + self._raw(HW_SELECT) + elif hw.upper() == "RESET": + self._raw(HW_RESET) + else: # DEFAULT: DOES NOTHING + pass + + + def control(self, ctl): + """ Feed control sequences """ + if ctl.upper() == "LF": + self._raw(CTL_LF) + elif ctl.upper() == "FF": + self._raw(CTL_FF) + elif ctl.upper() == "CR": + self._raw(CTL_CR) + elif ctl.upper() == "HT": + self._raw(CTL_HT) + elif ctl.upper() == "VT": + self._raw(CTL_VT) diff --git a/escpos/exceptions.py b/escpos/exceptions.py new file mode 100644 index 0000000..adbe648 --- /dev/null +++ b/escpos/exceptions.py @@ -0,0 +1,80 @@ +""" ESC/POS Exceptions classes """ + +import os + +class Error(Exception): + """ Base class for ESC/POS errors """ + def __init__(self, msg, status=None): + Exception.__init__(self) + self.msg = msg + self.resultcode = 1 + if status is not None: + self.resultcode = status + + def __str__(self): + return self.msg + +# Result/Exit codes +# 0 = success +# 10 = No Barcode type defined +# 20 = Barcode size values are out of range +# 30 = Barcode text not supplied +# 40 = Image height is too large +# 50 = No string supplied to be printed +# 60 = Invalid pin to send Cash Drawer pulse + + +class BarcodeTypeError(Error): + def __init__(self, msg=""): + Error.__init__(self, msg) + self.msg = msg + self.resultcode = 10 + + def __str__(self): + return "No Barcode type is defined" + +class BarcodeSizeError(Error): + def __init__(self, msg=""): + Error.__init__(self, msg) + self.msg = msg + self.resultcode = 20 + + def __str__(self): + return "Barcode size is out of range" + +class BarcodeCodeError(Error): + def __init__(self, msg=""): + Error.__init__(self, msg) + self.msg = msg + self.resultcode = 30 + + def __str__(self): + return "Code was not supplied" + +class ImageSizeError(Error): + def __init__(self, msg=""): + Error.__init__(self, msg) + self.msg = msg + self.resultcode = 40 + + def __str__(self): + return "Image height is longer than 255px and can't be printed" + +class TextError(Error): + def __init__(self, msg=""): + Error.__init__(self, msg) + self.msg = msg + self.resultcode = 50 + + def __str__(self): + return "Text string must be supplied to the text() method" + + +class CashDrawerError(Error): + def __init__(self, msg=""): + Error.__init__(self, msg) + self.msg = msg + self.resultcode = 60 + + def __str__(self): + return "Valid pin must be set to send pulse" diff --git a/escpos/printer.py b/escpos/printer.py new file mode 100644 index 0000000..dd5aa93 --- /dev/null +++ b/escpos/printer.py @@ -0,0 +1,136 @@ +#!/usr/bin/python +''' +@author: Manuel F Martinez +@organization: Bashlinux +@copyright: Copyright (c) 2012 Bashlinux +@license: GPL +''' + +import usb.core +import usb.util +import serial +import socket + +from escpos import * +from constants import * +from exceptions import * + +class Usb(Escpos): + """ Define USB printer """ + + def __init__(self, idVendor, idProduct, interface=0, in_ep=0x82, out_ep=0x01): + """ + @param idVendor : Vendor ID + @param idProduct : Product ID + @param interface : USB device interface + @param in_ep : Input end point + @param out_ep : Output end point + """ + self.idVendor = idVendor + self.idProduct = idProduct + self.interface = interface + self.in_ep = in_ep + self.out_ep = out_ep + self.open() + + + def open(self): + """ Search device on USB tree and set is as escpos device """ + self.device = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct) + if self.device is None: + print "Cable isn't plugged in" + + if self.device.is_kernel_driver_active(0): + try: + self.device.detach_kernel_driver(0) + except usb.core.USBError as e: + print "Could not detatch kernel driver: %s" % str(e) + + try: + self.device.set_configuration() + self.device.reset() + except usb.core.USBError as e: + print "Could not set configuration: %s" % str(e) + + + def _raw(self, msg): + """ Print any command sent in raw format """ + self.device.write(self.out_ep, msg, self.interface) + + + def __del__(self): + """ Release USB interface """ + if self.device: + usb.util.dispose_resources(self.device) + self.device = None + + + +class Serial(Escpos): + """ Define Serial printer """ + + def __init__(self, devfile="/dev/ttyS0", baudrate=9600, bytesize=8, timeout=1): + """ + @param devfile : Device file under dev filesystem + @param baudrate : Baud rate for serial transmission + @param bytesize : Serial buffer size + @param timeout : Read/Write timeout + """ + self.devfile = devfile + self.baudrate = baudrate + self.bytesize = bytesize + self.timeout = timeout + self.open() + + + def open(self): + """ Setup serial port and set is as escpos device """ + 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: + print "Serial printer enabled" + else: + print "Unable to open serial printer on: %s" % self.devfile + + + def _raw(self, msg): + """ Print any command sent in raw format """ + self.device.write(msg) + + + def __del__(self): + """ Close Serial interface """ + if self.device is not None: + self.device.close() + + + +class Network(Escpos): + """ Define Network printer """ + + def __init__(self,host,port=9100): + """ + @param host : Printer's hostname or IP address + @param port : Port to write to + """ + self.host = host + self.port = port + self.open() + + + def open(self): + """ Open TCP socket and set it as escpos device """ + self.device = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.device.connect((self.host, self.port)) + + if self.device is None: + print "Could not open socket for %s" % self.host + + + def _raw(self, msg): + self.device.send(msg) + + + def __del__(self): + """ Close TCP connection """ + self.device.close() From f273b51917bda0d7f4da7af8d125d6f85d807bb4 Mon Sep 17 00:00:00 2001 From: Thomas van den Berg Date: Wed, 27 Feb 2013 13:32:43 +0100 Subject: [PATCH 3/8] markdownify readme --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index a60323e..408387b 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ ESCPOS Python library to manipulate ESC/POS Printers. ------------------------------------------------------------------- 1. Dependencies +------------------------------------------------------------------ In order to start getting access to your printer, you must ensure you have previously installed the following python modules: @@ -12,8 +12,8 @@ you have previously installed the following python modules: * pyusb (python-usb) * PIL (Python Image Library) ------------------------------------------------------------------- 2. Description +------------------------------------------------------------------ Python ESC/POS is a library which lets the user have access to all those printers handled by ESC/POS commands, as defined by Epson, @@ -32,8 +32,8 @@ Also, this module handles some hardware functionalities like, cut paper, carrier return, printer reset and others concerned to the carriage alignment. ------------------------------------------------------------------- 3. Define your printer +------------------------------------------------------------------ Before start create your Python ESC/POS printer instance, you must see at your system for the printer parameters. This is done with @@ -43,7 +43,7 @@ First run the command to look for the "Vendor ID" and "Product ID", then write down the values, these values are displayed just before the name of the device with the following format: - xxxx:xxxx + xxxx:xxxx Example: Bus 002 Device 001: ID 1a2b:1a2b Device name @@ -51,8 +51,8 @@ Example: Write down the the values in question, then issue the following command so you can get the "Interface" number and "End Point" - lsusb -vvv -d xxxx:xxxx | grep iInterface - lsusb -vvv -d xxxx:xxxx | grep bEndpointAddress | grep OUT + lsusb -vvv -d xxxx:xxxx | grep iInterface + lsusb -vvv -d xxxx:xxxx | grep bEndpointAddress | grep OUT The first command will yields the "Interface" number that must be handy to have and the second yields the "Output Endpoint" @@ -62,25 +62,25 @@ By default the "Interface" number is "0" and the "Output Endpoint" address is "0x82", if you have other values then you can define with your instance. ------------------------------------------------------------------- 4. Define your instance +------------------------------------------------------------------ The following example shows how to initialize the Epson TM-TI88IV *** NOTE: Always finish the sequence with Epson.cut() otherwise you will endup with weird chars being printed. - from escpos import * + from escpos import * + + """ Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """ + Epson = escpos.Escpos(0x04b8,0x0202,0) + Epson.text("Hello World") + Epson.image("logo.gif") + Epson.barcode + Epson.barcode('1324354657687','EAN13',64,2,'','') + Epson.cut() - """ Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """ - Epson = escpos.Escpos(0x04b8,0x0202,0) - Epson.text("Hello World") - Epson.image("logo.gif") - Epson.barcode - Epson.barcode('1324354657687','EAN13',64,2,'','') - Epson.cut() - ------------------------------------------------------------------- 5. Links +------------------------------------------------------------------ Please visit project homepage at: http://repo.bashlinux.com/projects/escpos.html From 126dde8a7e04525df5b6249d37a959d2699ebd99 Mon Sep 17 00:00:00 2001 From: Thomas van den Berg Date: Wed, 27 Feb 2013 13:42:32 +0100 Subject: [PATCH 4/8] add support for printing PIL image objects directly --- escpos/escpos.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/escpos/escpos.py b/escpos/escpos.py index 3267125..d7ea18e 100644 --- a/escpos/escpos.py +++ b/escpos/escpos.py @@ -8,6 +8,7 @@ import Image import time +import os from constants import * from exceptions import * @@ -60,8 +61,10 @@ class Escpos: switch = 0 img_size = [ 0, 0 ] - im_open = Image.open(img) - im = im_open.convert("RGB") + if isinstance(img, Image): + im = img.convert("RGB") + else: + im = Image.open(img).convert("RGB") if im.size[0] > 512: print "WARNING: Image is wider than 512 and could be truncated at print time " From b4f8de002ccc8e87c0233378496303514697f40f Mon Sep 17 00:00:00 2001 From: Thomas van den Berg Date: Wed, 27 Feb 2013 15:10:34 +0100 Subject: [PATCH 5/8] add function to print full images including resizing --- escpos/__init__.py | 1 + escpos/escpos.py | 40 +++++++++++++++++++++++++++++++++++++++- escpos/exceptions.py | 3 +++ escpos/printer.py | 2 +- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/escpos/__init__.py b/escpos/__init__.py index 22a5af6..956f246 100644 --- a/escpos/__init__.py +++ b/escpos/__init__.py @@ -1 +1,2 @@ +import constants, escpos, exceptions, printer __all__ = ["constants","escpos","exceptions","printer"] diff --git a/escpos/escpos.py b/escpos/escpos.py index d7ea18e..caf4ef3 100644 --- a/escpos/escpos.py +++ b/escpos/escpos.py @@ -7,8 +7,10 @@ ''' import Image +import PIL.Image import time import os +import operator from constants import * from exceptions import * @@ -51,6 +53,42 @@ class Escpos: buffer = "" cont = 0 + def fullimage(self, img, max_height=860, width=512, histeq=True): + """ Resizes and prints an arbitrarily sized image """ + if isinstance(img, (Image.Image, PIL.Image.Image)): + im = img.convert("RGB") + else: + im = Image.open(img).convert("RGB") + + if histeq: + # Histogram equaliztion + h = im.histogram() + lut = [] + for b in range(0, len(h), 256): + # step size + step = reduce(operator.add, h[b:b+256]) / 255 + # create equalization lookup table + n = 0 + for i in range(256): + lut.append(n / step) + n = n + h[i+b] + im = im.point(lut) + + ratio = float(width) / im.size[0] + newheight = int(ratio * im.size[1]) + + # Resize the image + im = im.resize((width, newheight), Image.ANTIALIAS) + if im.size[1] > max_height: + im = im.crop((0, 0, im.size[0], max_height)) + + # Divide into bands + bandsize = 255 + current = 0 + while current < im.size[1]: + self.image(im.crop((0, current, width, current + bandsize))) + current += bandsize + def image(self, img): """ Parse image and prepare it to a printable format """ @@ -61,7 +99,7 @@ class Escpos: switch = 0 img_size = [ 0, 0 ] - if isinstance(img, Image): + if isinstance(img, (Image.Image, PIL.Image.Image)): im = img.convert("RGB") else: im = Image.open(img).convert("RGB") diff --git a/escpos/exceptions.py b/escpos/exceptions.py index adbe648..caad7a8 100644 --- a/escpos/exceptions.py +++ b/escpos/exceptions.py @@ -14,6 +14,9 @@ class Error(Exception): def __str__(self): return self.msg +class NotFoundError(Error): + """ Device wasn't found (not plugged in) """ + # Result/Exit codes # 0 = success # 10 = No Barcode type defined diff --git a/escpos/printer.py b/escpos/printer.py index dd5aa93..7ecd5d0 100644 --- a/escpos/printer.py +++ b/escpos/printer.py @@ -38,7 +38,7 @@ class Usb(Escpos): """ Search device on USB tree and set is as escpos device """ self.device = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct) if self.device is None: - print "Cable isn't plugged in" + raise NotFoundError("Device not found or cable not plugged in.") if self.device.is_kernel_driver_active(0): try: From 5db192751f5e13b96292aba5219e43764bd01a25 Mon Sep 17 00:00:00 2001 From: Thomas van den Berg Date: Wed, 27 Feb 2013 23:17:56 +0100 Subject: [PATCH 6/8] remove import PIL --- escpos/escpos.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/escpos/escpos.py b/escpos/escpos.py index caf4ef3..512a99e 100644 --- a/escpos/escpos.py +++ b/escpos/escpos.py @@ -7,7 +7,6 @@ ''' import Image -import PIL.Image import time import os import operator @@ -55,7 +54,7 @@ class Escpos: def fullimage(self, img, max_height=860, width=512, histeq=True): """ Resizes and prints an arbitrarily sized image """ - if isinstance(img, (Image.Image, PIL.Image.Image)): + if isinstance(img, Image.Image): im = img.convert("RGB") else: im = Image.open(img).convert("RGB") @@ -99,7 +98,7 @@ class Escpos: switch = 0 img_size = [ 0, 0 ] - if isinstance(img, (Image.Image, PIL.Image.Image)): + if isinstance(img, Image.Image): im = img.convert("RGB") else: im = Image.open(img).convert("RGB") From 7d4ef744de5e74cdf4d765aa3ba74120030be600 Mon Sep 17 00:00:00 2001 From: Thomas van den Berg Date: Wed, 27 Feb 2013 23:18:49 +0100 Subject: [PATCH 7/8] fix band printing --- escpos/escpos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/escpos/escpos.py b/escpos/escpos.py index 512a99e..906c985 100644 --- a/escpos/escpos.py +++ b/escpos/escpos.py @@ -85,7 +85,7 @@ class Escpos: bandsize = 255 current = 0 while current < im.size[1]: - self.image(im.crop((0, current, width, current + bandsize))) + self.image(im.crop((0, current, width, min(im.size[1], current + bandsize)))) current += bandsize From 84e316ac321ac13a3b39c7866de196a40215b0a4 Mon Sep 17 00:00:00 2001 From: Thomas van den Berg Date: Tue, 25 Jun 2013 22:32:14 +0300 Subject: [PATCH 8/8] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 408387b..c912612 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ The following example shows how to initialize the Epson TM-TI88IV Epson = escpos.Escpos(0x04b8,0x0202,0) Epson.text("Hello World") Epson.image("logo.gif") + Epson.fullimage("a.really.large.image.png") Epson.barcode Epson.barcode('1324354657687','EAN13',64,2,'','') Epson.cut()