Source code for steenzout.barcode.ean

# -*- coding: utf-8 -*-
"""Module: barcode.ean

:Provided barcodes: EAN-13, EAN-8, JAN
"""

from functools import reduce

from .base import Barcode
from .charsets import ean as _ean
from .errors import IllegalCharacterError, WrongCountryCodeError
from .helpers import sum_chars

# EAN13 Specs (all sizes in mm)
SIZES = dict(
    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
)


[docs]class EAN13(Barcode): """Class for EAN13 bar codes. Attributes: checksum (int): EAN checksum. Args: ean (str): the EAN number. writer (:py:class:`.writer.BaseWriter`): instance of writer class to render the bar code. """ name = 'EAN-13' digits = 13 def __init__(self, code, writer=None): super(EAN13, self).__init__(code, writer) def __unicode__(self): return self.code __str__ = __unicode__ @staticmethod
[docs] def calculate_checksum(code): """Calculates a EAN-13 code checksum. Args: code (str): EAN-13 code. Returns: (integer): the checksum for `self.ean`. """ sum_odd = reduce(sum_chars, code[:-1:2]) sum_even = reduce(sum_chars, code[1::2]) return (10 - ((sum_odd + sum_even * 3) % 10)) % 10
@staticmethod
[docs] def validate(code): """Calculates a EAN-13 code checksum. Args: code (str): EAN-13 code. Raises: IllegalCharacterError in case the bar code contains illegal characters. ValueError in case the bar code exceeds its maximum length or if the checksum digit doesn't match. """ if not code.isdigit(): raise IllegalCharacterError('[0-9]{%d}' % EAN13.digits) if len(code) != EAN13.digits: raise ValueError('Bar code %s requires %d digits' % (code, EAN13.digits)) checksum = EAN13.calculate_checksum(code) if checksum != int(code[-1]): raise ValueError('Checksum character mismatch %s != %s' % (checksum, code[-1]))
[docs] def build(self): """Builds the barcode pattern from `self.ean`. Returns: (str): The pattern as string. """ code = _ean.EDGE[:] pattern = _ean.LEFT_PATTERN[int(self.code[0])] for i, number in enumerate(self.code[1:7]): code += _ean.CODES[pattern[i]][int(number)] code = '%s%s' % (code, _ean.MIDDLE) for number in self.code[7:]: code += _ean.CODES['C'][int(number)] return ['%s%s' % (code, _ean.EDGE)]
[docs] def get_fullcode(self): return self._code
[docs] def render(self, writer_options=None): options = dict(module_width=SIZES['SC2']) options.update(writer_options or {}) return Barcode.render(self, options)
[docs] def to_ascii(self): """Returns an ascii representation of the barcode. Returns: (str): ascii representation of the barcode. """ code = self.build() for i, line in enumerate(code): code[i] = line.replace('1', '|').replace('0', ' ') return '\n'.join(code)
[docs]class JAN(EAN13): """Class for JAN bar codes. Args: code (str): the jan number. writer (:py:class:`.writer.BaseWriter`): instance of writer class to render the bar code. """ name = 'JAN' valid_country_codes = list(range(450, 460)) + list(range(490, 500)) def __init__(self, code, writer=None): if int(code[:3]) not in JapanArticleNumber.valid_country_codes: raise WrongCountryCodeError(code[:3]) super(JAN, self).__init__(code, writer)
[docs]class EAN8(EAN13): """Class for EAN-8 bar codes. See :py:class:`EAN-13` for details. :parameters: code (str): EAN-8 number. writer (:py:class:`.writer.BaseWriter`): instance of writer class to render the bar code. """ name = 'EAN-8' digits = 8 def __init__(self, code, writer=None): super(EAN8, self).__init__(code, writer) @staticmethod
[docs] def calculate_checksum(code): """Calculates an EAN-8 code checksum. Args: code (str): EAN-8 code. Returns: (int): EAN checksum. """ sum_odd = reduce(sum_chars, code[::2]) sum_even = reduce(sum_chars, code[1:-1:2]) return (10 - ((sum_odd * 3 + sum_even) % 10)) % 10
@staticmethod
[docs] def validate(code): """Calculates a EAN-8 code checksum. Args: code (str): EAN-8 code. Raises: IllegalCharacterError in case the bar code contains illegal characters. ValueError in case the bar code exceeds its maximum length or if the checksum digit doesn't match.. """ if not code.isdigit(): raise IllegalCharacterError('[0-9]{%d}' % EAN8.digits) if len(code) != EAN8.digits: raise ValueError('Bar code %s requires %d digits' % (code, EAN8.digits)) checksum = EAN8.calculate_checksum(code) if checksum != int(code[-1]): raise ValueError('Checksum character mismatch %d != %s' % (checksum, code[-1]))
[docs] def build(self): """Builds the barcode pattern from `self.ean`. Returns: (str): string representation of the pattern. """ code = _ean.EDGE[:] for number in self.code[:4]: code = '%s%s' % (code, _ean.CODES['A'][int(number)]) code = '%s%s' % (code, _ean.MIDDLE) for number in self.code[4:]: code = '%s%s' % (code, _ean.CODES['C'][int(number)]) return ['%s%s' % (code, _ean.EDGE)]
# Shortcuts EuropeanArticleNumber13 = EAN13 EuropeanArticleNumber8 = EAN8 JapanArticleNumber = JAN