diff --git a/barcode/__init__.py b/barcode/__init__.py index d0d15cd..dd8ad1e 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -1,5 +1,5 @@ -"""This package provides a simple way to create standard barcodes. -It needs no external packages to be installed, the barcodes are +"""This package provides a simple way to create standard barcodes. It needs no +external packages to be installed (exception: 'luhn' for MSI), the barcodes are created as SVG objects. If Pillow is installed, the barcodes can also be rendered as images (all formats supported by Pillow). """ @@ -9,6 +9,7 @@ from typing import Union from barcode.codabar import CODABAR +from barcode.codex import MSI from barcode.codex import PZN from barcode.codex import Code39 from barcode.codex import Code128 @@ -50,6 +51,7 @@ "gs1_128": Gs1_128, "codabar": CODABAR, "nw-7": CODABAR, + "msi": MSI, } PROVIDED_BARCODES = list(__BARCODE_MAP) diff --git a/barcode/codex.py b/barcode/codex.py index 74665be..6482887 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -1,6 +1,6 @@ """Module: barcode.codex -:Provided barcodes: Code 39, Code 128, PZN +:Provided barcodes: Code 39, Code 128, PZN, MSI """ from barcode.base import Barcode from barcode.charsets import code39 @@ -77,11 +77,10 @@ def render(self, writer_options=None, text=None): class PZN7(Code39): """Initializes new German number for pharmaceutical products. - :parameters: - pzn : String - Code to render. - writer : barcode.writer Instance - The writer to render the barcode (default: SVGWriter). + :param pzn: String + Code to render. + :param writer: barcode.writer Instance + The writer to render the barcode (default: SVGWriter). """ name = "Pharmazentralnummer" @@ -122,10 +121,9 @@ class Code128(Barcode): """Initializes a new Code128 instance. The checksum is added automatically when building the bars. - :parameters: - code : String + :param code: String Code 128 string without checksum (added automatically). - writer : barcode.writer Instance + :param writer: barcode.writer Instance The writer to render the barcode (default: SVGWriter). """ @@ -271,5 +269,78 @@ def get_fullcode(self): return super().get_fullcode()[1:] +class MSI(Barcode): + """Initializes a new MSI (Modified Plessey) instance. The checksum is added + automatically when building the bars. + + :parameters: + :param code: int or bytes + Code MSI int without checksum (added automatically). + Code bytes without checksum. requires byteorder (WARNING: non-standard use) + :param writer: barcode.writer Instance + The writer to render the barcode (default: SVGWriter). + :param byteorder: string + 'big' or 'little' ; to convert bytes to int + :param encoding: string + if set, convert bytes to string and use this as a label. defaults to utf-8 + if unset, use integer value as label + Note: for convenience ; label can also be set when calling save() with param + 'text' + + limitations: + - only one check digit (Luhn Mod10) + - only standard prefix/suffix + + """ + + name = "MSI/Modified Plessey" + + def __init__( + self, + code, + writer=None, + byteorder=None, + encoding="utf-8", + ): + self.writer = writer or self.default_writer() + self._buffer = "" + + if type(code) is int: + self.label = self.code = str(code) + elif type(code) is bytes: + self.code = str(int.from_bytes(code, byteorder)) + if encoding is not None: + self.label = code.decode(encoding) + else: + self.label = self.code if label is None else label + + def __str__(self): + return self.label + + @property + def encoded(self): + return self._build() + + def get_fullcode(self): + return self.label + + def _build(self): + """ + check digit is computed with https://pypi.org/project/luhn/ + """ + from luhn import append as luhn_check_append + + bcd = "".join([f"{bin(int(n))[2:]:0>4}" for n in luhn_check_append(self.code)]) + + conv = { + "0": "100", + "1": "110", + } + return ["110" + "".join([conv[i] for i in bcd]) + "1001"] + + def build(self): + return self.encoded + + # For pre 0.8 compatibility PZN = PZN7 diff --git a/barcode/writer.py b/barcode/writer.py index 3a3dcd0..90247fc 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -322,14 +322,15 @@ def _init(self, code): attributes = {"id": "barcode_group"} _set_attributes(group, **attributes) self._group = self._root.appendChild(group) - background = self._document.createElement("rect") - attributes = { - "width": "100%", - "height": "100%", - "style": f"fill:{self.background}", - } - _set_attributes(background, **attributes) - self._group.appendChild(background) + if self.background: + background = self._document.createElement("rect") + attributes = { + "width": "100%", + "height": "100%", + "style": f"fill:{self.background}", + } + _set_attributes(background, **attributes) + self._group.appendChild(background) def _create_module(self, xpos, ypos, width, color): # Background rect has been provided already, so skipping "spaces"