123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- #
- # ReportLab QRCode widget
- #
- # Ported from the Javascript library QRCode for Javascript by Sam Curren
- #
- # URL: http://www.d-project.com/
- # http://d-project.googlecode.com/svn/trunk/misc/qrcode/js/qrcode.js
- # qrcode.js is copyright (c) 2009 Kazuhiko Arase
- #
- # Original ReportLab module by German M. Bravo
- #
- # modified and improved by Anders Hammarquist <iko@openend.se>
- # and used with permission under the ReportLab License
- #
- # The word "QR Code" is registered trademark of
- # DENSO WAVE INCORPORATED
- # http://www.denso-wave.com/qrcode/faqpatent-e.html
- __all__ = ('QrCodeWidget')
- import itertools
- from reportlab.platypus.flowables import Flowable
- from reportlab.graphics.shapes import Group, Rect
- from reportlab.lib import colors
- from reportlab.lib.validators import isNumber, isNumberOrNone, isColor, isString, Validator
- from reportlab.lib.attrmap import AttrMap, AttrMapValue
- from reportlab.graphics.widgetbase import Widget
- from reportlab.lib.units import mm
- try:
- from reportlab.lib.utils import asUnicodeEx, isUnicode
- except ImportError:
- # ReportLab 2.x compatibility
- def asUnicodeEx(v, enc='utf8'):
- if isinstance(v, unicode):
- return v
- if isinstance(v, str):
- return v.decode(enc)
- return str(v).decode(enc)
- def isUnicode(v):
- return isinstance(v, unicode)
- from reportlab.graphics.barcode import qrencoder
- class isLevel(Validator):
- def test(self, x):
- return x in ['L', 'M', 'Q', 'H']
- isLevel = isLevel()
- class isUnicodeOrQRList(Validator):
- def _test(self, x):
- if isUnicode(x):
- return True
- if all(isinstance(v, qrencoder.QR) for v in x):
- return True
- return False
- def test(self, x):
- return self._test(x) or self.normalizeTest(x)
- def normalize(self, x):
- if self._test(x):
- return x
- try:
- return asUnicodeEx(x)
- except UnicodeError:
- raise ValueError("Can't convert to unicode: %r" % x)
- isUnicodeOrQRList = isUnicodeOrQRList()
- class SRect(Rect):
- def __init__(self, x, y, width, height, fillColor=colors.black):
- Rect.__init__(self, x, y, width, height, fillColor=fillColor,
- strokeColor=None, strokeWidth=0)
- class QrCodeWidget(Widget):
- codeName = "QR"
- _attrMap = AttrMap(
- BASE = Widget,
- value = AttrMapValue(isUnicodeOrQRList, desc='QRCode data'),
- x = AttrMapValue(isNumber, desc='x-coord'),
- y = AttrMapValue(isNumber, desc='y-coord'),
- barFillColor = AttrMapValue(isColor, desc='bar color'),
- barWidth = AttrMapValue(isNumber, desc='Width of bars.'), # maybe should be named just width?
- barHeight = AttrMapValue(isNumber, desc='Height of bars.'), # maybe should be named just height?
- barBorder = AttrMapValue(isNumber, desc='Width of QR border.'), # maybe should be named qrBorder?
- barLevel = AttrMapValue(isLevel, desc='QR Code level.'), # maybe should be named qrLevel
- qrVersion = AttrMapValue(isNumberOrNone, desc='QR Code version. None for auto'),
- # Below are ignored, they make no sense
- barStrokeWidth = AttrMapValue(isNumber, desc='Width of bar borders.'),
- barStrokeColor = AttrMapValue(isColor, desc='Color of bar borders.'),
- )
- x = 0
- y = 0
- barFillColor = colors.black
- barStrokeColor = None
- barStrokeWidth = 0
- barHeight = 32*mm
- barWidth = 32*mm
- barBorder = 4
- barLevel = 'L'
- qrVersion = None
- value = None
- def __init__(self, value='Hello World', **kw):
- self.value = isUnicodeOrQRList.normalize(value)
- for k, v in kw.items():
- setattr(self, k, v)
- ec_level = getattr(qrencoder.QRErrorCorrectLevel, self.barLevel)
- self.__dict__['qr'] = qrencoder.QRCode(self.qrVersion, ec_level)
- if isUnicode(self.value):
- self.addData(self.value)
- elif self.value:
- for v in self.value:
- self.addData(v)
- def addData(self, value):
- self.qr.addData(value)
- def draw(self):
- self.qr.make()
- g = Group()
- color = self.barFillColor
- border = self.barBorder
- width = self.barWidth
- height = self.barHeight
- x = self.x
- y = self.y
- g.add(SRect(x, y, width, height, fillColor=None))
- moduleCount = self.qr.getModuleCount()
- minwh = float(min(width, height))
- boxsize = minwh / (moduleCount + border * 2.0)
- offsetX = x + (width - minwh) / 2.0
- offsetY = y + (minwh - height) / 2.0
- for r, row in enumerate(self.qr.modules):
- row = map(bool, row)
- c = 0
- for t, tt in itertools.groupby(row):
- isDark = t
- count = len(list(tt))
- if isDark:
- x = (c + border) * boxsize
- y = (r + border + 1) * boxsize
- s = SRect(offsetX + x, offsetY + height - y, count * boxsize, boxsize,
- fillColor=color)
- g.add(s)
- c += count
- return g
- # Flowable version
- class QrCode(Flowable):
- height = 32*mm
- width = 32*mm
- qrBorder = 4
- qrLevel = 'L'
- qrVersion = None
- value = None
- def __init__(self, value=None, **kw):
- self.value = isUnicodeOrQRList.normalize(value)
- for k, v in kw.items():
- setattr(self, k, v)
- ec_level = getattr(qrencoder.QRErrorCorrectLevel, self.qrLevel)
- self.qr = qrencoder.QRCode(self.qrVersion, ec_level)
- if isUnicode(self.value):
- self.addData(self.value)
- elif self.value:
- for v in self.value:
- self.addData(v)
- def addData(self, value):
- self.qr.addData(value)
- def draw(self):
- self.qr.make()
- moduleCount = self.qr.getModuleCount()
- border = self.qrBorder
- xsize = self.width / (moduleCount + border * 2.0)
- ysize = self.height / (moduleCount + border * 2.0)
- for r, row in enumerate(self.qr.modules):
- row = map(bool, row)
- c = 0
- for t, tt in itertools.groupby(row):
- isDark = t
- count = len(list(tt))
- if isDark:
- x = (c + border) * xsize
- y = self.height - (r + border + 1) * ysize
- self.rect(x, y, count * xsize, ysize * 1.05)
- c += count
- def rect(self, x, y, w, h):
- self.canv.rect(x, y, w, h, stroke=0, fill=1)
|