Initial commit: IT Site Survey AI v2 with all features
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
#
|
||||
# Copyright (c) 1996-2000 Tyler C. Sarna <tsarna@sarna.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. All advertising materials mentioning features or use of this software
|
||||
# must display the following acknowledgement:
|
||||
# This product includes software developed by Tyler C. Sarna.
|
||||
# 4. Neither the name of the author nor the names of contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
__all__ = tuple('''registerWidget getCodes getCodeNames createBarcodeDrawing createBarcodeImageInMemory'''.split())
|
||||
__version__ = '0.9'
|
||||
__doc__='''Popular barcodes available as reusable widgets'''
|
||||
|
||||
_widgets = []
|
||||
def registerWidget(widget):
|
||||
_widgets.append(widget)
|
||||
|
||||
def _reset():
|
||||
_widgets[:] = []
|
||||
from reportlab.graphics.barcode.widgets import BarcodeI2of5, BarcodeCode128, BarcodeStandard93,\
|
||||
BarcodeExtended93, BarcodeStandard39, BarcodeExtended39,\
|
||||
BarcodeMSI, BarcodeCodabar, BarcodeCode11, BarcodeFIM,\
|
||||
BarcodePOSTNET, BarcodeUSPS_4State, BarcodeCode128Auto, BarcodeECC200DataMatrix
|
||||
|
||||
#newer codes will typically get their own module
|
||||
from reportlab.graphics.barcode.eanbc import Ean13BarcodeWidget, Ean8BarcodeWidget, UPCA, Ean5BarcodeWidget, ISBNBarcodeWidget
|
||||
from reportlab.graphics.barcode.qr import QrCodeWidget
|
||||
for widget in (BarcodeI2of5,
|
||||
BarcodeCode128,
|
||||
BarcodeCode128Auto,
|
||||
BarcodeStandard93,
|
||||
BarcodeExtended93,
|
||||
BarcodeStandard39,
|
||||
BarcodeExtended39,
|
||||
BarcodeMSI,
|
||||
BarcodeCodabar,
|
||||
BarcodeCode11,
|
||||
BarcodeFIM,
|
||||
BarcodePOSTNET,
|
||||
BarcodeUSPS_4State,
|
||||
Ean13BarcodeWidget,
|
||||
Ean8BarcodeWidget,
|
||||
UPCA,
|
||||
Ean5BarcodeWidget,
|
||||
ISBNBarcodeWidget,
|
||||
QrCodeWidget,
|
||||
BarcodeECC200DataMatrix,
|
||||
):
|
||||
registerWidget(widget)
|
||||
from reportlab.graphics.barcode import dmtx
|
||||
if dmtx.pylibdmtx:
|
||||
registerWidget(dmtx.DataMatrixWidget)
|
||||
|
||||
_reset()
|
||||
from reportlab.rl_config import register_reset
|
||||
register_reset(_reset)
|
||||
|
||||
def getCodes():
|
||||
"""Returns a dict mapping code names to widgets"""
|
||||
#the module exports a dictionary of names to widgets, to make it easy for
|
||||
#apps and doc tools to display information about them.
|
||||
codes = {}
|
||||
for widget in _widgets:
|
||||
codeName = widget.codeName
|
||||
codes[codeName] = widget
|
||||
|
||||
return codes
|
||||
|
||||
def getCodeNames():
|
||||
"""Returns sorted list of supported bar code names"""
|
||||
return sorted(getCodes().keys())
|
||||
|
||||
def createBarcodeDrawing(codeName, **options):
|
||||
"""This creates and returns a drawing with a barcode.
|
||||
"""
|
||||
from reportlab.graphics.shapes import Drawing
|
||||
|
||||
codes = getCodes()
|
||||
bcc = codes[codeName]
|
||||
width = options.pop('width',None)
|
||||
height = options.pop('height',None)
|
||||
isoScale = options.pop('isoScale',0)
|
||||
kw = {}
|
||||
for k,v in options.items():
|
||||
if k.startswith('_') or k in bcc._attrMap: kw[k] = v
|
||||
bc = bcc(**kw)
|
||||
|
||||
|
||||
#Robin's new ones validate when setting the value property.
|
||||
#Ty Sarna's old ones do not. We need to test.
|
||||
if hasattr(bc, 'validate'):
|
||||
bc.validate() #raise exception if bad value
|
||||
if not bc.valid:
|
||||
raise ValueError("Illegal barcode with value '%s' in code '%s'" % (options.get('value',None), codeName))
|
||||
|
||||
#size it after setting the data
|
||||
x1, y1, x2, y2 = bc.getBounds()
|
||||
w = float(x2 - x1)
|
||||
h = float(y2 - y1)
|
||||
sx = width not in ('auto',None)
|
||||
sy = height not in ('auto',None)
|
||||
if sx or sy:
|
||||
sx = sx and width/w or 1.0
|
||||
sy = sy and height/h or 1.0
|
||||
if isoScale:
|
||||
if sx<1.0 and sy<1.0:
|
||||
sx = sy = max(sx,sy)
|
||||
else:
|
||||
sx = sy = min(sx,sy)
|
||||
|
||||
w *= sx
|
||||
h *= sy
|
||||
else:
|
||||
sx = sy = 1
|
||||
|
||||
#bc.x = -sx*x1
|
||||
#bc.y = -sy*y1
|
||||
d = Drawing(width=w,height=h,transform=[sx,0,0,sy,-sx*x1,-sy*y1])
|
||||
d.add(bc, "_bc")
|
||||
return d
|
||||
|
||||
def createBarcodeImageInMemory(codeName,**options):
|
||||
"""This creates and returns barcode as an image in memory.
|
||||
Takes same arguments as createBarcodeDrawing and also an
|
||||
optional format keyword which can be anything acceptable
|
||||
to Drawing.asString eg gif, pdf, tiff, py ......
|
||||
"""
|
||||
format = options.pop('format','png')
|
||||
d = createBarcodeDrawing(codeName, **options)
|
||||
return d.asString(format)
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,460 @@
|
||||
#
|
||||
# Copyright (c) 2000 Tyler C. Sarna <tsarna@sarna.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. All advertising materials mentioning features or use of this software
|
||||
# must display the following acknowledgement:
|
||||
# This product includes software developed by Tyler C. Sarna.
|
||||
# 4. Neither the name of the author nor the names of contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.lib.utils import asNative
|
||||
from reportlab.graphics.barcode.common import MultiWidthBarcode
|
||||
from string import digits
|
||||
|
||||
_patterns = {
|
||||
0 : 'BaBbBb', 1 : 'BbBaBb', 2 : 'BbBbBa',
|
||||
3 : 'AbAbBc', 4 : 'AbAcBb', 5 : 'AcAbBb',
|
||||
6 : 'AbBbAc', 7 : 'AbBcAb', 8 : 'AcBbAb',
|
||||
9 : 'BbAbAc', 10 : 'BbAcAb', 11 : 'BcAbAb',
|
||||
12 : 'AaBbCb', 13 : 'AbBaCb', 14 : 'AbBbCa',
|
||||
15 : 'AaCbBb', 16 : 'AbCaBb', 17 : 'AbCbBa',
|
||||
18 : 'BbCbAa', 19 : 'BbAaCb', 20 : 'BbAbCa',
|
||||
21 : 'BaCbAb', 22 : 'BbCaAb', 23 : 'CaBaCa',
|
||||
24 : 'CaAbBb', 25 : 'CbAaBb', 26 : 'CbAbBa',
|
||||
27 : 'CaBbAb', 28 : 'CbBaAb', 29 : 'CbBbAa',
|
||||
30 : 'BaBaBc', 31 : 'BaBcBa', 32 : 'BcBaBa',
|
||||
33 : 'AaAcBc', 34 : 'AcAaBc', 35 : 'AcAcBa',
|
||||
36 : 'AaBcAc', 37 : 'AcBaAc', 38 : 'AcBcAa',
|
||||
39 : 'BaAcAc', 40 : 'BcAaAc', 41 : 'BcAcAa',
|
||||
42 : 'AaBaCc', 43 : 'AaBcCa', 44 : 'AcBaCa',
|
||||
45 : 'AaCaBc', 46 : 'AaCcBa', 47 : 'AcCaBa',
|
||||
48 : 'CaCaBa', 49 : 'BaAcCa', 50 : 'BcAaCa',
|
||||
51 : 'BaCaAc', 52 : 'BaCcAa', 53 : 'BaCaCa',
|
||||
54 : 'CaAaBc', 55 : 'CaAcBa', 56 : 'CcAaBa',
|
||||
57 : 'CaBaAc', 58 : 'CaBcAa', 59 : 'CcBaAa',
|
||||
60 : 'CaDaAa', 61 : 'BbAdAa', 62 : 'DcAaAa',
|
||||
63 : 'AaAbBd', 64 : 'AaAdBb', 65 : 'AbAaBd',
|
||||
66 : 'AbAdBa', 67 : 'AdAaBb', 68 : 'AdAbBa',
|
||||
69 : 'AaBbAd', 70 : 'AaBdAb', 71 : 'AbBaAd',
|
||||
72 : 'AbBdAa', 73 : 'AdBaAb', 74 : 'AdBbAa',
|
||||
75 : 'BdAbAa', 76 : 'BbAaAd', 77 : 'DaCaAa',
|
||||
78 : 'BdAaAb', 79 : 'AcDaAa', 80 : 'AaAbDb',
|
||||
81 : 'AbAaDb', 82 : 'AbAbDa', 83 : 'AaDbAb',
|
||||
84 : 'AbDaAb', 85 : 'AbDbAa', 86 : 'DaAbAb',
|
||||
87 : 'DbAaAb', 88 : 'DbAbAa', 89 : 'BaBaDa',
|
||||
90 : 'BaDaBa', 91 : 'DaBaBa', 92 : 'AaAaDc',
|
||||
93 : 'AaAcDa', 94 : 'AcAaDa', 95 : 'AaDaAc',
|
||||
96 : 'AaDcAa', 97 : 'DaAaAc', 98 : 'DaAcAa',
|
||||
99 : 'AaCaDa', 100 : 'AaDaCa', 101 : 'CaAaDa',
|
||||
102 : 'DaAaCa', 103 : 'BaAdAb', 104 : 'BaAbAd',
|
||||
105 : 'BaAbCb', 106 : 'BcCaAaB'
|
||||
}
|
||||
|
||||
starta, startb, startc, stop = 103, 104, 105, 106
|
||||
|
||||
seta = {
|
||||
' ' : 0, '!' : 1, '"' : 2, '#' : 3,
|
||||
'$' : 4, '%' : 5, '&' : 6, '\'' : 7,
|
||||
'(' : 8, ')' : 9, '*' : 10, '+' : 11,
|
||||
',' : 12, '-' : 13, '.' : 14, '/' : 15,
|
||||
'0' : 16, '1' : 17, '2' : 18, '3' : 19,
|
||||
'4' : 20, '5' : 21, '6' : 22, '7' : 23,
|
||||
'8' : 24, '9' : 25, ':' : 26, ';' : 27,
|
||||
'<' : 28, '=' : 29, '>' : 30, '?' : 31,
|
||||
'@' : 32, 'A' : 33, 'B' : 34, 'C' : 35,
|
||||
'D' : 36, 'E' : 37, 'F' : 38, 'G' : 39,
|
||||
'H' : 40, 'I' : 41, 'J' : 42, 'K' : 43,
|
||||
'L' : 44, 'M' : 45, 'N' : 46, 'O' : 47,
|
||||
'P' : 48, 'Q' : 49, 'R' : 50, 'S' : 51,
|
||||
'T' : 52, 'U' : 53, 'V' : 54, 'W' : 55,
|
||||
'X' : 56, 'Y' : 57, 'Z' : 58, '[' : 59,
|
||||
'\\' : 60, ']' : 61, '^' : 62, '_' : 63,
|
||||
'\x00' : 64, '\x01' : 65, '\x02' : 66, '\x03' : 67,
|
||||
'\x04' : 68, '\x05' : 69, '\x06' : 70, '\x07' : 71,
|
||||
'\x08' : 72, '\x09' : 73, '\x0a' : 74, '\x0b' : 75,
|
||||
'\x0c' : 76, '\x0d' : 77, '\x0e' : 78, '\x0f' : 79,
|
||||
'\x10' : 80, '\x11' : 81, '\x12' : 82, '\x13' : 83,
|
||||
'\x14' : 84, '\x15' : 85, '\x16' : 86, '\x17' : 87,
|
||||
'\x18' : 88, '\x19' : 89, '\x1a' : 90, '\x1b' : 91,
|
||||
'\x1c' : 92, '\x1d' : 93, '\x1e' : 94, '\x1f' : 95,
|
||||
'\xf3' : 96, '\xf2' : 97, 'SHIFT' : 98, 'TO_C' : 99,
|
||||
'TO_B' : 100, '\xf4' : 101, '\xf1' : 102
|
||||
}
|
||||
|
||||
setb = {
|
||||
' ' : 0, '!' : 1, '"' : 2, '#' : 3,
|
||||
'$' : 4, '%' : 5, '&' : 6, '\'' : 7,
|
||||
'(' : 8, ')' : 9, '*' : 10, '+' : 11,
|
||||
',' : 12, '-' : 13, '.' : 14, '/' : 15,
|
||||
'0' : 16, '1' : 17, '2' : 18, '3' : 19,
|
||||
'4' : 20, '5' : 21, '6' : 22, '7' : 23,
|
||||
'8' : 24, '9' : 25, ':' : 26, ';' : 27,
|
||||
'<' : 28, '=' : 29, '>' : 30, '?' : 31,
|
||||
'@' : 32, 'A' : 33, 'B' : 34, 'C' : 35,
|
||||
'D' : 36, 'E' : 37, 'F' : 38, 'G' : 39,
|
||||
'H' : 40, 'I' : 41, 'J' : 42, 'K' : 43,
|
||||
'L' : 44, 'M' : 45, 'N' : 46, 'O' : 47,
|
||||
'P' : 48, 'Q' : 49, 'R' : 50, 'S' : 51,
|
||||
'T' : 52, 'U' : 53, 'V' : 54, 'W' : 55,
|
||||
'X' : 56, 'Y' : 57, 'Z' : 58, '[' : 59,
|
||||
'\\' : 60, ']' : 61, '^' : 62, '_' : 63,
|
||||
'`' : 64, 'a' : 65, 'b' : 66, 'c' : 67,
|
||||
'd' : 68, 'e' : 69, 'f' : 70, 'g' : 71,
|
||||
'h' : 72, 'i' : 73, 'j' : 74, 'k' : 75,
|
||||
'l' : 76, 'm' : 77, 'n' : 78, 'o' : 79,
|
||||
'p' : 80, 'q' : 81, 'r' : 82, 's' : 83,
|
||||
't' : 84, 'u' : 85, 'v' : 86, 'w' : 87,
|
||||
'x' : 88, 'y' : 89, 'z' : 90, '{' : 91,
|
||||
'|' : 92, '}' : 93, '~' : 94, '\x7f' : 95,
|
||||
'\xf3' : 96, '\xf2' : 97, 'SHIFT' : 98, 'TO_C' : 99,
|
||||
'\xf4' : 100, 'TO_A' : 101, '\xf1' : 102
|
||||
}
|
||||
|
||||
setc = {
|
||||
'00': 0, '01': 1, '02': 2, '03': 3, '04': 4,
|
||||
'05': 5, '06': 6, '07': 7, '08': 8, '09': 9,
|
||||
'10':10, '11':11, '12':12, '13':13, '14':14,
|
||||
'15':15, '16':16, '17':17, '18':18, '19':19,
|
||||
'20':20, '21':21, '22':22, '23':23, '24':24,
|
||||
'25':25, '26':26, '27':27, '28':28, '29':29,
|
||||
'30':30, '31':31, '32':32, '33':33, '34':34,
|
||||
'35':35, '36':36, '37':37, '38':38, '39':39,
|
||||
'40':40, '41':41, '42':42, '43':43, '44':44,
|
||||
'45':45, '46':46, '47':47, '48':48, '49':49,
|
||||
'50':50, '51':51, '52':52, '53':53, '54':54,
|
||||
'55':55, '56':56, '57':57, '58':58, '59':59,
|
||||
'60':60, '61':61, '62':62, '63':63, '64':64,
|
||||
'65':65, '66':66, '67':67, '68':68, '69':69,
|
||||
'70':70, '71':71, '72':72, '73':73, '74':74,
|
||||
'75':75, '76':76, '77':77, '78':78, '79':79,
|
||||
'80':80, '81':81, '82':82, '83':83, '84':84,
|
||||
'85':85, '86':86, '87':87, '88':88, '89':89,
|
||||
'90':90, '91':91, '92':92, '93':93, '94':94,
|
||||
'95':95, '96':96, '97':97, '98':98, '99':99,
|
||||
|
||||
'TO_B' : 100, 'TO_A' : 101, '\xf1' : 102
|
||||
}
|
||||
|
||||
setmap = {
|
||||
'TO_A' : (seta, setb),
|
||||
'TO_B' : (setb, seta),
|
||||
'TO_C' : (setc, None),
|
||||
'START_A' : (starta, seta, setb),
|
||||
'START_B' : (startb, setb, seta),
|
||||
'START_C' : (startc, setc, None),
|
||||
}
|
||||
cStarts = ('START_B','TO_A','TO_B')
|
||||
tos = list(setmap.keys())
|
||||
|
||||
class Code128(MultiWidthBarcode):
|
||||
"""
|
||||
Code 128 is a very compact symbology that can encode the entire
|
||||
128 character ASCII set, plus 4 special control codes,
|
||||
(FNC1-FNC4, expressed in the input string as \xf1 to \xf4).
|
||||
Code 128 can also encode digits at double density (2 per byte)
|
||||
and has a mandatory checksum. Code 128 is well supported and
|
||||
commonly used -- for example, by UPS for tracking labels.
|
||||
|
||||
Because of these qualities, Code 128 is probably the best choice
|
||||
for a linear symbology today (assuming you have a choice).
|
||||
|
||||
Options that may be passed to constructor:
|
||||
|
||||
value (int, or numeric string. required.):
|
||||
The value to encode.
|
||||
|
||||
barWidth (float, default .0075):
|
||||
X-Dimension, or width of the smallest element
|
||||
Minumum is .0075 inch (7.5 mils).
|
||||
|
||||
barHeight (float, see default below):
|
||||
Height of the symbol. Default is the height of the two
|
||||
bearer bars (if they exist) plus the greater of .25 inch
|
||||
or .15 times the symbol's length.
|
||||
|
||||
quiet (bool, default 1):
|
||||
Wether to include quiet zones in the symbol.
|
||||
|
||||
lquiet (float, see default below):
|
||||
Quiet zone size to left of code, if quiet is true.
|
||||
Default is the greater of .25 inch, or 10 barWidth
|
||||
|
||||
rquiet (float, defaults as above):
|
||||
Quiet zone size to right left of code, if quiet is true.
|
||||
|
||||
Sources of Information on Code 128:
|
||||
|
||||
http://www.semiconductor.agilent.com/barcode/sg/Misc/code_128.html
|
||||
http://www.adams1.com/pub/russadam/128code.html
|
||||
http://www.barcodeman.com/c128.html
|
||||
|
||||
Official Spec, "ANSI/AIM BC4-1999, ISS" is available for US$45 from
|
||||
http://www.aimglobal.org/aimstore/
|
||||
"""
|
||||
barWidth = inch * 0.0075
|
||||
lquiet = None
|
||||
rquiet = None
|
||||
quiet = 1
|
||||
barHeight = None
|
||||
def __init__(self, value='', **args):
|
||||
value = str(value) if isinstance(value,int) else asNative(value)
|
||||
|
||||
for k, v in args.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
if self.quiet:
|
||||
if self.lquiet is None:
|
||||
self.lquiet = max(inch * 0.25, self.barWidth * 10.0)
|
||||
if self.rquiet is None:
|
||||
self.rquiet = max(inch * 0.25, self.barWidth * 10.0)
|
||||
else:
|
||||
self.lquiet = self.rquiet = 0.0
|
||||
|
||||
MultiWidthBarcode.__init__(self, value)
|
||||
|
||||
def validate(self):
|
||||
vval = ""
|
||||
self.valid = 1
|
||||
for c in self.value:
|
||||
if ord(c) > 127 and c not in '\xf1\xf2\xf3\xf4':
|
||||
self.valid = 0
|
||||
continue
|
||||
vval = vval + c
|
||||
self.validated = vval
|
||||
return vval
|
||||
|
||||
|
||||
def _try_TO_C(self, l):
|
||||
'''Improved version of old _trailingDigitsToC(self, l) inspired by'''
|
||||
i = 0
|
||||
nl = []
|
||||
while i < len(l):
|
||||
startpos = i
|
||||
rl = []
|
||||
savings = -1 # the TO_C costs one character
|
||||
while i < len(l):
|
||||
if l[i] in cStarts:
|
||||
j = i
|
||||
break
|
||||
elif l[i] == '\xf1':
|
||||
rl.append(l[i])
|
||||
i += 1
|
||||
continue
|
||||
elif l[i] in digits \
|
||||
and l[i+1] in digits:
|
||||
rl.append(l[i] + l[i+1])
|
||||
i += 2
|
||||
savings += 1
|
||||
continue
|
||||
else:
|
||||
if l[i] in digits and l[i+1]=='STOP':
|
||||
rrl = []
|
||||
rsavings = -1 #we need a TO_C
|
||||
k = i
|
||||
while k>startpos:
|
||||
if l[k]=='\xf1':
|
||||
rrl.append(l[i])
|
||||
k -= 1
|
||||
elif l[k] in digits and l[k-1] in digits:
|
||||
rrl.append(l[k-1]+l[k])
|
||||
rsavings += 1
|
||||
k -= 2
|
||||
else:
|
||||
break
|
||||
rrl.reverse()
|
||||
if rsavings>savings+int(savings>=0 and (startpos and nl[-1] in cStarts))-1:
|
||||
nl += l[startpos]
|
||||
startpos += 1
|
||||
rl = rrl
|
||||
del rrl
|
||||
i += 1
|
||||
break
|
||||
ta = not (l[i]=='STOP' or j==i)
|
||||
xs = savings>=0 and (startpos and nl[-1] in cStarts)
|
||||
if savings+int(xs) > int(ta):
|
||||
if xs:
|
||||
toc = nl[-1][:-1]+'C'
|
||||
del nl[-1]
|
||||
else:
|
||||
toc = 'TO_C'
|
||||
nl += [toc]+rl
|
||||
if ta:
|
||||
nl.append('TO'+l[j][-2:])
|
||||
nl.append(l[i])
|
||||
else:
|
||||
nl += l[startpos:i+1]
|
||||
i += 1
|
||||
return nl
|
||||
|
||||
def encode(self):
|
||||
# First, encode using only B
|
||||
s = self.validated
|
||||
l = ['START_B']
|
||||
for c in s:
|
||||
if c not in setb:
|
||||
l = l + ['TO_A', c, 'TO_B']
|
||||
else:
|
||||
l.append(c)
|
||||
l.append('STOP')
|
||||
|
||||
l = self._try_TO_C(l)
|
||||
|
||||
# Finally, replace START_X,TO_Y with START_Y
|
||||
if l[1] in tos:
|
||||
l[:2] = ['START_' + l[1][-1]]
|
||||
|
||||
# print repr(l)
|
||||
|
||||
# encode into numbers
|
||||
start, set, shset = setmap[l[0]]
|
||||
e = [start]
|
||||
|
||||
l = l[1:-1]
|
||||
while l:
|
||||
c = l[0]
|
||||
if c == 'SHIFT':
|
||||
e = e + [set[c], shset[l[1]]]
|
||||
l = l[2:]
|
||||
elif c in tos:
|
||||
e.append(set[c])
|
||||
set, shset = setmap[c]
|
||||
l = l[1:]
|
||||
else:
|
||||
e.append(set[c])
|
||||
l = l[1:]
|
||||
|
||||
c = e[0]
|
||||
for i in range(1, len(e)):
|
||||
c = c + i * e[i]
|
||||
self.encoded = e + [c % 103, stop]
|
||||
return self.encoded
|
||||
|
||||
def decompose(self):
|
||||
self.decomposed = ''.join([_patterns[c] for c in self.encoded])
|
||||
return self.decomposed
|
||||
|
||||
def _humanText(self):
|
||||
return self.value
|
||||
|
||||
class Code128Auto(Code128):
|
||||
'''contributed by https://bitbucket.org/kylemacfarlane/
|
||||
see https://bitbucket.org/rptlab/reportlab/issues/69/implementations-of-code-128-auto-and-data
|
||||
'''
|
||||
def encode(self):
|
||||
s = self.validated
|
||||
|
||||
current_set = None
|
||||
l = []
|
||||
value = list(s)
|
||||
while value:
|
||||
c = value.pop(0)
|
||||
if c in digits and value and value[0] in digits:
|
||||
c += value.pop(0)
|
||||
|
||||
if c in setc:
|
||||
set_ = 'C'
|
||||
elif c in setb:
|
||||
set_ = 'B'
|
||||
else:
|
||||
set_ = 'A'
|
||||
|
||||
if current_set != set_:
|
||||
if current_set:
|
||||
l.append('TO_' + set_)
|
||||
else:
|
||||
l.append('START_' + set_)
|
||||
current_set = set_
|
||||
|
||||
l.append(c)
|
||||
l.append('STOP')
|
||||
|
||||
start, set, shset = setmap[l[0]]
|
||||
e = [start]
|
||||
|
||||
l = l[1:-1]
|
||||
while l:
|
||||
c = l[0]
|
||||
if c == 'SHIFT':
|
||||
e = e + [set[c], shset[l[1]]]
|
||||
l = l[2:]
|
||||
elif c in tos:
|
||||
e.append(set[c])
|
||||
set, shset = setmap[c]
|
||||
l = l[1:]
|
||||
else:
|
||||
e.append(set[c])
|
||||
l = l[1:]
|
||||
|
||||
c = e[0]
|
||||
for i in range(1, len(e)):
|
||||
c = c + i * e[i]
|
||||
self.encoded = e + [c % 103, stop]
|
||||
return self.encoded
|
||||
|
||||
if __name__=='__main__':
|
||||
def main():
|
||||
from reportlab.graphics.barcode.code128 import Code128
|
||||
from reportlab.platypus import Spacer, SimpleDocTemplate
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
from reportlab.platypus.paragraph import Paragraph
|
||||
from reportlab.platypus.flowables import KeepTogether
|
||||
styles = getSampleStyleSheet()
|
||||
styleN = styles['Normal']
|
||||
styleH = styles['Heading1']
|
||||
story = []
|
||||
storyAdd = story.append
|
||||
for s in (
|
||||
'BBBB123456BBB',
|
||||
'BBBB12345BBB',
|
||||
'BBBB1234BBB',
|
||||
'BBBB123BBB',
|
||||
'BBBB12BBB',
|
||||
'BBBB1BBB',
|
||||
'BBBB123456aa',
|
||||
'BBBB1234aa',
|
||||
'BBBB123aa',
|
||||
'BBBB12aa',
|
||||
'BBBB1aa',
|
||||
'BBBB123456',
|
||||
'BBBB12345',
|
||||
'BBBB1234',
|
||||
'BBBB123',
|
||||
'BBBB12',
|
||||
'BBBB1',
|
||||
'\xf11234B',
|
||||
'Ba\xf11234B',
|
||||
'Ba12',
|
||||
'Ba123B',
|
||||
'Ba1234B',
|
||||
'BBBB1234567',
|
||||
'BBBB1234567aa',
|
||||
):
|
||||
storyAdd(KeepTogether([Paragraph('Code 128 %r' % s, styleN),Code128(s)]))
|
||||
storyAdd(Spacer(inch,inch))
|
||||
SimpleDocTemplate('code128-out.pdf').build(story)
|
||||
main()
|
||||
@@ -0,0 +1,244 @@
|
||||
#
|
||||
# Copyright (c) 1996-2000 Tyler C. Sarna <tsarna@sarna.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. All advertising materials mentioning features or use of this software
|
||||
# must display the following acknowledgement:
|
||||
# This product includes software developed by Tyler C. Sarna.
|
||||
# 4. Neither the name of the author nor the names of contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.lib.utils import asNative
|
||||
from reportlab.graphics.barcode.common import Barcode
|
||||
from string import ascii_uppercase, ascii_lowercase, digits as string_digits
|
||||
|
||||
_patterns = {
|
||||
'0': ("bsbSBsBsb", 0), '1': ("BsbSbsbsB", 1),
|
||||
'2': ("bsBSbsbsB", 2), '3': ("BsBSbsbsb", 3),
|
||||
'4': ("bsbSBsbsB", 4), '5': ("BsbSBsbsb", 5),
|
||||
'6': ("bsBSBsbsb", 6), '7': ("bsbSbsBsB", 7),
|
||||
'8': ("BsbSbsBsb", 8), '9': ("bsBSbsBsb", 9),
|
||||
'A': ("BsbsbSbsB", 10), 'B': ("bsBsbSbsB", 11),
|
||||
'C': ("BsBsbSbsb", 12), 'D': ("bsbsBSbsB", 13),
|
||||
'E': ("BsbsBSbsb", 14), 'F': ("bsBsBSbsb", 15),
|
||||
'G': ("bsbsbSBsB", 16), 'H': ("BsbsbSBsb", 17),
|
||||
'I': ("bsBsbSBsb", 18), 'J': ("bsbsBSBsb", 19),
|
||||
'K': ("BsbsbsbSB", 20), 'L': ("bsBsbsbSB", 21),
|
||||
'M': ("BsBsbsbSb", 22), 'N': ("bsbsBsbSB", 23),
|
||||
'O': ("BsbsBsbSb", 24), 'P': ("bsBsBsbSb", 25),
|
||||
'Q': ("bsbsbsBSB", 26), 'R': ("BsbsbsBSb", 27),
|
||||
'S': ("bsBsbsBSb", 28), 'T': ("bsbsBsBSb", 29),
|
||||
'U': ("BSbsbsbsB", 30), 'V': ("bSBsbsbsB", 31),
|
||||
'W': ("BSBsbsbsb", 32), 'X': ("bSbsBsbsB", 33),
|
||||
'Y': ("BSbsBsbsb", 34), 'Z': ("bSBsBsbsb", 35),
|
||||
'-': ("bSbsbsBsB", 36), '.': ("BSbsbsBsb", 37),
|
||||
' ': ("bSBsbsBsb", 38), '*': ("bSbsBsBsb", None),
|
||||
'$': ("bSbSbSbsb", 39), '/': ("bSbSbsbSb", 40),
|
||||
'+': ("bSbsbSbSb", 41), '%': ("bsbSbSbSb", 42)
|
||||
}
|
||||
|
||||
_stdchrs = string_digits + ascii_uppercase + "-. $/+%"
|
||||
|
||||
_extended = {
|
||||
'\0': "%U", '\01': "$A", '\02': "$B", '\03': "$C",
|
||||
'\04': "$D", '\05': "$E", '\06': "$F", '\07': "$G",
|
||||
'\010': "$H", '\011': "$I", '\012': "$J", '\013': "$K",
|
||||
'\014': "$L", '\015': "$M", '\016': "$N", '\017': "$O",
|
||||
'\020': "$P", '\021': "$Q", '\022': "$R", '\023': "$S",
|
||||
'\024': "$T", '\025': "$U", '\026': "$V", '\027': "$W",
|
||||
'\030': "$X", '\031': "$Y", '\032': "$Z", '\033': "%A",
|
||||
'\034': "%B", '\035': "%C", '\036': "%D", '\037': "%E",
|
||||
'!': "/A", '"': "/B", '#': "/C", '$': "/D",
|
||||
'%': "/E", '&': "/F", '\'': "/G", '(': "/H",
|
||||
')': "/I", '*': "/J", '+': "/K", ',': "/L",
|
||||
'/': "/O", ':': "/Z", ';': "%F", '<': "%G",
|
||||
'=': "%H", '>': "%I", '?': "%J", '@': "%V",
|
||||
'[': "%K", '\\': "%L", ']': "%M", '^': "%N",
|
||||
'_': "%O", '`': "%W", 'a': "+A", 'b': "+B",
|
||||
'c': "+C", 'd': "+D", 'e': "+E", 'f': "+F",
|
||||
'g': "+G", 'h': "+H", 'i': "+I", 'j': "+J",
|
||||
'k': "+K", 'l': "+L", 'm': "+M", 'n': "+N",
|
||||
'o': "+O", 'p': "+P", 'q': "+Q", 'r': "+R",
|
||||
's': "+S", 't': "+T", 'u': "+U", 'v': "+V",
|
||||
'w': "+W", 'x': "+X", 'y': "+Y", 'z': "+Z",
|
||||
'{': "%P", '|': "%Q", '}': "%R", '~': "%S",
|
||||
'\177': "%T"
|
||||
}
|
||||
|
||||
|
||||
_extchrs = _stdchrs + ascii_lowercase + \
|
||||
"\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017" + \
|
||||
"\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" + \
|
||||
"*!'#&\"(),:;<=>?@[\\]^_`{|}~\177"
|
||||
|
||||
def _encode39(value, cksum, stop):
|
||||
v = sum([_patterns[c][1] for c in value]) % 43
|
||||
if cksum:
|
||||
value += _stdchrs[v]
|
||||
if stop: value = '*'+value+'*'
|
||||
return value
|
||||
|
||||
class _Code39Base(Barcode):
|
||||
barWidth = inch * 0.0075
|
||||
lquiet = None
|
||||
rquiet = None
|
||||
quiet = 1
|
||||
gap = None
|
||||
barHeight = None
|
||||
ratio = 2.2
|
||||
checksum = 1
|
||||
bearers = 0.0
|
||||
stop = 1
|
||||
def __init__(self, value = "", **args):
|
||||
value = asNative(value)
|
||||
for k, v in args.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
if self.quiet:
|
||||
if self.lquiet is None:
|
||||
self.lquiet = max(inch * 0.25, self.barWidth * 10.0)
|
||||
self.rquiet = max(inch * 0.25, self.barWidth * 10.0)
|
||||
else:
|
||||
self.lquiet = self.rquiet = 0.0
|
||||
|
||||
Barcode.__init__(self, value)
|
||||
|
||||
def decompose(self):
|
||||
dval = ""
|
||||
for c in self.encoded:
|
||||
dval = dval + _patterns[c][0] + 'i'
|
||||
self.decomposed = dval[:-1]
|
||||
return self.decomposed
|
||||
|
||||
def _humanText(self):
|
||||
return self.stop and self.encoded[1:-1] or self.encoded
|
||||
|
||||
class Standard39(_Code39Base):
|
||||
"""
|
||||
Options that may be passed to constructor:
|
||||
|
||||
value (int, or numeric string required.):
|
||||
The value to encode.
|
||||
|
||||
barWidth (float, default .0075):
|
||||
X-Dimension, or width of the smallest element
|
||||
Minumum is .0075 inch (7.5 mils).
|
||||
|
||||
ratio (float, default 2.2):
|
||||
The ratio of wide elements to narrow elements.
|
||||
Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the
|
||||
barWidth is greater than 20 mils (.02 inch))
|
||||
|
||||
gap (float or None, default None):
|
||||
width of intercharacter gap. None means "use barWidth".
|
||||
|
||||
barHeight (float, see default below):
|
||||
Height of the symbol. Default is the height of the two
|
||||
bearer bars (if they exist) plus the greater of .25 inch
|
||||
or .15 times the symbol's length.
|
||||
|
||||
checksum (bool, default 1):
|
||||
Wether to compute and include the check digit
|
||||
|
||||
bearers (float, in units of barWidth. default 0):
|
||||
Height of bearer bars (horizontal bars along the top and
|
||||
bottom of the barcode). Default is 0 (no bearers).
|
||||
|
||||
quiet (bool, default 1):
|
||||
Wether to include quiet zones in the symbol.
|
||||
|
||||
lquiet (float, see default below):
|
||||
Quiet zone size to left of code, if quiet is true.
|
||||
Default is the greater of .25 inch, or .15 times the symbol's
|
||||
length.
|
||||
|
||||
rquiet (float, defaults as above):
|
||||
Quiet zone size to right left of code, if quiet is true.
|
||||
|
||||
stop (bool, default 1):
|
||||
Whether to include start/stop symbols.
|
||||
|
||||
Sources of Information on Code 39:
|
||||
|
||||
http://www.semiconductor.agilent.com/barcode/sg/Misc/code_39.html
|
||||
http://www.adams1.com/pub/russadam/39code.html
|
||||
http://www.barcodeman.com/c39_1.html
|
||||
|
||||
Official Spec, "ANSI/AIM BC1-1995, USS" is available for US$45 from
|
||||
http://www.aimglobal.org/aimstore/
|
||||
"""
|
||||
def validate(self):
|
||||
vval = [].append
|
||||
self.valid = 1
|
||||
for c in self.value:
|
||||
if c in ascii_lowercase:
|
||||
c = c.upper()
|
||||
if c not in _stdchrs:
|
||||
self.valid = 0
|
||||
continue
|
||||
vval(c)
|
||||
self.validated = ''.join(vval.__self__)
|
||||
return self.validated
|
||||
|
||||
def encode(self):
|
||||
self.encoded = _encode39(self.validated, self.checksum, self.stop)
|
||||
return self.encoded
|
||||
|
||||
class Extended39(_Code39Base):
|
||||
"""
|
||||
Extended Code 39 is a convention for encoding additional characters
|
||||
not present in stanmdard Code 39 by using pairs of characters to
|
||||
represent the characters missing in Standard Code 39.
|
||||
|
||||
See Standard39 for arguments.
|
||||
|
||||
Sources of Information on Extended Code 39:
|
||||
|
||||
http://www.semiconductor.agilent.com/barcode/sg/Misc/xcode_39.html
|
||||
http://www.barcodeman.com/c39_ext.html
|
||||
"""
|
||||
def validate(self):
|
||||
vval = ""
|
||||
self.valid = 1
|
||||
for c in self.value:
|
||||
if c not in _extchrs:
|
||||
self.valid = 0
|
||||
continue
|
||||
vval = vval + c
|
||||
self.validated = vval
|
||||
return vval
|
||||
|
||||
def encode(self):
|
||||
self.encoded = ""
|
||||
for c in self.validated:
|
||||
if c in _extended:
|
||||
self.encoded = self.encoded + _extended[c]
|
||||
elif c in _stdchrs:
|
||||
self.encoded = self.encoded + c
|
||||
else:
|
||||
raise ValueError
|
||||
self.encoded = _encode39(self.encoded, self.checksum,self.stop)
|
||||
return self.encoded
|
||||
@@ -0,0 +1,234 @@
|
||||
#
|
||||
# Copyright (c) 2000 Tyler C. Sarna <tsarna@sarna.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. All advertising materials mentioning features or use of this software
|
||||
# must display the following acknowledgement:
|
||||
# This product includes software developed by Tyler C. Sarna.
|
||||
# 4. Neither the name of the author nor the names of contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.lib.utils import asNative
|
||||
from reportlab.graphics.barcode.common import MultiWidthBarcode
|
||||
|
||||
_patterns = {
|
||||
'0' : ('AcAaAb', 0), '1' : ('AaAbAc', 1), '2' : ('AaAcAb', 2),
|
||||
'3' : ('AaAdAa', 3), '4' : ('AbAaAc', 4), '5' : ('AbAbAb', 5),
|
||||
'6' : ('AbAcAa', 6), '7' : ('AaAaAd', 7), '8' : ('AcAbAa', 8),
|
||||
'9' : ('AdAaAa', 9), 'A' : ('BaAaAc', 10), 'B' : ('BaAbAb', 11),
|
||||
'C' : ('BaAcAa', 12), 'D' : ('BbAaAb', 13), 'E' : ('BbAbAa', 14),
|
||||
'F' : ('BcAaAa', 15), 'G' : ('AaBaAc', 16), 'H' : ('AaBbAb', 17),
|
||||
'I' : ('AaBcAa', 18), 'J' : ('AbBaAb', 19), 'K' : ('AcBaAa', 20),
|
||||
'L' : ('AaAaBc', 21), 'M' : ('AaAbBb', 22), 'N' : ('AaAcBa', 23),
|
||||
'O' : ('AbAaBb', 24), 'P' : ('AcAaBa', 25), 'Q' : ('BaBaAb', 26),
|
||||
'R' : ('BaBbAa', 27), 'S' : ('BaAaBb', 28), 'T' : ('BaAbBa', 29),
|
||||
'U' : ('BbAaBa', 30), 'V' : ('BbBaAa', 31), 'W' : ('AaBaBb', 32),
|
||||
'X' : ('AaBbBa', 33), 'Y' : ('AbBaBa', 34), 'Z' : ('AbCaAa', 35),
|
||||
'-' : ('AbAaCa', 36), '.' : ('CaAaAb', 37), ' ' : ('CaAbAa', 38),
|
||||
'$' : ('CbAaAa', 39), '/' : ('AaBaCa', 40), '+' : ('AaCaBa', 41),
|
||||
'%' : ('BaAaCa', 42), '#' : ('AbAbBa', 43), '!' : ('CaBaAa', 44),
|
||||
'=' : ('CaAaBa', 45), '&' : ('AbBbAa', 46),
|
||||
'start' : ('AaAaDa', -1), 'stop' : ('AaAaDaA', -2)
|
||||
}
|
||||
|
||||
_charsbyval = {}
|
||||
for k, v in _patterns.items():
|
||||
_charsbyval[v[1]] = k
|
||||
|
||||
_extended = {
|
||||
'\x00' : '!U', '\x01' : '#A', '\x02' : '#B', '\x03' : '#C',
|
||||
'\x04' : '#D', '\x05' : '#E', '\x06' : '#F', '\x07' : '#G',
|
||||
'\x08' : '#H', '\x09' : '#I', '\x0a' : '#J', '\x0b' : '#K',
|
||||
'\x0c' : '#L', '\x0d' : '#M', '\x0e' : '#N', '\x0f' : '#O',
|
||||
'\x10' : '#P', '\x11' : '#Q', '\x12' : '#R', '\x13' : '#S',
|
||||
'\x14' : '#T', '\x15' : '#U', '\x16' : '#V', '\x17' : '#W',
|
||||
'\x18' : '#X', '\x19' : '#Y', '\x1a' : '#Z', '\x1b' : '!A',
|
||||
'\x1c' : '!B', '\x1d' : '!C', '\x1e' : '!D', '\x1f' : '!E',
|
||||
'!' : '=A', '"' : '=B', '#' : '=C', '$' : '=D',
|
||||
'%' : '=E', '&' : '=F', '\'' : '=G', '(' : '=H',
|
||||
')' : '=I', '*' : '=J', '+' : '=K', ',' : '=L',
|
||||
'/' : '=O', ':' : '=Z', ';' : '!F', '<' : '!G',
|
||||
'=' : '!H', '>' : '!I', '?' : '!J', '@' : '!V',
|
||||
'[' : '!K', '\\' : '!L', ']' : '!M', '^' : '!N',
|
||||
'_' : '!O', '`' : '!W', 'a' : '&A', 'b' : '&B',
|
||||
'c' : '&C', 'd' : '&D', 'e' : '&E', 'f' : '&F',
|
||||
'g' : '&G', 'h' : '&H', 'i' : '&I', 'j' : '&J',
|
||||
'k' : '&K', 'l' : '&L', 'm' : '&M', 'n' : '&N',
|
||||
'o' : '&O', 'p' : '&P', 'q' : '&Q', 'r' : '&R',
|
||||
's' : '&S', 't' : '&T', 'u' : '&U', 'v' : '&V',
|
||||
'w' : '&W', 'x' : '&X', 'y' : '&Y', 'z' : '&Z',
|
||||
'{' : '!P', '|' : '!Q', '}' : '!R', '~' : '!S',
|
||||
'\x7f' : '!T'
|
||||
}
|
||||
|
||||
def _encode93(str):
|
||||
s = list(str)
|
||||
s.reverse()
|
||||
|
||||
# compute 'C' checksum
|
||||
i = 0; v = 1; c = 0
|
||||
while i < len(s):
|
||||
c = c + v * _patterns[s[i]][1]
|
||||
i = i + 1; v = v + 1
|
||||
if v > 20:
|
||||
v = 1
|
||||
s.insert(0, _charsbyval[c % 47])
|
||||
|
||||
# compute 'K' checksum
|
||||
i = 0; v = 1; c = 0
|
||||
while i < len(s):
|
||||
c = c + v * _patterns[s[i]][1]
|
||||
i = i + 1; v = v + 1
|
||||
if v > 15:
|
||||
v = 1
|
||||
s.insert(0, _charsbyval[c % 47])
|
||||
|
||||
s.reverse()
|
||||
|
||||
return ''.join(s)
|
||||
|
||||
class _Code93Base(MultiWidthBarcode):
|
||||
barWidth = inch * 0.0075
|
||||
lquiet = None
|
||||
rquiet = None
|
||||
quiet = 1
|
||||
barHeight = None
|
||||
stop = 1
|
||||
def __init__(self, value='', **args):
|
||||
|
||||
if type(value) is type(1):
|
||||
value = asNative(value)
|
||||
|
||||
for (k, v) in args.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
if self.quiet:
|
||||
if self.lquiet is None:
|
||||
self.lquiet = max(inch * 0.25, self.barWidth * 10.0)
|
||||
self.rquiet = max(inch * 0.25, self.barWidth * 10.0)
|
||||
else:
|
||||
self.lquiet = self.rquiet = 0.0
|
||||
|
||||
MultiWidthBarcode.__init__(self, value)
|
||||
|
||||
def decompose(self):
|
||||
dval = self.stop and [_patterns['start'][0]] or []
|
||||
dval += [_patterns[c][0] for c in self.encoded]
|
||||
if self.stop: dval.append(_patterns['stop'][0])
|
||||
self.decomposed = ''.join(dval)
|
||||
return self.decomposed
|
||||
|
||||
class Standard93(_Code93Base):
|
||||
"""
|
||||
Code 93 is a Uppercase alphanumeric symbology with some punctuation.
|
||||
See Extended Code 93 for a variant that can represent the entire
|
||||
128 characrter ASCII set.
|
||||
|
||||
Options that may be passed to constructor:
|
||||
|
||||
value (int, or numeric string. required.):
|
||||
The value to encode.
|
||||
|
||||
barWidth (float, default .0075):
|
||||
X-Dimension, or width of the smallest element
|
||||
Minumum is .0075 inch (7.5 mils).
|
||||
|
||||
barHeight (float, see default below):
|
||||
Height of the symbol. Default is the height of the two
|
||||
bearer bars (if they exist) plus the greater of .25 inch
|
||||
or .15 times the symbol's length.
|
||||
|
||||
quiet (bool, default 1):
|
||||
Wether to include quiet zones in the symbol.
|
||||
|
||||
lquiet (float, see default below):
|
||||
Quiet zone size to left of code, if quiet is true.
|
||||
Default is the greater of .25 inch, or 10 barWidth
|
||||
|
||||
rquiet (float, defaults as above):
|
||||
Quiet zone size to right left of code, if quiet is true.
|
||||
|
||||
stop (bool, default 1):
|
||||
Whether to include start/stop symbols.
|
||||
|
||||
Sources of Information on Code 93:
|
||||
|
||||
http://www.semiconductor.agilent.com/barcode/sg/Misc/code_93.html
|
||||
|
||||
Official Spec, "NSI/AIM BC5-1995, USS" is available for US$45 from
|
||||
http://www.aimglobal.org/aimstore/
|
||||
"""
|
||||
def validate(self):
|
||||
vval = ""
|
||||
self.valid = 1
|
||||
for c in self.value.upper():
|
||||
if c not in _patterns:
|
||||
self.valid = 0
|
||||
continue
|
||||
vval = vval + c
|
||||
self.validated = vval
|
||||
return vval
|
||||
|
||||
def encode(self):
|
||||
self.encoded = _encode93(self.validated)
|
||||
return self.encoded
|
||||
|
||||
|
||||
class Extended93(_Code93Base):
|
||||
"""
|
||||
Extended Code 93 is a convention for encoding the entire 128 character
|
||||
set using pairs of characters to represent the characters missing in
|
||||
Standard Code 93. It is very much like Extended Code 39 in that way.
|
||||
|
||||
See Standard93 for arguments.
|
||||
"""
|
||||
|
||||
def validate(self):
|
||||
vval = []
|
||||
self.valid = 1
|
||||
a = vval.append
|
||||
for c in self.value:
|
||||
if c not in _patterns and c not in _extended:
|
||||
self.valid = 0
|
||||
continue
|
||||
a(c)
|
||||
self.validated = ''.join(vval)
|
||||
return self.validated
|
||||
|
||||
def encode(self):
|
||||
self.encoded = ""
|
||||
for c in self.validated:
|
||||
if c in _patterns:
|
||||
self.encoded = self.encoded + c
|
||||
elif c in _extended:
|
||||
self.encoded = self.encoded + _extended[c]
|
||||
else:
|
||||
raise ValueError
|
||||
self.encoded = _encode93(self.encoded)
|
||||
return self.encoded
|
||||
|
||||
def _humanText(self):
|
||||
return self.validated+self.encoded[-2:]
|
||||
@@ -0,0 +1,775 @@
|
||||
#
|
||||
# Copyright (c) 1996-2000 Tyler C. Sarna <tsarna@sarna.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. All advertising materials mentioning features or use of this software
|
||||
# must display the following acknowledgement:
|
||||
# This product includes software developed by Tyler C. Sarna.
|
||||
# 4. Neither the name of the author nor the names of contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
from reportlab.platypus.flowables import Flowable
|
||||
from reportlab.lib.units import inch
|
||||
from string import ascii_lowercase, ascii_uppercase, digits as string_digits
|
||||
|
||||
class Barcode(Flowable):
|
||||
"""Abstract Base for barcodes. Includes implementations of
|
||||
some methods suitable for the more primitive barcode types"""
|
||||
|
||||
fontName = 'Courier'
|
||||
fontSize = 12
|
||||
humanReadable = 0
|
||||
|
||||
def _humanText(self):
|
||||
return self.encoded
|
||||
|
||||
def __init__(self, value='',**kwd):
|
||||
self.value = str(value)
|
||||
|
||||
self._setKeywords(**kwd)
|
||||
if not hasattr(self, 'gap'):
|
||||
self.gap = None
|
||||
|
||||
|
||||
def _calculate(self):
|
||||
self.validate()
|
||||
self.encode()
|
||||
self.decompose()
|
||||
self.computeSize()
|
||||
|
||||
def _setKeywords(self,**kwd):
|
||||
for (k, v) in kwd.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
def validate(self):
|
||||
self.valid = 1
|
||||
self.validated = self.value
|
||||
|
||||
def encode(self):
|
||||
self.encoded = self.validated
|
||||
|
||||
def decompose(self):
|
||||
self.decomposed = self.encoded
|
||||
|
||||
def computeSize(self, *args):
|
||||
barWidth = self.barWidth
|
||||
wx = barWidth * self.ratio
|
||||
|
||||
if self.gap == None:
|
||||
self.gap = barWidth
|
||||
|
||||
w = 0.0
|
||||
|
||||
for c in self.decomposed:
|
||||
if c in 'sb':
|
||||
w = w + barWidth
|
||||
elif c in 'SB':
|
||||
w = w + wx
|
||||
else: # 'i'
|
||||
w = w + self.gap
|
||||
|
||||
if self.barHeight is None:
|
||||
self.barHeight = w * 0.15
|
||||
self.barHeight = max(0.25 * inch, self.barHeight)
|
||||
if self.bearers:
|
||||
self.barHeight = self.barHeight + self.bearers * 2.0 * barWidth
|
||||
|
||||
if self.quiet:
|
||||
w += self.lquiet + self.rquiet
|
||||
|
||||
|
||||
self._height = self.barHeight
|
||||
self._width = w
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
self._calculate()
|
||||
return self._width
|
||||
@width.setter
|
||||
def width(self,v):
|
||||
pass
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
self._calculate()
|
||||
return self._height
|
||||
@height.setter
|
||||
def height(self,v):
|
||||
pass
|
||||
|
||||
def draw(self):
|
||||
self._calculate()
|
||||
barWidth = self.barWidth
|
||||
wx = barWidth * self.ratio
|
||||
|
||||
left = self.quiet and self.lquiet or 0
|
||||
b = self.bearers * barWidth
|
||||
bb = b * 0.5
|
||||
tb = self.barHeight - (b * 1.5)
|
||||
|
||||
for c in self.decomposed:
|
||||
if c == 'i':
|
||||
left = left + self.gap
|
||||
elif c == 's':
|
||||
left = left + barWidth
|
||||
elif c == 'S':
|
||||
left = left + wx
|
||||
elif c == 'b':
|
||||
self.rect(left, bb, barWidth, tb)
|
||||
left = left + barWidth
|
||||
elif c == 'B':
|
||||
self.rect(left, bb, wx, tb)
|
||||
left = left + wx
|
||||
|
||||
if self.bearers:
|
||||
if getattr(self,'bearerBox', None):
|
||||
canv = self.canv
|
||||
if hasattr(canv,'_Gadd'):
|
||||
#this is a widget rect takes other arguments
|
||||
canv.rect(bb, bb, self.width, self.barHeight-b,
|
||||
strokeWidth=b, strokeColor=self.barFillColor or self.barStrokeColor, fillColor=None)
|
||||
else:
|
||||
canv.saveState()
|
||||
canv.setLineWidth(b)
|
||||
canv.rect(bb, bb, self.width, self.barHeight-b, stroke=1, fill=0)
|
||||
canv.restoreState()
|
||||
else:
|
||||
w = self._width - (self.lquiet + self.rquiet)
|
||||
self.rect(self.lquiet, 0, w, b)
|
||||
self.rect(self.lquiet, self.barHeight - b, w, b)
|
||||
|
||||
self.drawHumanReadable()
|
||||
|
||||
def drawHumanReadable(self):
|
||||
if self.humanReadable:
|
||||
#we have text
|
||||
from reportlab.pdfbase.pdfmetrics import getAscent, stringWidth
|
||||
s = str(self._humanText())
|
||||
fontSize = self.fontSize
|
||||
fontName = self.fontName
|
||||
w = stringWidth(s,fontName,fontSize)
|
||||
width = self._width
|
||||
if self.quiet:
|
||||
width -= self.lquiet+self.rquiet
|
||||
x = self.lquiet
|
||||
else:
|
||||
x = 0
|
||||
if w>width: fontSize *= width/float(w)
|
||||
y = 1.07*getAscent(fontName)*fontSize/1000.
|
||||
self.annotate(x+width/2.,-y,s,fontName,fontSize)
|
||||
|
||||
def rect(self, x, y, w, h):
|
||||
self.canv.rect(x, y, w, h, stroke=0, fill=1)
|
||||
|
||||
def annotate(self,x,y,text,fontName,fontSize,anchor='middle'):
|
||||
canv = self.canv
|
||||
canv.saveState()
|
||||
canv.setFont(self.fontName,fontSize)
|
||||
if anchor=='middle': func = 'drawCentredString'
|
||||
elif anchor=='end': func = 'drawRightString'
|
||||
else: func = 'drawString'
|
||||
getattr(canv,func)(x,y,text)
|
||||
canv.restoreState()
|
||||
|
||||
def _checkVal(self, name, v, allowed):
|
||||
if v not in allowed:
|
||||
raise ValueError('%s attribute %s is invalid %r\nnot in allowed %r' % (
|
||||
self.__class__.__name__, name, v, allowed))
|
||||
return v
|
||||
|
||||
class MultiWidthBarcode(Barcode):
|
||||
"""Base for variable-bar-width codes like Code93 and Code128"""
|
||||
|
||||
def computeSize(self, *args):
|
||||
barWidth = self.barWidth
|
||||
oa, oA = ord('a') - 1, ord('A') - 1
|
||||
|
||||
w = 0.0
|
||||
|
||||
for c in self.decomposed:
|
||||
oc = ord(c)
|
||||
if c in ascii_lowercase:
|
||||
w = w + barWidth * (oc - oa)
|
||||
elif c in ascii_uppercase:
|
||||
w = w + barWidth * (oc - oA)
|
||||
|
||||
if self.barHeight is None:
|
||||
self.barHeight = w * 0.15
|
||||
self.barHeight = max(0.25 * inch, self.barHeight)
|
||||
|
||||
if self.quiet:
|
||||
w += self.lquiet + self.rquiet
|
||||
|
||||
self._height = self.barHeight
|
||||
self._width = w
|
||||
|
||||
def draw(self):
|
||||
self._calculate()
|
||||
oa, oA = ord('a') - 1, ord('A') - 1
|
||||
barWidth = self.barWidth
|
||||
left = self.quiet and self.lquiet or 0
|
||||
|
||||
for c in self.decomposed:
|
||||
oc = ord(c)
|
||||
if c in ascii_lowercase:
|
||||
left = left + (oc - oa) * barWidth
|
||||
elif c in ascii_uppercase:
|
||||
w = (oc - oA) * barWidth
|
||||
self.rect(left, 0, w, self.barHeight)
|
||||
left += w
|
||||
self.drawHumanReadable()
|
||||
|
||||
class I2of5(Barcode):
|
||||
"""
|
||||
Interleaved 2 of 5 is a numeric-only barcode. It encodes an even
|
||||
number of digits; if an odd number is given, a 0 is prepended.
|
||||
|
||||
Options that may be passed to constructor:
|
||||
|
||||
value (int, or numeric string required.):
|
||||
The value to encode.
|
||||
|
||||
barWidth (float, default .0075):
|
||||
X-Dimension, or width of the smallest element
|
||||
Minumum is .0075 inch (7.5 mils).
|
||||
|
||||
ratio (float, default 2.2):
|
||||
The ratio of wide elements to narrow elements.
|
||||
Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the
|
||||
barWidth is greater than 20 mils (.02 inch))
|
||||
|
||||
gap (float or None, default None):
|
||||
width of intercharacter gap. None means "use barWidth".
|
||||
|
||||
barHeight (float, see default below):
|
||||
Height of the symbol. Default is the height of the two
|
||||
bearer bars (if they exist) plus the greater of .25 inch
|
||||
or .15 times the symbol's length.
|
||||
|
||||
checksum (bool, default 1):
|
||||
Whether to compute and include the check digit
|
||||
|
||||
bearers (float, in units of barWidth. default 3.0):
|
||||
Height of bearer bars (horizontal bars along the top and
|
||||
bottom of the barcode). Default is 3 x-dimensions.
|
||||
Set to zero for no bearer bars. (Bearer bars help detect
|
||||
misscans, so it is suggested to leave them on).
|
||||
|
||||
bearerBox (bool default False)
|
||||
if true draw a true rectangle of width bearers around the barcode.
|
||||
|
||||
quiet (bool, default 1):
|
||||
Whether to include quiet zones in the symbol.
|
||||
|
||||
lquiet (float, see default below):
|
||||
Quiet zone size to left of code, if quiet is true.
|
||||
Default is the greater of .25 inch, or .15 times the symbol's
|
||||
length.
|
||||
|
||||
rquiet (float, defaults as above):
|
||||
Quiet zone size to right left of code, if quiet is true.
|
||||
|
||||
stop (bool, default 1):
|
||||
Whether to include start/stop symbols.
|
||||
|
||||
Sources of Information on Interleaved 2 of 5:
|
||||
|
||||
http://www.semiconductor.agilent.com/barcode/sg/Misc/i_25.html
|
||||
http://www.adams1.com/pub/russadam/i25code.html
|
||||
|
||||
Official Spec, "ANSI/AIM BC2-1995, USS" is available for US$45 from
|
||||
http://www.aimglobal.org/aimstore/
|
||||
"""
|
||||
|
||||
patterns = {
|
||||
'start' : 'bsbs',
|
||||
'stop' : 'Bsb',
|
||||
|
||||
'B0' : 'bbBBb', 'S0' : 'ssSSs',
|
||||
'B1' : 'BbbbB', 'S1' : 'SsssS',
|
||||
'B2' : 'bBbbB', 'S2' : 'sSssS',
|
||||
'B3' : 'BBbbb', 'S3' : 'SSsss',
|
||||
'B4' : 'bbBbB', 'S4' : 'ssSsS',
|
||||
'B5' : 'BbBbb', 'S5' : 'SsSss',
|
||||
'B6' : 'bBBbb', 'S6' : 'sSSss',
|
||||
'B7' : 'bbbBB', 'S7' : 'sssSS',
|
||||
'B8' : 'BbbBb', 'S8' : 'SssSs',
|
||||
'B9' : 'bBbBb', 'S9' : 'sSsSs'
|
||||
}
|
||||
|
||||
barHeight = None
|
||||
barWidth = inch * 0.0075
|
||||
ratio = 2.2
|
||||
checksum = 1
|
||||
bearers = 3.0
|
||||
bearerBox = False
|
||||
quiet = 1
|
||||
lquiet = None
|
||||
rquiet = None
|
||||
stop = 1
|
||||
|
||||
def __init__(self, value='', **args):
|
||||
|
||||
if type(value) == type(1):
|
||||
value = str(value)
|
||||
|
||||
for k, v in args.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
if self.quiet:
|
||||
if self.lquiet is None:
|
||||
self.lquiet = min(inch * 0.25, self.barWidth * 10.0)
|
||||
self.rquiet = min(inch * 0.25, self.barWidth * 10.0)
|
||||
else:
|
||||
self.lquiet = self.rquiet = 0.0
|
||||
|
||||
Barcode.__init__(self, value)
|
||||
|
||||
def validate(self):
|
||||
vval = ""
|
||||
self.valid = 1
|
||||
for c in self.value.strip():
|
||||
if c not in string_digits:
|
||||
self.valid = 0
|
||||
continue
|
||||
vval = vval + c
|
||||
self.validated = vval
|
||||
return vval
|
||||
|
||||
def encode(self):
|
||||
s = self.validated
|
||||
cs = self.checksum
|
||||
c = len(s)
|
||||
|
||||
#ensure len(result)%2 == 0, checksum included
|
||||
if ((c % 2 == 0) and cs) or ((c % 2 == 1) and not cs):
|
||||
s = '0' + s
|
||||
c += 1
|
||||
|
||||
if cs:
|
||||
c = 3*sum([int(s[i]) for i in range(0,c,2)])+sum([int(s[i]) for i in range(1,c,2)])
|
||||
s += str((10 - c) % 10)
|
||||
|
||||
self.encoded = s
|
||||
|
||||
def decompose(self):
|
||||
dval = self.stop and [self.patterns['start']] or []
|
||||
a = dval.append
|
||||
|
||||
for i in range(0, len(self.encoded), 2):
|
||||
b = self.patterns['B' + self.encoded[i]]
|
||||
s = self.patterns['S' + self.encoded[i+1]]
|
||||
|
||||
for i in range(0, len(b)):
|
||||
a(b[i] + s[i])
|
||||
|
||||
if self.stop: a(self.patterns['stop'])
|
||||
self.decomposed = ''.join(dval)
|
||||
return self.decomposed
|
||||
|
||||
class MSI(Barcode):
|
||||
"""
|
||||
MSI is a numeric-only barcode.
|
||||
|
||||
Options that may be passed to constructor:
|
||||
|
||||
value (int, or numeric string required.):
|
||||
The value to encode.
|
||||
|
||||
barWidth (float, default .0075):
|
||||
X-Dimension, or width of the smallest element
|
||||
|
||||
ratio (float, default 2.2):
|
||||
The ratio of wide elements to narrow elements.
|
||||
|
||||
gap (float or None, default None):
|
||||
width of intercharacter gap. None means "use barWidth".
|
||||
|
||||
barHeight (float, see default below):
|
||||
Height of the symbol. Default is the height of the two
|
||||
bearer bars (if they exist) plus the greater of .25 inch
|
||||
or .15 times the symbol's length.
|
||||
|
||||
checksum (bool, default 1):
|
||||
Wether to compute and include the check digit
|
||||
|
||||
bearers (float, in units of barWidth. default 0):
|
||||
Height of bearer bars (horizontal bars along the top and
|
||||
bottom of the barcode). Default is 0 (no bearers).
|
||||
|
||||
lquiet (float, see default below):
|
||||
Quiet zone size to left of code, if quiet is true.
|
||||
Default is the greater of .25 inch, or 10 barWidths.
|
||||
|
||||
rquiet (float, defaults as above):
|
||||
Quiet zone size to right left of code, if quiet is true.
|
||||
|
||||
stop (bool, default 1):
|
||||
Whether to include start/stop symbols.
|
||||
|
||||
Sources of Information on MSI Bar Code:
|
||||
|
||||
http://www.semiconductor.agilent.com/barcode/sg/Misc/msi_code.html
|
||||
http://www.adams1.com/pub/russadam/plessy.html
|
||||
"""
|
||||
|
||||
patterns = {
|
||||
'start' : 'Bs', 'stop' : 'bSb',
|
||||
|
||||
'0' : 'bSbSbSbS', '1' : 'bSbSbSBs',
|
||||
'2' : 'bSbSBsbS', '3' : 'bSbSBsBs',
|
||||
'4' : 'bSBsbSbS', '5' : 'bSBsbSBs',
|
||||
'6' : 'bSBsBsbS', '7' : 'bSBsBsBs',
|
||||
'8' : 'BsbSbSbS', '9' : 'BsbSbSBs'
|
||||
}
|
||||
|
||||
stop = 1
|
||||
barHeight = None
|
||||
barWidth = inch * 0.0075
|
||||
ratio = 2.2
|
||||
checksum = 1
|
||||
bearers = 0.0
|
||||
quiet = 1
|
||||
lquiet = None
|
||||
rquiet = None
|
||||
|
||||
def __init__(self, value="", **args):
|
||||
|
||||
if type(value) == type(1):
|
||||
value = str(value)
|
||||
|
||||
for k, v in args.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
if self.quiet:
|
||||
if self.lquiet is None:
|
||||
self.lquiet = max(inch * 0.25, self.barWidth * 10.0)
|
||||
self.rquiet = max(inch * 0.25, self.barWidth * 10.0)
|
||||
else:
|
||||
self.lquiet = self.rquiet = 0.0
|
||||
|
||||
Barcode.__init__(self, value)
|
||||
|
||||
def validate(self):
|
||||
vval = ""
|
||||
self.valid = 1
|
||||
for c in self.value.strip():
|
||||
if c not in string_digits:
|
||||
self.valid = 0
|
||||
continue
|
||||
vval = vval + c
|
||||
self.validated = vval
|
||||
return vval
|
||||
|
||||
def encode(self):
|
||||
s = self.validated
|
||||
|
||||
if self.checksum:
|
||||
c = ''
|
||||
for i in range(1, len(s), 2):
|
||||
c = c + s[i]
|
||||
d = str(int(c) * 2)
|
||||
t = 0
|
||||
for c in d:
|
||||
t = t + int(c)
|
||||
for i in range(0, len(s), 2):
|
||||
t = t + int(s[i])
|
||||
c = 10 - (t % 10)
|
||||
|
||||
s = s + str(c)
|
||||
|
||||
self.encoded = s
|
||||
|
||||
def decompose(self):
|
||||
dval = self.stop and [self.patterns['start']] or []
|
||||
dval += [self.patterns[c] for c in self.encoded]
|
||||
if self.stop: dval.append(self.patterns['stop'])
|
||||
self.decomposed = ''.join(dval)
|
||||
return self.decomposed
|
||||
|
||||
class Codabar(Barcode):
|
||||
"""
|
||||
Codabar is a numeric plus some puntuation ("-$:/.+") barcode
|
||||
with four start/stop characters (A, B, C, and D).
|
||||
|
||||
Options that may be passed to constructor:
|
||||
|
||||
value (string required.):
|
||||
The value to encode.
|
||||
|
||||
barWidth (float, default .0065):
|
||||
X-Dimension, or width of the smallest element
|
||||
minimum is 6.5 mils (.0065 inch)
|
||||
|
||||
ratio (float, default 2.0):
|
||||
The ratio of wide elements to narrow elements.
|
||||
|
||||
gap (float or None, default None):
|
||||
width of intercharacter gap. None means "use barWidth".
|
||||
|
||||
barHeight (float, see default below):
|
||||
Height of the symbol. Default is the height of the two
|
||||
bearer bars (if they exist) plus the greater of .25 inch
|
||||
or .15 times the symbol's length.
|
||||
|
||||
checksum (bool, default 0):
|
||||
Whether to compute and include the check digit
|
||||
|
||||
bearers (float, in units of barWidth. default 0):
|
||||
Height of bearer bars (horizontal bars along the top and
|
||||
bottom of the barcode). Default is 0 (no bearers).
|
||||
|
||||
quiet (bool, default 1):
|
||||
Whether to include quiet zones in the symbol.
|
||||
|
||||
stop (bool, default 1):
|
||||
Whether to include start/stop symbols.
|
||||
|
||||
lquiet (float, see default below):
|
||||
Quiet zone size to left of code, if quiet is true.
|
||||
Default is the greater of .25 inch, or 10 barWidth
|
||||
|
||||
rquiet (float, defaults as above):
|
||||
Quiet zone size to right left of code, if quiet is true.
|
||||
|
||||
Sources of Information on Codabar
|
||||
|
||||
http://www.semiconductor.agilent.com/barcode/sg/Misc/codabar.html
|
||||
http://www.barcodeman.com/codabar.html
|
||||
|
||||
Official Spec, "ANSI/AIM BC3-1995, USS" is available for US$45 from
|
||||
http://www.aimglobal.org/aimstore/
|
||||
"""
|
||||
|
||||
patterns = {
|
||||
'0': 'bsbsbSB', '1': 'bsbsBSb', '2': 'bsbSbsB',
|
||||
'3': 'BSbsbsb', '4': 'bsBsbSb', '5': 'BsbsbSb',
|
||||
'6': 'bSbsbsB', '7': 'bSbsBsb', '8': 'bSBsbsb',
|
||||
'9': 'BsbSbsb', '-': 'bsbSBsb', '$': 'bsBSbsb',
|
||||
':': 'BsbsBsB', '/': 'BsBsbsB', '.': 'BsBsBsb',
|
||||
'+': 'bsBsBsB', 'A': 'bsBSbSb', 'B': 'bSbSbsB',
|
||||
'C': 'bsbSbSB', 'D': 'bsbSBSb'
|
||||
}
|
||||
|
||||
values = {
|
||||
'0' : 0, '1' : 1, '2' : 2, '3' : 3, '4' : 4,
|
||||
'5' : 5, '6' : 6, '7' : 7, '8' : 8, '9' : 9,
|
||||
'-' : 10, '$' : 11, ':' : 12, '/' : 13, '.' : 14,
|
||||
'+' : 15, 'A' : 16, 'B' : 17, 'C' : 18, 'D' : 19
|
||||
}
|
||||
|
||||
chars = string_digits + "-$:/.+"
|
||||
|
||||
stop = 1
|
||||
barHeight = None
|
||||
barWidth = inch * 0.0065
|
||||
ratio = 2.0 # XXX ?
|
||||
checksum = 0
|
||||
bearers = 0.0
|
||||
quiet = 1
|
||||
lquiet = None
|
||||
rquiet = None
|
||||
|
||||
def __init__(self, value='', **args):
|
||||
if type(value) == type(1):
|
||||
value = str(value)
|
||||
|
||||
for k, v in args.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
if self.quiet:
|
||||
if self.lquiet is None:
|
||||
self.lquiet = min(inch * 0.25, self.barWidth * 10.0)
|
||||
self.rquiet = min(inch * 0.25, self.barWidth * 10.0)
|
||||
else:
|
||||
self.lquiet = self.rquiet = 0.0
|
||||
|
||||
Barcode.__init__(self, value)
|
||||
|
||||
def validate(self):
|
||||
vval = ""
|
||||
self.valid = 1
|
||||
s = self.value.strip()
|
||||
for i in range(0, len(s)):
|
||||
c = s[i]
|
||||
if c not in self.chars:
|
||||
if ((i != 0) and (i != len(s) - 1)) or (c not in 'ABCD'):
|
||||
self.Valid = 0
|
||||
continue
|
||||
vval = vval + c
|
||||
|
||||
if self.stop:
|
||||
if vval[0] not in 'ABCD':
|
||||
vval = 'A' + vval
|
||||
if vval[-1] not in 'ABCD':
|
||||
vval = vval + vval[0]
|
||||
|
||||
self.validated = vval
|
||||
return vval
|
||||
|
||||
def encode(self):
|
||||
s = self.validated
|
||||
|
||||
if self.checksum:
|
||||
v = sum([self.values[c] for c in s])
|
||||
s += self.chars[v % 16]
|
||||
|
||||
self.encoded = s
|
||||
|
||||
def decompose(self):
|
||||
dval = ''.join([self.patterns[c]+'i' for c in self.encoded])
|
||||
self.decomposed = dval[:-1]
|
||||
return self.decomposed
|
||||
|
||||
class Code11(Barcode):
|
||||
"""
|
||||
Code 11 is an almost-numeric barcode. It encodes the digits 0-9 plus
|
||||
dash ("-"). 11 characters total, hence the name.
|
||||
|
||||
value (int or string required.):
|
||||
The value to encode.
|
||||
|
||||
barWidth (float, default .0075):
|
||||
X-Dimension, or width of the smallest element
|
||||
|
||||
ratio (float, default 2.2):
|
||||
The ratio of wide elements to narrow elements.
|
||||
|
||||
gap (float or None, default None):
|
||||
width of intercharacter gap. None means "use barWidth".
|
||||
|
||||
barHeight (float, see default below):
|
||||
Height of the symbol. Default is the height of the two
|
||||
bearer bars (if they exist) plus the greater of .25 inch
|
||||
or .15 times the symbol's length.
|
||||
|
||||
checksum (0 none, 1 1-digit, 2 2-digit, -1 auto, default -1):
|
||||
How many checksum digits to include. -1 ("auto") means
|
||||
1 if the number of digits is 10 or less, else 2.
|
||||
|
||||
bearers (float, in units of barWidth. default 0):
|
||||
Height of bearer bars (horizontal bars along the top and
|
||||
bottom of the barcode). Default is 0 (no bearers).
|
||||
|
||||
quiet (bool, default 1):
|
||||
Wether to include quiet zones in the symbol.
|
||||
|
||||
lquiet (float, see default below):
|
||||
Quiet zone size to left of code, if quiet is true.
|
||||
Default is the greater of .25 inch, or 10 barWidth
|
||||
|
||||
rquiet (float, defaults as above):
|
||||
Quiet zone size to right left of code, if quiet is true.
|
||||
|
||||
Sources of Information on Code 11:
|
||||
|
||||
http://www.cwi.nl/people/dik/english/codes/barcodes.html
|
||||
"""
|
||||
|
||||
chars = '0123456789-'
|
||||
|
||||
patterns = {
|
||||
'0' : 'bsbsB', '1' : 'BsbsB', '2' : 'bSbsB',
|
||||
'3' : 'BSbsb', '4' : 'bsBsB', '5' : 'BsBsb',
|
||||
'6' : 'bSBsb', '7' : 'bsbSB', '8' : 'BsbSb',
|
||||
'9' : 'Bsbsb', '-' : 'bsBsb', 'S' : 'bsBSb' # Start/Stop
|
||||
}
|
||||
|
||||
values = {
|
||||
'0' : 0, '1' : 1, '2' : 2, '3' : 3, '4' : 4,
|
||||
'5' : 5, '6' : 6, '7' : 7, '8' : 8, '9' : 9,
|
||||
'-' : 10,
|
||||
}
|
||||
|
||||
stop = 1
|
||||
barHeight = None
|
||||
barWidth = inch * 0.0075
|
||||
ratio = 2.2 # XXX ?
|
||||
checksum = -1 # Auto
|
||||
bearers = 0.0
|
||||
quiet = 1
|
||||
lquiet = None
|
||||
rquiet = None
|
||||
def __init__(self, value='', **args):
|
||||
if type(value) == type(1):
|
||||
value = str(value)
|
||||
|
||||
for k, v in args.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
if self.quiet:
|
||||
if self.lquiet is None:
|
||||
self.lquiet = min(inch * 0.25, self.barWidth * 10.0)
|
||||
self.rquiet = min(inch * 0.25, self.barWidth * 10.0)
|
||||
else:
|
||||
self.lquiet = self.rquiet = 0.0
|
||||
|
||||
Barcode.__init__(self, value)
|
||||
|
||||
def validate(self):
|
||||
vval = ""
|
||||
self.valid = 1
|
||||
s = self.value.strip()
|
||||
for i in range(0, len(s)):
|
||||
c = s[i]
|
||||
if c not in self.chars:
|
||||
self.Valid = 0
|
||||
continue
|
||||
vval = vval + c
|
||||
|
||||
self.validated = vval
|
||||
return vval
|
||||
|
||||
def _addCSD(self,s,m):
|
||||
# compute first checksum
|
||||
i = c = 0
|
||||
v = 1
|
||||
V = self.values
|
||||
while i < len(s):
|
||||
c += v * V[s[-(i+1)]]
|
||||
i += 1
|
||||
v += 1
|
||||
if v==m:
|
||||
v = 1
|
||||
return s+self.chars[c % 11]
|
||||
|
||||
def encode(self):
|
||||
s = self.validated
|
||||
|
||||
tcs = self.checksum
|
||||
if tcs<0:
|
||||
self.checksum = tcs = 1+int(len(s)>10)
|
||||
|
||||
if tcs > 0: s = self._addCSD(s,11)
|
||||
if tcs > 1: s = self._addCSD(s,10)
|
||||
|
||||
self.encoded = self.stop and ('S' + s + 'S') or s
|
||||
|
||||
def decompose(self):
|
||||
self.decomposed = ''.join([(self.patterns[c]+'i') for c in self.encoded])[:-1]
|
||||
return self.decomposed
|
||||
|
||||
def _humanText(self):
|
||||
return self.stop and self.encoded[1:-1] or self.encoded
|
||||
@@ -0,0 +1,273 @@
|
||||
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
|
||||
@@ -0,0 +1,574 @@
|
||||
__all__=(
|
||||
'Ean13BarcodeWidget','isEanString',
|
||||
'Ean8BarcodeWidget', 'UPCA', 'Ean5BarcodeWidget', 'ISBNBarcodeWidget',
|
||||
)
|
||||
from reportlab.graphics.shapes import Group, String, Rect
|
||||
from reportlab.lib import colors
|
||||
from reportlab.pdfbase.pdfmetrics import stringWidth
|
||||
from reportlab.lib.validators import isNumber, isColor, isString, Validator, isBoolean, NoneOr
|
||||
from reportlab.lib.attrmap import *
|
||||
from reportlab.graphics.charts.areas import PlotArea
|
||||
from reportlab.lib.units import mm
|
||||
from reportlab.lib.utils import asNative
|
||||
|
||||
#work out a list of manufacturer codes....
|
||||
_eanNumberSystems = [
|
||||
('00-13', 'USA & Canada'),
|
||||
('20-29', 'In-Store Functions'),
|
||||
('30-37', 'France'),
|
||||
('40-44', 'Germany'),
|
||||
('45', 'Japan (also 49)'),
|
||||
('46', 'Russian Federation'),
|
||||
('471', 'Taiwan'),
|
||||
('474', 'Estonia'),
|
||||
('475', 'Latvia'),
|
||||
('477', 'Lithuania'),
|
||||
('479', 'Sri Lanka'),
|
||||
('480', 'Philippines'),
|
||||
('482', 'Ukraine'),
|
||||
('484', 'Moldova'),
|
||||
('485', 'Armenia'),
|
||||
('486', 'Georgia'),
|
||||
('487', 'Kazakhstan'),
|
||||
('489', 'Hong Kong'),
|
||||
('49', 'Japan (JAN-13)'),
|
||||
('50', 'United Kingdom'),
|
||||
('520', 'Greece'),
|
||||
('528', 'Lebanon'),
|
||||
('529', 'Cyprus'),
|
||||
('531', 'Macedonia'),
|
||||
('535', 'Malta'),
|
||||
('539', 'Ireland'),
|
||||
('54', 'Belgium & Luxembourg'),
|
||||
('560', 'Portugal'),
|
||||
('569', 'Iceland'),
|
||||
('57', 'Denmark'),
|
||||
('590', 'Poland'),
|
||||
('594', 'Romania'),
|
||||
('599', 'Hungary'),
|
||||
('600-601', 'South Africa'),
|
||||
('609', 'Mauritius'),
|
||||
('611', 'Morocco'),
|
||||
('613', 'Algeria'),
|
||||
('619', 'Tunisia'),
|
||||
('622', 'Egypt'),
|
||||
('625', 'Jordan'),
|
||||
('626', 'Iran'),
|
||||
('64', 'Finland'),
|
||||
('690-692', 'China'),
|
||||
('70', 'Norway'),
|
||||
('729', 'Israel'),
|
||||
('73', 'Sweden'),
|
||||
('740', 'Guatemala'),
|
||||
('741', 'El Salvador'),
|
||||
('742', 'Honduras'),
|
||||
('743', 'Nicaragua'),
|
||||
('744', 'Costa Rica'),
|
||||
('746', 'Dominican Republic'),
|
||||
('750', 'Mexico'),
|
||||
('759', 'Venezuela'),
|
||||
('76', 'Switzerland'),
|
||||
('770', 'Colombia'),
|
||||
('773', 'Uruguay'),
|
||||
('775', 'Peru'),
|
||||
('777', 'Bolivia'),
|
||||
('779', 'Argentina'),
|
||||
('780', 'Chile'),
|
||||
('784', 'Paraguay'),
|
||||
('785', 'Peru'),
|
||||
('786', 'Ecuador'),
|
||||
('789', 'Brazil'),
|
||||
('80-83', 'Italy'),
|
||||
('84', 'Spain'),
|
||||
('850', 'Cuba'),
|
||||
('858', 'Slovakia'),
|
||||
('859', 'Czech Republic'),
|
||||
('860', 'Yugloslavia'),
|
||||
('869', 'Turkey'),
|
||||
('87', 'Netherlands'),
|
||||
('880', 'South Korea'),
|
||||
('885', 'Thailand'),
|
||||
('888', 'Singapore'),
|
||||
('890', 'India'),
|
||||
('893', 'Vietnam'),
|
||||
('899', 'Indonesia'),
|
||||
('90-91', 'Austria'),
|
||||
('93', 'Australia'),
|
||||
('94', 'New Zealand'),
|
||||
('955', 'Malaysia'),
|
||||
('977', 'International Standard Serial Number for Periodicals (ISSN)'),
|
||||
('978', 'International Standard Book Numbering (ISBN)'),
|
||||
('979', 'International Standard Music Number (ISMN)'),
|
||||
('980', 'Refund receipts'),
|
||||
('981-982', 'Common Currency Coupons'),
|
||||
('99', 'Coupons')
|
||||
]
|
||||
|
||||
manufacturerCodes = {}
|
||||
for (k, v) in _eanNumberSystems:
|
||||
words = k.split('-')
|
||||
if len(words)==2:
|
||||
fromCode = int(words[0])
|
||||
toCode = int(words[1])
|
||||
for code in range(fromCode, toCode+1):
|
||||
manufacturerCodes[code] = v
|
||||
else:
|
||||
manufacturerCodes[int(k)] = v
|
||||
|
||||
def nDigits(n):
|
||||
class _ndigits(Validator):
|
||||
def test(self,x):
|
||||
return type(x) is str and len(x)<=n and len([c for c in x if c in "0123456789"])==n
|
||||
return _ndigits()
|
||||
|
||||
class Ean13BarcodeWidget(PlotArea):
|
||||
codeName = "EAN13"
|
||||
_attrMap = AttrMap(BASE=PlotArea,
|
||||
value = AttrMapValue(nDigits(12), desc='the number'),
|
||||
fontName = AttrMapValue(isString, desc='fontName'),
|
||||
fontSize = AttrMapValue(isNumber, desc='font size'),
|
||||
x = AttrMapValue(isNumber, desc='x-coord'),
|
||||
y = AttrMapValue(isNumber, desc='y-coord'),
|
||||
barFillColor = AttrMapValue(isColor, desc='bar color'),
|
||||
barHeight = AttrMapValue(isNumber, desc='Height of bars.'),
|
||||
barWidth = AttrMapValue(isNumber, desc='Width of bars.'),
|
||||
barStrokeWidth = AttrMapValue(isNumber, desc='Width of bar borders.'),
|
||||
barStrokeColor = AttrMapValue(isColor, desc='Color of bar borders.'),
|
||||
textColor = AttrMapValue(isColor, desc='human readable text color'),
|
||||
humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
|
||||
quiet = AttrMapValue(isBoolean, desc='if quiet zone to be used'),
|
||||
lquiet = AttrMapValue(isBoolean, desc='left quiet zone length'),
|
||||
rquiet = AttrMapValue(isBoolean, desc='right quiet zone length'),
|
||||
)
|
||||
_digits=12
|
||||
_start_right = 7 #for ean-13 left = [0:7] right=[7:13]
|
||||
_nbars = 113
|
||||
barHeight = 25.93*mm #millimeters
|
||||
barWidth = (37.29/_nbars)*mm
|
||||
humanReadable = 1
|
||||
_0csw = 1
|
||||
_1csw = 3
|
||||
|
||||
#Left Hand Digits.
|
||||
_left = ( ("0001101", "0011001", "0010011", "0111101",
|
||||
"0100011", "0110001", "0101111", "0111011",
|
||||
"0110111", "0001011",
|
||||
), #odd left hand digits
|
||||
("0100111", "0110011", "0011011", "0100001",
|
||||
"0011101", "0111001", "0000101", "0010001",
|
||||
"0001001", "0010111"), #even left hand digits
|
||||
)
|
||||
|
||||
_right = ("1110010", "1100110", "1101100", "1000010",
|
||||
"1011100", "1001110", "1010000", "1000100",
|
||||
"1001000", "1110100")
|
||||
|
||||
quiet = 1
|
||||
rquiet = lquiet = None
|
||||
_tail = "101"
|
||||
_sep = "01010"
|
||||
|
||||
_lhconvert={
|
||||
"0": (0,0,0,0,0,0),
|
||||
"1": (0,0,1,0,1,1),
|
||||
"2": (0,0,1,1,0,1),
|
||||
"3": (0,0,1,1,1,0),
|
||||
"4": (0,1,0,0,1,1),
|
||||
"5": (0,1,1,0,0,1),
|
||||
"6": (0,1,1,1,0,0),
|
||||
"7": (0,1,0,1,0,1),
|
||||
"8": (0,1,0,1,1,0),
|
||||
"9": (0,1,1,0,1,0)
|
||||
}
|
||||
fontSize = 8 #millimeters
|
||||
fontName = 'Helvetica'
|
||||
textColor = barFillColor = colors.black
|
||||
barStrokeColor = None
|
||||
barStrokeWidth = 0
|
||||
x = 0
|
||||
y = 0
|
||||
def __init__(self,value='123456789012',**kw):
|
||||
value = str(value) if isinstance(value,int) else asNative(value)
|
||||
self.value=max(self._digits-len(value),0)*'0'+value[:self._digits]
|
||||
for k, v in kw.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
width = property(lambda self: self.barWidth*(self._nbars-18+self._calc_quiet(self.lquiet)+self._calc_quiet(self.rquiet)))
|
||||
|
||||
def wrap(self,aW,aH):
|
||||
return self.width,self.barHeight
|
||||
|
||||
def _encode_left(self,s,a):
|
||||
cp = self._lhconvert[s[0]] #convert the left hand numbers
|
||||
_left = self._left
|
||||
z = ord('0')
|
||||
for i,c in enumerate(s[1:self._start_right]):
|
||||
a(_left[cp[i]][ord(c)-z])
|
||||
|
||||
def _short_bar(self,i):
|
||||
i += 9 - self._lquiet
|
||||
return self.humanReadable and ((12<i<55) or (57<i<101))
|
||||
|
||||
def _calc_quiet(self,v):
|
||||
if self.quiet:
|
||||
if v is None:
|
||||
v = 9
|
||||
else:
|
||||
x = float(max(v,0))/self.barWidth
|
||||
v = int(x)
|
||||
if v-x>0: v += 1
|
||||
else:
|
||||
v = 0
|
||||
return v
|
||||
|
||||
def draw(self):
|
||||
g = Group()
|
||||
gAdd = g.add
|
||||
barWidth = self.barWidth
|
||||
width = self.width
|
||||
barHeight = self.barHeight
|
||||
x = self.x
|
||||
y = self.y
|
||||
gAdd(Rect(x,y,width,barHeight,fillColor=None,strokeColor=None,strokeWidth=0))
|
||||
s = self.value+self._checkdigit(self.value)
|
||||
self._lquiet = lquiet = self._calc_quiet(self.lquiet)
|
||||
rquiet = self._calc_quiet(self.rquiet)
|
||||
b = [lquiet*'0',self._tail] #the signal string
|
||||
a = b.append
|
||||
self._encode_left(s,a)
|
||||
a(self._sep)
|
||||
|
||||
z = ord('0')
|
||||
_right = self._right
|
||||
for c in s[self._start_right:]:
|
||||
a(_right[ord(c)-z])
|
||||
a(self._tail)
|
||||
a(rquiet*'0')
|
||||
|
||||
fontSize = self.fontSize
|
||||
barFillColor = self.barFillColor
|
||||
barStrokeWidth = self.barStrokeWidth
|
||||
barStrokeColor = self.barStrokeColor
|
||||
|
||||
fth = fontSize*1.2
|
||||
b = ''.join(b)
|
||||
|
||||
lrect = None
|
||||
for i,c in enumerate(b):
|
||||
if c=="1":
|
||||
dh = self._short_bar(i) and fth or 0
|
||||
yh = y+dh
|
||||
if lrect and lrect.y==yh:
|
||||
lrect.width += barWidth
|
||||
else:
|
||||
lrect = Rect(x,yh,barWidth,barHeight-dh,fillColor=barFillColor,strokeWidth=barStrokeWidth,strokeColor=barStrokeColor)
|
||||
gAdd(lrect)
|
||||
else:
|
||||
lrect = None
|
||||
x += barWidth
|
||||
|
||||
if self.humanReadable: self._add_human_readable(s,gAdd)
|
||||
return g
|
||||
|
||||
def _add_human_readable(self,s,gAdd):
|
||||
barWidth = self.barWidth
|
||||
fontSize = self.fontSize
|
||||
textColor = self.textColor
|
||||
fontName = self.fontName
|
||||
fth = fontSize*1.2
|
||||
# draw the num below the line.
|
||||
c = s[0]
|
||||
w = stringWidth(c,fontName,fontSize)
|
||||
x = self.x+barWidth*(self._lquiet-8)
|
||||
y = self.y + 0.2*fth
|
||||
|
||||
gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor))
|
||||
x = self.x + (33-9+self._lquiet)*barWidth
|
||||
|
||||
c = s[1:7]
|
||||
gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor,textAnchor='middle'))
|
||||
|
||||
x += 47*barWidth
|
||||
c = s[7:]
|
||||
gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor,textAnchor='middle'))
|
||||
|
||||
def _checkdigit(cls,num):
|
||||
z = ord('0')
|
||||
iSum = cls._0csw*sum([(ord(x)-z) for x in num[::2]]) \
|
||||
+ cls._1csw*sum([(ord(x)-z) for x in num[1::2]])
|
||||
return chr(z+((10-(iSum%10))%10))
|
||||
_checkdigit=classmethod(_checkdigit)
|
||||
|
||||
class Ean8BarcodeWidget(Ean13BarcodeWidget):
|
||||
codeName = "EAN8"
|
||||
_attrMap = AttrMap(BASE=Ean13BarcodeWidget,
|
||||
value = AttrMapValue(nDigits(7), desc='the number'),
|
||||
)
|
||||
_start_right = 4 #for ean-13 left = [0:7] right=[7:13]
|
||||
_nbars = 85
|
||||
_digits=7
|
||||
_0csw = 3
|
||||
_1csw = 1
|
||||
|
||||
def _encode_left(self,s,a):
|
||||
cp = self._lhconvert[s[0]] #convert the left hand numbers
|
||||
_left = self._left[0]
|
||||
z = ord('0')
|
||||
for i,c in enumerate(s[0:self._start_right]):
|
||||
a(_left[ord(c)-z])
|
||||
|
||||
def _short_bar(self,i):
|
||||
i += 9 - self._lquiet
|
||||
return self.humanReadable and ((12<i<41) or (43<i<73))
|
||||
|
||||
def _add_human_readable(self,s,gAdd):
|
||||
barWidth = self.barWidth
|
||||
fontSize = self.fontSize
|
||||
textColor = self.textColor
|
||||
fontName = self.fontName
|
||||
fth = fontSize*1.2
|
||||
# draw the num below the line.
|
||||
y = self.y + 0.2*fth
|
||||
|
||||
x = (26.5-9+self._lquiet)*barWidth
|
||||
|
||||
c = s[0:4]
|
||||
gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor,textAnchor='middle'))
|
||||
|
||||
x = (59.5-9+self._lquiet)*barWidth
|
||||
c = s[4:]
|
||||
gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor,textAnchor='middle'))
|
||||
|
||||
class UPCA(Ean13BarcodeWidget):
|
||||
codeName = "UPCA"
|
||||
_attrMap = AttrMap(BASE=Ean13BarcodeWidget,
|
||||
value = AttrMapValue(nDigits(11), desc='the number'),
|
||||
)
|
||||
_start_right = 6
|
||||
_digits = 11
|
||||
_0csw = 3
|
||||
_1csw = 1
|
||||
_nbars = 1+7*11+2*3+5
|
||||
|
||||
#these methods contributed by Kyle Macfarlane
|
||||
#https://bitbucket.org/kylemacfarlane/
|
||||
def _encode_left(self,s,a):
|
||||
cp = self._lhconvert[s[0]] #convert the left hand numbers
|
||||
_left = self._left[0]
|
||||
z = ord('0')
|
||||
for i,c in enumerate(s[0:self._start_right]):
|
||||
a(_left[ord(c)-z])
|
||||
|
||||
def _short_bar(self,i):
|
||||
i += 9 - self._lquiet
|
||||
return self.humanReadable and ((18<i<55) or (57<i<93))
|
||||
|
||||
def _add_human_readable(self,s,gAdd):
|
||||
barWidth = self.barWidth
|
||||
fontSize = self.fontSize
|
||||
textColor = self.textColor
|
||||
fontName = self.fontName
|
||||
fth = fontSize*1.2
|
||||
# draw the num below the line.
|
||||
c = s[0]
|
||||
w = stringWidth(c,fontName,fontSize)
|
||||
x = self.x+barWidth*(self._lquiet-8)
|
||||
y = self.y + 0.2*fth
|
||||
|
||||
gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor))
|
||||
x = self.x + (38-9+self._lquiet)*barWidth
|
||||
|
||||
c = s[1:6]
|
||||
gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor,textAnchor='middle'))
|
||||
|
||||
x += 36*barWidth
|
||||
c = s[6:11]
|
||||
gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor,textAnchor='middle'))
|
||||
|
||||
x += 32*barWidth
|
||||
c = s[11]
|
||||
gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor))
|
||||
|
||||
class Ean5BarcodeWidget(Ean13BarcodeWidget):
|
||||
"""
|
||||
EAN-5 barcodes can print the human readable price, set:
|
||||
price=True
|
||||
"""
|
||||
codeName = "EAN5"
|
||||
_attrMap = AttrMap(BASE=Ean13BarcodeWidget,
|
||||
price=AttrMapValue(isBoolean,
|
||||
desc='whether to display the price or not'),
|
||||
value=AttrMapValue(nDigits(5), desc='the number'),
|
||||
)
|
||||
_nbars = 48
|
||||
_digits = 5
|
||||
_sep = '01'
|
||||
_tail = '01011'
|
||||
_0csw = 3
|
||||
_1csw = 9
|
||||
|
||||
_lhconvert = {
|
||||
"0": (1, 1, 0, 0, 0),
|
||||
"1": (1, 0, 1, 0, 0),
|
||||
"2": (1, 0, 0, 1, 0),
|
||||
"3": (1, 0, 0, 0, 1),
|
||||
"4": (0, 1, 1, 0, 0),
|
||||
"5": (0, 0, 1, 1, 0),
|
||||
"6": (0, 0, 0, 1, 1),
|
||||
"7": (0, 1, 0, 1, 0),
|
||||
"8": (0, 1, 0, 0, 1),
|
||||
"9": (0, 0, 1, 0, 1)
|
||||
}
|
||||
|
||||
def _checkdigit(cls, num):
|
||||
z = ord('0')
|
||||
iSum = cls._0csw * sum([(ord(x) - z) for x in num[::2]]) \
|
||||
+ cls._1csw * sum([(ord(x) - z) for x in num[1::2]])
|
||||
return chr(z + iSum % 10)
|
||||
|
||||
def _encode_left(self, s, a):
|
||||
check = self._checkdigit(s)
|
||||
cp = self._lhconvert[check]
|
||||
_left = self._left
|
||||
_sep = self._sep
|
||||
z = ord('0')
|
||||
full_code = []
|
||||
for i, c in enumerate(s):
|
||||
full_code.append(_left[cp[i]][ord(c) - z])
|
||||
a(_sep.join(full_code))
|
||||
|
||||
def _short_bar(self, i):
|
||||
i += 9 - self._lquiet
|
||||
return self.humanReadable and ((12 < i < 41) or (43 < i < 73))
|
||||
|
||||
def _add_human_readable(self, s, gAdd):
|
||||
barWidth = self.barWidth
|
||||
fontSize = self.fontSize
|
||||
textColor = self.textColor
|
||||
fontName = self.fontName
|
||||
fth = fontSize * 1.2
|
||||
# draw the num below the line.
|
||||
y = self.y + 0.2 * fth
|
||||
|
||||
x = self.x + (self._nbars + self._lquiet * 2) * barWidth / 2
|
||||
|
||||
gAdd(String(x, y, s, fontName=fontName, fontSize=fontSize,
|
||||
fillColor=textColor, textAnchor='middle'))
|
||||
|
||||
price = getattr(self,'price',None)
|
||||
if price:
|
||||
price = None
|
||||
if s[0] in '3456':
|
||||
price = '$'
|
||||
elif s[0] in '01':
|
||||
price = asNative(b'\xc2\xa3')
|
||||
|
||||
if price is None:
|
||||
return
|
||||
|
||||
price += s[1:3] + '.' + s[3:5]
|
||||
y += self.barHeight
|
||||
gAdd(String(x, y, price, fontName=fontName, fontSize=fontSize,
|
||||
fillColor=textColor, textAnchor='middle'))
|
||||
|
||||
def draw(self):
|
||||
g = Group()
|
||||
gAdd = g.add
|
||||
barWidth = self.barWidth
|
||||
width = self.width
|
||||
barHeight = self.barHeight
|
||||
x = self.x
|
||||
y = self.y
|
||||
gAdd(Rect(x, y, width, barHeight, fillColor=None, strokeColor=None,
|
||||
strokeWidth=0))
|
||||
s = self.value
|
||||
self._lquiet = lquiet = self._calc_quiet(self.lquiet)
|
||||
rquiet = self._calc_quiet(self.rquiet)
|
||||
b = [lquiet * '0' + self._tail] # the signal string
|
||||
a = b.append
|
||||
self._encode_left(s, a)
|
||||
|
||||
a(rquiet * '0')
|
||||
|
||||
fontSize = self.fontSize
|
||||
barFillColor = self.barFillColor
|
||||
barStrokeWidth = self.barStrokeWidth
|
||||
barStrokeColor = self.barStrokeColor
|
||||
|
||||
fth = fontSize * 1.2
|
||||
b = ''.join(b)
|
||||
|
||||
lrect = None
|
||||
for i, c in enumerate(b):
|
||||
if c == "1":
|
||||
dh = fth
|
||||
yh = y + dh
|
||||
if lrect and lrect.y == yh:
|
||||
lrect.width += barWidth
|
||||
else:
|
||||
lrect = Rect(x, yh, barWidth, barHeight - dh,
|
||||
fillColor=barFillColor,
|
||||
strokeWidth=barStrokeWidth,
|
||||
strokeColor=barStrokeColor)
|
||||
gAdd(lrect)
|
||||
else:
|
||||
lrect = None
|
||||
x += barWidth
|
||||
|
||||
if self.humanReadable:
|
||||
self._add_human_readable(s, gAdd)
|
||||
return g
|
||||
|
||||
class ISBNBarcodeWidget(Ean13BarcodeWidget):
|
||||
"""
|
||||
ISBN Barcodes optionally print the EAN-5 supplemental price
|
||||
barcode (with the price in dollars or pounds). Set price to a string
|
||||
that follows the EAN-5 for ISBN spec:
|
||||
|
||||
leading digit 0, 1 = GBP
|
||||
3 = AUD
|
||||
4 = NZD
|
||||
5 = USD
|
||||
6 = CAD
|
||||
next 4 digits = price between 00.00 and 99.98, i.e.:
|
||||
|
||||
price='52499' # $24.99 USD
|
||||
"""
|
||||
codeName = 'ISBN'
|
||||
_attrMap = AttrMap(BASE=Ean13BarcodeWidget,
|
||||
price=AttrMapValue(
|
||||
NoneOr(nDigits(5)),
|
||||
desc='None or the price to display'),
|
||||
)
|
||||
def draw(self):
|
||||
g = Ean13BarcodeWidget.draw(self)
|
||||
|
||||
price = getattr(self,'price',None)
|
||||
if not price:
|
||||
return g
|
||||
|
||||
bounds = g.getBounds()
|
||||
x = bounds[2]
|
||||
pricecode = Ean5BarcodeWidget(x=x, value=price, price=True,
|
||||
humanReadable=True,
|
||||
barHeight=self.barHeight, quiet=self.quiet)
|
||||
g.add(pricecode)
|
||||
return g
|
||||
|
||||
def _add_human_readable(self, s, gAdd):
|
||||
Ean13BarcodeWidget._add_human_readable(self,s, gAdd)
|
||||
barWidth = self.barWidth
|
||||
barHeight = self.barHeight
|
||||
fontSize = self.fontSize
|
||||
textColor = self.textColor
|
||||
fontName = self.fontName
|
||||
fth = fontSize * 1.2
|
||||
y = self.y + 0.2 * fth + barHeight
|
||||
x = self._lquiet * barWidth
|
||||
|
||||
isbn = 'ISBN '
|
||||
segments = [s[0:3], s[3:4], s[4:9], s[9:12], s[12]]
|
||||
isbn += '-'.join(segments)
|
||||
|
||||
gAdd(String(x, y, isbn, fontName=fontName, fontSize=fontSize,
|
||||
fillColor=textColor))
|
||||
@@ -0,0 +1,445 @@
|
||||
#this code contributed by Kyle Macfarlane see
|
||||
#https://bitbucket.org/rptlab/reportlab/issues/69/implementations-of-code-128-auto-and-data
|
||||
__all__= ('ECC200datamatrix',)
|
||||
FACTORS = {
|
||||
5: (228, 48, 15, 111, 62),
|
||||
7: (23, 68, 144, 134, 240, 92, 254),
|
||||
10: (28, 24, 185, 166, 223, 248, 116, 255, 110, 61),
|
||||
11: (175, 138, 205, 12, 194, 168, 39, 245, 60, 97, 120),
|
||||
12: (41, 153, 158, 91, 61, 42, 142, 213, 97, 178, 100, 242),
|
||||
14: (156, 97, 192, 252, 95, 9, 157, 119, 138, 45, 18, 186, 83, 185),
|
||||
18: (83, 195, 100, 39, 188, 75, 66, 61, 241, 213, 109, 129,
|
||||
94, 254, 225, 48, 90, 188),
|
||||
20: (15, 195, 244, 9, 233, 71, 168, 2, 188, 160, 153, 145,
|
||||
253, 79, 108, 82, 27, 174, 186, 172),
|
||||
24: (52, 190, 88, 205, 109, 39, 176, 21, 155, 197, 251, 223, 155,
|
||||
21, 5, 172, 254, 124, 12, 181, 184, 96, 50, 193),
|
||||
28: (211, 231, 43, 97, 71, 96, 103, 174, 37, 151, 170, 53, 75, 34,
|
||||
249, 121, 17, 138, 110, 213, 141, 136, 120, 151, 233, 168, 93, 255),
|
||||
36: (245, 127, 242, 218, 130, 250, 162, 181, 102, 120, 84, 179, 220, 251,
|
||||
80, 182, 229, 18, 2, 4, 68, 33, 101, 137, 95, 119, 115, 44,
|
||||
175, 184, 59, 25, 225, 98, 81, 112),
|
||||
42: (77, 193, 137, 31, 19, 38, 22, 153, 247, 105, 122, 2, 245, 133,
|
||||
242, 8, 175, 95, 100, 9, 167, 105, 214, 111, 57, 121, 21,
|
||||
1, 253, 57, 54, 101, 248, 202, 69, 50, 150, 177, 226, 5, 9, 5),
|
||||
48: (245, 132, 172, 223, 96, 32, 117, 22, 238, 133, 238, 231, 205, 188,
|
||||
237, 87, 191, 106, 16, 147, 118, 23, 37, 90, 170, 205, 131, 88,
|
||||
120, 100, 66, 138, 186, 240, 82, 44, 176, 87, 187, 147, 160, 175,
|
||||
69, 213, 92, 253, 225, 19),
|
||||
56: (175, 9, 223, 238, 12, 17, 220, 208, 100, 29, 175, 170, 230, 192,
|
||||
215, 235, 150, 159, 36, 223, 38, 200, 132, 54, 228, 146, 218, 234,
|
||||
117, 203, 29, 232, 144, 238, 22, 150, 201, 117, 62, 207, 164, 13,
|
||||
137, 245, 127, 67, 247, 28, 155, 43, 203, 107, 233, 53, 143, 46),
|
||||
62: (242, 93, 169, 50, 144, 210, 39, 118, 202, 188, 201, 189, 143, 108,
|
||||
196, 37, 185, 112, 134, 230, 245, 63, 197, 190, 250, 106, 185, 221,
|
||||
175, 64, 114, 71, 161, 44, 147, 6, 27, 218, 51, 63, 87, 10,
|
||||
40, 130, 188, 17, 163, 31, 176, 170, 4, 107, 232, 7, 94, 166,
|
||||
224, 124, 86, 47, 11, 204),
|
||||
68: (220, 228, 173, 89, 251, 149, 159, 56, 89, 33, 147, 244, 154, 36,
|
||||
73, 127, 213, 136, 248, 180, 234, 197, 158, 177, 68, 122, 93, 213,
|
||||
15, 160, 227, 236, 66, 139, 153, 185, 202, 167, 179, 25, 220, 232,
|
||||
96, 210, 231, 136, 223, 239, 181, 241, 59, 52, 172, 25, 49, 232,
|
||||
211, 189, 64, 54, 108, 153, 132, 63, 96, 103, 82, 186)
|
||||
}
|
||||
|
||||
LOGVAL = (
|
||||
-255, 255, 1, 240, 2, 225, 241, 53, 3, 38, 226, 133, 242, 43,
|
||||
54, 210, 4, 195, 39, 114, 227, 106, 134, 28, 243, 140, 44, 23,
|
||||
55, 118, 211, 234, 5, 219, 196, 96, 40, 222, 115, 103, 228, 78,
|
||||
107, 125, 135, 8, 29, 162, 244, 186, 141, 180, 45, 99, 24, 49,
|
||||
56, 13, 119, 153, 212, 199, 235, 91, 6, 76, 220, 217, 197, 11,
|
||||
97, 184, 41, 36, 223, 253, 116, 138, 104, 193, 229, 86, 79, 171,
|
||||
108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84, 245, 173,
|
||||
187, 204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207,
|
||||
57, 147, 14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110,
|
||||
92, 176, 7, 161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152,
|
||||
98, 48, 185, 179, 42, 209, 37, 132, 224, 52, 254, 239, 117, 233,
|
||||
139, 22, 105, 27, 194, 113, 230, 206, 87, 158, 80, 189, 172, 203,
|
||||
109, 175, 166, 62, 127, 247, 146, 66, 137, 192, 35, 252, 10, 183,
|
||||
75, 216, 31, 83, 33, 73, 164, 144, 85, 170, 246, 65, 174, 61,
|
||||
188, 202, 205, 157, 143, 169, 82, 72, 182, 215, 191, 251, 47, 178,
|
||||
89, 151, 101, 94, 160, 123, 26, 112, 232, 21, 51, 238, 208, 131,
|
||||
58, 69, 148, 18, 15, 16, 68, 17, 121, 149, 129, 19, 155, 59,
|
||||
249, 70, 214, 250, 168, 71, 201, 156, 64, 60, 237, 130, 111, 20,
|
||||
93, 122, 177, 150
|
||||
)
|
||||
|
||||
ALOGVAL = (
|
||||
1, 2, 4, 8, 16, 32, 64, 128, 45, 90, 180, 69, 138, 57,
|
||||
114, 228, 229, 231, 227, 235, 251, 219, 155, 27, 54, 108, 216, 157,
|
||||
23, 46, 92, 184, 93, 186, 89, 178, 73, 146, 9, 18, 36, 72,
|
||||
144, 13, 26, 52, 104, 208, 141, 55, 110, 220, 149, 7, 14, 28,
|
||||
56, 112, 224, 237, 247, 195, 171, 123, 246, 193, 175, 115, 230, 225,
|
||||
239, 243, 203, 187, 91, 182, 65, 130, 41, 82, 164, 101, 202, 185,
|
||||
95, 190, 81, 162, 105, 210, 137, 63, 126, 252, 213, 135, 35, 70,
|
||||
140, 53, 106, 212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250,
|
||||
217, 159, 19, 38, 76, 152, 29, 58, 116, 232, 253, 215, 131, 43,
|
||||
86, 172, 117, 234, 249, 223, 147, 11, 22, 44, 88, 176, 77, 154,
|
||||
25, 50, 100, 200, 189, 87, 174, 113, 226, 233, 255, 211, 139, 59,
|
||||
118, 236, 245, 199, 163, 107, 214, 129, 47, 94, 188, 85, 170, 121,
|
||||
242, 201, 191, 83, 166, 97, 194, 169, 127, 254, 209, 143, 51, 102,
|
||||
204, 181, 71, 142, 49, 98, 196, 165, 103, 206, 177, 79, 158, 17,
|
||||
34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161, 111, 222, 145,
|
||||
15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132, 37, 74,
|
||||
148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248,
|
||||
221, 151, 3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207,
|
||||
179, 75, 150, 1
|
||||
)
|
||||
|
||||
from reportlab.graphics.barcode.common import Barcode
|
||||
class ECC200DataMatrix(Barcode):
|
||||
'''This code only supports a Type 12 (44x44) C40 encoded data matrix.
|
||||
This is the size and encoding that Royal Mail wants on all mail from October 1st 2015.
|
||||
see https://bitbucket.org/rptlab/reportlab/issues/69/implementations-of-code-128-auto-and-data
|
||||
'''
|
||||
barWidth = 4
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
Barcode.__init__(self,*args, **kwargs)
|
||||
|
||||
# These values below are hardcoded for a Type 12 44x44 data matrix
|
||||
self.row_modules = 44
|
||||
self.col_modules = 44
|
||||
self.row_regions = 2
|
||||
self.col_regions = 2
|
||||
self.cw_data = 144
|
||||
self.cw_ecc = 56
|
||||
self.row_usable_modules = self.row_modules - self.row_regions * 2
|
||||
self.col_usable_modules = self.col_modules - self.col_regions * 2
|
||||
|
||||
def validate(self):
|
||||
self.valid = 1
|
||||
for c in self.value:
|
||||
if ord(c) > 255:
|
||||
self.valid = 0
|
||||
break
|
||||
else:
|
||||
self.validated = self.value
|
||||
|
||||
def _encode_c40_char(self, char):
|
||||
o = ord(char)
|
||||
encoded = []
|
||||
|
||||
if o == 32 or (o >= 48 and o <= 57) or (o >= 65 and o <= 90):
|
||||
# Stay in set 0
|
||||
if o == 32:
|
||||
encoded.append(o - 29)
|
||||
elif o >= 48 and o <= 57:
|
||||
encoded.append(o - 44)
|
||||
else:
|
||||
encoded.append(o - 51)
|
||||
elif o >= 0 and o <= 31:
|
||||
encoded.append(0) # Shift to set 1
|
||||
encoded.append(o)
|
||||
elif (o >= 33 and o <= 64) or (o >= 91 and o <= 95):
|
||||
encoded.append(1) # Shift to set 2
|
||||
if o >= 33 and o <= 64:
|
||||
encoded.append(o - 33)
|
||||
else:
|
||||
encoded.append(o - 69)
|
||||
elif o >= 96 and o <= 127:
|
||||
encoded.append(2) # Shift to set 3
|
||||
encoded.append(o - 96)
|
||||
elif o >= 128 and o <= 255:
|
||||
# Extended ASCII
|
||||
encoded.append(1) # Shift to set 2
|
||||
encoded.append(30) # Upper shift / hibit
|
||||
encoded += self._encode_c40_char(chr(o - 128))
|
||||
else:
|
||||
raise Exception('Cannot encode %s (%s)' % (char, o))
|
||||
|
||||
return encoded
|
||||
|
||||
def _encode_c40(self, value):
|
||||
encoded = []
|
||||
|
||||
for c in value:
|
||||
encoded += self._encode_c40_char(c)
|
||||
|
||||
while len(encoded) % 3:
|
||||
encoded.append(0) # Fake padding that makes chunking in the next step easier
|
||||
|
||||
codewords = []
|
||||
codewords.append(230) # Switch to C40 encoding
|
||||
|
||||
for i in range(0, len(encoded), 3):
|
||||
chunk = encoded[i:i+3]
|
||||
total = chunk[0] * 1600 + chunk[1] * 40 + chunk[2] + 1
|
||||
codewords.append(total // 256)
|
||||
codewords.append(total % 256)
|
||||
|
||||
codewords.append(254) # End of data
|
||||
|
||||
if len(codewords) > self.cw_data:
|
||||
raise Exception('Too much data to fit into a data matrix of this size')
|
||||
|
||||
if len(codewords) < self.cw_data:
|
||||
# Real padding
|
||||
codewords.append(129) # Start padding
|
||||
while len(codewords) < self.cw_data:
|
||||
r = ((149 * (len(codewords) + 1)) % 253) + 1
|
||||
codewords.append((129 + r) % 254)
|
||||
|
||||
return codewords
|
||||
|
||||
def _gfsum(self, int1, int2):
|
||||
return int1 ^ int2
|
||||
|
||||
def _gfproduct(self, int1, int2):
|
||||
if int1 == 0 or int2 == 0:
|
||||
return 0
|
||||
else:
|
||||
return ALOGVAL[(LOGVAL[int1] + LOGVAL[int2]) % 255]
|
||||
|
||||
def _get_reed_solomon_code(self, data, num_code_words):
|
||||
"""
|
||||
This method is basically verbatim from "huBarcode" which is BSD licensed
|
||||
https://github.com/hudora/huBarcode/blob/master/hubarcode/datamatrix/reedsolomon.py
|
||||
"""
|
||||
cw_factors = FACTORS[num_code_words]
|
||||
code_words = [0] * num_code_words
|
||||
|
||||
for data_word in data:
|
||||
tmp = self._gfsum(data_word, code_words[-1])
|
||||
for j in range(num_code_words - 1, -1, -1):
|
||||
code_words[j] = self._gfproduct(tmp, cw_factors[j])
|
||||
if j > 0:
|
||||
code_words[j] = self._gfsum(code_words[j - 1], code_words[j])
|
||||
|
||||
code_words.reverse()
|
||||
return code_words
|
||||
|
||||
def _get_next_bits(self, data):
|
||||
value = data.pop(0)
|
||||
bits = []
|
||||
for i in range(0, 8):
|
||||
bits.append(value >> i & 1)
|
||||
bits.reverse()
|
||||
return bits
|
||||
|
||||
def _place_bit(self, row, col, bit):
|
||||
if row < 0:
|
||||
row += self.row_usable_modules
|
||||
col += (4 - ((self.row_usable_modules + 4) % 8))
|
||||
|
||||
if col < 0:
|
||||
col += self.col_usable_modules
|
||||
row += (4 - ((self.col_usable_modules + 4) % 8))
|
||||
|
||||
self._matrix[row][col] = bit
|
||||
|
||||
def _place_bit_corner_1(self, data):
|
||||
bits = self._get_next_bits(data)
|
||||
self._place_bit(self.row_usable_modules - 1, 0, bits[0])
|
||||
self._place_bit(self.row_usable_modules - 1, 1, bits[1])
|
||||
self._place_bit(self.row_usable_modules - 1, 2, bits[2])
|
||||
self._place_bit(0, self.col_usable_modules - 2, bits[3])
|
||||
self._place_bit(0, self.col_usable_modules - 1, bits[4])
|
||||
self._place_bit(1, self.col_usable_modules - 1, bits[5])
|
||||
self._place_bit(2, self.col_usable_modules - 1, bits[6])
|
||||
self._place_bit(3, self.col_usable_modules - 1, bits[7])
|
||||
|
||||
def _place_bit_corner_2(self, data):
|
||||
bits = self._get_next_bits(data)
|
||||
self._place_bit(self.row_usable_modules - 3, 0, bits[0])
|
||||
self._place_bit(self.row_usable_modules - 2, 0, bits[1])
|
||||
self._place_bit(self.row_usable_modules - 1, 0, bits[2])
|
||||
self._place_bit(0, self.col_usable_modules - 4, bits[3])
|
||||
self._place_bit(0, self.col_usable_modules - 3, bits[4])
|
||||
self._place_bit(0, self.col_usable_modules - 2, bits[5])
|
||||
self._place_bit(0, self.col_usable_modules - 1, bits[6])
|
||||
self._place_bit(1, self.col_usable_modules - 1, bits[7])
|
||||
|
||||
def _place_bit_corner_3(self, data):
|
||||
bits = self._get_next_bits(data)
|
||||
self._place_bit(self.row_usable_modules - 3, 0, bits[0])
|
||||
self._place_bit(self.row_usable_modules - 2, 0, bits[1])
|
||||
self._place_bit(self.row_usable_modules - 1, 0, bits[2])
|
||||
self._place_bit(0, self.col_usable_modules - 2, bits[3])
|
||||
self._place_bit(0, self.col_usable_modules - 1, bits[4])
|
||||
self._place_bit(1, self.col_usable_modules - 1, bits[5])
|
||||
self._place_bit(2, self.col_usable_modules - 1, bits[6])
|
||||
self._place_bit(3, self.col_usable_modules - 1, bits[7])
|
||||
|
||||
def _place_bit_corner_4(self, data):
|
||||
bits = self._get_next_bits(data)
|
||||
self._place_bit(self.row_usable_modules - 1, 0, bits[0])
|
||||
self._place_bit(self.row_usable_modules - 1, self.col_usable_modules - 1, bits[1])
|
||||
self._place_bit(0, self.col_usable_modules - 3, bits[2])
|
||||
self._place_bit(0, self.col_usable_modules - 2, bits[3])
|
||||
self._place_bit(0, self.col_usable_modules - 1, bits[4])
|
||||
self._place_bit(1, self.col_usable_modules - 3, bits[5])
|
||||
self._place_bit(1, self.col_usable_modules - 2, bits[6])
|
||||
self._place_bit(1, self.col_usable_modules - 1, bits[7])
|
||||
|
||||
def _place_bit_standard(self, data, row, col):
|
||||
bits = self._get_next_bits(data)
|
||||
self._place_bit(row - 2, col - 2, bits[0])
|
||||
self._place_bit(row - 2, col - 1, bits[1])
|
||||
self._place_bit(row - 1, col - 2, bits[2])
|
||||
self._place_bit(row - 1, col - 1, bits[3])
|
||||
self._place_bit(row - 1, col, bits[4])
|
||||
self._place_bit(row, col - 2, bits[5])
|
||||
self._place_bit(row, col - 1, bits[6])
|
||||
self._place_bit(row, col, bits[7])
|
||||
|
||||
def _create_matrix(self, data):
|
||||
"""
|
||||
This method is heavily influenced by "huBarcode" which is BSD licensed
|
||||
https://github.com/hudora/huBarcode/blob/master/hubarcode/datamatrix/placement.py
|
||||
"""
|
||||
rows = self.row_usable_modules
|
||||
cols = self.col_usable_modules
|
||||
|
||||
self._matrix = self._create_empty_matrix(rows, cols)
|
||||
|
||||
row = 4
|
||||
col = 0
|
||||
|
||||
while True:
|
||||
if row == rows and col == 0:
|
||||
self._place_bit_corner_1(data)
|
||||
elif row == (rows - 2) and col == 0 and (cols % 4):
|
||||
self._place_bit_corner_2(data)
|
||||
elif row == (rows - 2) and col == 0 and (cols % 8 == 4):
|
||||
self._place_bit_corner_3(data)
|
||||
elif row == (rows + 4) and col == 2 and (cols % 8 == 0):
|
||||
self._place_bit_corner_4(data)
|
||||
|
||||
while True:
|
||||
if row < rows and col >= 0 and self._matrix[row][col] is None:
|
||||
self._place_bit_standard(data, row, col)
|
||||
|
||||
row -= 2
|
||||
col += 2
|
||||
|
||||
if row < 0 or col >= cols:
|
||||
break
|
||||
|
||||
row += 1
|
||||
col += 3
|
||||
|
||||
while True:
|
||||
if row >= 0 and col < cols and self._matrix[row][col] is None:
|
||||
self._place_bit_standard(data, row, col)
|
||||
|
||||
row += 2
|
||||
col -= 2
|
||||
|
||||
if row >= rows or col < 0:
|
||||
break
|
||||
|
||||
row += 3
|
||||
col += 1
|
||||
|
||||
if row >= rows and col >= cols:
|
||||
break
|
||||
|
||||
for row in self._matrix:
|
||||
for i in range(0, cols):
|
||||
if row[i] is None:
|
||||
row[i] = 0
|
||||
|
||||
return self._matrix
|
||||
|
||||
def _create_data_regions(self, matrix):
|
||||
regions = []
|
||||
col_offset = 0
|
||||
row_offset = 0
|
||||
|
||||
rows = int(self.row_usable_modules / self.row_regions)
|
||||
cols = int(self.col_usable_modules / self.col_regions)
|
||||
|
||||
while col_offset < self.row_regions:
|
||||
while row_offset < self.col_regions:
|
||||
r_offset = col_offset * rows
|
||||
c_offset = row_offset * cols
|
||||
region = matrix[r_offset:rows+r_offset]
|
||||
for i in range(0, len(region)):
|
||||
region[i] = region[i][c_offset:cols+c_offset]
|
||||
regions.append(region)
|
||||
row_offset += 1
|
||||
row_offset = 0
|
||||
col_offset += 1
|
||||
|
||||
return regions
|
||||
|
||||
def _create_empty_matrix(self, row, col):
|
||||
matrix = []
|
||||
for i in range(0, row):
|
||||
matrix.append([None] * col)
|
||||
return matrix
|
||||
|
||||
def _wrap_data_regions_with_finders(self, regions):
|
||||
wrapped = []
|
||||
|
||||
for region in regions:
|
||||
matrix = self._create_empty_matrix(
|
||||
int(self.col_modules / self.col_regions),
|
||||
int(self.row_modules / self.row_regions)
|
||||
)
|
||||
|
||||
for i, rows in enumerate(region):
|
||||
for j, data in enumerate(rows):
|
||||
matrix[i+1][j+1] = data
|
||||
|
||||
for i, row in enumerate(matrix):
|
||||
if i == 0:
|
||||
for j, col in enumerate(row):
|
||||
row[j] = (j + 1) % 2
|
||||
elif i + 1 == len(matrix):
|
||||
for j, col in enumerate(row):
|
||||
row[j] = 1
|
||||
else:
|
||||
row[0] = 1
|
||||
row[-1] = i % 2
|
||||
|
||||
wrapped.append(matrix)
|
||||
|
||||
return wrapped
|
||||
|
||||
def _merge_data_regions(self, regions):
|
||||
merged = []
|
||||
|
||||
for i in range(0, len(regions), self.row_regions):
|
||||
chunk = regions[i:i+self.row_regions]
|
||||
j = 0
|
||||
while j < len(chunk[0]):
|
||||
merged_row = []
|
||||
for row in chunk:
|
||||
merged_row += row[j]
|
||||
merged.append(merged_row)
|
||||
j += 1
|
||||
|
||||
return merged
|
||||
|
||||
def encode(self):
|
||||
if hasattr(self, 'encoded'):
|
||||
return self.encoded
|
||||
|
||||
encoded = self._encode_c40(self.validated)
|
||||
encoded += self._get_reed_solomon_code(encoded, self.cw_ecc)
|
||||
|
||||
matrix = self._create_matrix(encoded)
|
||||
data_regions = self._create_data_regions(matrix)
|
||||
wrapped = self._wrap_data_regions_with_finders(data_regions)
|
||||
self.encoded = self._merge_data_regions(wrapped)
|
||||
|
||||
self.encoded.reverse() # Helpful since PDFs start at bottom left corner
|
||||
|
||||
return self.encoded
|
||||
|
||||
def computeSize(self, *args):
|
||||
self._height = self.row_modules * self.barWidth
|
||||
self._width = self.col_modules * self.barWidth
|
||||
|
||||
def draw(self):
|
||||
for y, row in enumerate(self.encoded):
|
||||
for x, data in enumerate(row):
|
||||
if data:
|
||||
self.rect(
|
||||
self.x + x * self.barWidth,
|
||||
self.y + y * self.barWidth,
|
||||
self.barWidth,
|
||||
self.barWidth
|
||||
)
|
||||
@@ -0,0 +1,77 @@
|
||||
#
|
||||
# Copyright (c) 2000 Tyler C. Sarna <tsarna@sarna.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. All advertising materials mentioning features or use of this software
|
||||
# must display the following acknowledgement:
|
||||
# This product includes software developed by Tyler C. Sarna.
|
||||
# 4. Neither the name of the author nor the names of contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
# . 3 T Tracker
|
||||
# , 2 D Descender
|
||||
# ' 1 A Ascender
|
||||
# | 0 H Ascender/Descender
|
||||
|
||||
_rm_patterns = {
|
||||
"0" : "--||", "1" : "-',|", "2" : "-'|,", "3" : "'-,|",
|
||||
"4" : "'-|,", "5" : "'',,", "6" : "-,'|", "7" : "-|-|",
|
||||
"8" : "-|',", "9" : "',-|", "A" : "',',", "B" : "'|-,",
|
||||
"C" : "-,|'", "D" : "-|,'", "E" : "-||-", "F" : "',,'",
|
||||
"G" : "',|-", "H" : "'|,-", "I" : ",-'|", "J" : ",'-|",
|
||||
"K" : ",'',", "L" : "|--|", "M" : "|-',", "N" : "|'-,",
|
||||
"O" : ",-|'", "P" : ",','", "Q" : ",'|-", "R" : "|-,'",
|
||||
"S" : "|-|-", "T" : "|',-", "U" : ",,''", "V" : ",|-'",
|
||||
"W" : ",|'-", "X" : "|,-'", "Y" : "|,'-", "Z" : "||--",
|
||||
|
||||
# start, stop
|
||||
"(" : "'-,'", ")" : "'|,|"
|
||||
}
|
||||
|
||||
_ozN_patterns = {
|
||||
"0" : "||", "1" : "|'", "2" : "|,", "3" : "'|", "4" : "''",
|
||||
"5" : "',", "6" : ",|", "7" : ",'", "8" : ",,", "9" : ".|"
|
||||
}
|
||||
|
||||
_ozC_patterns = {
|
||||
"A" : "|||", "B" : "||'", "C" : "||,", "D" : "|'|",
|
||||
"E" : "|''", "F" : "|',", "G" : "|,|", "H" : "|,'",
|
||||
"I" : "|,,", "J" : "'||", "K" : "'|'", "L" : "'|,",
|
||||
"M" : "''|", "N" : "'''", "O" : "'',", "P" : "',|",
|
||||
"Q" : "','", "R" : "',,", "S" : ",||", "T" : ",|'",
|
||||
"U" : ",|,", "V" : ",'|", "W" : ",''", "X" : ",',",
|
||||
"Y" : ",,|", "Z" : ",,'", "a" : "|,.", "b" : "|.|",
|
||||
"c" : "|.'", "d" : "|.,", "e" : "|..", "f" : "'|.",
|
||||
"g" : "''.", "h" : "',.", "i" : "'.|", "j" : "'.'",
|
||||
"k" : "'.,", "l" : "'..", "m" : ",|.", "n" : ",'.",
|
||||
"o" : ",,.", "p" : ",.|", "q" : ",.'", "r" : ",.,",
|
||||
"s" : ",..", "t" : ".|.", "u" : ".'.", "v" : ".,.",
|
||||
"w" : "..|", "x" : "..'", "y" : "..,", "z" : "...",
|
||||
"0" : ",,,", "1" : ".||", "2" : ".|'", "3" : ".|,",
|
||||
"4" : ".'|", "5" : ".''", "6" : ".',", "7" : ".,|",
|
||||
"8" : ".,'", "9" : ".,,", " " : "||.", "#" : "|'.",
|
||||
}
|
||||
|
||||
#http://www.auspost.com.au/futurepost/
|
||||
@@ -0,0 +1,195 @@
|
||||
# (c) 2008 Jerome Alet - <alet@librelogiciel.com>
|
||||
# Licensing terms : ReportLab's license.
|
||||
|
||||
from reportlab.graphics.barcode.code39 import Standard39
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.units import cm
|
||||
from string import ascii_uppercase, digits as string_digits
|
||||
|
||||
class BaseLTOLabel(Standard39) :
|
||||
"""
|
||||
Base class for LTO labels.
|
||||
|
||||
Specification taken from "IBM LTO Ultrium Cartridge Label Specification, Revision 3"
|
||||
available on May 14th 2008 from :
|
||||
http://www-1.ibm.com/support/docview.wss?rs=543&context=STCVQ6R&q1=ssg1*&uid=ssg1S7000429&loc=en_US&cs=utf-8&lang=en+en
|
||||
"""
|
||||
LABELWIDTH = 7.9 * cm
|
||||
LABELHEIGHT = 1.7 * cm
|
||||
LABELROUND = 0.15 * cm
|
||||
CODERATIO = 2.75
|
||||
CODENOMINALWIDTH = 7.4088 * cm
|
||||
CODEBARHEIGHT = 1.11 * cm
|
||||
CODEBARWIDTH = 0.0432 * cm
|
||||
CODEGAP = CODEBARWIDTH
|
||||
CODELQUIET = 10 * CODEBARWIDTH
|
||||
CODERQUIET = 10 * CODEBARWIDTH
|
||||
def __init__(self, prefix="",
|
||||
number=None,
|
||||
subtype="1",
|
||||
border=None,
|
||||
checksum=False,
|
||||
availheight=None) :
|
||||
"""
|
||||
Initializes an LTO label.
|
||||
|
||||
prefix : Up to six characters from [A-Z][0-9]. Defaults to "".
|
||||
number : Label's number or None. Defaults to None.
|
||||
subtype : LTO subtype string , e.g. "1" for LTO1. Defaults to "1".
|
||||
border : None, or the width of the label's border. Defaults to None.
|
||||
checksum : Boolean indicates if checksum char has to be printed. Defaults to False.
|
||||
availheight : Available height on the label, or None for automatic. Defaults to None.
|
||||
"""
|
||||
self.height = max(availheight, self.CODEBARHEIGHT)
|
||||
self.border = border
|
||||
if (len(subtype) != 1) \
|
||||
or (subtype not in ascii_uppercase + string_digits) :
|
||||
raise ValueError("Invalid subtype '%s'" % subtype)
|
||||
if ((not number) and (len(prefix) > 6)) \
|
||||
or not prefix.isalnum() :
|
||||
raise ValueError("Invalid prefix '%s'" % prefix)
|
||||
label = "%sL%s" % ((prefix + str(number or 0).zfill(6 - len(prefix)))[:6],
|
||||
subtype)
|
||||
if len(label) != 8 :
|
||||
raise ValueError("Invalid set of parameters (%s, %s, %s)" \
|
||||
% (prefix, number, subtype))
|
||||
self.label = label
|
||||
Standard39.__init__(self,
|
||||
label,
|
||||
ratio=self.CODERATIO,
|
||||
barHeight=self.height,
|
||||
barWidth=self.CODEBARWIDTH,
|
||||
gap=self.CODEGAP,
|
||||
lquiet=self.CODELQUIET,
|
||||
rquiet=self.CODERQUIET,
|
||||
quiet=True,
|
||||
checksum=checksum)
|
||||
|
||||
def drawOn(self, canvas, x, y) :
|
||||
"""Draws the LTO label onto the canvas."""
|
||||
canvas.saveState()
|
||||
canvas.translate(x, y)
|
||||
if self.border :
|
||||
canvas.setLineWidth(self.border)
|
||||
canvas.roundRect(0, 0,
|
||||
self.LABELWIDTH,
|
||||
self.LABELHEIGHT,
|
||||
self.LABELROUND)
|
||||
Standard39.drawOn(self,
|
||||
canvas,
|
||||
(self.LABELWIDTH-self.CODENOMINALWIDTH)/2.0,
|
||||
self.LABELHEIGHT-self.height)
|
||||
canvas.restoreState()
|
||||
|
||||
class VerticalLTOLabel(BaseLTOLabel) :
|
||||
"""
|
||||
A class for LTO labels with rectangular blocks around the tape identifier.
|
||||
"""
|
||||
LABELFONT = ("Helvetica-Bold", 14)
|
||||
BLOCKWIDTH = 1*cm
|
||||
BLOCKHEIGHT = 0.45*cm
|
||||
LINEWIDTH = 0.0125
|
||||
NBBLOCKS = 7
|
||||
COLORSCHEME = ("red",
|
||||
"yellow",
|
||||
"lightgreen",
|
||||
"lightblue",
|
||||
"grey",
|
||||
"orangered",
|
||||
"pink",
|
||||
"darkgreen",
|
||||
"orange",
|
||||
"purple")
|
||||
|
||||
def __init__(self, *args, **kwargs) :
|
||||
"""
|
||||
Initializes the label.
|
||||
|
||||
colored : boolean to determine if blocks have to be colorized.
|
||||
"""
|
||||
if "colored" in kwargs:
|
||||
self.colored = kwargs["colored"]
|
||||
del kwargs["colored"]
|
||||
else :
|
||||
self.colored = False
|
||||
kwargs["availheight"] = self.LABELHEIGHT-self.BLOCKHEIGHT
|
||||
BaseLTOLabel.__init__(self, *args, **kwargs)
|
||||
|
||||
def drawOn(self, canvas, x, y) :
|
||||
"""Draws some blocks around the identifier's characters."""
|
||||
BaseLTOLabel.drawOn(self,
|
||||
canvas,
|
||||
x,
|
||||
y)
|
||||
canvas.saveState()
|
||||
canvas.setLineWidth(self.LINEWIDTH)
|
||||
canvas.setStrokeColorRGB(0, 0, 0)
|
||||
canvas.translate(x, y)
|
||||
xblocks = (self.LABELWIDTH-(self.NBBLOCKS*self.BLOCKWIDTH))/2.0
|
||||
for i in range(self.NBBLOCKS) :
|
||||
(font, size) = self.LABELFONT
|
||||
newfont = self.LABELFONT
|
||||
if i == (self.NBBLOCKS - 1) :
|
||||
part = self.label[i:]
|
||||
(font, size) = newfont
|
||||
size /= 2.0
|
||||
newfont = (font, size)
|
||||
else :
|
||||
part = self.label[i]
|
||||
canvas.saveState()
|
||||
canvas.translate(xblocks+(i*self.BLOCKWIDTH), 0)
|
||||
if self.colored and part.isdigit() :
|
||||
canvas.setFillColorRGB(*getattr(colors,
|
||||
self.COLORSCHEME[int(part)],
|
||||
colors.Color(1, 1, 1)).rgb())
|
||||
else:
|
||||
canvas.setFillColorRGB(1, 1, 1)
|
||||
canvas.rect(0, 0, self.BLOCKWIDTH, self.BLOCKHEIGHT, fill=True)
|
||||
canvas.translate((self.BLOCKWIDTH+canvas.stringWidth(part, *newfont))/2.0,
|
||||
(self.BLOCKHEIGHT/2.0))
|
||||
canvas.rotate(90.0)
|
||||
canvas.setFont(*newfont)
|
||||
canvas.setFillColorRGB(0, 0, 0)
|
||||
canvas.drawCentredString(0, 0, part)
|
||||
canvas.restoreState()
|
||||
canvas.restoreState()
|
||||
|
||||
def test() :
|
||||
"""Test this."""
|
||||
from reportlab.pdfgen.canvas import Canvas
|
||||
from reportlab.lib import pagesizes
|
||||
|
||||
canvas = Canvas("labels.pdf", pagesize=pagesizes.A4)
|
||||
canvas.setFont("Helvetica", 30)
|
||||
(width, height) = pagesizes.A4
|
||||
canvas.drawCentredString(width/2.0, height-4*cm, "Sample LTO labels")
|
||||
xpos = xorig = 2 * cm
|
||||
ypos = yorig = 2 * cm
|
||||
colwidth = 10 * cm
|
||||
lineheight = 3.9 * cm
|
||||
count = 1234
|
||||
BaseLTOLabel("RL", count, "3").drawOn(canvas, xpos, ypos)
|
||||
ypos += lineheight
|
||||
count += 1
|
||||
BaseLTOLabel("RL", count, "3",
|
||||
border=0.0125).drawOn(canvas, xpos, ypos)
|
||||
ypos += lineheight
|
||||
count += 1
|
||||
VerticalLTOLabel("RL", count, "3").drawOn(canvas, xpos, ypos)
|
||||
ypos += lineheight
|
||||
count += 1
|
||||
VerticalLTOLabel("RL", count, "3",
|
||||
border=0.0125).drawOn(canvas, xpos, ypos)
|
||||
ypos += lineheight
|
||||
count += 1
|
||||
VerticalLTOLabel("RL", count, "3",
|
||||
colored=True).drawOn(canvas, xpos, ypos)
|
||||
ypos += lineheight
|
||||
count += 1
|
||||
VerticalLTOLabel("RL", count, "3",
|
||||
border=0.0125, colored=True).drawOn(canvas, xpos, ypos)
|
||||
canvas.showPage()
|
||||
canvas.save()
|
||||
|
||||
if __name__ == "__main__" :
|
||||
test()
|
||||
@@ -0,0 +1,197 @@
|
||||
#
|
||||
# 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, Validator
|
||||
from reportlab.lib.attrmap import AttrMap, AttrMapValue
|
||||
from reportlab.graphics.widgetbase import Widget
|
||||
from reportlab.lib.units import mm
|
||||
from reportlab.lib.utils import asUnicodeEx, isUnicode
|
||||
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)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,280 @@
|
||||
#!/usr/pkg/bin/python
|
||||
|
||||
import sys, time
|
||||
|
||||
from reportlab import Version as __RL_Version__
|
||||
from reportlab.graphics.barcode.common import *
|
||||
from reportlab.graphics.barcode.code39 import *
|
||||
from reportlab.graphics.barcode.code93 import *
|
||||
from reportlab.graphics.barcode.code128 import *
|
||||
from reportlab.graphics.barcode.usps import *
|
||||
from reportlab.graphics.barcode.usps4s import USPS_4State
|
||||
from reportlab.graphics.barcode.qr import QrCodeWidget
|
||||
from reportlab.graphics.barcode.dmtx import DataMatrixWidget, pylibdmtx
|
||||
|
||||
from reportlab.platypus import Spacer, SimpleDocTemplate, PageBreak
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.lib import colors
|
||||
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
from reportlab.platypus.paragraph import Paragraph
|
||||
from reportlab.platypus.flowables import XBox, KeepTogether
|
||||
from reportlab.graphics.shapes import Drawing, Rect, Line
|
||||
|
||||
from reportlab.graphics.barcode import getCodeNames, createBarcodeDrawing, createBarcodeImageInMemory
|
||||
|
||||
def run():
|
||||
styles = getSampleStyleSheet()
|
||||
styleN = styles['Normal']
|
||||
styleH = styles['Heading1']
|
||||
story = []
|
||||
storyAdd = story.append
|
||||
|
||||
#for codeNames in code
|
||||
storyAdd(Paragraph('I2of5', styleN))
|
||||
storyAdd(I2of5('05400141288766', barWidth = inch*0.02, checksum=0))
|
||||
storyAdd(I2of5('0001234567890', barWidth = inch*0.02, checksum=1))
|
||||
storyAdd(I2of5('00012345678905', barWidth = inch*0.02, checksum=0))
|
||||
storyAdd(I2of5('0001234567890', barWidth = inch*0.02, checksum=1, bearerBox = True))
|
||||
|
||||
|
||||
storyAdd(Paragraph('MSI', styleN))
|
||||
storyAdd(MSI(1234))
|
||||
|
||||
storyAdd(Paragraph('Codabar', styleN))
|
||||
storyAdd(Codabar("A012345B", barWidth = inch*0.02))
|
||||
|
||||
storyAdd(Paragraph('Code 11', styleN))
|
||||
storyAdd(Code11("01234545634563"))
|
||||
|
||||
storyAdd(Paragraph('Code 39', styleN))
|
||||
storyAdd(Standard39("A012345B%R"))
|
||||
|
||||
storyAdd(Paragraph('Extended Code 39', styleN))
|
||||
storyAdd(Extended39("A012345B}"))
|
||||
|
||||
storyAdd(Paragraph('Code93', styleN))
|
||||
storyAdd(Standard93("CODE 93"))
|
||||
|
||||
storyAdd(Paragraph('Extended Code93', styleN))
|
||||
storyAdd(Extended93("L@@K! Code 93 :-)")) #, barWidth=0.005 * inch))
|
||||
|
||||
storyAdd(Paragraph('Code 128', styleN))
|
||||
storyAdd(Code128("AB-12345678"))
|
||||
|
||||
storyAdd(Paragraph('Code 128 Auto', styleN))
|
||||
storyAdd(Code128Auto("AB-12345678"))
|
||||
|
||||
storyAdd(Paragraph('USPS FIM', styleN))
|
||||
storyAdd(FIM("A"))
|
||||
|
||||
storyAdd(Paragraph('USPS POSTNET', styleN))
|
||||
storyAdd(POSTNET('78247-1043'))
|
||||
|
||||
storyAdd(Paragraph('USPS 4 State', styleN))
|
||||
storyAdd(USPS_4State('01234567094987654321','01234567891'))
|
||||
|
||||
from reportlab.graphics.barcode import createBarcodeDrawing
|
||||
|
||||
storyAdd(Paragraph('EAN13', styleN))
|
||||
storyAdd(createBarcodeDrawing('EAN13', value='123456789012'))
|
||||
|
||||
storyAdd(Paragraph('EAN13 quiet=False', styleN))
|
||||
storyAdd(createBarcodeDrawing('EAN13', value='123456789012', quiet=False))
|
||||
|
||||
storyAdd(Paragraph('EAN8', styleN))
|
||||
storyAdd(createBarcodeDrawing('EAN8', value='1234567'))
|
||||
|
||||
storyAdd(PageBreak())
|
||||
|
||||
storyAdd(Paragraph('EAN5 price=True', styleN))
|
||||
storyAdd(createBarcodeDrawing('EAN5', value='11299', price=True))
|
||||
|
||||
storyAdd(Paragraph('EAN5 price=True quiet=False', styleN))
|
||||
storyAdd(createBarcodeDrawing('EAN5', value='11299', price=True, quiet=False))
|
||||
|
||||
storyAdd(Paragraph('EAN5 price=False', styleN))
|
||||
storyAdd(createBarcodeDrawing('EAN5', value='11299', price=False))
|
||||
|
||||
storyAdd(Paragraph('ISBN alone', styleN))
|
||||
storyAdd(createBarcodeDrawing('ISBN', value='9781565924796'))
|
||||
|
||||
storyAdd(Paragraph('ISBN with ean5 price', styleN))
|
||||
storyAdd(createBarcodeDrawing('ISBN', value='9781565924796',price='01299'))
|
||||
|
||||
storyAdd(Paragraph('ISBN with ean5 price, quiet=False', styleN))
|
||||
storyAdd(createBarcodeDrawing('ISBN', value='9781565924796',price='01299',quiet=False))
|
||||
|
||||
storyAdd(Paragraph('UPCA', styleN))
|
||||
storyAdd(createBarcodeDrawing('UPCA', value='03600029145'))
|
||||
|
||||
storyAdd(Paragraph('USPS_4State', styleN))
|
||||
storyAdd(createBarcodeDrawing('USPS_4State', value='01234567094987654321',routing='01234567891'))
|
||||
|
||||
storyAdd(Paragraph('QR', styleN))
|
||||
storyAdd(createBarcodeDrawing('QR', value='01234567094987654321'))
|
||||
|
||||
storyAdd(Paragraph('QR', styleN))
|
||||
storyAdd(createBarcodeDrawing('QR', value='01234567094987654321',x=30,y=50))
|
||||
|
||||
def addCross(d,x,y,w=5,h=5, strokeColor='black', strokeWidth=0.5):
|
||||
w *= 0.5
|
||||
h *= 0.5
|
||||
d.add(Line(x-w,y,x+w,y,strokeWidth=0.5,strokeColor=colors.blue))
|
||||
d.add(Line(x, y-h, x, y+h,strokeWidth=0.5,strokeColor=colors.blue))
|
||||
storyAdd(Paragraph('QR in drawing at (0,0)', styleN))
|
||||
d = Drawing(100,100)
|
||||
d.add(Rect(0,0,100,100,strokeWidth=1,strokeColor=colors.red,fillColor=None))
|
||||
d.add(QrCodeWidget(value='01234567094987654321'))
|
||||
storyAdd(d)
|
||||
|
||||
storyAdd(Paragraph('QR in drawing at (10,10)', styleN))
|
||||
d = Drawing(100,100)
|
||||
d.add(Rect(0,0,100,100,strokeWidth=1,strokeColor=colors.red,fillColor=None))
|
||||
addCross(d,10,10)
|
||||
d.add(QrCodeWidget(value='01234567094987654321',x=10,y=10))
|
||||
storyAdd(d)
|
||||
|
||||
storyAdd(Paragraph('Label Size', styleN))
|
||||
storyAdd(XBox((2.0 + 5.0/8.0)*inch, 1 * inch, '1x2-5/8"'))
|
||||
|
||||
storyAdd(Paragraph('Label Size', styleN))
|
||||
storyAdd(XBox((1.75)*inch, .5 * inch, '1/2x1-3/4"'))
|
||||
|
||||
if pylibdmtx:
|
||||
storyAdd(PageBreak())
|
||||
storyAdd(Paragraph('DataMatrix in drawing at (10,10)', styleN))
|
||||
d = Drawing(100,100)
|
||||
d.add(Rect(0,0,100,100,strokeWidth=1,strokeColor=colors.red,fillColor=None))
|
||||
addCross(d,10,10)
|
||||
d.add(DataMatrixWidget(value='1234567890',x=10,y=10))
|
||||
storyAdd(d)
|
||||
storyAdd(Paragraph('DataMatrix in drawing at (10,10)', styleN))
|
||||
d = Drawing(100,100)
|
||||
d.add(Rect(0,0,100,100,strokeWidth=1,strokeColor=colors.red,fillColor=None))
|
||||
addCross(d,10,10)
|
||||
d.add(DataMatrixWidget(value='1234567890',x=10,y=10,color='black',bgColor='lime'))
|
||||
storyAdd(d)
|
||||
|
||||
storyAdd(Paragraph('DataMatrix in drawing at (90,90) anchor=ne', styleN))
|
||||
d = Drawing(100,100)
|
||||
d.add(Rect(0,0,100,100,strokeWidth=1,strokeColor=colors.red,fillColor=None))
|
||||
addCross(d,90,90)
|
||||
d.add(DataMatrixWidget(value='1234567890',x=90,y=90,color='darkblue',bgColor='yellow', anchor='ne'))
|
||||
storyAdd(d)
|
||||
|
||||
|
||||
SimpleDocTemplate('out.pdf').build(story)
|
||||
print('saved out.pdf')
|
||||
|
||||
def fullTest(fileName="test_full.pdf"):
|
||||
"""Creates large-ish test document with a variety of parameters"""
|
||||
|
||||
story = []
|
||||
|
||||
styles = getSampleStyleSheet()
|
||||
styleN = styles['Normal']
|
||||
styleH = styles['Heading1']
|
||||
styleH2 = styles['Heading2']
|
||||
story = []
|
||||
|
||||
story.append(Paragraph('ReportLab %s Barcode Test Suite - full output' % __RL_Version__,styleH))
|
||||
story.append(Paragraph('Generated at %s' % time.ctime(time.time()), styleN))
|
||||
|
||||
story.append(Paragraph('About this document', styleH2))
|
||||
story.append(Paragraph('History and Status', styleH2))
|
||||
|
||||
story.append(Paragraph("""
|
||||
This is the test suite and docoumentation for the ReportLab open source barcode API.
|
||||
""", styleN))
|
||||
|
||||
story.append(Paragraph("""
|
||||
Several years ago Ty Sarna contributed a barcode module to the ReportLab community.
|
||||
Several of the codes were used by him in hiw work and to the best of our knowledge
|
||||
this was correct. These were written as flowable objects and were available in PDFs,
|
||||
but not in our graphics framework. However, we had no knowledge of barcodes ourselves
|
||||
and did not advertise or extend the package.
|
||||
""", styleN))
|
||||
|
||||
story.append(Paragraph("""
|
||||
We "wrapped" the barcodes to be usable within our graphics framework; they are now available
|
||||
as Drawing objects which can be rendered to EPS files or bitmaps. For the last 2 years this
|
||||
has been available in our Diagra and Report Markup Language products. However, we did not
|
||||
charge separately and use was on an "as is" basis.
|
||||
""", styleN))
|
||||
|
||||
story.append(Paragraph("""
|
||||
A major licensee of our technology has kindly agreed to part-fund proper productisation
|
||||
of this code on an open source basis in Q1 2006. This has involved addition of EAN codes
|
||||
as well as a proper testing program. Henceforth we intend to publicise the code more widely,
|
||||
gather feedback, accept contributions of code and treat it as "supported".
|
||||
""", styleN))
|
||||
|
||||
story.append(Paragraph("""
|
||||
This involved making available both downloads and testing resources. This PDF document
|
||||
is the output of the current test suite. It contains codes you can scan (if you use a nice sharp
|
||||
laser printer!), and will be extended over coming weeks to include usage examples and notes on
|
||||
each barcode and how widely tested they are. This is being done through documentation strings in
|
||||
the barcode objects themselves so should always be up to date.
|
||||
""", styleN))
|
||||
|
||||
story.append(Paragraph('Usage examples', styleH2))
|
||||
story.append(Paragraph("""
|
||||
To be completed
|
||||
""", styleN))
|
||||
|
||||
story.append(Paragraph('The codes', styleH2))
|
||||
story.append(Paragraph("""
|
||||
Below we show a scannable code from each barcode, with and without human-readable text.
|
||||
These are magnified about 2x from the natural size done by the original author to aid
|
||||
inspection. This will be expanded to include several test cases per code, and to add
|
||||
explanations of checksums. Be aware that (a) if you enter numeric codes which are too
|
||||
short they may be prefixed for you (e.g. "123" for an 8-digit code becomes "00000123"),
|
||||
and that the scanned results and readable text will generally include extra checksums
|
||||
at the end.
|
||||
""", styleN))
|
||||
|
||||
codeNames = getCodeNames()
|
||||
from reportlab.lib.utils import flatten
|
||||
width = [float(x[8:]) for x in sys.argv if x.startswith('--width=')]
|
||||
height = [float(x[9:]) for x in sys.argv if x.startswith('--height=')]
|
||||
isoScale = [int(x[11:]) for x in sys.argv if x.startswith('--isoscale=')]
|
||||
options = {}
|
||||
if width: options['width'] = width[0]
|
||||
if height: options['height'] = height[0]
|
||||
if isoScale: options['isoScale'] = isoScale[0]
|
||||
scales = [x[8:].split(',') for x in sys.argv if x.startswith('--scale=')]
|
||||
scales = list(map(float,scales and flatten(scales) or [1]))
|
||||
scales = list(map(float,scales and flatten(scales) or [1]))
|
||||
for scale in scales:
|
||||
story.append(PageBreak())
|
||||
story.append(Paragraph('Scale = %.1f'%scale, styleH2))
|
||||
story.append(Spacer(36, 12))
|
||||
for codeName in codeNames:
|
||||
s = [Paragraph('Code: ' + codeName, styleH2)]
|
||||
for hr in (0,1):
|
||||
s.append(Spacer(36, 12))
|
||||
dr = createBarcodeDrawing(codeName, humanReadable=hr,**options)
|
||||
dr.renderScale = scale
|
||||
s.append(dr)
|
||||
s.append(Spacer(36, 12))
|
||||
s.append(Paragraph('Barcode should say: ' + dr._bc.value, styleN))
|
||||
story.append(KeepTogether(s))
|
||||
|
||||
SimpleDocTemplate(fileName).build(story)
|
||||
print('created', fileName)
|
||||
|
||||
if __name__=='__main__':
|
||||
run()
|
||||
fullTest()
|
||||
def createSample(name,memory):
|
||||
f = open(name,'wb')
|
||||
f.write(memory)
|
||||
f.close()
|
||||
createSample('test_cbcim.png',createBarcodeImageInMemory('EAN13', value='123456789012'))
|
||||
createSample('test_cbcim.gif',createBarcodeImageInMemory('EAN8', value='1234567', format='gif'))
|
||||
createSample('test_cbcim.pdf',createBarcodeImageInMemory('UPCA', value='03600029145',format='pdf', barHeight=40))
|
||||
createSample('test_cbcim.tiff',createBarcodeImageInMemory('USPS_4State', value='01234567094987654321',routing='01234567891',format='tiff'))
|
||||
createSample('test_cbcim-1.pdf',createBarcodeImageInMemory('QR',
|
||||
value='This is the end my only friend the end at the end of the Universe.',format='pdf'))
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
#
|
||||
# Copyright (c) 1996-2000 Tyler C. Sarna <tsarna@sarna.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. All advertising materials mentioning features or use of this software
|
||||
# must display the following acknowledgement:
|
||||
# This product includes software developed by Tyler C. Sarna.
|
||||
# 4. Neither the name of the author nor the names of contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.graphics.barcode.common import Barcode
|
||||
from string import digits as string_digits, whitespace as string_whitespace
|
||||
from reportlab.lib.utils import asNative
|
||||
|
||||
_fim_patterns = {
|
||||
'A' : "|| | ||",
|
||||
'B' : "| || || |",
|
||||
'C' : "|| | | ||",
|
||||
'D' : "||| | |||",
|
||||
# XXX There is an E.
|
||||
# The below has been seen, but dunno if it is E or not:
|
||||
# 'E' : '|||| ||||'
|
||||
}
|
||||
|
||||
_postnet_patterns = {
|
||||
'1' : "...||", '2' : "..|.|", '3' : "..||.", '4' : ".|..|",
|
||||
'5' : ".|.|.", '6' : ".||..", '7' : "|...|", '8' : "|..|.",
|
||||
'9' : "|.|..", '0' : "||...", 'S' : "|",
|
||||
}
|
||||
|
||||
class FIM(Barcode):
|
||||
"""
|
||||
FIM (Facing ID Marks) encode only one letter.
|
||||
There are currently four defined:
|
||||
|
||||
A Courtesy reply mail with pre-printed POSTNET
|
||||
B Business reply mail without pre-printed POSTNET
|
||||
C Business reply mail with pre-printed POSTNET
|
||||
D OCR Readable mail without pre-printed POSTNET
|
||||
|
||||
Options that may be passed to constructor:
|
||||
|
||||
value (single character string from the set A - D. required.):
|
||||
The value to encode.
|
||||
|
||||
quiet (bool, default 0):
|
||||
Whether to include quiet zones in the symbol.
|
||||
|
||||
The following may also be passed, but doing so will generate nonstandard
|
||||
symbols which should not be used. This is mainly documented here to
|
||||
show the defaults:
|
||||
|
||||
barHeight (float, default 5/8 inch):
|
||||
Height of the code. This might legitimately be overriden to make
|
||||
a taller symbol that will 'bleed' off the edge of the paper,
|
||||
leaving 5/8 inch remaining.
|
||||
|
||||
lquiet (float, default 1/4 inch):
|
||||
Quiet zone size to left of code, if quiet is true.
|
||||
Default is the greater of .25 inch, or .15 times the symbol's
|
||||
length.
|
||||
|
||||
rquiet (float, default 15/32 inch):
|
||||
Quiet zone size to right left of code, if quiet is true.
|
||||
|
||||
Sources of information on FIM:
|
||||
|
||||
USPS Publication 25, A Guide to Business Mail Preparation
|
||||
http://new.usps.com/cpim/ftp/pubs/pub25.pdf
|
||||
"""
|
||||
barWidth = inch * (1.0/32.0)
|
||||
spaceWidth = inch * (1.0/16.0)
|
||||
barHeight = inch * (5.0/8.0)
|
||||
rquiet = inch * (0.25)
|
||||
lquiet = inch * (15.0/32.0)
|
||||
quiet = 0
|
||||
def __init__(self, value='', **args):
|
||||
value = str(value) if isinstance(value,int) else asNative(value)
|
||||
for k, v in args.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
Barcode.__init__(self, value)
|
||||
|
||||
def validate(self):
|
||||
self.valid = 1
|
||||
self.validated = ''
|
||||
for c in self.value:
|
||||
if c in string_whitespace:
|
||||
continue
|
||||
elif c in "abcdABCD":
|
||||
self.validated = self.validated + c.upper()
|
||||
else:
|
||||
self.valid = 0
|
||||
|
||||
if len(self.validated) != 1:
|
||||
raise ValueError("Input must be exactly one character")
|
||||
|
||||
return self.validated
|
||||
|
||||
def decompose(self):
|
||||
self.decomposed = ''
|
||||
for c in self.encoded:
|
||||
self.decomposed = self.decomposed + _fim_patterns[c]
|
||||
|
||||
return self.decomposed
|
||||
|
||||
def computeSize(self):
|
||||
self._width = (len(self.decomposed) - 1) * self.spaceWidth + self.barWidth
|
||||
if self.quiet:
|
||||
self._width += self.lquiet + self.rquiet
|
||||
self._height = self.barHeight
|
||||
|
||||
def draw(self):
|
||||
self._calculate()
|
||||
left = self.quiet and self.lquiet or 0
|
||||
for c in self.decomposed:
|
||||
if c == '|':
|
||||
self.rect(left, 0.0, self.barWidth, self.barHeight)
|
||||
left += self.spaceWidth
|
||||
self.drawHumanReadable()
|
||||
|
||||
def _humanText(self):
|
||||
return self.value
|
||||
|
||||
class POSTNET(Barcode):
|
||||
"""
|
||||
POSTNET is used in the US to encode "zip codes" (postal codes) on
|
||||
mail. It can encode 5, 9, or 11 digit codes. I've read that it's
|
||||
pointless to do 5 digits, since USPS will just have to re-print
|
||||
them with 9 or 11 digits.
|
||||
|
||||
Sources of information on POSTNET:
|
||||
|
||||
USPS Publication 25, A Guide to Business Mail Preparation
|
||||
http://new.usps.com/cpim/ftp/pubs/pub25.pdf
|
||||
"""
|
||||
quiet = 0
|
||||
shortHeight = inch * 0.050
|
||||
barHeight = inch * 0.125
|
||||
barWidth = inch * 0.018
|
||||
spaceWidth = inch * 0.0275
|
||||
def __init__(self, value='', **args):
|
||||
value = str(value) if isinstance(value,int) else asNative(value)
|
||||
for k, v in args.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
Barcode.__init__(self, value)
|
||||
|
||||
def validate(self):
|
||||
self.validated = ''
|
||||
self.valid = 1
|
||||
count = 0
|
||||
for c in self.value:
|
||||
if c in (string_whitespace + '-'):
|
||||
pass
|
||||
elif c in string_digits:
|
||||
count = count + 1
|
||||
if count == 6:
|
||||
self.validated = self.validated + '-'
|
||||
self.validated = self.validated + c
|
||||
else:
|
||||
self.valid = 0
|
||||
|
||||
if len(self.validated) not in [5, 10, 12]:
|
||||
self.valid = 0
|
||||
|
||||
return self.validated
|
||||
|
||||
def encode(self):
|
||||
self.encoded = "S"
|
||||
check = 0
|
||||
for c in self.validated:
|
||||
if c in string_digits:
|
||||
self.encoded = self.encoded + c
|
||||
check = check + int(c)
|
||||
elif c == '-':
|
||||
pass
|
||||
else:
|
||||
raise ValueError("Invalid character in input")
|
||||
check = (10 - check) % 10
|
||||
self.encoded = self.encoded + repr(check) + 'S'
|
||||
return self.encoded
|
||||
|
||||
def decompose(self):
|
||||
self.decomposed = ''
|
||||
for c in self.encoded:
|
||||
self.decomposed = self.decomposed + _postnet_patterns[c]
|
||||
return self.decomposed
|
||||
|
||||
def computeSize(self):
|
||||
self._width = len(self.decomposed) * self.barWidth + (len(self.decomposed) - 1) * self.spaceWidth
|
||||
self._height = self.barHeight
|
||||
|
||||
def draw(self):
|
||||
self._calculate()
|
||||
sdown = self.barHeight - self.shortHeight
|
||||
left = 0
|
||||
|
||||
for c in self.decomposed:
|
||||
if c == '.':
|
||||
h = self.shortHeight
|
||||
else:
|
||||
h = self.barHeight
|
||||
self.rect(left, 0.0, self.barWidth, h)
|
||||
left = left + self.barWidth + self.spaceWidth
|
||||
self.drawHumanReadable()
|
||||
|
||||
def _humanText(self):
|
||||
return self.encoded[1:-1]
|
||||
@@ -0,0 +1,465 @@
|
||||
#copyright ReportLab Inc. 2000-2016
|
||||
#see license.txt for license details
|
||||
from __future__ import print_function
|
||||
__version__='3.3.0'
|
||||
__all__ = ('USPS_4State',)
|
||||
|
||||
from reportlab.graphics.barcode.common import Barcode
|
||||
from reportlab.lib.utils import asNative
|
||||
|
||||
def nhex(i):
|
||||
'normalized hex'
|
||||
r = hex(i)
|
||||
r = r[:2]+r[2:].lower()
|
||||
if r.endswith('l'): r = r[:-1]
|
||||
return r
|
||||
|
||||
class USPS_4State(Barcode):
|
||||
''' USPS 4-State OneView (TM) barcode. All info from USPS-B-3200A
|
||||
'''
|
||||
_widthSize = 1
|
||||
_heightSize = 1
|
||||
_fontSize = 11
|
||||
_humanReadable = 0
|
||||
if True:
|
||||
tops = dict(
|
||||
F = (0.0625,0.0825),
|
||||
T = (0.0195,0.0285),
|
||||
A = (0.0625,0.0825),
|
||||
D = (0.0195,0.0285),
|
||||
)
|
||||
bottoms = dict(
|
||||
F = (-0.0625,-0.0825),
|
||||
T = (-0.0195,-0.0285),
|
||||
D = (-0.0625,-0.0825),
|
||||
A = (-0.0195,-0.0285),
|
||||
)
|
||||
dimensions = dict(
|
||||
width = (0.015, 0.025),
|
||||
pitch = (0.0416, 0.050),
|
||||
hcz = (0.125,0.125),
|
||||
vcz = (0.028,0.028),
|
||||
)
|
||||
else:
|
||||
tops = dict(
|
||||
F = (0.067,0.115),
|
||||
T = (0.021,0.040),
|
||||
A = (0.067,0.115),
|
||||
D = (0.021,0.040),
|
||||
)
|
||||
bottoms = dict(
|
||||
F = (-0.067,-0.115),
|
||||
D = (-0.067,-0.115),
|
||||
T = (-0.021,-0.040),
|
||||
A = (-0.021,-0.040),
|
||||
)
|
||||
dimensions = dict(
|
||||
width = (0.015, 0.025),
|
||||
pitch = (0.0416,0.050),
|
||||
hcz = (0.125,0.125),
|
||||
vcz = (0.040,0.040),
|
||||
)
|
||||
|
||||
def __init__(self,value='01234567094987654321',routing='',**kwd):
|
||||
self._init()
|
||||
value = str(value) if isinstance(value,int) else asNative(value)
|
||||
if not routing:
|
||||
#legal values for combined tracking + routing
|
||||
if len(value) in (20,25,29,31):
|
||||
value, routing = value[:20], value[20:]
|
||||
else:
|
||||
raise ValueError('value+routing length must be 20, 25, 29 or 31 digits not %d' % len(value))
|
||||
elif len(routing) not in (5,9,11):
|
||||
raise ValueError('routing length must be 5, 9 or 11 digits not %d' % len(routing))
|
||||
self._tracking = value
|
||||
self._routing = routing
|
||||
self._setKeywords(**kwd)
|
||||
|
||||
def _init(self):
|
||||
self._bvalue = None
|
||||
self._codewords = None
|
||||
self._characters = None
|
||||
self._barcodes = None
|
||||
|
||||
def scale(kind,D,s):
|
||||
V = D[kind]
|
||||
return 72*(V[0]*(1-s)+s*V[1])
|
||||
scale = staticmethod(scale)
|
||||
|
||||
def tracking(self,tracking):
|
||||
self._init()
|
||||
self._tracking = tracking
|
||||
tracking = property(lambda self: self._tracking,tracking)
|
||||
|
||||
def routing(self,routing):
|
||||
self._init()
|
||||
self._routing = routing
|
||||
routing = property(lambda self: self._routing,routing)
|
||||
|
||||
def widthSize(self,value):
|
||||
self._sized = None
|
||||
self._widthSize = min(max(0,value),1)
|
||||
widthSize = property(lambda self: self._widthSize,widthSize)
|
||||
|
||||
def heightSize(self,value):
|
||||
self._sized = None
|
||||
self._heightSize = value
|
||||
heightSize = property(lambda self: self._heightSize,heightSize)
|
||||
|
||||
def fontSize(self,value):
|
||||
self._sized = None
|
||||
self._fontSize = value
|
||||
fontSize = property(lambda self: self._fontSize,fontSize)
|
||||
|
||||
def humanReadable(self,value):
|
||||
self._sized = None
|
||||
self._humanReadable = value
|
||||
humanReadable = property(lambda self: self._humanReadable,humanReadable)
|
||||
|
||||
def binary(self):
|
||||
'''convert the 4 state string values to binary
|
||||
>>> print(nhex(USPS_4State('01234567094987654321','').binary))
|
||||
0x1122103b5c2004b1
|
||||
>>> print(nhex(USPS_4State('01234567094987654321','01234').binary))
|
||||
0xd138a87bab5cf3804b1
|
||||
>>> print(nhex(USPS_4State('01234567094987654321','012345678').binary))
|
||||
0x202bdc097711204d21804b1
|
||||
>>> print(nhex(USPS_4State('01234567094987654321','01234567891').binary))
|
||||
0x16907b2a24abc16a2e5c004b1
|
||||
'''
|
||||
value = self._bvalue
|
||||
if not value:
|
||||
routing = self.routing
|
||||
n = len(routing)
|
||||
try:
|
||||
if n==0:
|
||||
value = 0
|
||||
elif n==5:
|
||||
value = int(routing)+1
|
||||
elif n==9:
|
||||
value = int(routing)+100001
|
||||
elif n==11:
|
||||
value = int(routing)+1000100001
|
||||
else:
|
||||
raise ValueError
|
||||
except:
|
||||
raise ValueError('Problem converting %s, routing code must be 0, 5, 9 or 11 digits' % routing)
|
||||
|
||||
tracking = self.tracking
|
||||
svalue = tracking[0:2]
|
||||
try:
|
||||
value *= 10
|
||||
value += int(svalue[0])
|
||||
value *= 5
|
||||
value += int(svalue[1])
|
||||
except:
|
||||
raise ValueError('Problem converting %s, barcode identifier must be 2 digits' % svalue)
|
||||
|
||||
i = 2
|
||||
for name,nd in (('special services',3), ('customer identifier',6), ('sequence number',9)):
|
||||
j = i
|
||||
i += nd
|
||||
svalue = tracking[j:i]
|
||||
try:
|
||||
if len(svalue)!=nd: raise ValueError
|
||||
for j in range(nd):
|
||||
value *= 10
|
||||
value += int(svalue[j])
|
||||
except:
|
||||
raise ValueError('Problem converting %s, %s must be %d digits' % (svalue,name,nd))
|
||||
self._bvalue = value
|
||||
return value
|
||||
binary = property(binary)
|
||||
|
||||
def codewords(self):
|
||||
'''convert binary value into codewords
|
||||
>>> print(USPS_4State('01234567094987654321','01234567891').codewords)
|
||||
(673, 787, 607, 1022, 861, 19, 816, 1294, 35, 602)
|
||||
'''
|
||||
if not self._codewords:
|
||||
value = self.binary
|
||||
A, J = divmod(value,636)
|
||||
A, I = divmod(A,1365)
|
||||
A, H = divmod(A,1365)
|
||||
A, G = divmod(A,1365)
|
||||
A, F = divmod(A,1365)
|
||||
A, E = divmod(A,1365)
|
||||
A, D = divmod(A,1365)
|
||||
A, C = divmod(A,1365)
|
||||
A, B = divmod(A,1365)
|
||||
assert 0<=A<=658, 'improper value %s passed to _2codewords A-->%s' % (hex(int(value)),A)
|
||||
self._fcs = _crc11(value)
|
||||
if self._fcs&1024: A += 659
|
||||
J *= 2
|
||||
self._codewords = tuple(map(int,(A,B,C,D,E,F,G,H,I,J)))
|
||||
return self._codewords
|
||||
codewords = property(codewords)
|
||||
|
||||
|
||||
def table1(self):
|
||||
self.__class__.table1 = _initNof13Table(5,1287)
|
||||
return self.__class__.table1
|
||||
table1 = property(table1)
|
||||
|
||||
def table2(self):
|
||||
self.__class__.table2 = _initNof13Table(2,78)
|
||||
return self.__class__.table2
|
||||
table2 = property(table2)
|
||||
|
||||
def characters(self):
|
||||
''' convert own codewords to characters
|
||||
>>> print(' '.join(hex(c)[2:] for c in USPS_4State('01234567094987654321','01234567891').characters))
|
||||
dcb 85c 8e4 b06 6dd 1740 17c6 1200 123f 1b2b
|
||||
'''
|
||||
if not self._characters:
|
||||
codewords = self.codewords
|
||||
fcs = self._fcs
|
||||
C = []
|
||||
aC = C.append
|
||||
table1 = self.table1
|
||||
table2 = self.table2
|
||||
for i in range(10):
|
||||
cw = codewords[i]
|
||||
if cw<=1286:
|
||||
c = table1[cw]
|
||||
else:
|
||||
c = table2[cw-1287]
|
||||
if (fcs>>i)&1:
|
||||
c = ~c & 0x1fff
|
||||
aC(c)
|
||||
self._characters = tuple(C)
|
||||
return self._characters
|
||||
characters = property(characters)
|
||||
|
||||
def barcodes(self):
|
||||
'''Get 4 state bar codes for current routing and tracking
|
||||
>>> print(USPS_4State('01234567094987654321','01234567891').barcodes)
|
||||
AADTFFDFTDADTAADAATFDTDDAAADDTDTTDAFADADDDTFFFDDTTTADFAAADFTDAADA
|
||||
'''
|
||||
if not self._barcodes:
|
||||
C = self.characters
|
||||
B = []
|
||||
aB = B.append
|
||||
bits2bars = self._bits2bars
|
||||
for dc,db,ac,ab in self.table4:
|
||||
aB(bits2bars[((C[dc]>>db)&1)+2*((C[ac]>>ab)&1)])
|
||||
self._barcodes = ''.join(B)
|
||||
return self._barcodes
|
||||
barcodes = property(barcodes)
|
||||
|
||||
table4 = ((7, 2, 4, 3), (1, 10, 0, 0), (9, 12, 2, 8), (5, 5, 6, 11),
|
||||
(8, 9, 3, 1), (0, 1, 5, 12), (2, 5, 1, 8), (4, 4, 9, 11),
|
||||
(6, 3, 8, 10), (3, 9, 7, 6), (5, 11, 1, 4), (8, 5, 2, 12),
|
||||
(9, 10, 0, 2), (7, 1, 6, 7), (3, 6, 4, 9), (0, 3, 8, 6),
|
||||
(6, 4, 2, 7), (1, 1, 9, 9), (7, 10, 5, 2), (4, 0, 3, 8),
|
||||
(6, 2, 0, 4), (8, 11, 1, 0), (9, 8, 3, 12), (2, 6, 7, 7),
|
||||
(5, 1, 4, 10), (1, 12, 6, 9), (7, 3, 8, 0), (5, 8, 9, 7),
|
||||
(4, 6, 2, 10), (3, 4, 0, 5), (8, 4, 5, 7), (7, 11, 1, 9),
|
||||
(6, 0, 9, 6), (0, 6, 4, 8), (2, 1, 3, 2), (5, 9, 8, 12),
|
||||
(4, 11, 6, 1), (9, 5, 7, 4), (3, 3, 1, 2), (0, 7, 2, 0),
|
||||
(1, 3, 4, 1), (6, 10, 3, 5), (8, 7, 9, 4), (2, 11, 5, 6),
|
||||
(0, 8, 7, 12), (4, 2, 8, 1), (5, 10, 3, 0), (9, 3, 0, 9),
|
||||
(6, 5, 2, 4), (7, 8, 1, 7), (5, 0, 4, 5), (2, 3, 0, 10),
|
||||
(6, 12, 9, 2), (3, 11, 1, 6), (8, 8, 7, 9), (5, 4, 0, 11),
|
||||
(1, 5, 2, 2), (9, 1, 4, 12), (8, 3, 6, 6), (7, 0, 3, 7),
|
||||
(4, 7, 7, 5), (0, 12, 1, 11), (2, 9, 9, 0), (6, 8, 5, 3),
|
||||
(3, 10, 8, 2))
|
||||
|
||||
_bits2bars = 'T','D','A','F'
|
||||
horizontalClearZone = property(lambda self: self.scale('hcz',self.dimensions,self.widthScale))
|
||||
verticalClearZone = property(lambda self: self.scale('vcz',self.dimensions,self.heightScale))
|
||||
|
||||
@property
|
||||
def barWidth(self):
|
||||
if '_barWidth' in self.__dict__:
|
||||
return self.__dict__['_barWidth']
|
||||
return self.scale('width',self.dimensions,self.widthScale)
|
||||
|
||||
@barWidth.setter
|
||||
def barWidth(self,value):
|
||||
n, x = self.dimensions['width']
|
||||
self.__dict__['_barWidth'] = 72*min(max(value/72.0,n),x)
|
||||
|
||||
@property
|
||||
def pitch(self):
|
||||
if '_pitch' in self.__dict__:
|
||||
return self.__dict__['_pitch']
|
||||
return self.scale('pitch',self.dimensions,self.widthScale)
|
||||
|
||||
@pitch.setter
|
||||
def pitch(self,value):
|
||||
n, x = self.dimensions['pitch']
|
||||
self.__dict__['_pitch'] = 72*min(max(value/72.0,n),x)
|
||||
|
||||
@property
|
||||
def barHeight(self):
|
||||
if '_barHeight' in self.__dict__:
|
||||
return self.__dict__['_barHeight']
|
||||
return self.scale('F',self.tops,self.heightScale) - self.scale('F',self.bottoms,self.heightScale)
|
||||
|
||||
@barHeight.setter
|
||||
def barHeight(self,value):
|
||||
n = self.tops['F'][0] - self.bottoms['F'][0]
|
||||
x = self.tops['F'][1] - self.bottoms['F'][1]
|
||||
value = self.__dict__['_barHeight'] = 72*min(max(value/72.0,n),x)
|
||||
self.heightSize = (value - n)/(x-n)
|
||||
|
||||
widthScale = property(lambda self: min(1,max(0,self.widthSize)))
|
||||
heightScale = property(lambda self: min(1,max(0,self.heightSize)))
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
self.computeSize()
|
||||
return self._width
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
self.computeSize()
|
||||
return self._height
|
||||
|
||||
#we ignore attempts to set the dimensions
|
||||
@width.setter
|
||||
def width(self,v):
|
||||
pass
|
||||
@height.setter
|
||||
def height(self,v):
|
||||
pass
|
||||
|
||||
def computeSize(self):
|
||||
if not getattr(self,'_sized',None):
|
||||
ws = self.widthScale
|
||||
hs = self.heightScale
|
||||
barHeight = self.barHeight
|
||||
barWidth = self.barWidth
|
||||
pitch = self.pitch
|
||||
hcz = self.horizontalClearZone
|
||||
vcz = self.verticalClearZone
|
||||
self._width = 2*hcz + barWidth + 64*pitch
|
||||
self._height = 2*vcz+barHeight
|
||||
if self.humanReadable:
|
||||
self._height += self.fontSize*1.2+vcz
|
||||
self._sized = True
|
||||
|
||||
def wrap(self,aW,aH):
|
||||
self.computeSize()
|
||||
return self.width, self.height
|
||||
|
||||
def _getBarVInfo(self,y0=0):
|
||||
vInfo = {}
|
||||
hs = self.heightScale
|
||||
for b in ('T','D','A','F'):
|
||||
y = self.scale(b,self.bottoms,hs)+y0
|
||||
vInfo[b] = y,self.scale(b,self.tops,hs)+y0 - y
|
||||
return vInfo
|
||||
|
||||
def draw(self):
|
||||
self.computeSize()
|
||||
hcz = self.horizontalClearZone
|
||||
vcz = self.verticalClearZone
|
||||
bw = self.barWidth
|
||||
x = hcz
|
||||
y0 = vcz+self.barHeight*0.5
|
||||
dw = self.pitch
|
||||
vInfo = self._getBarVInfo(y0)
|
||||
for b in self.barcodes:
|
||||
yb, hb = vInfo[b]
|
||||
self.rect(x,yb,bw,hb)
|
||||
x += dw
|
||||
self.drawHumanReadable()
|
||||
|
||||
def value(self):
|
||||
tracking = self.tracking
|
||||
routing = self.routing
|
||||
routing = routing and (routing,) or ()
|
||||
return ' '.join((tracking[0:2],tracking[2:5],tracking[5:11],tracking[11:])+routing)
|
||||
value = property(value,lambda self,value: self.__dict__.__setitem__('tracking',value))
|
||||
|
||||
def drawHumanReadable(self):
|
||||
if self.humanReadable:
|
||||
hcz = self.horizontalClearZone
|
||||
vcz = self.verticalClearZone
|
||||
fontName = self.fontName
|
||||
fontSize = self.fontSize
|
||||
y = self.barHeight+2*vcz+0.2*fontSize
|
||||
self.annotate(hcz,y,self.value,fontName,fontSize)
|
||||
|
||||
def annotate(self,x,y,text,fontName,fontSize,anchor='middle'):
|
||||
Barcode.annotate(self,x,y,text,fontName,fontSize,anchor='start')
|
||||
|
||||
def _crc11(value):
|
||||
'''
|
||||
>>> usps = [USPS_4State('01234567094987654321',x).binary for x in ('','01234','012345678','01234567891')]
|
||||
>>> print(' '.join(nhex(x) for x in usps))
|
||||
0x1122103b5c2004b1 0xd138a87bab5cf3804b1 0x202bdc097711204d21804b1 0x16907b2a24abc16a2e5c004b1
|
||||
>>> print(' '.join(nhex(_crc11(x)) for x in usps))
|
||||
0x51 0x65 0x606 0x751
|
||||
'''
|
||||
hexbytes = nhex(int(value))[2:]
|
||||
hexbytes = '0'*(26-len(hexbytes))+hexbytes
|
||||
gp = 0x0F35
|
||||
fcs = 0x07FF
|
||||
data = int(hexbytes[:2],16)<<5
|
||||
for b in range(2,8):
|
||||
if (fcs ^ data)&0x400:
|
||||
fcs = (fcs<<1)^gp
|
||||
else:
|
||||
fcs = fcs<<1
|
||||
fcs &= 0x7ff
|
||||
data <<= 1
|
||||
|
||||
for x in range(2,2*13,2):
|
||||
data = int(hexbytes[x:x+2],16)<<3
|
||||
for b in range(8):
|
||||
if (fcs ^ data)&0x400:
|
||||
fcs = (fcs<<1)^gp
|
||||
else:
|
||||
fcs = fcs<<1
|
||||
fcs &= 0x7ff
|
||||
data <<= 1
|
||||
return fcs
|
||||
|
||||
def _ru13(i):
|
||||
'''reverse unsigned 13 bit number
|
||||
>>> print(_ru13(7936), _ru13(31), _ru13(47), _ru13(7808))
|
||||
31 7936 7808 47
|
||||
'''
|
||||
r = 0
|
||||
for x in range(13):
|
||||
r <<= 1
|
||||
r |= i & 1
|
||||
i >>= 1
|
||||
return r
|
||||
|
||||
def _initNof13Table(N,lenT):
|
||||
'''create and return table of 13 bit values with N bits on
|
||||
>>> T = _initNof13Table(5,1287)
|
||||
>>> print(' '.join('T[%d]=%d' % (i, T[i]) for i in (0,1,2,3,4,1271,1272,1284,1285,1286)))
|
||||
T[0]=31 T[1]=7936 T[2]=47 T[3]=7808 T[4]=55 T[1271]=6275 T[1272]=6211 T[1284]=856 T[1285]=744 T[1286]=496
|
||||
'''
|
||||
T = lenT*[None]
|
||||
l = 0
|
||||
u = lenT-1
|
||||
for c in range(8192):
|
||||
bc = 0
|
||||
for b in range(13):
|
||||
bc += (c&(1<<b))!=0
|
||||
if bc!=N: continue
|
||||
r = _ru13(c)
|
||||
if r<c: continue #we already looked at this pair
|
||||
if r==c:
|
||||
T[u] = c
|
||||
u -= 1
|
||||
else:
|
||||
T[l] = c
|
||||
l += 1
|
||||
T[l] = r
|
||||
l += 1
|
||||
assert l==(u+1), 'u+1(%d)!=l(%d) for %d of 13 table' % (u+1,l,N)
|
||||
return T
|
||||
|
||||
def _test():
|
||||
import doctest
|
||||
return doctest.testmod()
|
||||
|
||||
if __name__ == "__main__":
|
||||
_test()
|
||||
@@ -0,0 +1,357 @@
|
||||
#copyright ReportLab Europe Limited. 2000-2016
|
||||
#see license.txt for license details
|
||||
__version__='3.3.0'
|
||||
__all__= (
|
||||
'BarcodeI2of5',
|
||||
'BarcodeCode128',
|
||||
'BarcodeStandard93',
|
||||
'BarcodeExtended93',
|
||||
'BarcodeStandard39',
|
||||
'BarcodeExtended39',
|
||||
'BarcodeMSI',
|
||||
'BarcodeCodabar',
|
||||
'BarcodeCode11',
|
||||
'BarcodeFIM',
|
||||
'BarcodePOSTNET',
|
||||
'BarcodeUSPS_4State',
|
||||
)
|
||||
|
||||
from reportlab.lib.validators import isInt, isNumber, isString, isColorOrNone, isBoolean, EitherOr, isNumberOrNone
|
||||
from reportlab.lib.attrmap import AttrMap, AttrMapValue
|
||||
from reportlab.lib.colors import black
|
||||
from reportlab.lib.utils import rl_exec
|
||||
from reportlab.graphics.shapes import Rect, Group, String
|
||||
from reportlab.graphics.charts.areas import PlotArea
|
||||
|
||||
'''
|
||||
#snippet
|
||||
|
||||
#first make your Drawing
|
||||
from reportlab.graphics.shapes import Drawing
|
||||
d= Drawing(100,50)
|
||||
|
||||
#create and set up the widget
|
||||
from reportlab.graphics.barcode.widgets import BarcodeStandard93
|
||||
bc = BarcodeStandard93()
|
||||
bc.value = 'RGB-123456'
|
||||
|
||||
#add to the drawing and save
|
||||
d.add(bc)
|
||||
# d.save(formats=['gif','pict'],fnRoot='bc_sample')
|
||||
'''
|
||||
|
||||
class _BarcodeWidget(PlotArea):
|
||||
_attrMap = AttrMap(BASE=PlotArea,
|
||||
barStrokeColor = AttrMapValue(isColorOrNone, desc='Color of bar borders.'),
|
||||
barFillColor = AttrMapValue(isColorOrNone, desc='Color of bar interior areas.'),
|
||||
barStrokeWidth = AttrMapValue(isNumber, desc='Width of bar borders.'),
|
||||
value = AttrMapValue(EitherOr((isString,isNumber)), desc='Value.'),
|
||||
textColor = AttrMapValue(isColorOrNone, desc='Color of human readable text.'),
|
||||
valid = AttrMapValue(isBoolean),
|
||||
validated = AttrMapValue(isString,desc="validated form of input"),
|
||||
encoded = AttrMapValue(None,desc="encoded form of input"),
|
||||
decomposed = AttrMapValue(isString,desc="decomposed form of input"),
|
||||
canv = AttrMapValue(None,desc="temporarily used for internal methods"),
|
||||
gap = AttrMapValue(isNumberOrNone, desc='Width of inter character gaps.'),
|
||||
)
|
||||
|
||||
textColor = barFillColor = black
|
||||
barStrokeColor = None
|
||||
barStrokeWidth = 0
|
||||
_BCC = None
|
||||
def __init__(self,_value='',**kw):
|
||||
PlotArea.__init__(self)
|
||||
if 'width' in self.__dict__: del self.__dict__['width']
|
||||
if 'height' in self.__dict__: del self.__dict__['height']
|
||||
self.x = self.y = 0
|
||||
kw.setdefault('value',_value)
|
||||
self._BCC.__init__(self,**kw)
|
||||
|
||||
def rect(self,x,y,w,h,**kw):
|
||||
#this allows the base code to draw rectangles for us using self.rect
|
||||
#using direct keyword argument overrides see eg common.py line 140 on
|
||||
for k,v in (('strokeColor',self.barStrokeColor),
|
||||
('strokeWidth',self.barStrokeWidth),
|
||||
('fillColor',self.barFillColor)):
|
||||
kw.setdefault(k,v)
|
||||
self._Gadd(Rect(self.x+x,self.y+y,w,h, **kw))
|
||||
|
||||
def draw(self):
|
||||
if not self._BCC: raise NotImplementedError("Abstract class %s cannot be drawn" % self.__class__.__name__)
|
||||
self.canv = self
|
||||
G = Group()
|
||||
self._Gadd = G.add
|
||||
self._Gadd(Rect(self.x,self.y,self.width,self.height,fillColor=None,strokeColor=None,strokeWidth=0.0001))
|
||||
self._BCC.draw(self)
|
||||
del self.canv, self._Gadd
|
||||
return G
|
||||
|
||||
def annotate(self,x,y,text,fontName,fontSize,anchor='middle'):
|
||||
self._Gadd(String(self.x+x,self.y+y,text,fontName=fontName,fontSize=fontSize,
|
||||
textAnchor=anchor,fillColor=self.textColor))
|
||||
|
||||
def _BCW(doc,codeName,attrMap,mod,value,**kwds):
|
||||
"""factory for Barcode Widgets"""
|
||||
_pre_init = kwds.pop('_pre_init','')
|
||||
_methods = kwds.pop('_methods','')
|
||||
name = 'Barcode'+codeName
|
||||
ns = vars().copy()
|
||||
code = 'from %s import %s' % (mod,codeName)
|
||||
rl_exec(code,ns)
|
||||
ns['_BarcodeWidget'] = _BarcodeWidget
|
||||
ns['doc'] = ("\n\t'''%s'''" % doc) if doc else ''
|
||||
code = '''class %(name)s(_BarcodeWidget,%(codeName)s):%(doc)s
|
||||
\t_BCC = %(codeName)s
|
||||
\tcodeName = %(codeName)r
|
||||
\tdef __init__(self,**kw):%(_pre_init)s
|
||||
\t\t_BarcodeWidget.__init__(self,%(value)r,**kw)%(_methods)s''' % ns
|
||||
rl_exec(code,ns)
|
||||
Klass = ns[name]
|
||||
if attrMap: Klass._attrMap = attrMap
|
||||
for k, v in kwds.items():
|
||||
setattr(Klass,k,v)
|
||||
return Klass
|
||||
|
||||
BarcodeI2of5 = _BCW(
|
||||
"""Interleaved 2 of 5 is used in distribution and warehouse industries.
|
||||
|
||||
It encodes an even-numbered sequence of numeric digits. There is an optional
|
||||
module 10 check digit; if including this, the total length must be odd so that
|
||||
it becomes even after including the check digit. Otherwise the length must be
|
||||
even. Since the check digit is optional, our library does not check it.
|
||||
""",
|
||||
"I2of5",
|
||||
AttrMap(BASE=_BarcodeWidget,
|
||||
barWidth = AttrMapValue(isNumber,'''(float, default .0075):
|
||||
X-Dimension, or width of the smallest element
|
||||
Minumum is .0075 inch (7.5 mils).'''),
|
||||
ratio = AttrMapValue(isNumber,'''(float, default 2.2):
|
||||
The ratio of wide elements to narrow elements.
|
||||
Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the
|
||||
barWidth is greater than 20 mils (.02 inch))'''),
|
||||
gap = AttrMapValue(isNumberOrNone,'''(float or None, default None):
|
||||
width of intercharacter gap. None means "use barWidth".'''),
|
||||
barHeight = AttrMapValue(isNumber,'''(float, see default below):
|
||||
Height of the symbol. Default is the height of the two
|
||||
bearer bars (if they exist) plus the greater of .25 inch
|
||||
or .15 times the symbol's length.'''),
|
||||
checksum = AttrMapValue(isBoolean,'''(bool, default 1):
|
||||
Whether to compute and include the check digit'''),
|
||||
bearers = AttrMapValue(isNumber,'''(float, in units of barWidth. default 3.0):
|
||||
Height of bearer bars (horizontal bars along the top and
|
||||
bottom of the barcode). Default is 3 x-dimensions.
|
||||
Set to zero for no bearer bars. (Bearer bars help detect
|
||||
misscans, so it is suggested to leave them on).'''),
|
||||
bearerBox = AttrMapValue(isBoolean,'''(bool, default 0):
|
||||
if True turn bearers into a box'''),
|
||||
quiet = AttrMapValue(isBoolean,'''(bool, default 1):
|
||||
Whether to include quiet zones in the symbol.'''),
|
||||
|
||||
lquiet = AttrMapValue(isNumber,'''(float, see default below):
|
||||
Quiet zone size to left of code, if quiet is true.
|
||||
Default is the greater of .25 inch, or .15 times the symbol's
|
||||
length.'''),
|
||||
|
||||
rquiet = AttrMapValue(isNumber,'''(float, defaults as above):
|
||||
Quiet zone size to right left of code, if quiet is true.'''),
|
||||
fontName = AttrMapValue(isString, desc='human readable font'),
|
||||
fontSize = AttrMapValue(isNumber, desc='human readable font size'),
|
||||
humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
|
||||
stop = AttrMapValue(isBoolean, desc='if we use start/stop symbols (default 1)'),
|
||||
),
|
||||
'reportlab.graphics.barcode.common',
|
||||
1234,
|
||||
_tests = [
|
||||
'12',
|
||||
'1234',
|
||||
'123456',
|
||||
'12345678',
|
||||
'1234567890'
|
||||
],
|
||||
)
|
||||
|
||||
BarcodeCode128 = _BCW("""Code 128 encodes any number of characters in the ASCII character set.""",
|
||||
"Code128",
|
||||
AttrMap(BASE=BarcodeI2of5,UNWANTED=('bearers','checksum','ratio','checksum','stop')),
|
||||
'reportlab.graphics.barcode.code128',
|
||||
"AB-12345678",
|
||||
_tests = ['ReportLab Rocks!', 'PFWZF'],
|
||||
)
|
||||
|
||||
BarcodeCode128Auto = _BCW(
|
||||
'Modified Code128 to use auto encoding',
|
||||
'Code128Auto',
|
||||
AttrMap(BASE=BarcodeCode128),
|
||||
'reportlab.graphics.barcode.code128',
|
||||
'XY149740345GB'
|
||||
)
|
||||
|
||||
BarcodeStandard93=_BCW("""This is a compressed form of Code 39""",
|
||||
"Standard93",
|
||||
AttrMap(BASE=BarcodeCode128,
|
||||
stop = AttrMapValue(isBoolean, desc='if we use start/stop symbols (default 1)'),
|
||||
),
|
||||
'reportlab.graphics.barcode.code93',
|
||||
"CODE 93",
|
||||
)
|
||||
|
||||
BarcodeExtended93=_BCW("""This is a compressed form of Code 39, allowing the full ASCII charset""",
|
||||
"Extended93",
|
||||
AttrMap(BASE=BarcodeCode128,
|
||||
stop = AttrMapValue(isBoolean, desc='if we use start/stop symbols (default 1)'),
|
||||
),
|
||||
'reportlab.graphics.barcode.code93',
|
||||
"L@@K! Code 93 ;-)",
|
||||
)
|
||||
|
||||
BarcodeStandard39=_BCW("""Code39 is widely used in non-retail, especially US defence and health.
|
||||
Allowed characters are 0-9, A-Z (caps only), space, and -.$/+%*.""",
|
||||
"Standard39",
|
||||
AttrMap(BASE=BarcodeI2of5),
|
||||
'reportlab.graphics.barcode.code39',
|
||||
"A012345B%R",
|
||||
)
|
||||
|
||||
BarcodeExtended39=_BCW("""Extended 39 encodes the full ASCII character set by encoding
|
||||
characters as pairs of Code 39 characters; $, /, % and + are used as
|
||||
shift characters.""",
|
||||
"Extended39",
|
||||
AttrMap(BASE=BarcodeI2of5),
|
||||
'reportlab.graphics.barcode.code39',
|
||||
"A012345B}",
|
||||
)
|
||||
|
||||
BarcodeMSI=_BCW("""MSI is used for inventory control in retail applications.
|
||||
|
||||
There are several methods for calculating check digits so we
|
||||
do not implement one.
|
||||
""",
|
||||
"MSI",
|
||||
AttrMap(BASE=BarcodeI2of5),
|
||||
'reportlab.graphics.barcode.common',
|
||||
1234,
|
||||
)
|
||||
|
||||
BarcodeCodabar=_BCW("""Used in blood banks, photo labs and FedEx labels.
|
||||
Encodes 0-9, -$:/.+, and four start/stop characters A-D.""",
|
||||
"Codabar",
|
||||
AttrMap(BASE=BarcodeI2of5),
|
||||
'reportlab.graphics.barcode.common',
|
||||
"A012345B",
|
||||
)
|
||||
|
||||
BarcodeCode11=_BCW("""Used mostly for labelling telecommunications equipment.
|
||||
It encodes numeric digits.""",
|
||||
'Code11',
|
||||
AttrMap(BASE=BarcodeI2of5,
|
||||
checksum = AttrMapValue(isInt,'''(integer, default 2):
|
||||
Whether to compute and include the check digit(s).
|
||||
(0 none, 1 1-digit, 2 2-digit, -1 auto, default -1):
|
||||
How many checksum digits to include. -1 ("auto") means
|
||||
1 if the number of digits is 10 or less, else 2.'''),
|
||||
),
|
||||
'reportlab.graphics.barcode.common',
|
||||
"01234545634563",
|
||||
)
|
||||
|
||||
BarcodeFIM=_BCW("""
|
||||
FIM was developed as part of the POSTNET barcoding system.
|
||||
FIM (Face Identification Marking) is used by the cancelling machines
|
||||
to sort mail according to whether or not they have bar code
|
||||
and their postage requirements. There are four types of FIM
|
||||
called FIM A, FIM B, FIM C, and FIM D.
|
||||
|
||||
The four FIM types have the following meanings:
|
||||
FIM A- Postage required pre-barcoded
|
||||
FIM B - Postage pre-paid, no bar code exists
|
||||
FIM C- Postage prepaid prebarcoded
|
||||
FIM D- Postage required, no bar code exists""",
|
||||
"FIM",
|
||||
AttrMap(BASE=_BarcodeWidget,
|
||||
barWidth = AttrMapValue(isNumber,'''(float, default 1/32in): the bar width.'''),
|
||||
spaceWidth = AttrMapValue(isNumber,'''(float or None, default 1/16in):
|
||||
width of intercharacter gap. None means "use barWidth".'''),
|
||||
barHeight = AttrMapValue(isNumber,'''(float, default 5/8in): The bar height.'''),
|
||||
quiet = AttrMapValue(isBoolean,'''(bool, default 0):
|
||||
Whether to include quiet zones in the symbol.'''),
|
||||
lquiet = AttrMapValue(isNumber,'''(float, default: 15/32in):
|
||||
Quiet zone size to left of code, if quiet is true.'''),
|
||||
rquiet = AttrMapValue(isNumber,'''(float, default 1/4in):
|
||||
Quiet zone size to right left of code, if quiet is true.'''),
|
||||
fontName = AttrMapValue(isString, desc='human readable font'),
|
||||
fontSize = AttrMapValue(isNumber, desc='human readable font size'),
|
||||
humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
|
||||
),
|
||||
'reportlab.graphics.barcode.usps',
|
||||
"A",
|
||||
)
|
||||
|
||||
BarcodePOSTNET=_BCW('',
|
||||
"POSTNET",
|
||||
AttrMap(BASE=_BarcodeWidget,
|
||||
barWidth = AttrMapValue(isNumber,'''(float, default 0.018*in): the bar width.'''),
|
||||
spaceWidth = AttrMapValue(isNumber,'''(float or None, default 0.0275in): width of intercharacter gap.'''),
|
||||
shortHeight = AttrMapValue(isNumber,'''(float, default 0.05in): The short bar height.'''),
|
||||
barHeight = AttrMapValue(isNumber,'''(float, default 0.125in): The full bar height.'''),
|
||||
fontName = AttrMapValue(isString, desc='human readable font'),
|
||||
fontSize = AttrMapValue(isNumber, desc='human readable font size'),
|
||||
humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
|
||||
),
|
||||
'reportlab.graphics.barcode.usps',
|
||||
"78247-1043",
|
||||
)
|
||||
|
||||
BarcodeUSPS_4State=_BCW('',
|
||||
"USPS_4State",
|
||||
AttrMap(BASE=_BarcodeWidget,
|
||||
widthSize = AttrMapValue(isNumber,'''(float, default 1): the bar width size adjustment between 0 and 1.'''),
|
||||
heightSize = AttrMapValue(isNumber,'''(float, default 1): the bar height size adjustment between 0 and 1.'''),
|
||||
fontName = AttrMapValue(isString, desc='human readable font'),
|
||||
fontSize = AttrMapValue(isNumber, desc='human readable font size'),
|
||||
tracking = AttrMapValue(isString, desc='tracking data'),
|
||||
routing = AttrMapValue(isString, desc='routing data'),
|
||||
humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
|
||||
barWidth = AttrMapValue(isNumber, desc='barWidth'),
|
||||
barHeight = AttrMapValue(isNumber, desc='barHeight'),
|
||||
pitch = AttrMapValue(isNumber, desc='pitch'),
|
||||
),
|
||||
'reportlab.graphics.barcode.usps4s',
|
||||
'01234567094987654321',
|
||||
_pre_init="\n\t\tkw.setdefault('routing','01234567891')\n",
|
||||
_methods = "\n\tdef annotate(self,x,y,text,fontName,fontSize,anchor='middle'):\n\t\t_BarcodeWidget.annotate(self,x,y,text,fontName,fontSize,anchor='start')\n"
|
||||
)
|
||||
BarcodeECC200DataMatrix = _BCW(
|
||||
'ECC200DataMatrix',
|
||||
'ECC200DataMatrix',
|
||||
AttrMap(BASE=_BarcodeWidget,
|
||||
x=AttrMapValue(isNumber, desc='X position of the lower-left corner of the barcode.'),
|
||||
y=AttrMapValue(isNumber, desc='Y position of the lower-left corner of the barcode.'),
|
||||
barWidth=AttrMapValue(isNumber, desc='Size of data modules.'),
|
||||
barFillColor=AttrMapValue(isColorOrNone, desc='Color of data modules.'),
|
||||
value=AttrMapValue(EitherOr((isString,isNumber)), desc='Value.'),
|
||||
height=AttrMapValue(None, desc='ignored'),
|
||||
width=AttrMapValue(None, desc='ignored'),
|
||||
strokeColor=AttrMapValue(None, desc='ignored'),
|
||||
strokeWidth=AttrMapValue(None, desc='ignored'),
|
||||
fillColor=AttrMapValue(None, desc='ignored'),
|
||||
background=AttrMapValue(None, desc='ignored'),
|
||||
debug=AttrMapValue(None, desc='ignored'),
|
||||
gap=AttrMapValue(None, desc='ignored'),
|
||||
row_modules=AttrMapValue(None, desc='???'),
|
||||
col_modules=AttrMapValue(None, desc='???'),
|
||||
row_regions=AttrMapValue(None, desc='???'),
|
||||
col_regions=AttrMapValue(None, desc='???'),
|
||||
cw_data=AttrMapValue(None, desc='???'),
|
||||
cw_ecc=AttrMapValue(None, desc='???'),
|
||||
row_usable_modules = AttrMapValue(None, desc='???'),
|
||||
col_usable_modules = AttrMapValue(None, desc='???'),
|
||||
valid = AttrMapValue(None, desc='???'),
|
||||
validated = AttrMapValue(None, desc='???'),
|
||||
decomposed = AttrMapValue(None, desc='???'),
|
||||
),
|
||||
'reportlab.graphics.barcode.ecc200datamatrix',
|
||||
'JGB 0204H20B012722900021AC35B2100001003241014241014TPS01 WJ067073605GB185 MOUNT PLEASANT MAIL CENTER EC1A1BB9ZGBREC1A1BB EC1A1BB STEST FILE FOR SPEC '
|
||||
)
|
||||
|
||||
if __name__=='__main__':
|
||||
raise ValueError('widgets.py has no script function')
|
||||
Reference in New Issue
Block a user