Skip to content

Instantly share code, notes, and snippets.

@derlin
Created August 18, 2018 07:44
Show Gist options
  • Save derlin/3270a8d3cf8b8771e19d86c986c48e9e to your computer and use it in GitHub Desktop.
Save derlin/3270a8d3cf8b8771e19d86c986c48e9e to your computer and use it in GitHub Desktop.

Barcode checker

A little PYTHON code for basic barcode checking before using an ESC/POS printer barcode renderig function. This ensures the barcode will be renderer correctly.

Note that most ESC/POS printers, such as the Epson TM T20ii, don't usually bother with the checksums and stuff; they only care about the character set and the length of the string. There are also some special formats, for example the CODE128 should be prefixed with {B for alphanumeric barcodes, etc.

Barcode specifications

  • UPC: UPC barcodes are 12 digits long, numeric only and include a checksum as the last digit. You calculate the checksum by summing each of the odd position numbers multiplied by 3 and then by adding the sum of the even position numbers. The final digit of the result is subtracted from 10 to calculate the check digit (or left as-is if already zero).

  • EAN EAN barcodes are either 8 (EAN8) or 13 (EAN13) digits long, numeric only nd include a checksum as the last digit. The checksum is the same as UPC, but the numbers are processed from the end, so the odd digits are last, third before last, etc.

  • NW7: the NW7 barcode has a start and stop character comprised between A to D, then the content of the barcode is comprised of numbers and characters -, ., $, /, +. It has a variable length and no checksum is required.

  • CODABAR: the codabar barcode has the same restrictions as NW7.

  • CODE39: Code 39 specification defines 43 characters, consisting of uppercase letters (A through Z), numeric digits and a number of special characters (-, ., $, /, +, %, and space). The symbol '*' can be used as a start/stop chacacter. It is sometimes used with an optional modulo 43 check digit.

  • CODE93: Code 93 uses the same character set as Code 39 with 2 check digits (module 47).

  • CODE128: it is identical to Code128, but with a maximum of 35 symbol characters (43 data characters).

  • GS1: http://www.databar-barcode.info/ https://www.gs1.org/barcodes/databar

    • GS1 Omnidirectional: 14 digits
    • GS1 truncated: 14 digits
    • GS1 limited: 14 digits, leading digit is always 0 or 1
    • GS1 expanded: maximum 74 numeric/41 alphanumberic

Other resources

