qr.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #
  2. # ReportLab QRCode widget
  3. #
  4. # Ported from the Javascript library QRCode for Javascript by Sam Curren
  5. #
  6. # URL: http://www.d-project.com/
  7. # http://d-project.googlecode.com/svn/trunk/misc/qrcode/js/qrcode.js
  8. # qrcode.js is copyright (c) 2009 Kazuhiko Arase
  9. #
  10. # Original ReportLab module by German M. Bravo
  11. #
  12. # modified and improved by Anders Hammarquist <iko@openend.se>
  13. # and used with permission under the ReportLab License
  14. #
  15. # The word "QR Code" is registered trademark of
  16. # DENSO WAVE INCORPORATED
  17. # http://www.denso-wave.com/qrcode/faqpatent-e.html
  18. __all__ = ('QrCodeWidget')
  19. import itertools
  20. from reportlab.platypus.flowables import Flowable
  21. from reportlab.graphics.shapes import Group, Rect
  22. from reportlab.lib import colors
  23. from reportlab.lib.validators import isNumber, isNumberOrNone, isColor, isString, Validator
  24. from reportlab.lib.attrmap import AttrMap, AttrMapValue
  25. from reportlab.graphics.widgetbase import Widget
  26. from reportlab.lib.units import mm
  27. try:
  28. from reportlab.lib.utils import asUnicodeEx, isUnicode
  29. except ImportError:
  30. # ReportLab 2.x compatibility
  31. def asUnicodeEx(v, enc='utf8'):
  32. if isinstance(v, unicode):
  33. return v
  34. if isinstance(v, str):
  35. return v.decode(enc)
  36. return str(v).decode(enc)
  37. def isUnicode(v):
  38. return isinstance(v, unicode)
  39. from reportlab.graphics.barcode import qrencoder
  40. class isLevel(Validator):
  41. def test(self, x):
  42. return x in ['L', 'M', 'Q', 'H']
  43. isLevel = isLevel()
  44. class isUnicodeOrQRList(Validator):
  45. def _test(self, x):
  46. if isUnicode(x):
  47. return True
  48. if all(isinstance(v, qrencoder.QR) for v in x):
  49. return True
  50. return False
  51. def test(self, x):
  52. return self._test(x) or self.normalizeTest(x)
  53. def normalize(self, x):
  54. if self._test(x):
  55. return x
  56. try:
  57. return asUnicodeEx(x)
  58. except UnicodeError:
  59. raise ValueError("Can't convert to unicode: %r" % x)
  60. isUnicodeOrQRList = isUnicodeOrQRList()
  61. class SRect(Rect):
  62. def __init__(self, x, y, width, height, fillColor=colors.black):
  63. Rect.__init__(self, x, y, width, height, fillColor=fillColor,
  64. strokeColor=None, strokeWidth=0)
  65. class QrCodeWidget(Widget):
  66. codeName = "QR"
  67. _attrMap = AttrMap(
  68. BASE = Widget,
  69. value = AttrMapValue(isUnicodeOrQRList, desc='QRCode data'),
  70. x = AttrMapValue(isNumber, desc='x-coord'),
  71. y = AttrMapValue(isNumber, desc='y-coord'),
  72. barFillColor = AttrMapValue(isColor, desc='bar color'),
  73. barWidth = AttrMapValue(isNumber, desc='Width of bars.'), # maybe should be named just width?
  74. barHeight = AttrMapValue(isNumber, desc='Height of bars.'), # maybe should be named just height?
  75. barBorder = AttrMapValue(isNumber, desc='Width of QR border.'), # maybe should be named qrBorder?
  76. barLevel = AttrMapValue(isLevel, desc='QR Code level.'), # maybe should be named qrLevel
  77. qrVersion = AttrMapValue(isNumberOrNone, desc='QR Code version. None for auto'),
  78. # Below are ignored, they make no sense
  79. barStrokeWidth = AttrMapValue(isNumber, desc='Width of bar borders.'),
  80. barStrokeColor = AttrMapValue(isColor, desc='Color of bar borders.'),
  81. )
  82. x = 0
  83. y = 0
  84. barFillColor = colors.black
  85. barStrokeColor = None
  86. barStrokeWidth = 0
  87. barHeight = 32*mm
  88. barWidth = 32*mm
  89. barBorder = 4
  90. barLevel = 'L'
  91. qrVersion = None
  92. value = None
  93. def __init__(self, value='Hello World', **kw):
  94. self.value = isUnicodeOrQRList.normalize(value)
  95. for k, v in kw.items():
  96. setattr(self, k, v)
  97. ec_level = getattr(qrencoder.QRErrorCorrectLevel, self.barLevel)
  98. self.__dict__['qr'] = qrencoder.QRCode(self.qrVersion, ec_level)
  99. if isUnicode(self.value):
  100. self.addData(self.value)
  101. elif self.value:
  102. for v in self.value:
  103. self.addData(v)
  104. def addData(self, value):
  105. self.qr.addData(value)
  106. def draw(self):
  107. self.qr.make()
  108. g = Group()
  109. color = self.barFillColor
  110. border = self.barBorder
  111. width = self.barWidth
  112. height = self.barHeight
  113. x = self.x
  114. y = self.y
  115. g.add(SRect(x, y, width, height, fillColor=None))
  116. moduleCount = self.qr.getModuleCount()
  117. minwh = float(min(width, height))
  118. boxsize = minwh / (moduleCount + border * 2.0)
  119. offsetX = x + (width - minwh) / 2.0
  120. offsetY = y + (minwh - height) / 2.0
  121. for r, row in enumerate(self.qr.modules):
  122. row = map(bool, row)
  123. c = 0
  124. for t, tt in itertools.groupby(row):
  125. isDark = t
  126. count = len(list(tt))
  127. if isDark:
  128. x = (c + border) * boxsize
  129. y = (r + border + 1) * boxsize
  130. s = SRect(offsetX + x, offsetY + height - y, count * boxsize, boxsize,
  131. fillColor=color)
  132. g.add(s)
  133. c += count
  134. return g
  135. # Flowable version
  136. class QrCode(Flowable):
  137. height = 32*mm
  138. width = 32*mm
  139. qrBorder = 4
  140. qrLevel = 'L'
  141. qrVersion = None
  142. value = None
  143. def __init__(self, value=None, **kw):
  144. self.value = isUnicodeOrQRList.normalize(value)
  145. for k, v in kw.items():
  146. setattr(self, k, v)
  147. ec_level = getattr(qrencoder.QRErrorCorrectLevel, self.qrLevel)
  148. self.qr = qrencoder.QRCode(self.qrVersion, ec_level)
  149. if isUnicode(self.value):
  150. self.addData(self.value)
  151. elif self.value:
  152. for v in self.value:
  153. self.addData(v)
  154. def addData(self, value):
  155. self.qr.addData(value)
  156. def draw(self):
  157. self.qr.make()
  158. moduleCount = self.qr.getModuleCount()
  159. border = self.qrBorder
  160. xsize = self.width / (moduleCount + border * 2.0)
  161. ysize = self.height / (moduleCount + border * 2.0)
  162. for r, row in enumerate(self.qr.modules):
  163. row = map(bool, row)
  164. c = 0
  165. for t, tt in itertools.groupby(row):
  166. isDark = t
  167. count = len(list(tt))
  168. if isDark:
  169. x = (c + border) * xsize
  170. y = self.height - (r + border + 1) * ysize
  171. self.rect(x, y, count * xsize, ysize * 1.05)
  172. c += count
  173. def rect(self, x, y, w, h):
  174. self.canv.rect(x, y, w, h, stroke=0, fill=1)