# 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, dummy_width=24): self.dummy_width = dummy_width self.last_status = None def __str__(self): return 'Dummy PTPrinter, Tape width %d' % self.dummy_width def setup(self): pass def check_status(self): self.refresh_status() def find_tape(self): for tape in PTTape.tapes: if int(tape.sizemm) == self.media_width: return tape return None def is_interactive(self): return False 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 refresh_status(self): self.err1 = 0x00 self.err2 = 0x00 self.media_width = self.dummy_width self.media_type = 0x01 self.media_length = 8000 self.status_type = 0x00 self.phase_type = 0x00 self.phase_bytes = 0x00 self.notify_num = 0x00 self.last_status = time.time() def send(self, data): pass 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): super(PTUSB, self).__init__() self.usbdev = usbdev 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): self.usbdev.default_timeout = 1000 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 and time.time() < start + 0.25: 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]) else: ar = data 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 check_status(self): ar = self.if_rd.read(16) if len(ar) == 0: return start = time.time() while len(ar) < 32 and time.time() < start + 0.25: ar += self.if_rd.read(16) self.refresh_status(ar) 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], timeout = 5000) 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\x40' + '\x1bid\x00\x00' + '\x1biK\x00' self.raw = '' def addpixels(self, raw): row = 'G' + struct.pack(' 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, feedncut = True): fini = '\x1a' if feedncut else '\x0c' return self.init + self.raw + 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())