274 lines
7.7 KiB
Python
274 lines
7.7 KiB
Python
try:
|
|
from pylibdmtx import pylibdmtx
|
|
except ImportError:
|
|
pylibdmtx = None
|
|
__all__ = ()
|
|
else:
|
|
__all__=('DataMatrix',)
|
|
|
|
from reportlab.graphics.barcode.common import Barcode
|
|
from reportlab.lib.utils import asBytes
|
|
from reportlab.platypus.paraparser import _num as paraparser_num
|
|
from reportlab.graphics.widgetbase import Widget
|
|
from reportlab.lib.validators import isColor, isString, isColorOrNone, isNumber, isBoxAnchor
|
|
from reportlab.lib.attrmap import AttrMap, AttrMapValue
|
|
from reportlab.lib.colors import toColor
|
|
from reportlab.graphics.shapes import Group, Rect
|
|
|
|
def _numConv(x):
|
|
return x if isinstance(x,(int,float)) else paraparser_num(x)
|
|
|
|
class _DMTXCheck:
|
|
@classmethod
|
|
def pylibdmtx_check(cls):
|
|
if not pylibdmtx:
|
|
raise ValueError('The %s class requires package pylibdmtx' % cls.__name__)
|
|
|
|
class DataMatrix(Barcode,_DMTXCheck):
|
|
def __init__(self, value='', **kwds):
|
|
self.pylibdmtx_check()
|
|
self._recalc = True
|
|
self.value = value
|
|
self.cellSize = kwds.pop('cellSize','5x5')
|
|
self.size = kwds.pop('size','SquareAuto')
|
|
self.encoding = kwds.pop('encoding','Ascii')
|
|
self.anchor = kwds.pop('anchor','sw')
|
|
self.color = kwds.pop('color',(0,0,0))
|
|
self.bgColor = kwds.pop('bgColor',None)
|
|
self.x = kwds.pop('x',0)
|
|
self.y = kwds.pop('y',0)
|
|
self.border = kwds.pop('border',5)
|
|
|
|
@property
|
|
def value(self):
|
|
return self._value
|
|
|
|
@value.setter
|
|
def value(self,v):
|
|
self._value = asBytes(v)
|
|
self._recalc = True
|
|
|
|
@property
|
|
def size(self):
|
|
return self._size
|
|
|
|
@size.setter
|
|
def size(self,v):
|
|
self._size = self._checkVal('size', v, pylibdmtx.ENCODING_SIZE_NAMES)
|
|
self._recalc = True
|
|
|
|
@property
|
|
def border(self):
|
|
return self._border
|
|
|
|
@border.setter
|
|
def border(self,v):
|
|
self._border = _numConv(v)
|
|
self._recalc = True
|
|
|
|
@property
|
|
def x(self):
|
|
return self._x
|
|
|
|
@x.setter
|
|
def x(self,v):
|
|
self._x = _numConv(v)
|
|
self._recalc = True
|
|
|
|
@property
|
|
def y(self):
|
|
return self._y
|
|
|
|
@y.setter
|
|
def y(self,v):
|
|
self._y = _numConv(v)
|
|
self._recalc = True
|
|
|
|
@property
|
|
def cellSize(self):
|
|
return self._cellSize
|
|
|
|
@size.setter
|
|
def cellSize(self,v):
|
|
self._cellSize = v
|
|
self._recalc = True
|
|
|
|
@property
|
|
def encoding(self):
|
|
return self._encoding
|
|
|
|
@encoding.setter
|
|
def encoding(self,v):
|
|
self._encoding = self._checkVal('encoding', v, pylibdmtx.ENCODING_SCHEME_NAMES)
|
|
self._recalc = True
|
|
|
|
@property
|
|
def anchor(self):
|
|
return self._anchor
|
|
|
|
@anchor.setter
|
|
def anchor(self,v):
|
|
self._anchor = self._checkVal('anchor', v, ('n','ne','e','se','s','sw','w','nw','c'))
|
|
self._recalc = True
|
|
|
|
def recalc(self):
|
|
if not self._recalc: return
|
|
data = self._value
|
|
size = self._size
|
|
encoding = self._encoding
|
|
e = pylibdmtx.encode(data, size=size, scheme=encoding)
|
|
iW = e.width
|
|
iH = e.height
|
|
p = e.pixels
|
|
iCellSize = 5
|
|
bpp = 3 #bytes per pixel
|
|
rowLen = iW*bpp
|
|
cellLen = iCellSize*bpp
|
|
assert len(p)//rowLen == iH
|
|
matrix = list(filter(None,
|
|
(''.join(
|
|
(('x' if p[j:j+bpp] != b'\xff\xff\xff' else ' ')
|
|
for j in range(i,i+rowLen,cellLen))).strip()
|
|
for i in range(0,iH*rowLen,rowLen*iCellSize))))
|
|
self._nRows = len(matrix)
|
|
self._nCols = len(matrix[-1])
|
|
self._matrix = '\n'.join(matrix)
|
|
|
|
cellWidth = self._cellSize
|
|
if cellWidth:
|
|
cellWidth = cellWidth.split('x')
|
|
if len(cellWidth)>2:
|
|
raise ValueError('cellSize needs to be distance x distance not %r' % self._cellSize)
|
|
elif len(cellWidth)==2:
|
|
cellWidth, cellHeight = cellWidth
|
|
else:
|
|
cellWidth = cellHeight = cellWidth[0]
|
|
cellWidth = _numConv(cellWidth)
|
|
cellHeight = _numConv(cellHeight)
|
|
else:
|
|
cellWidth = cellHeight = iCellSize
|
|
self._cellWidth = cellWidth
|
|
self._cellHeight = cellHeight
|
|
self._recalc = False
|
|
self._bord = max(self.border,cellWidth,cellHeight)
|
|
self._width = cellWidth*self._nCols + 2*self._bord
|
|
self._height = cellHeight*self._nRows + 2*self._bord
|
|
|
|
@property
|
|
def matrix(self):
|
|
self.recalc()
|
|
return self._matrix
|
|
|
|
@property
|
|
def width(self):
|
|
self.recalc()
|
|
return self._width
|
|
|
|
@property
|
|
def height(self):
|
|
self.recalc()
|
|
return self._height
|
|
|
|
@property
|
|
def cellWidth(self):
|
|
self.recalc()
|
|
return self._cellWidth
|
|
|
|
@property
|
|
def cellHeight(self):
|
|
self.recalc()
|
|
return self._cellHeight
|
|
|
|
def draw(self):
|
|
self.recalc()
|
|
canv = self.canv
|
|
w = self.width
|
|
h = self.height
|
|
x = self.x
|
|
y = self.y
|
|
b = self._bord
|
|
|
|
anchor = self.anchor
|
|
if anchor in ('nw','n','ne'):
|
|
y -= h
|
|
elif anchor in ('c','e','w'):
|
|
y -= h//2
|
|
if anchor in ('ne','e','se'):
|
|
x -= w
|
|
elif anchor in ('n','c','s'):
|
|
x -= w//2
|
|
|
|
canv.saveState()
|
|
if self.bgColor:
|
|
canv.setFillColor(toColor(self.bgColor))
|
|
canv.rect(x, y-h, w, h, fill=1, stroke=0)
|
|
canv.setFillColor(toColor(self.color))
|
|
canv.setStrokeColor(None)
|
|
|
|
cellWidth = self.cellWidth
|
|
cellHeight = self.cellHeight
|
|
yr = y - b - cellHeight
|
|
x += b
|
|
for row in self.matrix.split('\n'):
|
|
xr = x
|
|
for c in row:
|
|
if c=='x':
|
|
canv.rect(xr, yr, cellWidth, cellHeight, fill=1, stroke=0)
|
|
xr += cellWidth
|
|
yr -= cellHeight
|
|
canv.restoreState()
|
|
|
|
|
|
class DataMatrixWidget(Widget,_DMTXCheck):
|
|
codeName = "DataMatrix"
|
|
_attrMap = AttrMap(
|
|
BASE = Widget,
|
|
value = AttrMapValue(isString, desc='Datamatrix data'),
|
|
x = AttrMapValue(isNumber, desc='x-coord'),
|
|
y = AttrMapValue(isNumber, desc='y-coord'),
|
|
color = AttrMapValue(isColor, desc='foreground color'),
|
|
bgColor = AttrMapValue(isColorOrNone, desc='background color'),
|
|
encoding = AttrMapValue(isString, desc='encoding'),
|
|
size = AttrMapValue(isString, desc='size'),
|
|
cellSize = AttrMapValue(isString, desc='cellSize'),
|
|
anchor = AttrMapValue(isBoxAnchor, desc='anchor pooint for x,y'),
|
|
)
|
|
|
|
_defaults = dict(
|
|
x = ('0',_numConv),
|
|
y = ('0',_numConv),
|
|
color = ('black',toColor),
|
|
bgColor = (None,lambda _: toColor(_) if _ is not None else _),
|
|
encoding = ('Ascii',None),
|
|
size = ('SquareAuto',None),
|
|
cellSize = ('5x5',None),
|
|
anchor = ('sw', None),
|
|
)
|
|
def __init__(self,value='Hello Cruel World!', **kwds):
|
|
self.pylibdmtx_check()
|
|
self.value = value
|
|
for k,(d,c) in self._defaults.items():
|
|
v = kwds.pop(k,d)
|
|
if c: v = c(v)
|
|
setattr(self,k,v)
|
|
|
|
def rect(self, x, y, w, h, fill=1, stroke=0):
|
|
self._gadd(Rect(x,y,w,h,strokeColor=None,fillColor=self._fillColor))
|
|
|
|
def saveState(self,*args,**kwds):
|
|
pass
|
|
|
|
restoreState = setStrokeColor = saveState
|
|
|
|
def setFillColor(self,c):
|
|
self._fillColor = c
|
|
|
|
def draw(self):
|
|
m = DataMatrix(value=self.value,**{k: getattr(self,k) for k in self._defaults})
|
|
m.canv = self
|
|
m.y += m.height
|
|
g = Group()
|
|
self._gadd = g.add
|
|
m.draw()
|
|
return g
|