dmtx.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. try:
  2. from pylibdmtx import pylibdmtx
  3. except ImportError:
  4. pylibdmtx = None
  5. __all__ = ()
  6. else:
  7. __all__=('DataMatrix',)
  8. from reportlab.graphics.barcode.common import Barcode
  9. from reportlab.lib.utils import asBytes
  10. from reportlab.platypus.paraparser import _num as paraparser_num
  11. from reportlab.graphics.widgetbase import Widget
  12. from reportlab.lib.validators import isColor, isString, isColorOrNone, isNumber, isBoxAnchor
  13. from reportlab.lib.attrmap import AttrMap, AttrMapValue
  14. from reportlab.lib.colors import toColor
  15. from reportlab.graphics.shapes import Group, Rect
  16. def _numConv(x):
  17. return x if isinstance(x,(int,float)) else paraparser_num(x)
  18. class _DMTXCheck(object):
  19. @classmethod
  20. def pylibdmtx_check(cls):
  21. if not pylibdmtx:
  22. raise ValueError('The %s class requires package pylibdmtx' % cls.__name__)
  23. class DataMatrix(Barcode,_DMTXCheck):
  24. def __init__(self, value='', **kwds):
  25. self.pylibdmtx_check()
  26. self._recalc = True
  27. self.value = value
  28. self.cellSize = kwds.pop('cellSize','5x5')
  29. self.size = kwds.pop('size','SquareAuto')
  30. self.encoding = kwds.pop('encoding','Ascii')
  31. self.anchor = kwds.pop('anchor','sw')
  32. self.color = kwds.pop('color',(0,0,0))
  33. self.bgColor = kwds.pop('bgColor',None)
  34. self.x = kwds.pop('x',0)
  35. self.y = kwds.pop('y',0)
  36. self.border = kwds.pop('border',5)
  37. @property
  38. def value(self):
  39. return self._value
  40. @value.setter
  41. def value(self,v):
  42. self._value = asBytes(v)
  43. self._recalc = True
  44. @property
  45. def size(self):
  46. return self._size
  47. @size.setter
  48. def size(self,v):
  49. self._size = self._checkVal('size', v, pylibdmtx.ENCODING_SIZE_NAMES)
  50. self._recalc = True
  51. @property
  52. def border(self):
  53. return self._border
  54. @border.setter
  55. def border(self,v):
  56. self._border = _numConv(v)
  57. self._recalc = True
  58. @property
  59. def x(self):
  60. return self._x
  61. @x.setter
  62. def x(self,v):
  63. self._x = _numConv(v)
  64. self._recalc = True
  65. @property
  66. def y(self):
  67. return self._y
  68. @y.setter
  69. def y(self,v):
  70. self._y = _numConv(v)
  71. self._recalc = True
  72. @property
  73. def cellSize(self):
  74. return self._cellSize
  75. @size.setter
  76. def cellSize(self,v):
  77. self._cellSize = v
  78. self._recalc = True
  79. @property
  80. def encoding(self):
  81. return self._encoding
  82. @encoding.setter
  83. def encoding(self,v):
  84. self._encoding = self._checkVal('encoding', v, pylibdmtx.ENCODING_SCHEME_NAMES)
  85. self._recalc = True
  86. @property
  87. def anchor(self):
  88. return self._anchor
  89. @anchor.setter
  90. def anchor(self,v):
  91. self._anchor = self._checkVal('anchor', v, ('n','ne','e','se','s','sw','w','nw','c'))
  92. self._recalc = True
  93. def recalc(self):
  94. if not self._recalc: return
  95. data = self._value
  96. size = self._size
  97. encoding = self._encoding
  98. e = pylibdmtx.encode(data, size=size, scheme=encoding)
  99. iW = e.width
  100. iH = e.height
  101. p = e.pixels
  102. iCellSize = 5
  103. bpp = 3 #bytes per pixel
  104. rowLen = iW*bpp
  105. cellLen = iCellSize*bpp
  106. assert len(p)//rowLen == iH
  107. matrix = list(filter(None,
  108. (''.join(
  109. (('x' if p[j:j+bpp] != b'\xff\xff\xff' else ' ')
  110. for j in range(i,i+rowLen,cellLen))).strip()
  111. for i in range(0,iH*rowLen,rowLen*iCellSize))))
  112. self._nRows = len(matrix)
  113. self._nCols = len(matrix[-1])
  114. self._matrix = '\n'.join(matrix)
  115. cellWidth = self._cellSize
  116. if cellWidth:
  117. cellWidth = cellWidth.split('x')
  118. if len(cellWidth)>2:
  119. raise ValueError('cellSize needs to be distance x distance not %r' % self._cellSize)
  120. elif len(cellWidth)==2:
  121. cellWidth, cellHeight = cellWidth
  122. else:
  123. cellWidth = cellHeight = cellWidth[0]
  124. cellWidth = _numConv(cellWidth)
  125. cellHeight = _numConv(cellHeight)
  126. else:
  127. cellWidth = cellHeight = iCellSize
  128. self._cellWidth = cellWidth
  129. self._cellHeight = cellHeight
  130. self._recalc = False
  131. self._bord = max(self.border,cellWidth,cellHeight)
  132. self._width = cellWidth*self._nCols + 2*self._bord
  133. self._height = cellHeight*self._nRows + 2*self._bord
  134. @property
  135. def matrix(self):
  136. self.recalc()
  137. return self._matrix
  138. @property
  139. def width(self):
  140. self.recalc()
  141. return self._width
  142. @property
  143. def height(self):
  144. self.recalc()
  145. return self._height
  146. @property
  147. def cellWidth(self):
  148. self.recalc()
  149. return self._cellWidth
  150. @property
  151. def cellHeight(self):
  152. self.recalc()
  153. return self._cellHeight
  154. def draw(self):
  155. self.recalc()
  156. canv = self.canv
  157. w = self.width
  158. h = self.height
  159. x = self.x
  160. y = self.y
  161. b = self._bord
  162. anchor = self.anchor
  163. if anchor in ('nw','n','ne'):
  164. y -= h
  165. elif anchor in ('c','e','w'):
  166. y -= h//2
  167. if anchor in ('ne','e','se'):
  168. x -= w
  169. elif anchor in ('n','c','s'):
  170. x -= w//2
  171. canv.saveState()
  172. if self.bgColor:
  173. canv.setFillColor(toColor(self.bgColor))
  174. canv.rect(x, y-h, w, h, fill=1, stroke=0)
  175. canv.setFillColor(toColor(self.color))
  176. canv.setStrokeColor(None)
  177. cellWidth = self.cellWidth
  178. cellHeight = self.cellHeight
  179. yr = y - b - cellHeight
  180. x += b
  181. for row in self.matrix.split('\n'):
  182. xr = x
  183. for c in row:
  184. if c=='x':
  185. canv.rect(xr, yr, cellWidth, cellHeight, fill=1, stroke=0)
  186. xr += cellWidth
  187. yr -= cellHeight
  188. canv.restoreState()
  189. class DataMatrixWidget(Widget,_DMTXCheck):
  190. codeName = "DataMatrix"
  191. _attrMap = AttrMap(
  192. BASE = Widget,
  193. value = AttrMapValue(isString, desc='Datamatrix data'),
  194. x = AttrMapValue(isNumber, desc='x-coord'),
  195. y = AttrMapValue(isNumber, desc='y-coord'),
  196. color = AttrMapValue(isColor, desc='foreground color'),
  197. bgColor = AttrMapValue(isColorOrNone, desc='background color'),
  198. encoding = AttrMapValue(isString, desc='encoding'),
  199. size = AttrMapValue(isString, desc='size'),
  200. cellSize = AttrMapValue(isString, desc='cellSize'),
  201. anchor = AttrMapValue(isBoxAnchor, desc='anchor pooint for x,y'),
  202. )
  203. _defaults = dict(
  204. x = ('0',_numConv),
  205. y = ('0',_numConv),
  206. color = ('black',toColor),
  207. bgColor = (None,lambda _: toColor(_) if _ is not None else _),
  208. encoding = ('Ascii',None),
  209. size = ('SquareAuto',None),
  210. cellSize = ('5x5',None),
  211. anchor = ('sw', None),
  212. )
  213. def __init__(self,value='Hello Cruel World!', **kwds):
  214. self.pylibdmtx_check()
  215. self.value = value
  216. for k,(d,c) in self._defaults.items():
  217. v = kwds.pop(k,d)
  218. if c: v = c(v)
  219. setattr(self,k,v)
  220. def rect(self, x, y, w, h, fill=1, stroke=0):
  221. self._gadd(Rect(x,y,w,h,strokeColor=None,fillColor=self._fillColor))
  222. def saveState(self,*args,**kwds):
  223. pass
  224. restoreState = setStrokeColor = saveState
  225. def setFillColor(self,c):
  226. self._fillColor = c
  227. def draw(self):
  228. m = DataMatrix(value=self.value,**{k: getattr(self,k) for k in self._defaults})
  229. m.canv = self
  230. m.y += m.height
  231. g = Group()
  232. self._gadd = g.add
  233. m.draw()
  234. return g