summaryrefslogtreecommitdiff
path: root/pttarget.py
diff options
context:
space:
mode:
Diffstat (limited to 'pttarget.py')
-rw-r--r--pttarget.py308
1 files changed, 308 insertions, 0 deletions
diff --git a/pttarget.py b/pttarget.py
new file mode 100644
index 0000000..d8f9eb2
--- /dev/null
+++ b/pttarget.py
@@ -0,0 +1,308 @@
+# import cups
+import usb.core, usb.util
+
+import cairo, pango, pangocairo
+import tempfile, os, struct, sys, array, time
+import qrcode
+
+class PTPrinter(object):
+ def __init__(self):
+ pass
+ def setup(self):
+ pass
+ def is_interactive(self):
+ return False
+
+media_types = {
+ 0x01: 'Laminated',
+ 0x02: 'Lettering',
+ 0x03: 'Non-laminated',
+ 0x08: 'AV tape',
+ 0x09: 'HG tape',
+ 0x0a: 'Paper tape, white',
+ 0x0b: 'Address labels',
+}
+codes_err1 = {
+ 0x01: 'No media',
+ 0x02: 'End of media',
+ 0x04: 'Cutter jammed',
+}
+codes_err2 = {
+ 0x01: 'Replace media',
+ 0x02: 'Expansion buffer full',
+ 0x04: 'Transmission error',
+ 0x08: 'Transmission buffer full',
+ 0x10: 'Cover open',
+}
+
+class PTUSB(PTPrinter):
+ def __init__(self, usbdev):
+ self.usbdev = usbdev
+ self.last_status = None
+
+ def __str__(self):
+ return '%s %s (%04x:%04x @ %d.%d)' % (
+ usb.util.get_string(self.usbdev, 256, self.usbdev.iManufacturer),
+ usb.util.get_string(self.usbdev, 256, self.usbdev.iProduct),
+ self.usbdev.idVendor, self.usbdev.idProduct,
+ self.usbdev.bus, self.usbdev.address)
+
+ def setup(self):
+ for cfg in self.usbdev:
+ iface = usb.util.find_descriptor(cfg, bInterfaceClass = 7)
+ if iface is not None:
+ cfg.set()
+ break
+ else:
+ raise RuntimeError, 'no configuration/interface found'
+ usb.util.claim_interface(self.usbdev, iface)
+ iface.set_altsetting()
+ self.iface = iface
+ self.if_rd = iface[0]
+ self.if_wr = iface[1]
+
+ self.if_wr.write(64*'\x00')
+ self.if_wr.write(64*'\x00')
+ self.if_wr.write(64*'\x00')
+ self.if_wr.write(8*'\x00')
+ self.if_wr.write('\x1b@')
+
+ self.refresh_status()
+
+ def refresh_status(self, data = None):
+ if data is None:
+ self.if_wr.write('\x1biS')
+ ar = array.array('B')
+ start = time.time()
+ while len(ar) < 32 or time.time() > start + 0.1:
+ ar += self.if_rd.read(16)
+ if len(ar) != 32:
+ raise IOError, 'failed to get status'
+ if ar[0] != 0x80 or ar[1] != 32:
+ raise IOError, 'invalid status (hdr: %02x %02x)' % (ar[0], ar[1])
+
+ self.err1 = ar[8]
+ self.err2 = ar[9]
+ self.media_width = ar[10]
+ self.media_type = ar[11]
+ self.media_length = ar[17]
+ self.status_type = ar[18]
+ self.phase_type = ar[19]
+ self.phase_bytes = ar[20] * 256 + ar[21]
+ self.notify_num = ar[22]
+ self.last_status = time.time()
+
+ def find_tape(self):
+ for tape in PTTape.tapes:
+ if int(tape.sizemm) == self.media_width:
+ return tape
+ return None
+
+ def check_status(self):
+ ar = self.if_rd.read(16)
+ if len(ar) == 0:
+ return
+ start = time.time()
+ while len(ar) < 32 or time.time() > start + 0.1:
+ ar += self.if_rd.read(16)
+ self.refresh_status(ar)
+
+ def get_errstr(self):
+ errs = []
+ for i in range(0, 8):
+ if self.err1 & (1 << i):
+ errs.append(codes_err1.get(1 << i, '<1:%02x>' % (1 << i)))
+ for i in range(0, 8):
+ if self.err2 & (1 << i):
+ errs.append(codes_err2.get(1 << i, '<2:%02x>' % (1 << i)))
+ if len(errs) == 0:
+ return 'OK'
+ return ', '.join(errs)
+
+ def send(self, data):
+ offset = 0
+ size = len(data)
+ while offset < size:
+ wsize = self.if_wr.wMaxPacketSize
+ if wsize > size - offset:
+ wsize = size - offset
+ written = self.if_wr.write(data[offset:offset+wsize])
+ if written <= 0:
+ raise IOError, 'failed to write'
+ offset += written
+
+ @classmethod
+ def scan(klass):
+ klass.printers = []
+
+ def match(dev):
+ if dev.idVendor != 0x04f9: return False
+ for cfg in dev:
+ if usb.util.find_descriptor(cfg, bInterfaceClass = 7) is not None:
+ return True
+ return False
+
+ printers = usb.core.find(find_all = True, custom_match = match)
+ for printer in printers:
+ klass.printers.append(klass(printer))
+
+# class PTCups(PTPrinter):
+# try:
+# conn = cups.Connection()
+# except RuntimeError:
+# conn = None
+#
+# def __init__(self, pname, printer):
+# self.pname = pname
+# self.printer = printer
+#
+# def __str__(self):
+# return '%s (%s)' % (self.pname, self.printer['device-uri'])
+#
+# def send(self, data):
+# fd, fn = tempfile.mkstemp()
+# os.fdopen(fd, 'w').write(data)
+# PTCups.conn.printFile(self.pname, fn, 'label', {})
+# os.unlink(fn)
+#
+# @classmethod
+# def scan(klass):
+# printers = klass.conn.getPrinters()
+#
+# klass.printers = []
+# for printer in printers:
+# devuri = printers[printer]["device-uri"]
+# if devuri.startswith('usb://Brother/PT'):
+# klass.printers.append(klass(printer, printers[printer]))
+
+class PTLabelData(object):
+ def __init__(self):
+ # margins: '\x1bid\x00\x00' +
+ self.init = '\x1biR\x01' + '\x1biM\x00'
+ self.raw = ''
+ # self.fini = '\x1a' # Z / feed ?
+ self.fini = '\x0c'
+
+ def addpixels(self, raw):
+ row = 'G' + struct.pack('<H', len(raw)) + raw
+ self.raw += row
+
+ def addcairo(self, surface, offset = 0):
+ data = surface.get_data()
+ stride = surface.get_stride()
+ # cairo: byte0:01 byte0:02 byte0:04 ... byte1:01
+ # byteS:01 byteS:02 ...
+ #
+ # ptouch: byte0:80 byteS:80 ...
+ # byte0:40 byteS:40 ...
+
+ pt_height = surface.get_height() + offset
+ if pt_height > 128:
+ pt_height = 128
+ # pt_bytes = (pt_height + 7) / 8
+ pt_bytes = 128 / 8
+
+ for x in range(0, surface.get_width()):
+ pixels = bytearray(pt_bytes * '\x00')
+ for y in range(0, pt_height - offset):
+ py = y + offset
+ cbit = ord(data[y * stride + (x / 8)]) \
+ & (1 << (x & 7))
+ if cbit != 0:
+ pixels[py / 8] |= 1 << (7 - (py & 7))
+ self.addpixels(pixels)
+
+ def get(self):
+ return self.init + self.raw + self.fini
+
+class PTTape(object):
+ tapes = []
+ def __init__(self, sizemm, pixels, offset):
+ self.sizemm = sizemm
+ self.pixels = pixels
+ self.offset = offset
+ PTTape.tapes.append(self)
+
+PTTape(24.0, 128, 0)
+PTTape(12.0, 83, 22)
+PTTape(9.0, 63, 34)
+
+if __name__ == '__main__':
+ klass = PTUSB
+ klass.scan()
+ for p in klass.printers:
+ print p
+ p.setup()
+ print 'Status: ', p.get_errstr()
+ print 'Media: ', \
+ media_types.get(p.media_type, 'unknown (%02x)' % (p.media_type)), \
+ '%dx%dmm' % (p.media_width, p.media_length) if p.media_length != 0 else \
+ '%dmm continuous tape' % (p.media_width)
+
+ data = PTLabelData()
+ tape = PTTape.tapes[2]
+
+ sys.exit(0)
+
+ surface = cairo.ImageSurface(cairo.FORMAT_A1, 80, tape.pixels)
+
+ ctx = cairo.Context(surface)
+ # ctx.translate(0, 64)
+ pctx = pangocairo.CairoContext(ctx)
+
+ #for i in range(0, 128 / 16):
+ # x = 16 * (i & 1)
+ # ctx.rectangle(x, i * 16, 16, 16)
+ # ctx.fill()
+ #for i in range(0, 128 / 16):
+ # x = 32 + 16 * (i & 1)
+ # ctx.rectangle(x, i * 16, 16, 16)
+ # ctx.fill()
+
+ #font = pango.FontDescription('Delicious 24')
+ #
+ #layout = pctx.create_layout()
+ #layout.set_font_description(font)
+ #layout.set_text(u'Hello')
+ #pctx.update_layout(layout)
+ #pctx.show_layout(layout)
+
+ qr = qrcode.QRCode(border = 0)
+ qr.add_data('test')
+ qr.make(fit = True)
+ qm = qr.get_matrix()
+ print 'QR:', len(qm)
+ for y, line in enumerate(qm):
+ for x, dot in enumerate(line):
+ if dot:
+ ctx.rectangle(3 * x, 3 * y, 3, 3)
+ ctx.fill()
+
+ ctx.rectangle(70, 0, 10, 1)
+ ctx.fill()
+ ctx.rectangle(70, 2, 10, 1)
+ ctx.fill()
+ ctx.rectangle(70, tape.pixels - 3, 10, 1)
+ ctx.fill()
+ ctx.rectangle(70, tape.pixels - 1, 10, 1)
+ ctx.fill()
+
+ data.addcairo(surface, offset = tape.offset)
+
+ #data.addpixels(
+ # bytearray(((w + 7) / 8) * '\xff'))
+ #data.addpixels(
+ # bytearray(((w + 7) / 8) * '\x00'))
+ #data.addpixels(
+ # bytearray(((w + 7) / 8) * '\x55'))
+ #for i in range(0, w):
+ # pixels = bytearray(((w + 7) / 8) * '\x00')
+ # if i & 7 == 0:
+ # pixels[i / 8] = chr(0xff)
+ # else:
+ # pixels[i / 8] = chr(1 << (7 - (i & 7)))
+ # data.addpixels(pixels)
+ #data.addpixels(
+ # bytearray(((w + 7) / 8) * '\xff'))
+ p.send(data.get())
+