text: __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
  
  #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
  
  class isEan13String(Validator):
      def test(self,x):
          return type(x) is str and len(x)<=12 and len([c for c in x if c in "0123456789"])==12
  isEan13String = isEan13String()
  
  class Ean13BarcodeWidget(PlotArea):
      codeName = "EAN13"
      _attrMap = AttrMap(BASE=PlotArea,
          value = AttrMapValue(isEan13String, 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 = barStrokeColor = colors.black
      barStrokeWidth = 0
      x = 0
      y = 0
      def __init__(self,value='123456789012',**kw):
          self.value=max(self._digits-len(value),0)*'0'+value[:self._digits]
          for k, v in kw.iteritems():
              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
  
          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=barFillColor)
                      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 isEan8String(Validator):
      def test(self,x):
          return type(x) is str and len(x)<=7 and len([c for c in x if c in "0123456789"])==7
  isEan8String = isEan8String()
  
  class Ean8BarcodeWidget(Ean13BarcodeWidget):
      codeName = "EAN8"
      _attrMap = AttrMap(BASE=Ean13BarcodeWidget,
          value = AttrMapValue(isEan8String, 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'))
  