Added printing requirements

This commit is contained in:
Tom Price
2014-12-07 17:32:24 +00:00
parent c76d12d877
commit 7ba11a2db9
571 changed files with 143368 additions and 6 deletions

View File

@@ -0,0 +1,59 @@
Symbologies Currently Supported
===============================
The following have, at a minimum, been verified to scan with a WASP
CCD barcode scanner (found one bug in my code, two in the scanner!).
Some have had more extensive testing:
Interleaved 2 of 5
MSI
Codabar
Code 39 (Standard Character Set)
Code 39 (Extended Character Set)
Code 93 (Standard Character Set)
Code 93 (Extended Character Set)
Code 128 (Automatic use of A, B, C, with some optimizations --
more coming)
The following have been tested by sending a fair number of mailpieces
with them:
USPS FIM
USPS POSTNET
The following have not been tested, as none of the scanners I have
access to support them:
Code 11
Future Plans, Consulting
========================
Soon:
I plan to implement the following linear codes soon:
UPC/EAN(/JAN)
The following are in progress, but I lack a way to verify them
(scanners I have access to don't read them), and I don't have complete
specs for the UK style.
Royal Mail 4-State (UK/NL/etc style, and Australian style)
Down the road, I'd like to do some 2D symbologies. Likely first candidate
is PDF417. MaxiCode, Aztec Code, and some of the stacked symbologies are
also good candidates.
I am available to do implementation of additional symbologies for hire.
Because I enjoy hacking barcodes, my rates for work in this particular
area are very low and are mainly to help offset costs associated with
obtaining related documents and/or to buy or gain access to scanning
equipment for symbologies if I don't already have a scanner that
supports them. Loans of equipment are also accepted.
For more information, contact:
Ty Sarna
tsarna@sarna.org

View File

@@ -0,0 +1,24 @@
See also README for some plans and info on consulting.
- Overall framework docs
- Finish Aussie Rules 4-State, for which I have complete docs now (yay
USPS and aupost.com.au for putting specs online. Too bad UKPost doesn't.)
- Investigate USPS PLANET stuff
- Higher-level objects that handle barcoded address blocks with correct
spacings and such (US, AU, UK/etc?)
- Even higher-level objects that represent mailpieces and place the
above-style address block objects, FIM codes, "place stamp here" blocks,
etc, correctly?
- Framework for laying out labels on various styles of n-up label
sheets, like Avery labels, etc?
- Decide if Plessey is worth doing. MSI-like (MSI is actually derived from
it), but specs were never formalized. Probably only useful for legacy
applications. If you need it, mail me.
- Get someone to test Code 11, or find a scanner that handles it

View File

@@ -0,0 +1 @@
0.9

View File

@@ -0,0 +1,136 @@
#
# 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.
#
__version__ = '0.9'
__doc__='''Popular barcodes available as reusable widgets'''
def getCodes():
"""Returns a dict mapping code names to widgets"""
from reportlab.graphics.barcode.widgets import BarcodeI2of5, BarcodeCode128, BarcodeStandard93,\
BarcodeExtended93, BarcodeStandard39, BarcodeExtended39,\
BarcodeMSI, BarcodeCodabar, BarcodeCode11, BarcodeFIM,\
BarcodePOSTNET, BarcodeUSPS_4State
#newer codes will typically get their own module
from reportlab.graphics.barcode.eanbc import Ean13BarcodeWidget, Ean8BarcodeWidget, UPCA
from reportlab.graphics.barcode.qr import QrCodeWidget
#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 (
BarcodeI2of5,
BarcodeCode128,
BarcodeStandard93,
BarcodeExtended93,
BarcodeStandard39,
BarcodeExtended39,
BarcodeMSI,
BarcodeCodabar,
BarcodeCode11,
BarcodeFIM,
BarcodePOSTNET,
BarcodeUSPS_4State,
Ean13BarcodeWidget,
Ean8BarcodeWidget,
UPCA,
QrCodeWidget,
):
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, Group
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)

View File

