This is an accepted answer, so I'm leaving the original code underneath. But I prefer this refinement.
def list_join(seq):
''' Join a sequence of lists into a single list, much like str.join
will join a sequence of strings into a single string.
'''
return [x for sub in seq for x in sub]
code128B_mapping = dict((chr(c), [98, c+64] if c < 32 else [c-32]) for c in range(128))
code128C_mapping = dict([(u'%02d' % i, [i]) for i in range(100)] + [(u'%d' % i, [100, 16+i]) for i in range(10)])
code128_chars = u''.join(chr(c) for c in [212] + list(range(33,126+1)) + list(range(200,211+1)))
def encode128(s):
''' Code 128 conversion for a font as described at
https://en.wikipedia.org/wiki/Code_128 and downloaded
from http://www.barcodelink.net/barcode-font.php
Only encodes ASCII characters, does not take advantage of
FNC4 for bytes with the upper bit set. Control characters
are not optimized and expand to 2 characters each.
Coded for https://mcmap.net/q/2001884/-python-code128-encoder-for-code128-barcode-font/5987
'''
if s.isdigit() and len(s) >= 2:
# use Code 128C, pairs of digits
codes = [105] + list_join(code128C_mapping[s[i:i+2]] for i in range(0, len(s), 2))
else:
# use Code 128B and shift for Code 128A
codes = [104] + list_join(code128B_mapping[c] for c in s)
check_digit = (codes[0] + sum(i * x for i,x in enumerate(codes))) % 103
codes.append(check_digit)
codes.append(106) # stop code
return u''.join(code128_chars[x] for x in codes)
def encode128(s):
''' Code 128 conversion for a font as described at
https://en.wikipedia.org/wiki/Code_128 and downloaded
from http://www.barcodelink.net/barcode-font.php
Only encodes ASCII characters, does not take advantage of
FNC4 for bytes with the upper bit set.
It does not attempt to optimize the length of the string,
Code B is the default to prefer lower case over control characters.
Coded for https://mcmap.net/q/2001884/-python-code128-encoder-for-code128-barcode-font/5987
'''
s = s.encode('ascii').decode('ascii')
if s.isdigit() and len(s) % 2 == 0:
# use Code 128C, pairs of digits
codes = [105]
for i in range(0, len(s), 2):
codes.append(int(s[i:i+2], 10))
else:
# use Code 128B and shift for Code 128A
mapping = dict((chr(c), [98, c + 64] if c < 32 else [c - 32]) for c in range(128))
codes = [104]
for c in s:
codes.extend(mapping[c])
check_digit = (codes[0] + sum(i * x for i,x in enumerate(codes))) % 103
codes.append(check_digit)
codes.append(106) # stop code
chars = (b'\xd4' + bytes(range(33,126+1)) + bytes(range(200,211+1))).decode('latin-1')
return ''.join(chars[x] for x in codes)