diff options
Diffstat (limited to 'pttarget.py')
-rw-r--r-- | pttarget.py | 308 |
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()) + |