Générateur de code barres
J'ai cherché un moment un générateur de code barres qui fabrique des images, et je n'en ai pas trouvé, du coup j'ai décidé d'en écrire un, en espérant qu'il serve à quelqu'un.
Il génère des images (gif ou png) contenant des codes EAN, il nécessite l'installation de PIL (http://www.pythonware.com/products/pil/). Il génère aussi les codes barres au format HTML, dans ce cas PIL n'a pas besoin d'être installé.
Exemple de génération d'une image :
Source du fichier EANBarCode.py
1 """
2 This class generate EAN bar code, it required PIL (python imaging library)
3 installed.
4
5 If the code has not checksum (12 digits), it added automatically.
6
7 Create bar code sample :
8 from EANBarCode import EanBarCode
9 bar = EanBarCode()
10 bar.getImage("9782212110708",50,"gif")
11
12 """
13
14 # courbB08.pil PIL Font file base64 encoded
15 courB08_pil ="""eJztl91rFkcUxp+Zt7vGFYzVtiJKICgYlLRWkaBBVGgDraFGCH5gsQp+QMBqabAVRYJYAlakCkoh
16 CpYgxaLkIu1NvLBeSAStglpqL6xQAsVe2AuL5u2buH3mzGaYPf9AKWTl8d3nl7MzZ2bnazvea9+9
17 7+PurFWut5e0Zu+s7VybYfKavP7LK3X/5TlM4Q3/OWbyf1ARD/6mgb2SjwtPhbpnq0iKZ6ahrmCj
18 wqbxdgamRnHOA69jimN5zvIS8cDcUEeVdYzRAw1FHcJYXgPvG4s6Jlgj7xeEequS3wLeNvGvnrEO
19 tq+Jt82szT+b86+WHlgS2jHGuHF6YHnog1zaupxqCcy3t4X3rVG9iXhgjW+bsFQ80BaxRDywTrF1
20 VId6toPaqOI2UlsV20ptV2w7tUuxXVSXYl3UvoIZ9kFFPPBJ6D/HLD3QXbwjyDjI6YHPiz5FXiN7
21 SQ8cDu/N9/1h3veEOP/Oe6gvQnmuvYYe+NL3qYyNVDxw2seF8XKa+jrKJREPnFdx56l+xfqpS4pd
22 ogZUeQPU91FcKh64GveBeOCaKu8adUM9e4O6reJuU/cUu0c9VM8+pB6r/B5TI+rZEerPUpyhB/6K
23 5lsqHniuyntO1VR5Nb5CU86FHqZOsTqqXrF66o2ojlQ8zDwVN4+aX86FHqYpXg9YLeevWRzPc7LF
24 ZG+V1wN6mKXxvMzH6GFaJua5zGNLD7MqmtNcc+hh1oT1oCb5cf6aNj92mbPMGXqY9jCPasLaqQ1h
25 jMv8pYfZpOI2UR9GcYl4mB1RnMtvB9me8N583B5qb3mNoIf5NGJc1+hhPvPrrjybioc5op49Qh0L
26 dfj8jlHHQ3s9O059Fc3zRDzMmVKcpYfpU+3oI/umxJyH+TYqLxUPc0X13xVqMMovFQ8zpPIbon6M
27 WCoeZljVMUz9VIqz9DAP1Dt6QP0a9gpZ7+lhHhXjysreaOhhfiv1vaGH+T2Mv5rbU+hh/uAaOnlN
28 Xv+Hy4/7mtv3OW5hnpTODIYe5mm0xqbiYf4OcbLv08NU1ZyuuqKLOEvm6sjhJkd8TjRustgkrO3u
29 vFGjh60r1uyiPHrY6eH84tb7l/SwM8vrAT3snHgNY9wcsoby+Y8edn5UxxTxsIuitrlcFpG9GcVx
30 /6CHXRrKk72MHrYl3stYB/ceu7I4X02wlWSrCmaF1ehhV7NrovWKHrattI4betj20Fc8r7E87kf2
31 g+gcy32BHnZDfKZmHPco2xnl4vqlk2yz6r/N1EfRPpiKh90d7VGpeNi9inGPst2lNdbSwx4McS8k
32 7iDVE/Ytz3qoXsV6qZOKnaTOBDYqjPuRPRfOkz7uHNUf4uQMQg/7XekMYulhB6JnE/GwP0T1JuJh
33 ryrGM6G9HuWSiIcdDnPmhTs70sPeCuPes1vUXcXuUvcDGxV2n/olOisn4mEfhfOVby/3KDsSlZeI
34 h32iGOe0faoY57R9ptgzajTKJREPOx7aJnOfHhUbxov0Mz0qU8v50aMyo/wu6VGZrdhsqqH8fnll
35 HEEz4zj6DNMxK+4X+gyv8cszyoU+4zfmjNAO9zuXrNGXF1gj2ULFFpI1K9ZMtiww//22jGwFXg39
36 535XkK0O+cl5gz7Du6iP5wd9hvfDs9LP9BnWR/U6tp6sU7FOsi1RLo5tIdsWled+t5HtVO3YSdal
37 WBfZftW2/WQHVH4HyA6F9+GfPUR2VOV3lKxXsV6yE4qdIDul2Cmys6ptZ8n6Qi7+m7OP7ELoU/8t
38 dIHsoo8L+V0ku6xyvkw2qNgg2VBgvg+GyK6XyrP0GW5ydE3EuXd5k+xOeOdVibtD9jNm/Qv15O4i"""
39
40 # courbB08.pbm font file base64 encoded
41 courB08_pbm ="""eJxNkntM01cUx8+P2/1apUAZEpECq4KRjKhF0E55FYEp4yG6mglz2Q8Q1BhERhhls/zKI+CID4wb
42 IAPKpk4GAzqZPKKMX2GIUwGJG+ImtKwKjIzXcGuBtncV0Hn+uLnn5Nzv55xv7mdRkbusVjquBACr
43 0N3B+wCQi/m+ijAf4LGl/wgAiwkNDpRIyyABSjGkBQ/fa3c1bfLs4U8ulDcYUs/502rTpIlO9pyc
44 Kp/Buql6f3rmZ1NqvpO2SZXf0duY3j0563zjoZpW8AvHRmVeZ/Co36mFR8bERzlsxOMJ+oJshsS5
45 7rlfzFzmnZFEFnIEZjTGizgLsLzjl4QtrNprBRu10e+u9GgePHjG63bPDw/H87uix0Vtsvkqg9qO
46 lUimPLiOM4z69YfqIu5Pa2Sr/io6n9Xmf9e+57W1Iapo4lLQBdLSWc/z3KOSlgznDXTW/Flh21kX
47 IeUIX8FZVL9dwP4NBH5jglYxkBNFmWgMcfsAxM/9gEL5TTwYpnfElR8qQ+WiCgeTHOAfb2bW/cQC
48 /FozFOOQzAebtjRvQLI7HBtXvaZe25a3Q/1vZpPa+kd1XXKuflr5Cm48YUsUcjMXjsm/sf+22s6z
49 QAbGZ8mEXMzSE4y9AHhRpltwB1N9ynz5H2MOi0MEi4E5O1ov9ogrFU5cMWAcdgQb3xHFtFK+0pkh
50 VnYWxltx92j69p6jJ9OnHr+Cq5x5X6Mz70JcX2tEG5LIShM4EHIGoLIRsHzcvEuGwMYA4DZPn7gP
51 MA1QIgltnt82cTu7j5n76mmz3TU5Bh3PFRTHku52aBgaTnJD7m1c0a3hNjbWWjBtMsP/OFac/LYA
52 NAAWepdYodB58NBFIuOjNSQ4cgXplqP2RyOe8fd999T8weqBRwLwNFdQobHgA1/YTV8PH+TwV59v
53 Bo7Y1J4rmHFv3T9e8rmmXdGSuPpSbBnhYJ7V8ICz6AfGcdTpRkpCUU8WcOT8wb+dSHIb6QZapx0M
54 Y2DO4i7jYV2AUNkkErpQFHVYmFRmYD7OJhDyQSiow4IkrS3TbpQqFA9slE4jnj6peXMTC+N8buJ2
55 0Uv5eOothuGIiluyCDtff3miBzJHjncOIC3bPT8FLabRPd0TCWy346Mmn9Rz23WyNMJcsnqhQani
56 3CMFOZuYU7c20zTNVqNbGPNxALWnybeLEcTvXWpc10leI5ae/CI9qBqI686cnO6P6F33e2vAp0nz
57 9+hnbNeueh/261UJK5aVeSf73ZSXA7dOBXvkXODEb9hVww4KtPNAbPvaZbi0q9kICCl+CiBJSzLv
58 a8TlntYlC4UHvCRTlaXOy13VAbN0eae2v3hNesWXLsWPkjfOPq7e6zd1fOfc1TckDaylrvleinnT
59 8Ui87ScLMVhhEx7SUJ8U2zKrRR2Z1dEqZlkr7kDTuhFjpkvse9ZXN0R9H+DlYA4TXVm6/kXDQMyT
60 eGnJFXlLlSgva5iLUEcbiyDzNqf4Wr9kKYVUIcY40DrnsW4E4zW9QxnHVYx+bo64mIskDWjZgCrq
61 eVQFrS7Sh/uFLftIidKWbgj6Oq652d4c3v88Dw2JDK7bSWX/ByuaLZI="""
62
63
64 class EanBarCode:
65 """ Compute the EAN bar code """
66 def __init__(self):
67 A = {0 : "0001101", 1 : "0011001", 2 : "0010011", 3 : "0111101", 4 : "0100011",
68 5 : "0110001", 6 : "0101111", 7 : "0111011", 8 : "0110111", 9 : "0001011"}
69 B = {0 : "0100111", 1 : "0110011", 2 : "0011011", 3 : "0100001", 4 : "0011101",
70 5 : "0111001", 6 : "0000101", 7 : "0010001", 8 : "0001001", 9 : "0010111"}
71 C = {0 : "1110010", 1 : "1100110", 2 : "1101100", 3 : "1000010", 4 : "1011100",
72 5 : "1001110", 6 : "1010000", 7 : "1000100", 8 : "1001000", 9 : "1110100"}
73 self.groupC = C
74
75 self.family = {0 : (A,A,A,A,A,A), 1 : (A,A,B,A,B,B), 2 : (A,A,B,B,A,B), 3 : (A,A,B,B,B,A), 4 : (A,B,A,A,B,B),
76 5 : (A,B,B,A,A,B), 6 : (A,B,B,B,A,A), 7 : (A,B,A,B,A,B), 8 : (A,B,A,B,B,A), 9 : (A,B,B,A,B,A)}
77
78
79 def makeCode(self, code):
80 """ Create the binary code
81 return a string which contains "0" for white bar, "1" for black bar, "L" for long bar """
82
83 # Convert code string in integer list
84 self.EAN13 = []
85 for digit in code:
86 self.EAN13.append(int(digit))
87
88 # If the code has already a checksum
89 if len(self.EAN13) == 13:
90 # Verify checksum
91 self.verifyChecksum(self.EAN13)
92 # If the code has not yet checksum
93 elif len(self.EAN13) == 12:
94 # Add checksum value
95 self.EAN13.append(self.computeChecksum(self.EAN13))
96
97 # Get the left codage class
98 left = self.family[self.EAN13[0]]
99
100 # Add start separator
101 strCode = 'L0L'
102
103 # Compute the left part of bar code
104 for i in range(0,6):
105 strCode += left[i][self.EAN13[i+1]]
106
107 # Add middle separator
108 strCode += '0L0L0'
109
110 # Compute the right codage class
111 for i in range (7,13):
112 strCode += self.groupC[self.EAN13[i]]
113
114 # Add stop separator
115 strCode += 'L0L'
116
117 return strCode
118
119
120 def computeChecksum(self, arg):
121 """ Compute the checksum of bar code """
122 # UPCA/EAN13
123 weight=[1,3]*6
124 magic=10
125 sum = 0
126
127 for i in range(12): # checksum based on first 12 digits.
128 sum = sum + int(arg[i]) * weight[i]
129 z = ( magic - (sum % magic) ) % magic
130 if z < 0 or z >= magic:
131 return None
132 return z
133
134
135 def verifyChecksum(self, bits):
136 """ Verify the checksum """
137 computedChecksum = self.computeChecksum(bits[:12])
138 codeBarChecksum = bits[12]
139
140 if codeBarChecksum != computedChecksum:
141 raise Exception ("Bad checksum is %s and should be %s"%(codeBarChecksum, computedChecksum))
142
143
144 def getImage(self, value, height = 50, extension = "PNG"):
145 """ Get an image with PIL library
146 value code barre value
147 height height in pixel of the bar code
148 extension image file extension"""
149 from PIL import Image, ImageFont, ImageDraw
150 from string import lower, upper
151
152 # Create a missing font file
153 decodeFontFile(courB08_pil ,"courB08.pil")
154 decodeFontFile(courB08_pbm ,"courB08.pbm")
155
156 # Get the bar code list
157 bits = self.makeCode(value)
158
159 # Get thee bar code with the checksum added
160 code = ""
161 for digit in self.EAN13:
162 code += "%d"%digit
163
164 # Create a new image
165 position = 8
166 im = Image.new("1",(len(bits)+position,height))
167
168 # Load font
169 font = ImageFont.load("courB08.pil")
170
171 # Create drawer
172 draw = ImageDraw.Draw(im)
173
174 # Erase image
175 draw.rectangle(((0,0),(im.size[0],im.size[1])),fill=256)
176
177 # Draw first part of number
178 draw.text((0, height-9), code[0], font=font, fill=0)
179
180 # Draw first part of number
181 draw.text((position+7, height-9), code[1:7], font=font, fill=0)
182
183 # Draw second part of number
184 draw.text((len(bits)/2+6+position, height-9), code[7:], font=font, fill=0)
185
186 # Draw the bar codes
187 for bit in range(len(bits)):
188 # Draw normal bar
189 if bits[bit] == '1':
190 draw.rectangle(((bit+position,0),(bit+position,height-10)),fill=0)
191 # Draw long bar
192 elif bits[bit] == 'L':
193 draw.rectangle(((bit+position,0),(bit+position,height-3)),fill=0)
194
195 # Save the result image
196 im.save(code+"."+lower(extension), upper(extension))
197
198
199 def getHtml(self, value, height = 50):
200 """ Build an HTML bar code """
201 result = ""
202
203 # Get the bar code list
204 bits = self.makeCode(value)
205
206 # Get thee bar code with the checksum added
207 code = ""
208 for digit in self.EAN13:
209 code += "%d"%digit
210
211 result += '<table><tr><td><center><table cellspacing="0" cellpadding="0" border="0" bgcolor="white"><tr height=%d >'%height
212
213 bitsList = []
214 previousBit = ""
215 for bit in bits:
216 if bit == previousBit:
217 if bit == "0":
218 bitsList[-1] -= 1
219 else:
220 bitsList[-1] += 1
221 else:
222 if bit == "0":
223 bitsList.append(-1)
224 else:
225 bitsList.append(1)
226
227 previousBit = bit
228
229 result += '<td width=10></td>'
230 for bit in bitsList:
231 if bit < 0:
232 result += '<td width=%d></td>'%(-bit)
233 else:
234 result += '<td bgcolor="black" width=%d></td>'%(+bit)
235
236 result += '<td width=10></td>'
237 result += "</tr></table></center></td></tr><tr><td><center>%s</center></td></tr></table>"%(code)
238
239 return result
240
241
242 def decodeFontFile(data, file):
243 """ Decode font file embedded in this script and create file """
244 from zlib import decompress
245 from base64 import decodestring
246 from os.path import exists
247
248 # If the font file is missing
249 if not exists(file):
250 # Write font file
251 open (file, "wb").write(decompress(decodestring(data)))
252
253
254 def testWithChecksum():
255 """ Test bar code with checksum """
256 bar = EanBarCode()
257 assert(bar.makeCode('0000000000000')== 'L0L0001101000110100011010001101000110100011010L0L0111001011100101110010111001011100101110010L0L' )
258 assert(bar.makeCode('1111111111116')== 'L0L0011001001100101100110011001011001101100110L0L0110011011001101100110110011011001101010000L0L' )
259 assert(bar.makeCode('2222222222222')== 'L0L0010011001001100110110011011001001100110110L0L0110110011011001101100110110011011001101100L0L' )
260 assert(bar.makeCode('3333333333338')== 'L0L0111101011110101000010100001010000101111010L0L0100001010000101000010100001010000101001000L0L' )
261 assert(bar.makeCode('4444444444444')== 'L0L0100011001110101000110100011001110100111010L0L0101110010111001011100101110010111001011100L0L' )
262 assert(bar.makeCode('5555555555550')== 'L0L0110001011100101110010110001011000101110010L0L0100111010011101001110100111010011101110010L0L' )
263 assert(bar.makeCode('6666666666666')== 'L0L0101111000010100001010000101010111101011110L0L0101000010100001010000101000010100001010000L0L' )
264 assert(bar.makeCode('7777777777772')== 'L0L0111011001000101110110010001011101100100010L0L0100010010001001000100100010010001001101100L0L' )
265 assert(bar.makeCode('8888888888888')== 'L0L0110111000100101101110001001000100101101110L0L0100100010010001001000100100010010001001000L0L' )
266 assert(bar.makeCode('9999999999994')== 'L0L0001011001011100101110001011001011100010110L0L0111010011101001110100111010011101001011100L0L' )
267
268
269 def testWithoutChecksum():
270 """ Test bar code without checksum """
271 bar = EanBarCode()
272 assert(bar.makeCode('000000000000')== 'L0L0001101000110100011010001101000110100011010L0L0111001011100101110010111001011100101110010L0L' )
273 assert(bar.makeCode('111111111111')== 'L0L0011001001100101100110011001011001101100110L0L0110011011001101100110110011011001101010000L0L' )
274 assert(bar.makeCode('222222222222')== 'L0L0010011001001100110110011011001001100110110L0L0110110011011001101100110110011011001101100L0L' )
275 assert(bar.makeCode('333333333333')== 'L0L0111101011110101000010100001010000101111010L0L0100001010000101000010100001010000101001000L0L' )
276 assert(bar.makeCode('444444444444')== 'L0L0100011001110101000110100011001110100111010L0L0101110010111001011100101110010111001011100L0L' )
277 assert(bar.makeCode('555555555555')== 'L0L0110001011100101110010110001011000101110010L0L0100111010011101001110100111010011101110010L0L' )
278 assert(bar.makeCode('666666666666')== 'L0L0101111000010100001010000101010111101011110L0L0101000010100001010000101000010100001010000L0L' )
279 assert(bar.makeCode('777777777777')== 'L0L0111011001000101110110010001011101100100010L0L0100010010001001000100100010010001001101100L0L' )
280 assert(bar.makeCode('888888888888')== 'L0L0110111000100101101110001001000100101101110L0L0100100010010001001000100100010010001001000L0L' )
281 assert(bar.makeCode('999999999999')== 'L0L0001011001011100101110001011001011100010110L0L0111010011101001110100111010011101001011100L0L' )
282
283
284 def testImage():
285 """ Test images generation with PIL """
286 bar = EanBarCode()
287 bar.getImage("9782212110708",50,"gif")
288 bar.getImage("978221211070",50,"png")
289
290
291 def testHtml():
292 """ Test HTML generation """
293 bar = EanBarCode()
294 open("9782212110708.html","w").write(bar.getHtml("9782212110708",50))
295 assert(bar.getHtml("9782212110708",50) == '<table><tr><td><center><table cellspacing="0" cellpadding="0" border="0" bgcolor="white"><tr height=50 ><td width=10></td><td bgcolor="black" width=1></td><td width=1></td><td bgcolor="black" width=1></td><td width=1></td><td bgcolor="black" width=3></td><td width=1></td><td bgcolor="black" width=2></td><td width=3></td><td bgcolor="black" width=1></td><td width=2></td><td bgcolor="black" width=1></td><td width=2></td><td bgcolor="black" width=2></td><td width=1></td><td bgcolor="black" width=2></td><td width=2></td><td bgcolor="black" width=1></td><td width=2></td><td bgcolor="black" width=2></td><td width=1></td><td bgcolor="black" width=2></td><td width=2></td><td bgcolor="black" width=2></td><td width=2></td><td bgcolor="black" width=1></td><td width=2></td><td bgcolor="black" width=2></td><td width=1></td><td bgcolor="black" width=1></td><td width=1></td><td bgcolor="black" width=1></td><td width=1></td><td bgcolor="black" width=2></td><td width=2></td><td bgcolor="black" width=2></td><td width=1></td><td bgcolor="black" width=2></td><td width=2></td><td bgcolor="black" width=2></td><td width=1></td><td bgcolor="black" width=3></td><td width=2></td><td bgcolor="black" width=1></td><td width=1></td><td bgcolor="black" width=1></td><td width=3></td><td bgcolor="black" width=1></td><td width=2></td><td bgcolor="black" width=3></td><td width=2></td><td bgcolor="black" width=1></td><td width=1></td><td bgcolor="black" width=1></td><td width=2></td><td bgcolor="black" width=1></td><td width=3></td><td bgcolor="black" width=1></td><td width=1></td><td bgcolor="black" width=1></td><td width=10></td></tr></table></center></td></tr><tr><td><center>9782212110708</center></td></tr></table>')
296
297
298 def test():
299 """ Execute all tests """
300 testWithChecksum()
301 testWithoutChecksum()
302 testHtml()
303 testImage()
304
305
306 if __name__ == "__main__":
307 test()
Liens
alternatives libres
- http://www.cgpp.com/bookland/ en Python également (utilise TeX)
- http://www.kbarcode.net/ pour créer des codes barre sous KDE et les enregistrer au format vectoriel (EPS).