Skip to content

Instantly share code, notes, and snippets.

@yasoob
Last active October 26, 2022 13:24
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yasoob/f970de335a99a542461977033b8b0a02 to your computer and use it in GitHub Desktop.
Save yasoob/f970de335a99a542461977033b8b0a02 to your computer and use it in GitHub Desktop.
A pure Python Universal Product Code A barcode generator.
import xml.dom
import os
class UPC:
"""
The all-in-one class that represents the UPC-A
barcode.
"""
EDGE = "101"
MIDDLE = "01010"
CODES = {
"L": (
"0001101",
"0011001",
"0010011",
"0111101",
"0100011",
"0110001",
"0101111",
"0111011",
"0110111",
"0001011",
),
"R": (
"1110010",
"1100110",
"1101100",
"1000010",
"1011100",
"1001110",
"1010000",
"1000100",
"1001000",
"1110100",
),
}
SIZE = "{0:.3f}mm"
MODULE_WIDTH = 0.33
MODULE_HEIGHT = 25.9
EXTENDED_MODULE_HEIGHT = MODULE_HEIGHT + 5 * MODULE_WIDTH
BARCODE_HEIGHT = EXTENDED_MODULE_HEIGHT
TOTAL_MODULES = 113
def __init__(self, upc):
self.upc = list(str(upc))[:11]
if len(self.upc) < 11:
raise Exception(f"The UPC has to be of length 11 or 12 (with check digit)")
self.upc = self.upc + self.calculate_check_digit()
encoded_code = self.encode()
self.create_barcode()
self.save("upc_custom.svg")
def calculate_check_digit(self):
"""
Calculate the check digit
"""
upc = [int(digit) for digit in self.upc[:11]]
oddsum = sum(upc[::2])
evensum = sum(upc[1::2])
check = (evensum + oddsum * 3) % 10
if check == 0:
return [0]
else:
return [10 - check]
def encode(self):
"""
Encode the upc based on the mapping defined above
"""
code = self.EDGE[:]
for _i, number in enumerate(self.upc[0:6]):
code += self.CODES["L"][int(number)]
code += self.MIDDLE
for number in self.upc[6:]:
code += self.CODES["R"][int(number)]
code += self.EDGE
self.encoded_upc = code
def create_barcode(self):
self.prepare_svg()
# Quiet zone is automatically added as the background is white We will
# simply skip the space for 9 modules and start the guard from there
x_position = 9 * self.MODULE_WIDTH
for count, extended in self.packed(self.encoded_upc):
if count < 0:
color = "white"
else:
color = "black"
config = {
"xpos": x_position,
"ypos": 1,
"color": color,
"width": abs(count) * self.MODULE_WIDTH,
"height": self.EXTENDED_MODULE_HEIGHT if extended else self.MODULE_HEIGHT,
}
self.create_module(**config)
x_position += abs(count) * self.MODULE_WIDTH
def packed(self, encoded_upc):
"""
Pack the encoded UPC to a list. Ex:
"001101" -> [-2, 2, -1, 1]
"""
encoded_upc += " "
extended_bars = [1,2,3, 4,5,6,7, 28,29,30,31,32, 53,54,55,56, 57,58,59]
count = 1
bar_count = 1
for i in range(0, len(encoded_upc) - 1):
if encoded_upc[i] == encoded_upc[i + 1]:
count += 1
else:
if encoded_upc[i] == "1":
yield count, bar_count in extended_bars
else:
yield -count, bar_count in extended_bars
bar_count += 1
count = 1
def prepare_svg(self):
"""
Create the complete boilerplate SVG for the barcode
"""
self._document = self.create_svg_object()
self._root = self._document.documentElement
group = self._document.createElement("g")
group.setAttribute("id", "barcode_group")
self._group = self._root.appendChild(group)
background = self._document.createElement("rect")
background.setAttribute("width", "100%")
background.setAttribute("height", "100%")
background.setAttribute("style", "fill: white")
self._group.appendChild(background)
def create_svg_object(self):
"""
Create a SVG object
"""
imp = xml.dom.getDOMImplementation()
doctype = imp.createDocumentType(
"svg",
"-//W3C//DTD SVG 1.1//EN",
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd",
)
document = imp.createDocument(None, "svg", doctype)
document.documentElement.setAttribute("version", "1.1")
document.documentElement.setAttribute("xmlns", "http://www.w3.org/2000/svg")
document.documentElement.setAttribute(
"width", self.SIZE.format(self.TOTAL_MODULES * self.MODULE_WIDTH)
)
document.documentElement.setAttribute(
"height", self.SIZE.format(self.BARCODE_HEIGHT)
)
return document
def create_module(self, xpos, ypos, width, height, color):
"""
Create a module and append a corresponding rect to the SVG group
"""
element = self._document.createElement("rect")
element.setAttribute("x", self.SIZE.format(xpos))
element.setAttribute("y", self.SIZE.format(ypos))
element.setAttribute("width", self.SIZE.format(width))
element.setAttribute("height", self.SIZE.format(height))
element.setAttribute("style", "fill:{};".format(color))
self._group.appendChild(element)
def save(self, filename):
"""
Dump the final SVG XML code to a file
"""
output = self._document.toprettyxml(
indent=4 * " ", newl=os.linesep, encoding="UTF-8"
)
with open(filename, "wb") as f:
f.write(output)
if __name__ == "__main__":
upc = UPC(12345678910)
@yasoob
Copy link
Author

yasoob commented Apr 12, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment