summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lamparter <equinox@diac24.net>2013-11-28 20:13:35 +0100
committerDavid Lamparter <equinox@diac24.net>2013-11-28 20:13:35 +0100
commit8c849b650ed534362b107e5ac4d12fdee81908e1 (patch)
tree139efa20f065f1c69060782eadfcdf26b6aa5769
parenta085769fceb3a7ab8f6db53bda51566bb622682e (diff)
initial code
-rw-r--r--.gitignore1
-rw-r--r--ptgui.glade214
-rw-r--r--ptgui.py233
-rw-r--r--ptlayout.py170
-rw-r--r--pttarget.py308
5 files changed, 926 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/ptgui.glade b/ptgui.glade
new file mode 100644
index 0000000..f1fc0fa
--- /dev/null
+++ b/ptgui.glade
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkAction" id="action1"/>
+ <object class="GtkListStore" id="devlist">
+ <columns>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ <!-- column-name obj -->
+ <column type="GObject"/>
+ </columns>
+ </object>
+ <object class="GtkWindow" id="mainwnd">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkComboBox" id="devbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">devlist</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="statuslabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">5</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">3</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="printbtn">
+ <property name="label">gtk-print</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <signal name="clicked" handler="on_print" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="vscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="resultimg">
+ <property name="height_request">128</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkColorButton" id="color_fg">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="title" translatable="yes">Text Color</property>
+ <property name="color">#000000000000</property>
+ <signal name="color-set" handler="display_refresh" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkColorButton" id="color_bg">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="title" translatable="yes">Background Color</property>
+ <property name="color">#ffffffffffff</property>
+ <signal name="color-set" handler="display_refresh" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="viewport2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkGrid" id="ctlgrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/ptgui.py b/ptgui.py
new file mode 100644
index 0000000..032dbe2
--- /dev/null
+++ b/ptgui.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python
+# vim: set expandtab ts=4:
+
+from gi.repository import GLib, Gtk
+import cairo, pango, pangocairo
+ml = GLib.MainLoop()
+import pttarget, ptlayout
+from ptlayout import PTLText
+import time
+
+class MainWindow(object):
+ def __init__(self):
+ builder = Gtk.Builder()
+ builder.add_from_file('ptgui.glade')
+ builder.connect_signals(self)
+
+ self.tape = None
+ self.tgtwidth = 192
+
+ self.wnd = builder.get_object('mainwnd')
+ self.wnd.connect("delete-event", Gtk.main_quit)
+ self.statuslabel = builder.get_object('statuslabel')
+ self.ctlgrid = builder.get_object('ctlgrid')
+ self.printbtn = builder.get_object('printbtn')
+ self.color_fg = builder.get_object('color_fg')
+ self.color_bg = builder.get_object('color_bg')
+
+ self.layout = ptlayout.PTLHSeq()
+ self.layout.spacing = 0
+ # vs1 = ptlayout.PTLVStack()
+ # vs1.add(PTLText())
+ # vs1.add(PTLText())
+ # vs2 = ptlayout.PTLVStack()
+ # vs2.add(PTLText())
+ # vs2.add(PTLText())
+ t = PTLText()
+ t.font = 'FrutigerNextLT Medium 20'
+ t.mode = 'left'
+ self.layout.add(t)
+ t = PTLText()
+ t.font = 'FrutigerNextLT Heavy 60'
+ t.mode = 'left'
+ self.layout.add(t)
+
+ self.layout.add(PTLText())
+
+ t = PTLText()
+ t.font = 'FrutigerNextLT Heavy 60'
+ t.mode = 'right'
+ self.layout.add(t)
+ t = PTLText()
+ t.font = 'FrutigerNextLT Medium 20'
+ t.mode = 'right'
+ self.layout.add(t)
+ self.layout.add(PTLText())
+ self.layout_put_controls()
+
+ self.img = builder.get_object('resultimg')
+ self.sizing_apply()
+ self.img.connect('draw', self.draw)
+ # help(self.img)
+
+ self.devlist = Gtk.ListStore(str, object)
+ self.devbox = builder.get_object('devbox')
+ self.devbox.set_model(self.devlist)
+ self.scan()
+ self.dev_select()
+ self.display_refresh()
+ self.wnd.show_all()
+
+ GLib.timeout_add(100, self.status_update)
+
+ def scan(self):
+ pttarget.PTUSB.scan()
+ for dev in pttarget.PTUSB.printers:
+ print str(dev)
+ self.devlist.append([str(dev), dev])
+
+ if len(self.devlist) > 0:
+ itr = self.devlist.get_iter((0, ))
+ self.devbox.set_active_iter(itr)
+
+ def dev_select(self):
+ itr = self.devbox.get_active_iter()
+ dev = self.devlist[itr][1]
+ dev.setup()
+ self.status_update()
+
+ def status_update(self):
+ itr = self.devbox.get_active_iter()
+ dev = self.devlist[itr][1]
+
+ if dev.last_status is None or time.time() - dev.last_status >= 1:
+ dev.refresh_status()
+ else:
+ dev.check_status()
+
+ if dev.err1 == 0 and dev.err2 == 0:
+ self.printbtn.set_sensitive(True)
+ if dev.find_tape() != self.tape:
+ self.tape = dev.find_tape()
+ self.sizing_apply()
+ self.layout_update()
+ else:
+ self.printbtn.set_sensitive(False)
+
+ mtype = pttarget.media_types.get(dev.media_type, 'unknown (%02x)' % (dev.media_type))
+ msize = '%dx%dmm' % (dev.media_width, dev.media_length) if dev.media_length != 0 else \
+ '%dmm continuous tape' % (dev.media_width)
+ self.statuslabel.set_text('Status: %s, %s %s' % (dev.get_errstr(), mtype, msize))
+ return True
+
+ def sizing_apply(self):
+ if self.tape is None:
+ self.label_surface = None
+ tapeh = 16
+ else:
+ self.label_surface = cairo.ImageSurface(cairo.FORMAT_A1, self.tgtwidth, self.tape.pixels)
+ tapeh = self.tape.pixels
+
+ self.display_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
+ self.tgtwidth + 40, tapeh + 40)
+ self.img.set_size_request(self.display_surface.get_width(),
+ self.display_surface.get_height())
+
+ #ctx = cairo.Context(self.label_surface)
+ # for i in range(0, 128 / 16):
+ # x = 16 * (i & 1)
+ # ctx.rectangle(x, i * 16, 12, 12)
+ # ctx.fill()
+ # for i in range(0, 128 / 16):
+ # x = 32 + 16 * (i & 1)
+ # ctx.rectangle(x, i * 16, 12, 12)
+ # ctx.fill()
+
+ # pctx = pangocairo.CairoContext(ctx)
+ # 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)
+
+ # self.display_refresh()
+
+ def display_refresh(self, *args):
+ ctx = cairo.Context(self.display_surface)
+ w = self.display_surface.get_width()
+ h = self.display_surface.get_height()
+ for x in range(0, w, 16):
+ for y in range(0, h, 16):
+ col = 0.3 + 0.1 * (((x + y) / 16) & 1)
+ ctx.rectangle(x, y, 16, 16)
+ ctx.set_source_rgb(col, col, col)
+ ctx.fill()
+
+ if self.label_surface is None:
+ return
+
+ pw = self.label_surface.get_width()
+ ph = self.label_surface.get_height()
+ px, py = (w - pw) / 2, (h - ph) / 2
+ ctx.rectangle(px, py, pw, ph)
+ bg = self.color_bg.get_rgba()
+ ctx.set_source_rgb(bg.red, bg.green, bg.blue)
+ ctx.fill()
+
+ fg = self.color_fg.get_rgba()
+ ctx.set_source_rgb(fg.red, fg.green, fg.blue)
+ ctx.mask_surface(self.label_surface, px, py)
+
+ self.img.queue_draw()
+
+ def draw(self, widget, ctx):
+ ctx.set_source_surface(self.display_surface)
+ w = self.display_surface.get_width()
+ h = self.display_surface.get_height()
+ ctx.rectangle(0, 0, w, h)
+ ctx.fill()
+
+ def layout_update(self):
+ w, h = self.layout.prep_size(self.label_surface.get_height())
+ if w < 32:
+ w = 32
+ if w != self.tgtwidth:
+ self.tgtwidth = w
+ self.sizing_apply()
+
+ if self.label_surface is None:
+ return
+
+ self.label_surface = cairo.ImageSurface(cairo.FORMAT_A1, self.tgtwidth, self.tape.pixels)
+ ctx = cairo.Context(self.label_surface)
+ ctx.set_operator(cairo.OPERATOR_SOURCE)
+ finw = self.layout.render(ctx, self.label_surface.get_height())
+ self.display_refresh()
+
+ def layout_control(self, node, name, spec):
+ elem = Gtk.Entry()
+ buf = elem.get_buffer()
+ def update(buf, *args):
+ node.__dict__[name] = buf.get_text()
+ self.layout_update()
+
+ buf.set_text(node.__dict__[name], -1)
+ buf.connect('deleted-text', update)
+ buf.connect('inserted-text', update)
+ return elem
+
+ def layout_put_controls(self):
+ nodes = [self.layout]
+ while len(nodes) > 0:
+ node = nodes.pop(0)
+ nodes.extend(reversed(node.children()))
+ for p in reversed(node.properties()):
+ elem = self.layout_control(node, p[0], p[1])
+ self.ctlgrid.insert_row(0)
+ self.ctlgrid.attach(Gtk.Label(p[0]), 0, 0, 1, 1)
+ self.ctlgrid.attach(elem, 1, 0, 1, 1)
+
+ def on_print(self, *args):
+ if self.label_surface is None:
+ return
+
+ data = pttarget.PTLabelData()
+ data.addcairo(self.label_surface, offset = self.tape.offset)
+ itr = self.devbox.get_active_iter()
+ dev = self.devlist[itr][1]
+ dev.send(data.get())
+
+mw = MainWindow()
+Gtk.main()
+# ml.run()
diff --git a/ptlayout.py b/ptlayout.py
new file mode 100644
index 0000000..f9518b7
--- /dev/null
+++ b/ptlayout.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+# vim: set expandtab ts=4:
+
+import cairo, pango, pangocairo
+import qrcode
+from math import pi
+
+dummysurface = cairo.ImageSurface(cairo.FORMAT_A1, 1, 1)
+dummyctx = cairo.Context(dummysurface)
+
+class PTLayoutElem(object):
+ def __init__(self):
+ super(PTLayoutElem, self).__init__()
+
+ def prep_size(self, h):
+ return (0, 0)
+
+ def render(self, cctx, h):
+ return 0
+
+ def children(self):
+ return []
+
+ def properties(self):
+ return []
+
+class PTLText(PTLayoutElem):
+ def __init__(self):
+ super(PTLText, self).__init__()
+ self.text = ''
+ self.font = ''
+ self.mode = ''
+
+ def properties(self):
+ return [
+ ('text', 'text'),
+ ('font', 'text'),
+ ('mode', 'text'),
+ ]
+
+ def prep_size(self, h):
+ return self._render(dummyctx, True, h)
+
+ def render(self, cctx, h):
+ return self._render(cctx, False, h)
+
+ def _render(self, cctx, sizeonly, h):
+ pctx = pangocairo.CairoContext(cctx)
+ font = pango.FontDescription(self.font if self.font != '' else 'Delicious 24')
+ layout = pctx.create_layout()
+ layout.set_font_description(font)
+ layout.set_text(unicode(self.text))
+ size = layout.get_pixel_size()
+ if sizeonly:
+ if self.mode in ['left', 'right']:
+ return (size[1], size[0])
+ return size
+ w = size[0]
+
+ matrix = cctx.get_matrix()
+ if self.mode == 'left':
+ cctx.rotate(90. * pi / 180.)
+ cctx.translate((h - size[0]) / 2, -size[1])
+ w = size[1]
+ elif self.mode == 'right':
+ cctx.rotate(-90. * pi / 180.)
+ cctx.translate(-h + (h - size[0]) / 2, 0)
+ w = size[1]
+
+ pctx.update_layout(layout)
+ pctx.show_layout(layout)
+
+ cctx.set_matrix(matrix)
+ return w
+
+class PTLQRCode(PTLayoutElem):
+ def __init__(self):
+ super(PTLQRCode, self).__init__()
+ self.qrcontent = ''
+ self.hborder = 4
+ self.invert = True
+
+ def properties(self):
+ return [
+ ('qrcontent', 'text'),
+ ]
+
+ def prep_size(self, h):
+ return self._render(dummyctx, True, h)
+
+ def render(self, cctx, h):
+ return self._render(cctx, False, h)
+
+ def _render(self, cctx, sizeonly, h):
+ qr = qrcode.QRCode(border = 0)
+ qr.add_data(self.qrcontent)
+ qr.make(fit = True)
+ qm = qr.get_matrix()
+ qmlen = len(qm)
+ bpp = h / qmlen
+ if sizeonly: return (bpp * qmlen + self.hborder * 2, bpp * qmlen)
+
+ if self.invert:
+ cctx.rectangle(0, 0, bpp * qmlen + self.hborder * 2, h)
+ cctx.fill()
+ cctx.set_source_rgba(1.0, 1.0, 1.0, 0.0)
+
+ yoffs = (h - bpp * qmlen) / 2
+ for y, line in enumerate(qm):
+ for x, dot in enumerate(line):
+ if dot:
+ cctx.rectangle(bpp * x + self.hborder, bpp * y + yoffs, bpp, bpp)
+ cctx.fill()
+
+ cctx.set_source_rgba(0.0, 0.0, 0.0, 1.0)
+ return bpp * qmlen
+
+class PTLContainer(PTLayoutElem):
+ def __init__(self):
+ super(PTLContainer, self).__init__()
+ self._children = []
+
+ def children(self):
+ return self._children
+
+ def add(self, child):
+ self._children.append(child)
+
+class PTLHSeq(PTLContainer):
+ def __init__(self):
+ super(PTLHSeq, self).__init__()
+ self.spacing = 5
+
+ def prep_size(self, hh):
+ w, h = len(self._children) * self.spacing, 0
+ for k in self._children:
+ cw, ch = k.prep_size(hh)
+ w += cw
+ h = max(h, ch)
+ return (w, h)
+
+ def render(self, cctx, h):
+ wpos = 0
+ for k in self._children:
+ cw = k.render(cctx, h)
+ wpos += cw + self.spacing
+ cctx.translate(cw + self.spacing, 0)
+ cctx.translate(-wpos, 0)
+ return wpos - self.spacing
+
+class PTLVStack(PTLContainer):
+ def __init__(self):
+ super(PTLVStack, self).__init__()
+
+ def prep_size(self, hh):
+ w = 0
+ for k in self._children:
+ cw, ch = k.prep_size(hh / len(self._children))
+ w = max(w, cw)
+ return (w, hh)
+
+ def render(self, cctx, h):
+ w = 0
+ for i, k in enumerate(self._children):
+ vpos = int(h / float(len(self._children)) * i)
+ cctx.translate(0, vpos)
+ cw = k.render(cctx, h)
+ w = max(cw, w)
+ cctx.translate(0, -vpos)
+ return w
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())
+