1 #
2 # Copyright (C) 2010 Martin Owens
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 #
18 """
19 Some basic common code shared between EAN and UCP generators.
20 """
22 from Base import Barcode
23 import sys
25 MAPPING = [
26 # Left side of barcode Family '0'
27 [ "0001101", "0011001", "0010011", "0111101", "0100011",
28 "0110001", "0101111", "0111011", "0110111", "0001011" ],
29 # Left side of barcode Family '1' and flipped to right side.
30 [ "0100111", "0110011", "0011011", "0100001", "0011101",
31 "0111001", "0000101", "0010001", "0001001", "0010111" ],
32 ]
33 # This chooses which of the two encodings above to use.
34 FAMILIES = [ '000000', '001011', '001101', '001110', '010011',
35 '011001', '011100', '010101', '010110', '011010' ]
37 GUARD_BAR = '202'
38 CENTER_BAR = '02020'
40 class EanBarcode(Barcode):
41 """Simple base class for all EAN type barcodes"""
42 length = None
43 lengths = None
44 checks = []
46 def intarray(self, number):
47 """Convert a string of digits into an array of ints"""
48 return [ int(i) for i in number ]
51 def encode_interleaved(self, family, number, fams=FAMILIES):
52 """Encode any side of the barcode, interleaved"""
53 result = []
54 encset = self.intarray(fams[family])
55 for i in range(len(number)):
56 thismap = MAPPING[encset[i]]
57 result.append( thismap[number[i]] )
58 return result
61 def encode_right(self, number):
62 """Encode the right side of the barcode, non-interleaved"""
63 result = []
64 for n in number:
65 # The right side is always the reverse of the left's family '1'
66 result.append( MAPPING[1][n][::-1] )
67 return result
70 def encode_left(self, number):
71 """Encode the left side of the barcode, non-interleaved"""
72 result = []
73 for n in number:
74 result.append( MAPPING[0][n] )
75 return result
78 def space(self, *spacing):
79 """Space out an array of numbers"""
80 result = ''
81 for space in spacing:
82 if isinstance(space, list):
83 for i in space:
84 result += str(i)
85 elif isinstance(space, int):
86 result += ' ' * space
87 return result
90 def getLengths(self):
91 """Return a list of acceptable lengths"""
92 if self.length:
93 return [ self.length ]
94 return self.lengths[:]
97 def encode(self, code):
98 """Encode any EAN barcode"""
99 if not code.isdigit():
100 return self.error(code, 'Not a Number, must be digits 0-9 only')
101 lengths = self.getLengths() + self.checks
103 if len(code) not in lengths:
104 return self.error(code, 'Wrong size, must be %s digits' %
105 (', '.join(self.space(lengths))))
107 if self.checks:
108 if len(code) not in self.checks:
109 code = self.appendChecksum(code)
110 elif not self.verifyChecksum(code):
111 return self.error(code, 'Checksum failed, omit for new sum')
112 return self._encode(self.intarray(code))
115 def _encode(self, n):
116 raise NotImplementedError("_encode should be provided by parent EAN")
118 def enclose(self, left, right=[], guard=GUARD_BAR, center=CENTER_BAR):
119 """Standard Enclosure"""
120 parts = [ guard ] + left + [ center ] + right + [ guard ]
121 return ''.join( parts )
123 def getChecksum(self, number, magic=10):
124 """Generate a UPCA/EAN13/EAN8 Checksum"""
125 weight = [3,1] * len(number)
126 result = 0
127 # We need to work from left to right so reverse
128 number = number[::-1]
129 # checksum based on first digits.
130 for i in range(len(number)):
131 result += int(number[i]) * weight[i]
132 # Modulous result to a single digit checksum
133 checksum = magic - (result % magic)
134 if checksum < 0 or checksum >= magic:
135 return '0'
136 return str(checksum)
138 def appendChecksum(self, number):
139 """Apply the checksum to a short number"""
140 return number + self.getChecksum(number)
142 def verifyChecksum(self, number):
143 """Verify any checksum"""
144 return self.getChecksum(number[:-1]) == number[-1]