@@ -0,0 +1,321 @@
#
# 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),
}
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 _trailingDigitsToC(self, l):
# Optimization: trailing digits -> set C double-digits
c = 1
savings = -1 # the TO_C costs one character
rl = ['STOP']
while c < len(l):
i = (-c - 1)
if l[i] == '\xf1':
c += 1
rl.insert(0, '\xf1')
continue
elif len(l[i]) == 1 and l[i] in digits \
and len(l[i-1]) == 1 and l[i-1] in digits:
c += 2
savings += 1
rl.insert(0, l[i-1] + l[i])
continue
else:
break
if savings > 0:
return l[:-c] + ['TO_C'] + rl
else:
return l
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._trailingDigitsToC(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

View File

@@ -0,0 +1,245 @@
#
# 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 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)
}
from reportlab.lib.utils import ascii_uppercase, ascii_lowercase
_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

View File

@@ -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:]

View File

@@ -0,0 +1,749 @@
#
# 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 reportlab.lib.utils import ascii_uppercase, ascii_lowercase
from string import 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
def width(self):
self._calculate()
return self._width
width = property(width)
def height(self):
self._calculate()
return self._height
height = property(height)
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:
self.rect(self.lquiet, 0, \
self._width - (self.lquiet + self.rquiet), b)
self.rect(self.lquiet, self.barHeight - b, \
self._width - (self.lquiet + self.rquiet), 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()
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).
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
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

View File

@@ -0,0 +1,350 @@
__all__=(
'Ean13BarcodeWidget','isEanString',
)
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
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

View File

@@ -0,0 +1,81 @@
#
# 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.graphics.barcode.common import Barcode
import string
# . 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/

View File

@@ -0,0 +1,196 @@
# (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 digits as string_digits
from reportlab.lib.utils import ascii_uppercase
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()

View File

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

View File

