summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Adafruit_Thermal.py735
1 files changed, 735 insertions, 0 deletions
diff --git a/Adafruit_Thermal.py b/Adafruit_Thermal.py
new file mode 100644
index 0000000..551e251
--- /dev/null
+++ b/Adafruit_Thermal.py
@@ -0,0 +1,735 @@
+#*************************************************************************
+# This is a Python library for the Adafruit Thermal Printer.
+# Pick one up at --> http://www.adafruit.com/products/597
+# These printers use TTL serial to communicate, 2 pins are required.
+# IMPORTANT: On 3.3V systems (e.g. Raspberry Pi), use a 10K resistor on
+# the RX pin (TX on the printer, green wire), or simply leave unconnected.
+#
+# Adafruit invests time and resources providing this open source code.
+# Please support Adafruit and open-source hardware by purchasing products
+# from Adafruit!
+#
+# Written by Limor Fried/Ladyada for Adafruit Industries.
+# Python port by Phil Burgess for Adafruit Industries.
+# MIT license, all text above must be included in any redistribution.
+#*************************************************************************
+
+# This is pretty much a 1:1 direct Python port of the Adafruit_Thermal
+# library for Arduino. All methods use the same naming conventions as the
+# Arduino library, with only slight changes in parameter behavior where
+# needed. This should simplify porting existing Adafruit_Thermal-based
+# printer projects to Raspberry Pi, BeagleBone, etc. See printertest.py
+# for an example.
+#
+# One significant change is the addition of the printImage() function,
+# which ties this to the Python Imaging Library and opens the door to a
+# lot of cool graphical stuff!
+#
+# TO DO:
+# - Might use standard ConfigParser library to put thermal calibration
+# settings in a global configuration file (rather than in the library).
+# - Make this use proper Python library installation procedure.
+# - Trap errors properly. Some stuff just falls through right now.
+# - Add docstrings throughout!
+
+from serial import Serial
+import time
+import sys
+import math
+
+class Adafruit_Thermal(Serial):
+
+ resumeTime = 0.0
+ byteTime = 0.0
+ dotPrintTime = 0.0
+ dotFeedTime = 0.0
+ prevByte = '\n'
+ column = 0
+ maxColumn = 32
+ charHeight = 24
+ lineSpacing = 8
+ barcodeHeight = 50
+ printMode = 0
+ defaultHeatTime = 120
+ firmwareVersion = 268
+ writeToStdout = False
+
+ def __init__(self, *args, **kwargs):
+ # NEW BEHAVIOR: if no parameters given, output is written
+ # to stdout, to be piped through 'lp -o raw' (old behavior
+ # was to use default port & baud rate).
+ baudrate = 19200
+ if len(args) == 0:
+ self.writeToStdout = True
+ if len(args) == 1:
+ # If only port is passed, use default baud rate.
+ args = [ args[0], baudrate ]
+ elif len(args) == 2:
+ # If both passed, use those values.
+ baudrate = args[1]
+
+ # Firmware is assumed version 2.68. Can override this
+ # with the 'firmware=X' argument, where X is the major
+ # version number * 100 + the minor version number (e.g.
+ # pass "firmware=264" for version 2.64.
+ self.firmwareVersion = kwargs.get('firmware', 268)
+
+ if self.writeToStdout is False:
+ # Calculate time to issue one byte to the printer.
+ # 11 bits (not 8) to accommodate idle, start and
+ # stop bits. Idle time might be unnecessary, but
+ # erring on side of caution here.
+ self.byteTime = 11.0 / float(baudrate)
+
+ Serial.__init__(self, *args, **kwargs)
+
+ # Remainder of this method was previously in begin()
+
+ # The printer can't start receiving data immediately
+ # upon power up -- it needs a moment to cold boot
+ # and initialize. Allow at least 1/2 sec of uptime
+ # before printer can receive data.
+ self.timeoutSet(0.5)
+
+ self.wake()
+ self.reset()
+
+ # Description of print settings from p. 23 of manual:
+ # ESC 7 n1 n2 n3 Setting Control Parameter Command
+ # Decimal: 27 55 n1 n2 n3
+ # max heating dots, heating time, heating interval
+ # n1 = 0-255 Max heat dots, Unit (8dots), Default: 7 (64 dots)
+ # n2 = 3-255 Heating time, Unit (10us), Default: 80 (800us)
+ # n3 = 0-255 Heating interval, Unit (10us), Default: 2 (20us)
+ # The more max heating dots, the more peak current
+ # will cost when printing, the faster printing speed.
+ # The max heating dots is 8*(n1+1). The more heating
+ # time, the more density, but the slower printing
+ # speed. If heating time is too short, blank page
+ # may occur. The more heating interval, the more
+ # clear, but the slower printing speed.
+
+ heatTime = kwargs.get('heattime', self.defaultHeatTime)
+ self.writeBytes(
+ 27, # Esc
+ 55, # 7 (print settings)
+ 11, # Heat dots
+ heatTime, # Lib default
+ 40) # Heat interval
+
+ # Description of print density from p. 23 of manual:
+ # DC2 # n Set printing density
+ # Decimal: 18 35 n
+ # D4..D0 of n is used to set the printing density.
+ # Density is 50% + 5% * n(D4-D0) printing density.
+ # D7..D5 of n is used to set the printing break time.
+ # Break time is n(D7-D5)*250us.
+ # (Unsure of default values -- not documented)
+
+ printDensity = 10 # 100%
+ printBreakTime = 2 # 500 uS
+
+ self.writeBytes(
+ 18, # DC2
+ 35, # Print density
+ (printBreakTime << 5) | printDensity)
+ self.dotPrintTime = 0.03
+ self.dotFeedTime = 0.0021
+ else:
+ self.reset() # Inits some vars
+
+ # Because there's no flow control between the printer and computer,
+ # special care must be taken to avoid overrunning the printer's
+ # buffer. Serial output is throttled based on serial speed as well
+ # as an estimate of the device's print and feed rates (relatively
+ # slow, being bound to moving parts and physical reality). After
+ # an operation is issued to the printer (e.g. bitmap print), a
+ # timeout is set before which any other printer operations will be
+ # suspended. This is generally more efficient than using a delay
+ # in that it allows the calling code to continue with other duties
+ # (e.g. receiving or decoding an image) while the printer
+ # physically completes the task.
+
+ # Sets estimated completion time for a just-issued task.
+ def timeoutSet(self, x):
+ self.resumeTime = time.time() + x
+
+ # Waits (if necessary) for the prior task to complete.
+ def timeoutWait(self):
+ if self.writeToStdout is False:
+ while (time.time() - self.resumeTime) < 0: pass
+
+ # Printer performance may vary based on the power supply voltage,
+ # thickness of paper, phase of the moon and other seemingly random
+ # variables. This method sets the times (in microseconds) for the
+ # paper to advance one vertical 'dot' when printing and feeding.
+ # For example, in the default initialized state, normal-sized text
+ # is 24 dots tall and the line spacing is 32 dots, so the time for
+ # one line to be issued is approximately 24 * print time + 8 * feed
+ # time. The default print and feed times are based on a random
+ # test unit, but as stated above your reality may be influenced by
+ # many factors. This lets you tweak the timing to avoid excessive
+ # delays and/or overrunning the printer buffer.
+ def setTimes(self, p, f):
+ # Units are in microseconds for
+ # compatibility with Arduino library
+ self.dotPrintTime = p / 1000000.0
+ self.dotFeedTime = f / 1000000.0
+
+ # 'Raw' byte-writing method
+ def writeBytes(self, *args):
+ if self.writeToStdout:
+ for arg in args:
+ sys.stdout.write(bytes([arg]))
+ else:
+ for arg in args:
+ self.timeoutWait()
+ self.timeoutSet(len(args) * self.byteTime)
+ super(Adafruit_Thermal, self).write(bytes([arg]))
+
+ # Override write() method to keep track of paper feed.
+ def write(self, *data):
+ for i in range(len(data)):
+ c = data[i]
+ if self.writeToStdout:
+ sys.stdout.write(c)
+ continue
+ if c != 0x13:
+ self.timeoutWait()
+ super(Adafruit_Thermal, self).write(c)
+ d = self.byteTime
+ if ((c == '\n') or
+ (self.column == self.maxColumn)):
+ # Newline or wrap
+ if self.prevByte == '\n':
+ # Feed line (blank)
+ d += ((self.charHeight +
+ self.lineSpacing) *
+ self.dotFeedTime)
+ else:
+ # Text line
+ d += ((self.charHeight *
+ self.dotPrintTime) +
+ (self.lineSpacing *
+ self.dotFeedTime))
+ self.column = 0
+ # Treat wrap as newline
+ # on next pass
+ c = '\n'
+ else:
+ self.column += 1
+ self.timeoutSet(d)
+ self.prevByte = c
+
+ # The bulk of this method was moved into __init__,
+ # but this is left here for compatibility with older
+ # code that might get ported directly from Arduino.
+ def begin(self, heatTime=defaultHeatTime):
+ self.writeBytes(
+ 27, # Esc
+ 55, # 7 (print settings)
+ 11, # Heat dots
+ heatTime,
+ 40) # Heat interval
+
+ def reset(self):
+ self.writeBytes(27, 64) # Esc @ = init command
+ self.prevByte = '\n' # Treat as if prior line is blank
+ self.column = 0
+ self.maxColumn = 32
+ self.charHeight = 24
+ self.lineSpacing = 6
+ self.barcodeHeight = 50
+ if self.firmwareVersion >= 264:
+ # Configure tab stops on recent printers
+ self.writeBytes(27, 68) # Set tab stops
+ self.writeBytes( 4, 8, 12, 16) # every 4 columns,
+ self.writeBytes(20, 24, 28, 0) # 0 is end-of-list.
+
+ # Reset text formatting parameters.
+ def setDefault(self):
+ self.online()
+ self.justify('L')
+ self.inverseOff()
+ self.doubleHeightOff()
+ self.setLineHeight(30)
+ self.boldOff()
+ self.underlineOff()
+ self.setBarcodeHeight(50)
+ self.setSize('s')
+ self.setCharset()
+ self.setCodePage()
+
+ def test(self):
+ self.write("Hello world!".encode('cp437', 'ignore'))
+ self.feed(2)
+
+ def testPage(self):
+ self.writeBytes(18, 84)
+ self.timeoutSet(
+ self.dotPrintTime * 24 * 26 +
+ self.dotFeedTime * (6 * 26 + 30))
+
+ def setBarcodeHeight(self, val=50):
+ if val < 1: val = 1
+ self.barcodeHeight = val
+ self.writeBytes(29, 104, val)
+
+ UPC_A = 0
+ UPC_E = 1
+ EAN13 = 2
+ EAN8 = 3
+ CODE39 = 4
+ I25 = 5
+ CODEBAR = 6
+ CODE93 = 7
+ CODE128 = 8
+ CODE11 = 9
+ MSI = 10
+ ITF = 11
+ CODABAR = 12
+
+ def printBarcode(self, text, type):
+
+ newDict = { # UPC codes & values for firmwareVersion >= 264
+ self.UPC_A : 65,
+ self.UPC_E : 66,
+ self.EAN13 : 67,
+ self.EAN8 : 68,
+ self.CODE39 : 69,
+ self.ITF : 70,
+ self.CODABAR : 71,
+ self.CODE93 : 72,
+ self.CODE128 : 73,
+ self.I25 : -1, # NOT IN NEW FIRMWARE
+ self.CODEBAR : -1,
+ self.CODE11 : -1,
+ self.MSI : -1
+ }
+ oldDict = { # UPC codes & values for firmwareVersion < 264
+ self.UPC_A : 0,
+ self.UPC_E : 1,
+ self.EAN13 : 2,
+ self.EAN8 : 3,
+ self.CODE39 : 4,
+ self.I25 : 5,
+ self.CODEBAR : 6,
+ self.CODE93 : 7,
+ self.CODE128 : 8,
+ self.CODE11 : 9,
+ self.MSI : 10,
+ self.ITF : -1, # NOT IN OLD FIRMWARE
+ self.CODABAR : -1
+ }
+
+ if self.firmwareVersion >= 264:
+ n = newDict[type]
+ else:
+ n = oldDict[type]
+ if n == -1: return
+ self.feed(1) # Recent firmware requires this?
+ self.writeBytes(
+ 29, 72, 2, # Print label below barcode
+ 29, 119, 3, # Barcode width
+ 29, 107, n) # Barcode type
+ self.timeoutWait()
+ self.timeoutSet((self.barcodeHeight + 40) * self.dotPrintTime)
+ # Print string
+ if self.firmwareVersion >= 264:
+ # Recent firmware: write length byte + string sans NUL
+ n = len(text)
+ if n > 255: n = 255
+ if self.writeToStdout:
+ sys.stdout.write((chr(n)).encode('cp437', 'ignore'))
+ for i in range(n):
+ sys.stdout.write(text[i].encode('utf-8', 'ignore'))
+ else:
+ super(Adafruit_Thermal, self).write((chr(n)).encode('utf-8', 'ignore'))
+ for i in range(n):
+ super(Adafruit_Thermal,
+ self).write(text[i].encode('utf-8', 'ignore'))
+ else:
+ # Older firmware: write string + NUL
+ if self.writeToStdout:
+ sys.stdout.write(text.encode('utf-8', 'ignore'))
+ else:
+ super(Adafruit_Thermal, self).write(text.encode('utf-8', 'ignore'))
+ self.prevByte = '\n'
+
+ # === Character commands ===
+
+ INVERSE_MASK = (1 << 1) # Not in 2.6.8 firmware (see inverseOn())
+ UPDOWN_MASK = (1 << 2)
+ BOLD_MASK = (1 << 3)
+ DOUBLE_HEIGHT_MASK = (1 << 4)
+ DOUBLE_WIDTH_MASK = (1 << 5)
+ STRIKE_MASK = (1 << 6)
+
+ def setPrintMode(self, mask):
+ self.printMode |= mask
+ self.writePrintMode()
+ if self.printMode & self.DOUBLE_HEIGHT_MASK:
+ self.charHeight = 48
+ else:
+ self.charHeight = 24
+ if self.printMode & self.DOUBLE_WIDTH_MASK:
+ self.maxColumn = 16
+ else:
+ self.maxColumn = 32
+
+ def unsetPrintMode(self, mask):
+ self.printMode &= ~mask
+ self.writePrintMode()
+ if self.printMode & self.DOUBLE_HEIGHT_MASK:
+ self.charHeight = 48
+ else:
+ self.charHeight = 24
+ if self.printMode & self.DOUBLE_WIDTH_MASK:
+ self.maxColumn = 16
+ else:
+ self.maxColumn = 32
+
+ def writePrintMode(self):
+ self.writeBytes(27, 33, self.printMode)
+
+ def normal(self):
+ self.printMode = 0
+ self.writePrintMode()
+
+ def inverseOn(self):
+ if self.firmwareVersion >= 268:
+ self.writeBytes(29, 66, 1)
+ else:
+ self.setPrintMode(self.INVERSE_MASK)
+
+ def inverseOff(self):
+ if self.firmwareVersion >= 268:
+ self.writeBytes(29, 66, 0)
+ else:
+ self.unsetPrintMode(self.INVERSE_MASK)
+
+ def upsideDownOn(self):
+ self.setPrintMode(self.UPDOWN_MASK)
+
+ def upsideDownOff(self):
+ self.unsetPrintMode(self.UPDOWN_MASK)
+
+ def doubleHeightOn(self):
+ self.setPrintMode(self.DOUBLE_HEIGHT_MASK)
+
+ def doubleHeightOff(self):
+ self.unsetPrintMode(self.DOUBLE_HEIGHT_MASK)
+
+ def doubleWidthOn(self):
+ self.setPrintMode(self.DOUBLE_WIDTH_MASK)
+
+ def doubleWidthOff(self):
+ self.unsetPrintMode(self.DOUBLE_WIDTH_MASK)
+
+ def strikeOn(self):
+ self.setPrintMode(self.STRIKE_MASK)
+
+ def strikeOff(self):
+ self.unsetPrintMode(self.STRIKE_MASK)
+
+ def boldOn(self):
+ self.setPrintMode(self.BOLD_MASK)
+
+ def boldOff(self):
+ self.unsetPrintMode(self.BOLD_MASK)
+
+ def justify(self, value):
+ c = value.upper()
+ if c == 'C':
+ pos = 1
+ elif c == 'R':
+ pos = 2
+ else:
+ pos = 0
+ self.writeBytes(0x1B, 0x61, pos)
+
+ # Feeds by the specified number of lines
+ def feed(self, x=1):
+ if self.firmwareVersion >= 264:
+ self.writeBytes(27, 100, x)
+ self.timeoutSet(self.dotFeedTime * self.charHeight)
+ self.prevByte = '\n'
+ self.column = 0
+
+ else:
+ # datasheet claims sending bytes 27, 100, <x> works,
+ # but it feeds much more than that. So, manually:
+ while x > 0:
+ self.write('\n'.encode('cp437', 'ignore'))
+ x -= 1
+
+ # Feeds by the specified number of individual pixel rows
+ def feedRows(self, rows):
+ self.writeBytes(27, 74, rows)
+ self.timeoutSet(rows * dotFeedTime)
+ self.prevByte = '\n'
+ self.column = 0
+
+ def flush(self):
+ self.writeBytes(12) # ASCII FF
+
+ def setSize(self, value):
+ c = value.upper()
+ if c == 'L': # Large: double width and height
+ size = 0x11
+ self.charHeight = 48
+ self.maxColumn = 16
+ elif c == 'M': # Medium: double height
+ size = 0x01
+ self.charHeight = 48
+ self.maxColumn = 32
+ else: # Small: standard width and height
+ size = 0x00
+ self.charHeight = 24
+ self.maxColumn = 32
+
+ self.writeBytes(29, 33, size)
+ prevByte = '\n' # Setting the size adds a linefeed
+
+ # Underlines of different weights can be produced:
+ # 0 - no underline
+ # 1 - normal underline
+ # 2 - thick underline
+ def underlineOn(self, weight=1):
+ if weight > 2: weight = 2
+ self.writeBytes(27, 45, weight)
+
+ def underlineOff(self):
+ self.writeBytes(27, 45, 0)
+
+ def printBitmap(self, w, h, bitmap, LaaT=False):
+ rowBytes = math.floor((w + 7) / 8) # Round up to next byte boundary
+ if rowBytes >= 48:
+ rowBytesClipped = 48 # 384 pixels max width
+ else:
+ rowBytesClipped = rowBytes
+
+ # if LaaT (line-at-a-time) is True, print bitmaps
+ # scanline-at-a-time (rather than in chunks).
+ # This tends to make for much cleaner printing
+ # (no feed gaps) on large images...but has the
+ # opposite effect on small images that would fit
+ # in a single 'chunk', so use carefully!
+ if LaaT: maxChunkHeight = 1
+ else: maxChunkHeight = 255
+
+ i = 0
+ for rowStart in range(0, h, maxChunkHeight):
+ chunkHeight = h - rowStart
+ if chunkHeight > maxChunkHeight:
+ chunkHeight = maxChunkHeight
+
+ # Timeout wait happens here
+ self.writeBytes(18, 42, chunkHeight, rowBytesClipped)
+
+ for y in range(chunkHeight):
+ for x in range(rowBytesClipped):
+ if self.writeToStdout:
+ sys.stdout.write(bytes([bitmap[i]]))
+ else:
+ super(Adafruit_Thermal,
+ self).write(bytes([bitmap[i]]))
+ i += 1
+ i += rowBytes - rowBytesClipped
+ self.timeoutSet(chunkHeight * self.dotPrintTime)
+
+ self.prevByte = '\n'
+
+ # Print Image. Requires Python Imaging Library. This is
+ # specific to the Python port and not present in the Arduino
+ # library. Image will be cropped to 384 pixels width if
+ # necessary, and converted to 1-bit w/diffusion dithering.
+ # For any other behavior (scale, B&W threshold, etc.), use
+ # the Imaging Library to perform such operations before
+ # passing the result to this function.
+ def printImage(self, image_file, LaaT=False):
+ from PIL import Image
+ # image = Image.open(image_file)
+ image = image_file
+ if image.mode != '1':
+ image = image.convert('1')
+
+ width = image.size[0]
+ height = image.size[1]
+ if width > 384:
+ width = 384
+ rowBytes = math.floor((width + 7) / 8)
+ bitmap = bytearray(rowBytes * height)
+ pixels = image.load()
+
+ for y in range(height):
+ n = y * rowBytes
+ x = 0
+ for b in range(rowBytes):
+ sum = 0
+ bit = 128
+ while bit > 0:
+ if x >= width: break
+ if pixels[x, y] == 0:
+ sum |= bit
+ x += 1
+ bit >>= 1
+ bitmap[n + b] = sum
+
+ self.printBitmap(width, height, bitmap, LaaT)
+
+ # Take the printer offline. Print commands sent after this
+ # will be ignored until 'online' is called.
+ def offline(self):
+ self.writeBytes(27, 61, 0)
+
+ # Take the printer online. Subsequent print commands will be obeyed.
+ def online(self):
+ self.writeBytes(27, 61, 1)
+
+ # Put the printer into a low-energy state immediately.
+ def sleep(self):
+ self.sleepAfter(1) # Can't be 0, that means "don't sleep"
+
+ # Put the printer into a low-energy state after
+ # the given number of seconds.
+ def sleepAfter(self, seconds):
+ if self.firmwareVersion >= 264:
+ self.writeBytes(27, 56, seconds & 0xFF, seconds >> 8)
+ else:
+ self.writeBytes(27, 56, seconds)
+
+ def wake(self):
+ self.timeoutSet(0)
+ self.writeBytes(255)
+ if self.firmwareVersion >= 264:
+ time.sleep(0.05) # 50 ms
+ self.writeBytes(27, 118, 0) # Sleep off (important!)
+ else:
+ for i in range(10):
+ self.writeBytes(27)
+ self.timeoutSet(0.1)
+
+ # Empty method, included for compatibility
+ # with existing code ported from Arduino.
+ def listen(self):
+ pass
+
+ # Check the status of the paper using the printers self reporting
+ # ability. Doesn't match the datasheet...
+ # Returns True for paper, False for no paper.
+ def hasPaper(self):
+ if self.firmwareVersion >= 264:
+ self.writeBytes(27, 118, 0)
+ else:
+ self.writeBytes(29, 114, 0)
+ # Bit 2 of response seems to be paper status
+ stat = ord(self.read(1)) & 0b00000100
+ # If set, we have paper; if clear, no paper
+ return stat == 0
+
+ def setLineHeight(self, val=32):
+ if val < 24: val = 24
+ self.lineSpacing = val - 24
+
+ # The printer doesn't take into account the current text
+ # height when setting line height, making this more akin
+ # to inter-line spacing. Default line spacing is 32
+ # (char height of 24, line spacing of 8).
+ self.writeBytes(27, 51, val)
+
+ CHARSET_USA = 0
+ CHARSET_FRANCE = 1
+ CHARSET_GERMANY = 2
+ CHARSET_UK = 3
+ CHARSET_DENMARK1 = 4
+ CHARSET_SWEDEN = 5
+ CHARSET_ITALY = 6
+ CHARSET_SPAIN1 = 7
+ CHARSET_JAPAN = 8
+ CHARSET_NORWAY = 9
+ CHARSET_DENMARK2 = 10
+ CHARSET_SPAIN2 = 11
+ CHARSET_LATINAMERICA = 12
+ CHARSET_KOREA = 13
+ CHARSET_SLOVENIA = 14
+ CHARSET_CROATIA = 14
+ CHARSET_CHINA = 15
+
+ # Alters some chars in ASCII 0x23-0x7E range; see datasheet
+ def setCharset(self, val=0):
+ if val > 15: val = 15
+ self.writeBytes(27, 82, val)
+
+ CODEPAGE_CP437 = 0 # USA, Standard Europe
+ CODEPAGE_KATAKANA = 1
+ CODEPAGE_CP850 = 2 # Multilingual
+ CODEPAGE_CP860 = 3 # Portuguese
+ CODEPAGE_CP863 = 4 # Canadian-French
+ CODEPAGE_CP865 = 5 # Nordic
+ CODEPAGE_WCP1251 = 6 # Cyrillic
+ CODEPAGE_CP866 = 7 # Cyrillic #2
+ CODEPAGE_MIK = 8 # Cyrillic/Bulgarian
+ CODEPAGE_CP755 = 9 # East Europe, Latvian 2
+ CODEPAGE_IRAN = 10
+ CODEPAGE_CP862 = 15 # Hebrew
+ CODEPAGE_WCP1252 = 16 # Latin 1
+ CODEPAGE_WCP1253 = 17 # Greek
+ CODEPAGE_CP852 = 18 # Latin 2
+ CODEPAGE_CP858 = 19 # Multilingual Latin 1 + Euro
+ CODEPAGE_IRAN2 = 20
+ CODEPAGE_LATVIAN = 21
+ CODEPAGE_CP864 = 22 # Arabic
+ CODEPAGE_ISO_8859_1 = 23 # West Europe
+ CODEPAGE_CP737 = 24 # Greek
+ CODEPAGE_WCP1257 = 25 # Baltic
+ CODEPAGE_THAI = 26
+ CODEPAGE_CP720 = 27 # Arabic
+ CODEPAGE_CP855 = 28
+ CODEPAGE_CP857 = 29 # Turkish
+ CODEPAGE_WCP1250 = 30 # Central Europe
+ CODEPAGE_CP775 = 31
+ CODEPAGE_WCP1254 = 32 # Turkish
+ CODEPAGE_WCP1255 = 33 # Hebrew
+ CODEPAGE_WCP1256 = 34 # Arabic
+ CODEPAGE_WCP1258 = 35 # Vietnam
+ CODEPAGE_ISO_8859_2 = 36 # Latin 2
+ CODEPAGE_ISO_8859_3 = 37 # Latin 3
+ CODEPAGE_ISO_8859_4 = 38 # Baltic
+ CODEPAGE_ISO_8859_5 = 39 # Cyrillic
+ CODEPAGE_ISO_8859_6 = 40 # Arabic
+ CODEPAGE_ISO_8859_7 = 41 # Greek
+ CODEPAGE_ISO_8859_8 = 42 # Hebrew
+ CODEPAGE_ISO_8859_9 = 43 # Turkish
+ CODEPAGE_ISO_8859_15 = 44 # Latin 3
+ CODEPAGE_THAI2 = 45
+ CODEPAGE_CP856 = 46
+ CODEPAGE_CP874 = 47
+
+ # Selects alt symbols for 'upper' ASCII values 0x80-0xFF
+ def setCodePage(self, val=0):
+ if val > 47: val = 47
+ self.writeBytes(27, 116, val)
+
+ # Copied from Arduino lib for parity; may not work on all printers
+ def tab(self):
+ self.writeBytes(9)
+ self.column = (self.column + 4) & 0xFC
+
+ # Copied from Arduino lib for parity; may not work on all printers
+ def setCharSpacing(self, spacing):
+ self.writeBytes(27, 32, spacing)
+
+ # Overloading print() in Python pre-3.0 is dirty pool,
+ # but these are here to provide more direct compatibility
+ # with existing code written for the Arduino library.
+ def print(self, *args, **kwargs):
+ for arg in args:
+ self.write((str(arg)).encode('cp437', 'ignore'))
+
+ # For Arduino code compatibility again
+ def println(self, *args, **kwargs):
+ for arg in args:
+ self.write((str(arg)).encode('cp437', 'ignore'))
+ self.write('\n'.encode('cp437', 'ignore'))
+