"""Module: barcode.ean :Provided barcodes: EAN-14, EAN-13, EAN-8, JAN """ __docformat__ = "restructuredtext en" from functools import reduce from barcode.base import Barcode from barcode.charsets import ean as _ean from barcode.errors import ( IllegalCharacterError, NumberOfDigitsError, WrongCountryCodeError, ) # EAN13 Specs (all sizes in mm) SIZES = { "SC0": 0.27, "SC1": 0.297, "SC2": 0.33, "SC3": 0.363, "SC4": 0.396, "SC5": 0.445, "SC6": 0.495, "SC7": 0.544, "SC8": 0.61, "SC9": 0.66, } class EuropeanArticleNumber13(Barcode): """Initializes EAN13 object. :parameters: ean : String The ean number as string. writer : barcode.writer Instance The writer to render the barcode (default: SVGWriter). """ name = "EAN-13" digits = 12 def __init__(self, ean, writer=None, no_checksum=False): ean = ean[: self.digits] if not ean.isdigit(): raise IllegalCharacterError("EAN code can only contain numbers.") if len(ean) != self.digits: raise NumberOfDigitsError( "EAN must have {} digits, not {}.".format( self.digits, len(ean), ) ) self.ean = ean # If no checksum if no_checksum: # Add a thirteen char if given in parameter, # otherwise pad with zero self.ean = "{}{}".format( ean, ean[self.digits] if len(ean) > self.digits else 0 ) else: self.ean = "{}{}".format(ean, self.calculate_checksum()) self.writer = writer or Barcode.default_writer() def __str__(self): return self.ean def get_fullcode(self): return self.ean def calculate_checksum(self): """Calculates the checksum for EAN13-Code. :returns: The checksum for `self.ean`. :rtype: Integer """ def sum_(x, y): return int(x) + int(y) evensum = reduce(sum_, self.ean[-2::-2]) oddsum = reduce(sum_, self.ean[-1::-2]) return (10 - ((evensum + oddsum * 3) % 10)) % 10 def build(self): """Builds the barcode pattern from `self.ean`. :returns: The pattern as string :rtype: String """ code = _ean.EDGE[:] pattern = _ean.LEFT_PATTERN[int(self.ean[0])] for i, number in enumerate(self.ean[1:7]): code += _ean.CODES[pattern[i]][int(number)] code += _ean.MIDDLE for number in self.ean[7:]: code += _ean.CODES["C"][int(number)] code += _ean.EDGE return [code] def to_ascii(self): """Returns an ascii representation of the barcode. :rtype: String """ code = self.build() for i, line in enumerate(code): code[i] = line.replace("1", "|").replace("0", " ") return "\n".join(code) def render(self, writer_options=None, text=None): options = {"module_width": SIZES["SC2"]} options.update(writer_options or {}) return Barcode.render(self, options, text) class JapanArticleNumber(EuropeanArticleNumber13): """Initializes JAN barcode. :parameters: jan : String The jan number as string. writer : barcode.writer Instance The writer to render the barcode (default: SVGWriter). """ name = "JAN" valid_country_codes = list(range(450, 460)) + list(range(490, 500)) def __init__(self, jan, writer=None): if int(jan[:3]) not in JapanArticleNumber.valid_country_codes: raise WrongCountryCodeError( "Country code isn't between 450-460 or 490-500." ) EuropeanArticleNumber13.__init__(self, jan, writer) class EuropeanArticleNumber8(EuropeanArticleNumber13): """Represents an EAN-8 barcode. See EAN13's __init__ for details. :parameters: ean : String The ean number as string. writer : barcode.writer Instance The writer to render the barcode (default: SVGWriter). """ name = "EAN-8" digits = 7 def __init__(self, ean, writer=None): EuropeanArticleNumber13.__init__(self, ean, writer) def build(self): """Builds the barcode pattern from `self.ean`. :returns: The pattern as string :rtype: String """ code = _ean.EDGE[:] for number in self.ean[:4]: code += _ean.CODES["A"][int(number)] code += _ean.MIDDLE for number in self.ean[4:]: code += _ean.CODES["C"][int(number)] code += _ean.EDGE return [code] class EuropeanArticleNumber14(EuropeanArticleNumber13): """Represents an EAN-14 barcode. See EAN13's __init__ for details. :parameters: ean : String The ean number as string. writer : barcode.writer Instance The writer to render the barcode (default: SVGWriter). """ name = "EAN-14" digits = 13 def calculate_checksum(self): """Calculates the checksum for EAN13-Code. :returns: The checksum for `self.ean`. :rtype: Integer """ def sum_(x, y): return int(x) + int(y) evensum = reduce(sum_, self.ean[::2]) oddsum = reduce(sum_, self.ean[1::2]) return (10 - (((evensum * 3) + oddsum) % 10)) % 10 # Shortcuts EAN14 = EuropeanArticleNumber14 EAN13 = EuropeanArticleNumber13 EAN8 = EuropeanArticleNumber8 JAN = JapanArticleNumber