diff options
Diffstat (limited to 'ptlayout.py')
-rw-r--r-- | ptlayout.py | 170 |
1 files changed, 170 insertions, 0 deletions
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 |