from decimal import Decimal # On Python 2.6 must install lxml since the older xml.etree.ElementTree # version can not be used to create SVG images. try: import lxml.etree as ET except ImportError: import xml.etree.ElementTree as ET import qrcode.image.base class SvgFragmentImage(qrcode.image.base.BaseImage): """ SVG image builder Creates a QR-code image as a SVG document fragment. """ _SVG_namespace = "http://www.w3.org/2000/svg" kind = "SVG" allowed_kinds = ("SVG",) def __init__(self, *args, **kwargs): ET.register_namespace("svg", self._SVG_namespace) super(SvgFragmentImage, self).__init__(*args, **kwargs) # Save the unit size, for example the default box_size of 10 is '1mm'. self.unit_size = self.units(self.box_size) def drawrect(self, row, col): self._img.append(self._rect(row, col)) def units(self, pixels, text=True): """ A box_size of 10 (default) equals 1mm. """ units = Decimal(pixels) / 10 if not text: return units return '%smm' % units def save(self, stream, kind=None): self.check_kind(kind=kind) self._write(stream) def new_image(self, **kwargs): return self._svg() def _svg(self, tag=None, version='1.1', **kwargs): if tag is None: tag = ET.QName(self._SVG_namespace, "svg") dimension = self.units(self.pixel_size) return ET.Element( tag, width=dimension, height=dimension, version=version, **kwargs) def _rect(self, row, col, tag=None): if tag is None: tag = ET.QName(self._SVG_namespace, "rect") x, y = self.pixel_box(row, col)[0] return ET.Element( tag, x=self.units(x), y=self.units(y), width=self.unit_size, height=self.unit_size) def _write(self, stream): ET.ElementTree(self._img).write(stream, xml_declaration=False) class SvgImage(SvgFragmentImage): """ Standalone SVG image builder Creates a QR-code image as a standalone SVG document. """ background = None def _svg(self, tag='svg', **kwargs): svg = super(SvgImage, self)._svg(tag=tag, **kwargs) svg.set("xmlns", self._SVG_namespace) if self.background: svg.append( ET.Element( 'rect', fill=self.background, x='0', y='0', width='100%', height='100%')) return svg def _rect(self, row, col): return super(SvgImage, self)._rect(row, col, tag="rect") def _write(self, stream): ET.ElementTree(self._img).write(stream, encoding="UTF-8", xml_declaration=True) class SvgPathImage(SvgImage): """ SVG image builder with one single element (removes white spaces between individual QR points). """ QR_PATH_STYLE = 'fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' def __init__(self, *args, **kwargs): self._points = set() super(SvgPathImage, self).__init__(*args, **kwargs) def _svg(self, viewBox=None, **kwargs): if viewBox is None: dimension = self.units(self.pixel_size, text=False) viewBox = '0 0 %(d)s %(d)s' % {'d': dimension} return super(SvgPathImage, self)._svg(viewBox=viewBox, **kwargs) def drawrect(self, row, col): # (x, y) self._points.add((col, row)) def _generate_subpaths(self): """Generates individual QR points as subpaths""" rect_size = self.units(self.box_size, text=False) for point in self._points: x_base = self.units( (point[0]+self.border)*self.box_size, text=False) y_base = self.units( (point[1]+self.border)*self.box_size, text=False) yield ( 'M %(x0)s %(y0)s L %(x0)s %(y1)s L %(x1)s %(y1)s L %(x1)s ' '%(y0)s z' % dict( x0=x_base, y0=y_base, x1=x_base+rect_size, y1=y_base+rect_size, )) def make_path(self): subpaths = self._generate_subpaths() return ET.Element( ET.QName("path"), style=self.QR_PATH_STYLE, d=' '.join(subpaths), id="qr-path" ) def _write(self, stream): self._img.append(self.make_path()) super(SvgPathImage, self)._write(stream) class SvgFillImage(SvgImage): """ An SvgImage that fills the background to white. """ background = 'white' class SvgPathFillImage(SvgPathImage): """ An SvgPathImage that fills the background to white. """ background = 'white'