@@ -0,0 +1,200 @@
#!/usr/pkg/bin/python
import os, sys, time
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.platypus import Spacer, SimpleDocTemplate, Table, TableStyle, Preformatted, PageBreak
from reportlab.lib.units import inch, cm
from reportlab.lib import colors
from reportlab.pdfgen.canvas import Canvas
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.frames import Frame
from reportlab.platypus.flowables import XBox, KeepTogether
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.barcode import getCodes, getCodeNames, createBarcodeDrawing, createBarcodeImageInMemory
def run():
styles = getSampleStyleSheet()
styleN = styles['Normal']
styleH = styles['Heading1']
story = []
#for codeNames in code
story.append(Paragraph('I2of5', styleN))
story.append(I2of5(1234, barWidth = inch*0.02, checksum=0))
story.append(Paragraph('MSI', styleN))
story.append(MSI(1234))
story.append(Paragraph('Codabar', styleN))
story.append(Codabar("A012345B", barWidth = inch*0.02))
story.append(Paragraph('Code 11', styleN))
story.append(Code11("01234545634563"))
story.append(Paragraph('Code 39', styleN))
story.append(Standard39("A012345B%R"))
story.append(Paragraph('Extended Code 39', styleN))
story.append(Extended39("A012345B}"))
story.append(Paragraph('Code93', styleN))
story.append(Standard93("CODE 93"))
story.append(Paragraph('Extended Code93', styleN))
story.append(Extended93("L@@K! Code 93 :-)")) #, barWidth=0.005 * inch))
story.append(Paragraph('Code 128', styleN))
c=Code128("AB-12345678") #, barWidth=0.005 * inch)
#print 'WIDTH =', (c.width / inch), 'barWidth =', (c.barWidth / inch)
#print 'LQ =', (c.lquiet / inch), 'RQ =', (c.rquiet / inch)
story.append(c)
story.append(Paragraph('USPS FIM', styleN))
story.append(FIM("A"))
story.append(Paragraph('USPS POSTNET', styleN))
story.append(POSTNET('78247-1043'))
story.append(Paragraph('USPS 4 State', styleN))
story.append(USPS_4State('01234567094987654321','01234567891'))
from reportlab.graphics.barcode import createBarcodeDrawing
story.append(Paragraph('EAN13', styleN))
bcd = createBarcodeDrawing('EAN13', value='123456789012')
story.append(bcd)
story.append(Paragraph('EAN8', styleN))
bcd = createBarcodeDrawing('EAN8', value='1234567')
story.append(bcd)
story.append(Paragraph('UPCA', styleN))
bcd = createBarcodeDrawing('UPCA', value='03600029145')
story.append(bcd)
story.append(Paragraph('USPS_4State', styleN))
bcd = createBarcodeDrawing('USPS_4State', value='01234567094987654321',routing='01234567891')
story.append(bcd)
story.append(Paragraph('Label Size', styleN))
story.append(XBox((2.0 + 5.0/8.0)*inch, 1 * inch, '1x2-5/8"'))
story.append(Paragraph('Label Size', styleN))
story.append(XBox((1.75)*inch, .5 * inch, '1/2x1-3/4"'))
c = Canvas('out.pdf')
f = Frame(inch, inch, 6*inch, 9*inch, showBoundary=1)
f.addFromList(story, c)
c.save()
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 Barcode Test Suite - full output', styleH))
story.append(Paragraph('Generated on %s' % time.ctime(time.time()), styleN))
story.append(Paragraph('', styleN))
story.append(Paragraph('Repository information for this build:', styleN))
#see if we can figure out where it was built, if we're running in source
if os.path.split(os.getcwd())[-1] == 'barcode' and os.path.isdir('.svn'):
#runnning in a filesystem svn copy
infoLines = os.popen('svn info').read()
story.append(Preformatted(infoLines, styles["Code"]))
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,
being re-released as part of the forthcoming ReportLab 2.0 release.
""", 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'))
createSample('test_cbcim.tiff',createBarcodeImageInMemory('USPS_4State', value='01234567094987654321',routing='01234567891',format='tiff'))

View File

@@ -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]

View File

@@ -0,0 +1,386 @@
#copyright ReportLab Inc. 2000-2012
#see license.txt for license details
__version__=''' $Id$ '''
__all__ = ('USPS_4State',)
from reportlab.lib.colors import black
from reportlab.graphics.barcode.common import Barcode
from reportlab.lib.utils import asNative
class USPS_4State(Barcode):
''' USPS 4-State OneView (TM) barcode. All info from USPS-B-3200A
'''
_widthSize = 1
_heightSize = 1
_fontSize = 11
_humanReadable = 0
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)
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 = value
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 hex(USPS_4State('01234567094987654321','').binary)
0x1122103B5C2004B1L
>>> print hex(USPS_4State('01234567094987654321','01234').binary)
0xD138A87BAB5CF3804B1L
>>> print hex(USPS_4State('01234567094987654321','012345678').binary)
0x202BDC097711204D21804B1L
>>> print hex(USPS_4State('01234567094987654321','01234567891').binary)
0x16907B2A24ABC16A2E5C004B1L
'''
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))
pitch = property(lambda self: self.scale('pitch',self.dimensions,self.widthScale))
barWidth = property(lambda self: self.scale('width',self.dimensions,self.widthScale))
barHeight = property(lambda self: self.scale('F',self.tops,self.heightScale) - self.scale('F',self.bottoms,self.heightScale))
widthScale = property(lambda self: min(1,max(0,self.widthSize)))
heightScale = property(lambda self: min(1,max(0,self.heightSize)))
def width(self):
self.computeSize()
return self._width
width = property(width)
def height(self):
self.computeSize()
return self._height
height = property(height)
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):
'''
>>> print ' '.join(hex(_crc11(USPS_4State('01234567094987654321',x).binary)) for x in ('','01234','012345678','01234567891'))
0x51 0x65 0x606 0x751
'''
bytes = hex(int(value))[2:-1]
bytes = '0'*(26-len(bytes))+bytes
gp = 0x0F35
fcs = 0x07FF
data = int(bytes[: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(bytes[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()

View File

@@ -0,0 +1,307 @@
#copyright ReportLab Europe Limited. 2000-2012
#see license.txt for license details
__version__=''' $Id$ '''
__all__= (
'BarcodeI2of5',
'BarcodeCode128',
'BarcodeStandard93',
'BarcodeExtended93',
'BarcodeStandard39',
'BarcodeExtended39',
'BarcodeMSI',
'BarcodeCodabar',
'BarcodeCode11',
'BarcodeFIM',
'BarcodePOSTNET',
'BarcodeUSPS_4State',
)
from reportlab.lib.validators import isInt, isNumber, isColor, isString, isColorOrNone, OneOf, 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 Line, Rect, Group, NotImplementedError, 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)
del self.__dict__['width']
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):
self._Gadd(Rect(self.x+x,self.y+y,w,h,
strokeColor=self.barStrokeColor,strokeWidth=self.barStrokeWidth, fillColor=self.barFillColor))
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
code = '''class %(name)s(_BarcodeWidget,%(codeName)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
if doc: Klass.__doc__ = doc
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).'''),
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'],
)
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'),
),
'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"
)
if __name__=='__main__':
raise ValueError('widgets.py has no script function')