123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755 |
- #
- # 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,object):
- """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()
- def _checkVal(self, name, v, allowed):
- if v not in allowed:
- raise ValueError('%s attribute %s is invalid %r\nnot in allowed %r' % (
- self.__class__.__name__, name, v, allowed))
- return v
- class MultiWidthBarcode(Barcode):
- """Base for variable-bar-width codes like Code93 and Code128"""
- def computeSize(self, *args):
- barWidth = self.barWidth
- oa, oA = ord('a') - 1, ord('A') - 1
- w = 0.0
- for c in self.decomposed:
- oc = ord(c)
- if c in ascii_lowercase:
- w = w + barWidth * (oc - oa)
- elif c in ascii_uppercase:
- w = w + barWidth * (oc - oA)
- if self.barHeight is None:
- self.barHeight = w * 0.15
- self.barHeight = max(0.25 * inch, self.barHeight)
- if self.quiet:
- w += self.lquiet + self.rquiet
- self._height = self.barHeight
- self._width = w
- def draw(self):
- self._calculate()
- oa, oA = ord('a') - 1, ord('A') - 1
- barWidth = self.barWidth
- left = self.quiet and self.lquiet or 0
- for c in self.decomposed:
- oc = ord(c)
- if c in ascii_lowercase:
- left = left + (oc - oa) * barWidth
- elif c in ascii_uppercase:
- w = (oc - oA) * barWidth
- self.rect(left, 0, w, self.barHeight)
- left += w
- self.drawHumanReadable()
- class I2of5(Barcode):
- """
- Interleaved 2 of 5 is a numeric-only barcode. It encodes an even
- number of digits; if an odd number is given, a 0 is prepended.
- Options that may be passed to constructor:
- value (int, or numeric string required.):
- The value to encode.
- barWidth (float, default .0075):
- X-Dimension, or width of the smallest element
- Minumum is .0075 inch (7.5 mils).
- ratio (float, default 2.2):
- The ratio of wide elements to narrow elements.
- Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the
- barWidth is greater than 20 mils (.02 inch))
- gap (float or None, default None):
- width of intercharacter gap. None means "use barWidth".
- barHeight (float, see default below):
- Height of the symbol. Default is the height of the two
- bearer bars (if they exist) plus the greater of .25 inch
- or .15 times the symbol's length.
- checksum (bool, default 1):
- Whether to compute and include the check digit
- bearers (float, in units of barWidth. default 3.0):
- Height of bearer bars (horizontal bars along the top and
- bottom of the barcode). Default is 3 x-dimensions.
- Set to zero for no bearer bars. (Bearer bars help detect
- misscans, so it is suggested to leave them on).
- 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
|