def check_ean_checksum(barcode):
"""
Checks the checksum for EAN13-Code.
:param barcode: The barcode to check
:returns: The checksum for `barcode`.
:rtype: Boolean
"""
ean = [ord(c) - 48 for c in barcode] # 48 is ord('0')
digits = ean[0:-1]
# Numbers are examined going from right to left, so the first odd position is the last digit in the code.
digits.reverse()
return _compute_one_check_digit(digits) == ean[-1]
def check_upc_checksum(barcode):
"""Calculates the checksum for UPCA/UPCE codes
:param barcode: the barcode to check
:return: The checksum for `barcode`
:rtype: Boolean
"""
upc = [ord(c) - 48 for c in barcode] # 48 is ord('0')
digits = upc[0:-1]
return _compute_one_check_digit(digits) == upc[-1]
def _compute_one_check_digit(digits):
"""
Calculates the checksum by summing each of the odd position numbers multiplied by 3 and then
by adding the sum of the even position numbers.
The final digit of the result is subtracted from 10 to calculate the check digit (or left as-is if already zero).
Note that positions starts at one, so odd-numbered positions are first, third, fifth, etc. and even-numbered
positions are second, fourth, sixth, etc.
:param digits: the digits
:return: the checksum
:rtype: Integer
"""
evensum, oddsum = sum(digits[1::2]), sum(digits[::2])
checksum = ((evensum + oddsum * 3) % 10) % 10
return checksum
def _compute_mod_checksum(mod, digits):
return sum(digits) % mod
import re
def _create_barcode_validation_func(bounds, regex):
return lambda s: bounds[0] <= len(s) <= bounds[1] and re.match(regex, s)
_SET_BARCODE_TYPE = lambda m: m
# Barcodes for printing function type A
BARCODE_TYPE_A = {
'UPC-A': _SET_BARCODE_TYPE(0),
'UPC-E': _SET_BARCODE_TYPE(1),
'EAN13': _SET_BARCODE_TYPE(2),
'EAN8': _SET_BARCODE_TYPE(3),
'CODE39': _SET_BARCODE_TYPE(4),
'ITF': _SET_BARCODE_TYPE(5),
'NW7': _SET_BARCODE_TYPE(6),
'CODABAR': _SET_BARCODE_TYPE(6), # Same as NW7
}
# Barcodes for printing function type B
# The first 8 are the same barcodes as type A
BARCODE_TYPE_B = {
'UPC-A': _SET_BARCODE_TYPE(65),
'UPC-E': _SET_BARCODE_TYPE(66),
'EAN13': _SET_BARCODE_TYPE(67),
'EAN8': _SET_BARCODE_TYPE(68),
'CODE39': _SET_BARCODE_TYPE(69),
'ITF': _SET_BARCODE_TYPE(70),
'NW7': _SET_BARCODE_TYPE(71),
'CODABAR': _SET_BARCODE_TYPE(71), # Same as NW7
'CODE93': _SET_BARCODE_TYPE(72),
'CODE128': _SET_BARCODE_TYPE(73),
'GS1-128': _SET_BARCODE_TYPE(74),
'GS1 DATABAR OMNIDIRECTIONAL': _SET_BARCODE_TYPE(75),
'GS1 DATABAR TRUNCATED': _SET_BARCODE_TYPE(76),
'GS1 DATABAR LIMITED': _SET_BARCODE_TYPE(77),
'GS1 DATABAR EXPANDED': _SET_BARCODE_TYPE(78),
}
# 255 is a limitation of the "function b" command, not the barcode formats.
BARCODE_FORMAT_CHECKERS = {
'UPC-A': _create_barcode_validation_func((12, 12), "^[0-9]{12}$"),
'UPC-E': _create_barcode_validation_func((12, 12), "^[0-9]{12}$"),
'EAN13': _create_barcode_validation_func((13, 13), "^[0-9]{13}$"),
'EAN8': ((8, 8), "^[0-9]{8}$"),
'CODE39': ((2, 255), "^([0-9A-Z \$\%\+\-\.\/]+|\*[0-9A-Z \$\%\+\-\.\/]+\*)$"),
'ITF': ((2, 255), "^([0-9]{2})+$"),
'NW7': ((2, 255), "^[A-D][0-9\$\+\-\.\/\:]+[A-D]$"),
'CODABAR': ((2, 255), "^[A-D][0-9\$\+\-\.\/\:]+[A-D]$"), # same as NW7
'CODE93': ((2, 255), "^([0-9A-Z \$\%\+\-\.\/]+|\*[0-9A-Z \$\%\+\-\.\/]+\*)$"), # same as code39
# The CODE128 encoder is quite complex, so only a very basic header-check is applied here.
'CODE128': ((2, 255), "^\{[A-C][\\x00-\\x7F]+$"),
'GS1-128': ((2, 48), "^\{[A-C][\\x00-\\x7F]+$"), # same as code128
'GS1 DATABAR OMNIDIRECTIONAL': ((14, 14), "^[0-9]{14}$"),
'GS1 DATABAR TRUNCATED': ((14, 14), "^[0-9]{14}$"), # TODO : (01) ?
'GS1 DATABAR LIMITED': ((14, 14), "^[0-9]{14}$"),
'GS1 DATABAR EXPANDED': ((2, 74), "")
}
from bcchecker.constants import *
if __name__ == "__main__":
alphanum_prefix = "{B"
code128 = "ABC-abc-1234"
print(BARCODE_FORMAT_CHECKERS['CODE128'](f"{alphanum_prefix}{code128}"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment