Source code for pystrich.ean13

"""EAN-13 barcode encoder

All needed by the user is done via the EAN13Encoder class:

>>> encoder = EAN13Encoder("012345678901")
>>> encoder.save("test.png")

Implemented by Helen Taylor for HUDORA GmbH.
Updated and ported to Python 3 by Michael Mulqueen for Method B Ltd.

Detailed documentation on the format here:
http://www.barcodeisland.com/ean13.phtml

You may use this under a BSD License.
"""

from __future__ import annotations

from functools import reduce

from pystrich.bar_encoder import Bar1DEncoder
from pystrich.exceptions import PyStrichInvalidInput

from . import encoding
from .renderer import EAN13Renderer, EAN13RenderOptions

GUARDS = ("101", "01010", "101")


[docs] class EAN13Encoder(Bar1DEncoder): """Encode a 12- or 13-digit code as an EAN-13 1D barcode. The check digit is computed for you. If a 13-digit code is supplied, its final digit is discarded and recomputed. Typical use:: encoder = EAN13Encoder("012345678901") encoder.save("ean13.png") :ivar code: The 12-digit input (number system + manufacturer + product). :ivar check_digit: The computed mod-10 check digit. :ivar full_code: The 13-digit code including check digit. :ivar left_bars: Bar pattern for the left half of the symbol. :ivar right_bars: Bar pattern for the right half. :ivar options: Render-time options dict (empty if none were supplied). :ivar width: Pixel width of the most recently rendered image. :ivar height: Pixel height of the most recently rendered image. """ options: EAN13RenderOptions code: str check_digit: int full_code: str left_bars: str right_bars: str def __init__( self, code: str, options: EAN13RenderOptions | None = None, ) -> None: """Validate ``code`` and compute its check digit. :param code: A string of 12 digits in the form ``nnmmmmmppppp``, where ``n`` is the number system, ``m`` is the manufacturer code and ``p`` is the product code. A 13-digit code is also accepted; its final digit is treated as a check digit and recomputed. :param options: Optional dict tweaking the rendered output. See :class:`pystrich.ean13.EAN13RenderOptions` for accepted keys. :raises pystrich.exceptions.PyStrichInvalidInput: if ``code`` is not exactly 12 (or 13) digits. """ super().__init__(options) # Normalise to 12 digits: a 13-digit input has its trailing check # digit dropped (we recompute it below). if len(code) == 13: code = code[:-1] if not (code.isdigit() and len(code) == 12): raise PyStrichInvalidInput("code must be 12 or 13 digits long") self.code = code self.check_digit = self.calculate_check_digit() self.full_code = self.code + str(self.check_digit) self.left_bars = "" self.right_bars = "" self.encode()
[docs] def encode(self) -> tuple[str, str]: """Encode the barcode number and return the left and right data strings""" parity_values = self.get_parity() self.left_bars = "" self.right_bars = "" # Exclude the first number system digit, this was # for determining the left parity for parity, digit in zip(parity_values, self.full_code[1:7], strict=False): self.left_bars += encoding.get_left_encoded(int(digit), parity) for digit in self.full_code[7:]: self.right_bars += encoding.get_right_encoded(int(digit)) return self.left_bars, self.right_bars
[docs] def get_parity(self) -> tuple[int, int, int, int, int, int]: """Return the parity mappings applicable to this code""" return encoding.parity_table[int(self.code[0])]
[docs] def calculate_check_digit(self) -> int: """Compute the EAN-13 mod-10 check digit for :attr:`code`. Working right-to-left across the 12-digit input, odd-positioned digits are weighted by 3 and even-positioned digits by 1; the check digit is the value that brings the weighted sum to a multiple of 10. """ def sum_str(total, digit): """add a stringified digit to the total sum""" return total + int(digit) # sum the "odd" digits (1,3,5,7,9,11) and multiply by 3 oddsum = reduce(sum_str, self.code[1::2], 0) # sum the "even" digits (0,2,4,6,8,10) evensum = reduce(sum_str, self.code[:12:2], 0) # add them up total = oddsum * 3 + evensum # check digit is the number that can be added to the total # to get to a multiple of 10 return (10 - (total % 10)) % 10
[docs] def init_renderer(self) -> EAN13Renderer: """Construct an :class:`EAN13Renderer` for the encoded code. :rtype: :class:`EAN13Renderer` """ return EAN13Renderer( self.full_code, self.left_bars, self.right_bars, GUARDS, self.options, )