From cca66b9ec4e494c1d919bff0f71a820d8afab1fa Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:24:48 +0200 Subject: Adding upstream version 1.2.2. Signed-off-by: Daniel Baumann --- share/extensions/barcode/Base.py | 171 +++++++++++++++++++++++ share/extensions/barcode/BaseEan.py | 204 ++++++++++++++++++++++++++++ share/extensions/barcode/Code128.py | 246 ++++++++++++++++++++++++++++++++++ share/extensions/barcode/Code25i.py | 71 ++++++++++ share/extensions/barcode/Code39.py | 98 ++++++++++++++ share/extensions/barcode/Code39Ext.py | 97 ++++++++++++++ share/extensions/barcode/Code93.py | 187 ++++++++++++++++++++++++++ share/extensions/barcode/Ean13.py | 44 ++++++ share/extensions/barcode/Ean2.py | 40 ++++++ share/extensions/barcode/Ean5.py | 50 +++++++ share/extensions/barcode/Ean8.py | 38 ++++++ share/extensions/barcode/Rm4scc.py | 139 +++++++++++++++++++ share/extensions/barcode/Upca.py | 40 ++++++ share/extensions/barcode/Upce.py | 112 ++++++++++++++++ share/extensions/barcode/__init__.py | 77 +++++++++++ 15 files changed, 1614 insertions(+) create mode 100644 share/extensions/barcode/Base.py create mode 100644 share/extensions/barcode/BaseEan.py create mode 100644 share/extensions/barcode/Code128.py create mode 100644 share/extensions/barcode/Code25i.py create mode 100644 share/extensions/barcode/Code39.py create mode 100644 share/extensions/barcode/Code39Ext.py create mode 100644 share/extensions/barcode/Code93.py create mode 100644 share/extensions/barcode/Ean13.py create mode 100644 share/extensions/barcode/Ean2.py create mode 100644 share/extensions/barcode/Ean5.py create mode 100644 share/extensions/barcode/Ean8.py create mode 100644 share/extensions/barcode/Rm4scc.py create mode 100644 share/extensions/barcode/Upca.py create mode 100644 share/extensions/barcode/Upce.py create mode 100644 share/extensions/barcode/__init__.py (limited to 'share/extensions/barcode') diff --git a/share/extensions/barcode/Base.py b/share/extensions/barcode/Base.py new file mode 100644 index 0000000..024213c --- /dev/null +++ b/share/extensions/barcode/Base.py @@ -0,0 +1,171 @@ +# coding=utf-8 +# +# Copyright (C) 2010 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Base module for rendering barcodes for Inkscape. +""" + +import itertools +import sys + +from inkex import Group, TextElement, Rectangle +from inkex.localization import inkex_gettext as _ + +(TEXT_POS_BOTTOM, TEXT_POS_TOP) = range(2) +(WHITE_BAR, BLACK_BAR, TALL_BAR) = range(3) +TEXT_TEMPLATE = "font-size:%dpx;text-align:center;text-anchor:middle;" + +try: + from typing import Optional +except ImportError: + pass + + +class Barcode(object): + """Provide a base class for all barcode renderers""" + + default_height = 30 + font_size = 9 + name = None # type: Optional[str] + + def error(self, text, msg): + """Cause an error to be reported""" + sys.stderr.write( + _("Error encoding '{}' as {} barcode: {}\n").format(text, self.name, msg) + ) + return "ERROR" + + def encode(self, text): + """ + Replace this with the encoding function, it should return + a string of ones and zeros + """ + raise NotImplementedError("You need to write an encode() function.") + + def __init__(self, param): + param = param or {} + self.document = param.get("document", None) + self.known_ids = [] + self._extra = [] + + self.pos_x = int(param.get("x", 0)) + self.pos_y = int(param.get("y", 0)) + self.text = param.get("text", None) + self.scale = param.get("scale", 1) + self.height = param.get("height", self.default_height) + self.pos_text = param.get("text_pos", TEXT_POS_BOTTOM) + + if self.document: + self.known_ids = list(self.document.xpath("//@id")) + + if not self.text: + raise ValueError(_("No string specified for barcode.")) + + def get_id(self, name="element"): + """Get the next useful id (and claim it)""" + index = 0 + while name in self.known_ids: + index += 1 + name = "barcode{:d}".format(index) + self.known_ids.append(name) + return name + + def add_extra_barcode(self, barcode, **kw): + """Add an extra barcode along side this one, used for ean13 extras""" + from . import get_barcode + + kw["height"] = self.height + kw["document"] = self.document + kw["scale"] = None + self._extra.append(get_barcode(barcode, **kw).generate()) + + def generate(self): + """Generate the actual svg from the coding""" + string = self.encode(self.text) + + if string == "ERROR": + return + + name = self.get_id("barcode") + + # use an svg group element to contain the barcode + barcode = Group() + barcode.set("id", name) + barcode.set("style", "fill: black;") + + barcode.transform.add_translate(self.pos_x, self.pos_y) + if self.scale: + barcode.transform.add_scale(self.scale) + + bar_id = 1 + bar_offset = 0 + tops = set() + + for datum in self.graphical_array(string): + # Datum 0 tells us what style of bar is to come next + style = self.get_style(int(datum[0])) + # Datum 1 tells us what width in units, + # style tells us how wide a unit is + width = int(datum[1]) * int(style["width"]) + + if style["write"]: + tops.add(style["top"]) + rect = Rectangle() + rect.set("x", str(bar_offset)) + rect.set("y", str(style["top"])) + if self.pos_text == TEXT_POS_TOP: + rect.set("y", str(style["top"] + self.font_size)) + rect.set("id", "{}_bar{:d}".format(name, bar_id)) + rect.set("width", str(width)) + rect.set("height", str(style["height"])) + barcode.append(rect) + bar_offset += width + bar_id += 1 + + for extra in self._extra: + if extra is not None: + barcode.append(extra) + + bar_width = bar_offset + # Add text at the bottom of the barcode + text = TextElement() + text.set("x", str(int(bar_width / 2))) + text.set("y", str(min(tops) + self.font_size - 1)) + if self.pos_text == TEXT_POS_BOTTOM: + text.set("y", str(self.height + max(tops) + self.font_size)) + text.set("style", TEXT_TEMPLATE % self.font_size) + text.set("xml:space", "preserve") + text.set("id", "{}_text".format(name)) + text.text = str(self.text) + barcode.append(text) + return barcode + + def graphical_array(self, code): + """Converts black and white markets into a space array""" + return [(x, len(list(y))) for x, y in itertools.groupby(code)] + + def get_style(self, index): + """Returns the styles that should be applied to each bar""" + result = {"width": 1, "top": 0, "write": True} + if index == BLACK_BAR: + result["height"] = int(self.height) + if index == TALL_BAR: + result["height"] = int(self.height) + int(self.font_size / 2) + if index == WHITE_BAR: + result["write"] = False + return result diff --git a/share/extensions/barcode/BaseEan.py b/share/extensions/barcode/BaseEan.py new file mode 100644 index 0000000..d1ab58e --- /dev/null +++ b/share/extensions/barcode/BaseEan.py @@ -0,0 +1,204 @@ +# coding=utf-8 +# +# Copyright (C) 2010 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Some basic common code shared between EAN and UCP generators. +""" + +from .Base import Barcode, TEXT_POS_TOP +from inkex.localization import inkex_gettext as _ + +try: + from typing import Optional, List, Dict +except ImportError: + pass + +MAPPING = [ + # Left side of barcode Family '0' + [ + "0001101", + "0011001", + "0010011", + "0111101", + "0100011", + "0110001", + "0101111", + "0111011", + "0110111", + "0001011", + ], + # Left side of barcode Family '1' and flipped to right side. + [ + "0100111", + "0110011", + "0011011", + "0100001", + "0011101", + "0111001", + "0000101", + "0010001", + "0001001", + "0010111", + ], +] +# This chooses which of the two encodings above to use. +FAMILIES = ( + "000000", + "001011", + "001101", + "001110", + "010011", + "011001", + "011100", + "010101", + "010110", + "011010", +) + + +class EanBarcode(Barcode): + """Simple base class for all EAN type barcodes""" + + lengths = None # type: Optional[List[int]] + length = None # type: Optional[int] + checks = [] # type: List[int] + extras = {} # type: Dict[int, str] + magic = 10 + guard_bar = "202" + center_bar = "02020" + + @staticmethod + def intarray(number): + """Convert a string of digits into an array of ints""" + return [int(i) for i in number] + + @staticmethod + def encode_interleaved(family, number, fams=FAMILIES): + """Encode any side of the barcode, interleaved""" + result = [] + encset = EanBarcode.intarray(fams[family]) + for i, _ in enumerate(number): + thismap = MAPPING[encset[i]] + result.append(thismap[number[i]]) + return result + + @staticmethod + def encode_right(number): + """Encode the right side of the barcode, non-interleaved""" + result = [] + for num in number: + # The right side is always the reverse of the left's family '1' + result.append(MAPPING[1][num][::-1]) + return result + + @staticmethod + def encode_left(number): + """Encode the left side of the barcode, non-interleaved""" + result = [] + for num in number: + result.append(MAPPING[0][num]) + return result + + @staticmethod + def space(*spacing): + """Space out an array of numbers""" + result = "" + for space in spacing: + if isinstance(space, list): + for i in space: + result += str(i) + elif isinstance(space, int): + result += " " * space + return result + + def get_lengths(self): + """Return a list of acceptable lengths""" + if self.length: + return [self.length] + return self.lengths[:] + + def encode(self, text): + """Encode any EAN barcode""" + code = text.replace(" ", "").strip() + guide = code.endswith(">") + code = code.strip(">") + + if not code.isdigit(): + return self.error(code, _("Not a Number, must be digits 0-9 only")) + lengths = self.get_lengths() + self.checks + + # Allow extra barcodes after the first one + if len(code) not in lengths: + for extra in self.extras: + sep = len(code) - extra + if sep in lengths: + # Generate a barcode along side this one. + self.add_extra_barcode( + self.extras[extra], + text=code[sep:], + x=self.pos_x + 400 * self.scale, + text_pos=TEXT_POS_TOP, + ) + code = code[:sep] + + if len(code) not in lengths: + return self.error( + code, + _("Wrong size {:d}, must be {} digits").format( + len(code), ", ".join([str(length) for length in lengths]) + ), + ) + + if self.checks: + if len(code) not in self.checks: + code = self.append_checksum(code) + elif not self.verify_checksum(code): + return self.error(code, _("Checksum failed, omit for new sum")) + return self._encode(EanBarcode.intarray(code), guide=guide) + + def _encode(self, num, guide=False): + """ + Write your EAN encoding function, it's passed in an array of int and + it should return a string on 1 and 0 for black and white parts + """ + raise NotImplementedError("_encode should be provided by parent EAN") + + def enclose(self, left, right=()): + """Standard Enclosure""" + parts = [self.guard_bar] + left + parts.append(self.center_bar) + parts += list(right) + [self.guard_bar] + return "".join(parts) + + def get_checksum(self, num): + """Generate a UPCA/EAN13/EAN8 Checksum""" + # Left to right,checksum based on first digits. + total = sum([int(n) * (3, 1)[x % 2] for x, n in enumerate(num[::-1])]) + # Modulous result to a single digit checksum + checksum = self.magic - (total % self.magic) + if checksum < 0 or checksum >= self.magic: + return "0" + return str(checksum) + + def append_checksum(self, number): + """Apply the checksum to a short number""" + return number + self.get_checksum(number) + + def verify_checksum(self, number): + """Verify any checksum""" + return self.get_checksum(number[:-1]) == number[-1] diff --git a/share/extensions/barcode/Code128.py b/share/extensions/barcode/Code128.py new file mode 100644 index 0000000..e5f8701 --- /dev/null +++ b/share/extensions/barcode/Code128.py @@ -0,0 +1,246 @@ +# coding=utf-8 +# +# Authored by Martin Owens +# Debugged by Ralf Heinecke & Martin Siepmann 2007-09-07 +# Horst Schottky 2010-02-27 +# +# Copyright (C) 2007 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Renderer for Code128/EAN128 codes. Designed for use with Inkscape. +""" + +import re + +from .Base import Barcode + +CODE_MAP = [ + "11011001100", + "11001101100", + "11001100110", + "10010011000", + "10010001100", + "10001001100", + "10011001000", + "10011000100", + "10001100100", + "11001001000", + "11001000100", + "11000100100", + "10110011100", + "10011011100", + "10011001110", + "10111001100", + "10011101100", + "10011100110", + "11001110010", + "11001011100", + "11001001110", + "11011100100", + "11001110100", + "11101101110", + "11101001100", + "11100101100", + "11100100110", + "11101100100", + "11100110100", + "11100110010", + "11011011000", + "11011000110", + "11000110110", + "10100011000", + "10001011000", + "10001000110", + "10110001000", + "10001101000", + "10001100010", + "11010001000", + "11000101000", + "11000100010", + "10110111000", + "10110001110", + "10001101110", + "10111011000", + "10111000110", + "10001110110", + "11101110110", + "11010001110", + "11000101110", + "11011101000", + "11011100010", + "11011101110", + "11101011000", + "11101000110", + "11100010110", + "11101101000", + "11101100010", + "11100011010", + "11101111010", + "11001000010", + "11110001010", + "10100110000", + "10100001100", + "10010110000", + "10010000110", + "10000101100", + "10000100110", + "10110010000", + "10110000100", + "10011010000", + "10011000010", + "10000110100", + "10000110010", + "11000010010", + "11001010000", + "11110111010", + "11000010100", + "10001111010", + "10100111100", + "10010111100", + "10010011110", + "10111100100", + "10011110100", + "10011110010", + "11110100100", + "11110010100", + "11110010010", + "11011011110", + "11011110110", + "11110110110", + "10101111000", + "10100011110", + "10001011110", + "10111101000", + "10111100010", + "11110101000", + "11110100010", + "10111011110", + "10111101110", + "11101011110", + "11110101110", + "11010000100", + "11010010000", + "11010011100", + "11000111010", + "11", +] + + +def map_extra(data, chars): + """Maps the data into the chars""" + result = list(data) + for char in chars: + result.append(chr(char)) + result.append("FNC3") + result.append("FNC2") + result.append("SHIFT") + return result + + +# The map_extra method is used to slim down the amount +# of pre code and instead we generate the lists +CHAR_AB = list(" !\"#$%&'()*+,-./0123456789:;<=>?@" "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_") +CHAR_A = map_extra(CHAR_AB, range(0, 31)) # Offset 64 +CHAR_B = map_extra(CHAR_AB, range(96, 127)) # Offset -32 + + +class Code128(Barcode): + """Main barcode object, generates the encoding bits here""" + + def encode(self, text): + blocks = [] + block = "" + + # Split up into sections of numbers, or characters + # This makes sure that all the characters are encoded + # In the best way possible for Code128 + for datum in re.findall(r"(?:(?:\d\d){2,})|(?:^\d\d)|.", text): + if len(datum) == 1: + block = block + datum + else: + if block: + blocks.append(self.best_block(block)) + block = "" + blocks.append(["C", datum]) + + if block: + blocks.append(Code128.best_block(block)) + block = "" + + return Code128.encode_blocks(blocks) + + @staticmethod + def best_block(block): + """If this has characters above 63, select B over A""" + if any(ord(x) > 63 for x in block): + return ["B", block] + return ["A", block] + + @staticmethod + def encode_blocks(blocks): + """Encode the given blocks into A, B or C codes""" + encode = "" + total = 0 + pos = 0 + + for block in blocks: + b_set = block[0] + datum = block[1] + + # POS : 0, 1 + # A : 101, 103 + # B : 100, 104 + # C : 99, 105 + num = 0 + if b_set == "A": + num = 103 + elif b_set == "B": + num = 104 + elif b_set == "C": + num = 105 + + i = pos + if pos: + num = 204 - num + else: + i = 1 + + total = total + num * i + encode = encode + CODE_MAP[num] + pos += 1 + + if b_set in ("A", "B"): + chars = CHAR_B + if b_set == "A": + chars = CHAR_A + + for char in datum: + total = total + (chars.index(char) * pos) + encode = encode + CODE_MAP[chars.index(char)] + pos += 1 + else: + for char in (datum[i : i + 2] for i in range(0, len(datum), 2)): + total = total + (int(char) * pos) + encode = encode + CODE_MAP[int(char)] + pos += 1 + + checksum = total % 103 + encode = encode + CODE_MAP[checksum] + encode = encode + CODE_MAP[106] + encode = encode + CODE_MAP[107] + + return encode diff --git a/share/extensions/barcode/Code25i.py b/share/extensions/barcode/Code25i.py new file mode 100644 index 0000000..adc0161 --- /dev/null +++ b/share/extensions/barcode/Code25i.py @@ -0,0 +1,71 @@ +# coding=utf-8 +# +# Copyright (C) 2010 Geoffrey Mosini +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Generate barcodes for Code25-interleaved 2 of 5, for Inkscape. +""" + +from .Base import Barcode +from inkex.localization import inkex_gettext as _ + +# 1 means thick, 0 means thin +ENCODE = { + "0": "00110", + "1": "10001", + "2": "01001", + "3": "11000", + "4": "00101", + "5": "10100", + "6": "01100", + "7": "00011", + "8": "10010", + "9": "01010", +} + + +class Code25i(Barcode): + """Convert a text into string binary of black and white markers""" + + # Start and stop code are already encoded into white (0) and black(1) bars + def encode(self, text): + if not text.isdigit(): + return self.error(text, _("CODE25 can only encode numbers.")) + number = text + + # Number of figures to encode must be even, + # a 0 is added to the left in case it's odd. + if len(number) % 2 > 0: + number = "0" + number + + # Number is encoded by pairs of 2 figures + size = len(number) // 2 + encoded = "1010" + for i in range(size): + # First in the pair is encoded in black (1), second in white (0) + black = ENCODE[number[i * 2]] + white = ENCODE[number[i * 2 + 1]] + for j in range(5): + if black[j] == "1": + encoded += "11" + else: + encoded += "1" + if white[j] == "1": + encoded += "00" + else: + encoded += "0" + return encoded + "1101" diff --git a/share/extensions/barcode/Code39.py b/share/extensions/barcode/Code39.py new file mode 100644 index 0000000..87f346a --- /dev/null +++ b/share/extensions/barcode/Code39.py @@ -0,0 +1,98 @@ +# coding=utf-8 +# +# Copyright (C) 2007 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Python barcode renderer for Code39 barcodes. Designed for use with Inkscape. +""" + +from .Base import Barcode + +ENCODE = { + "0": "000110100", + "1": "100100001", + "2": "001100001", + "3": "101100000", + "4": "000110001", + "5": "100110000", + "6": "001110000", + "7": "000100101", + "8": "100100100", + "9": "001100100", + "A": "100001001", + "B": "001001001", + "C": "101001000", + "D": "000011001", + "E": "100011000", + "F": "001011000", + "G": "000001101", + "H": "100001100", + "I": "001001100", + "J": "000011100", + "K": "100000011", + "L": "001000011", + "M": "101000010", + "N": "000010011", + "O": "100010010", + "P": "001010010", + "Q": "000000111", + "R": "100000110", + "S": "001000110", + "T": "000010110", + "U": "110000001", + "V": "011000001", + "W": "111000000", + "X": "010010001", + "Y": "110010000", + "Z": "011010000", + "-": "010000101", + "*": "010010100", + "+": "010001010", + "$": "010101000", + "%": "000101010", + "/": "010100010", + ".": "110000100", + " ": "011000100", +} + + +class Code39(Barcode): + """Convert a text into string binary of black and white markers""" + + def encode(self, text): + self.text = text.upper() + result = "" + # It is possible for us to encode code39 + # into full ascii, but this feature is + # not enabled here + for char in "*" + self.text + "*": + if char not in ENCODE: + char = "-" + result = result + ENCODE[char] + "0" + + # Now we need to encode the code39, best read + # the code to understand what it's up to: + encoded = "" + colour = "1" # 1 = Black, 0 = White + for data in result: + if data == "1": + encoded = encoded + colour + colour + else: + encoded = encoded + colour + colour = "0" if colour == "1" else "1" + + return encoded diff --git a/share/extensions/barcode/Code39Ext.py b/share/extensions/barcode/Code39Ext.py new file mode 100644 index 0000000..d990680 --- /dev/null +++ b/share/extensions/barcode/Code39Ext.py @@ -0,0 +1,97 @@ +# coding=utf-8 +# +# Copyright (C) 2007 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +""" +Python barcode renderer for Code39 Extended barcodes. Designed for Inkscape. +""" + +from .Code39 import Code39 + +encode = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + +charmap = {} + +i = 0 +for char in encode: + charmap[char] = i + i += 1 + + +def get_map(array): + """Extended encoding maps for full ASCII Code93""" + result = {} + for y, x in enumerate(array): + result[chr(x)] = encode[y] + + return result + + +# MapA is eclectic, but B, C, D are all ASCII ranges +mapA = get_map( + [ + 27, + 28, + 29, + 30, + 31, + 59, + 60, + 61, + 62, + 63, + 91, + 92, + 93, + 94, + 95, + 123, + 124, + 125, + 126, + 127, + 0, + 64, + 96, + 127, + 127, + 127, + ] +) # % +mapB = get_map(range(1, 26)) # $ +mapC = get_map(range(33, 58)) # / +mapD = get_map(range(97, 122)) # + + + +class Code39Ext(Code39): + """Render Code39 Barcodes""" + + def encode(self, text): + # We are only going to extend the Code39 barcodes + result = "" + for character in text: + if character in mapA: + character = "%" + mapA[character] + elif character in mapB: + character = "$" + mapB[character] + elif character in mapC: + character = "/" + mapC[character] + elif character in mapD: + character = "+" + mapD[character] + result = result + character + + return Code39.encode(self, result) diff --git a/share/extensions/barcode/Code93.py b/share/extensions/barcode/Code93.py new file mode 100644 index 0000000..1417b3e --- /dev/null +++ b/share/extensions/barcode/Code93.py @@ -0,0 +1,187 @@ +# coding=utf-8 +# +# Copyright (C) 2007 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Python barcode renderer for Code93 barcodes. Designed for use with Inkscape. +""" + +from .Base import Barcode + +PALLET = list("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%") +PALLET.append("($)") +PALLET.append("(/)") +PALLET.append("(+)") +PALLET.append("(%)") +PALLET.append("MARKER") + +MAP = dict((PALLET[i], i) for i in range(len(PALLET))) + + +def get_map(array): + """Extended ENCODE maps for full ASCII Code93""" + result = {} + pos = 10 + for char in array: + result[chr(char)] = PALLET[pos] + pos += 1 + return result + + +# MapA is eclectic, but B, C, D are all ASCII ranges +MAP_A = get_map( + [ + 27, + 28, + 29, + 30, + 31, + 59, + 60, + 61, + 62, + 63, + 91, + 92, + 93, + 94, + 95, + 123, + 124, + 125, + 126, + 127, + 0, + 64, + 96, + 127, + 127, + 127, + ] +) # % +MAP_B = get_map(range(1, 26)) # $ +MAP_C = get_map(range(33, 58)) # / +MAP_D = get_map(range(97, 122)) # + + +ENCODE = [ + "100010100", + "101001000", + "101000100", + "101000010", + "100101000", + "100100100", + "100100010", + "101010000", + "100010010", + "100001010", + "110101000", + "110100100", + "110100010", + "110010100", + "110010010", + "110001010", + "101101000", + "101100100", + "101100010", + "100110100", + "100011010", + "101011000", + "101001100", + "101000110", + "100101100", + "100010110", + "110110100", + "110110010", + "110101100", + "110100110", + "110010110", + "110011010", + "101101100", + "101100110", + "100110110", + "100111010", + "100101110", + "111010100", + "111010010", + "111001010", + "101101110", + "101110110", + "110101110", + "100100110", + "111011010", + "111010110", + "100110010", + "101011110", + "", +] + + +class Code93(Barcode): + """Encode Code93 Barcode""" + + def encode(self, text): + # start marker + bits = ENCODE[MAP.get("MARKER", -1)] + + # Extend to ASCII charset ( return Array ) + text = Code93.encode_ascii(text) + + # Calculate the checksums + text.append(Code93.checksum(text, 20)) # C + text.append(Code93.checksum(text, 15)) # K + + # Now convert text into the ENCODE bits (black and white stripes) + for char in text: + bits = bits + ENCODE[MAP.get(char, -1)] + + # end marker and termination bar + return bits + ENCODE[MAP.get("MARKER", -1)] + "1" + + @staticmethod + def checksum(text, mod): + """Generate a code 93 checksum""" + weight = len(text) % mod + check = 0 + for char in text: + check = check + (MAP[char] * weight) + # Reset the weight is required + weight -= 1 + if weight == 0: + weight = mod + + return PALLET[check % 47] + + @staticmethod + def encode_ascii(text): + """Some characters need re-ENCODE into the code93 specification""" + result = [] + for char in text: + if char in MAP: + result.append(char) + elif char in MAP_A: + result.append("(%)") + result.append(MAP_A[char]) + elif char in MAP_B: + result.append("($)") + result.append(MAP_B[char]) + elif char in MAP_C: + result.append("(/)") + result.append(MAP_C[char]) + elif char in MAP_D: + result.append("(+)") + result.append(MAP_D[char]) + return result diff --git a/share/extensions/barcode/Ean13.py b/share/extensions/barcode/Ean13.py new file mode 100644 index 0000000..21a2e44 --- /dev/null +++ b/share/extensions/barcode/Ean13.py @@ -0,0 +1,44 @@ +# coding=utf-8 +# +# Copyright (C) 2010 Martin Owens +# +# Thanks to Lineaire Chez of Inkbar ( www.inkbar.lineaire.net ) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Python barcode renderer for EAN13 barcodes. Designed for use with Inkscape. +""" + +from .BaseEan import EanBarcode + + +class Ean13(EanBarcode): + """Provide an Ean13 barcode generator""" + + name = "ean13" + extras = {2: "Ean2", 5: "Ean5"} + checks = [13] + lengths = [12] + + def _encode(self, num, guide=False): + """Encode an ean13 barcode""" + self.text = EanBarcode.space(num[0:1], 4, num[1:7], 5, num[7:], 7) + if guide: + self.text = self.text[:-4] + ">" + return self.enclose( + EanBarcode.encode_interleaved(num[0], num[1:7]), + EanBarcode.encode_right(num[7:]), + ) diff --git a/share/extensions/barcode/Ean2.py b/share/extensions/barcode/Ean2.py new file mode 100644 index 0000000..1025965 --- /dev/null +++ b/share/extensions/barcode/Ean2.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# +# Copyright (C) 2016 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Python barcode renderer for EAN2 barcodes. Designed for use with Inkscape. +""" + +from .BaseEan import EanBarcode + +FAMS = ["00", "01", "10", "11"] +START = "01011" + + +class Ean2(EanBarcode): + """Provide an Ean5 barcode generator""" + + length = 2 + name = "ean5" + + def _encode(self, num, guide=False): + if len(num) != 2: + num = ([0, 0] + num)[-2:] + self.text = " ".join(EanBarcode.space(num)) + family = ((num[0] * 10) + num[1]) % 4 + return START + "01".join(EanBarcode.encode_interleaved(family, num, FAMS)) diff --git a/share/extensions/barcode/Ean5.py b/share/extensions/barcode/Ean5.py new file mode 100644 index 0000000..3c84290 --- /dev/null +++ b/share/extensions/barcode/Ean5.py @@ -0,0 +1,50 @@ +# coding=utf-8 +# +# Copyright (C) 2009 Aaron C Spike +# 2010 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Python barcode renderer for EAN5 barcodes. Designed for use with Inkscape. +""" + +from .BaseEan import EanBarcode + +FAMS = [ + "11000", + "10100", + "10010", + "10001", + "01100", + "00110", + "00011", + "01010", + "01001", + "00101", +] +START = "01011" + + +class Ean5(EanBarcode): + """Provide an Ean5 barcode generator""" + + name = "ean5" + length = 5 + + def _encode(self, num, guide=False): + self.text = " ".join(EanBarcode.space(num)) + family = sum([int(n) * int(m) for n, m in zip(num, "39393")]) % 10 + return START + "01".join(EanBarcode.encode_interleaved(family, num, FAMS)) diff --git a/share/extensions/barcode/Ean8.py b/share/extensions/barcode/Ean8.py new file mode 100644 index 0000000..d6875fc --- /dev/null +++ b/share/extensions/barcode/Ean8.py @@ -0,0 +1,38 @@ +# coding=utf-8 +# +# Copyright (C) 2010 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Python barcode renderer for EAN8 barcodes. Designed for use with Inkscape. +""" + +from .BaseEan import EanBarcode + + +class Ean8(EanBarcode): + """Provide an EAN8 barcode generator""" + + name = "ean8" + checks = [8] + lengths = [7] + + def _encode(self, num, guide=False): + """Encode an ean8 barcode""" + self.text = EanBarcode.space(num[:4], 3, num[4:]) + return self.enclose( + EanBarcode.encode_left(num[:4]), EanBarcode.encode_right(num[4:]) + ) diff --git a/share/extensions/barcode/Rm4scc.py b/share/extensions/barcode/Rm4scc.py new file mode 100644 index 0000000..b4d52ba --- /dev/null +++ b/share/extensions/barcode/Rm4scc.py @@ -0,0 +1,139 @@ +# coding=utf-8 +# +# Copyright (C) 2007 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +""" +Python barcode renderer for RM4CC barcodes. Designed for use with Inkscape. +""" + +from .Base import Barcode + +charmap = { + "(": "25", + ")": "3", + "0": "05053535", + "1": "05152535", + "2": "05153525", + "3": "15052535", + "4": "15053525", + "5": "15152525", + "6": "05251535", + "7": "05350535", + "8": "05351525", + "9": "15250535", + "A": "15251525", + "B": "15350525", + "C": "05253515", + "D": "05352515", + "E": "05353505", + "F": "15252515", + "G": "15253505", + "H": "15352505", + "I": "25051535", + "J": "25150535", + "K": "25151525", + "L": "35050535", + "M": "35051525", + "N": "35150525", + "O": "25053525", + "P": "25152515", + "Q": "25153505", + "R": "35052515", + "S": "35053505", + "T": "35152505", + "U": "25251515", + "V": "25350515", + "W": "25351505", + "X": "35250515", + "Y": "35251505", + "Z": "35350505", +} + +check = ["ZUVWXY", "501234", "B6789A", "HCDEFG", "NIJKLM", "TOPQRS"] +(BAR_TRACK, BAR_DOWN, BAR_UP, BAR_FULL, BAR_NONE, WHITE_SPACE) = range(6) + + +class Rm4scc(Barcode): + """Provice a Rm4scc barcode generator""" + + default_height = 18 + + def encode(self, text): + result = "" + + text = text.upper() + text.replace("(", "") + text.replace(")", "") + + text = "(" + text + Rm4scc.checksum(text) + ")" + + i = 0 + for char in text: + if char in charmap: + result = result + charmap[char] + i += 1 + + return result + + @staticmethod + def checksum(text): + """given a string of data, return the check character""" + total_lower = 0 + total_upper = 0 + for char in text: + if char in charmap: + bars = charmap[char][0:8:2] + lower = 0 + upper = 0 + + if int(bars[0]) & 1: + lower += 4 + if int(bars[1]) & 1: + lower += 2 + if int(bars[2]) & 1: + lower += 1 + if int(bars[0]) & 2: + upper += 4 + if int(bars[1]) & 2: + upper += 2 + if int(bars[2]) & 2: + upper += 1 + total_lower += lower % 6 + total_upper += upper % 6 + + total_lower = total_upper % 6 + total_upper %= 6 + + checkchar = check[total_upper][total_lower] + return checkchar + + def get_style(self, index): + """Royal Mail Barcodes use a completely different style""" + result = {"width": 2, "write": True, "top": 0} + if index == BAR_TRACK: # Track Bar + result["top"] = 6 + result["height"] = 5 + elif index == BAR_DOWN: # Decender Bar + result["top"] = 6 + result["height"] = 11 + elif index == BAR_UP: # Accender Bar + result["height"] = 11 + elif index == BAR_FULL: # Full Bar + result["height"] = 17 + elif index == WHITE_SPACE: # White Space + result["write"] = False + return result diff --git a/share/extensions/barcode/Upca.py b/share/extensions/barcode/Upca.py new file mode 100644 index 0000000..f278fca --- /dev/null +++ b/share/extensions/barcode/Upca.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# +# Copyright (C) 2007 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Python barcode renderer for UPCA barcodes. Designed for use with Inkscape. +""" + +from .BaseEan import EanBarcode + + +class Upca(EanBarcode): + """Provides a renderer for EAN12 aka UPC-A Barcodes""" + + name = "upca" + font_size = 10 + lengths = [11] + checks = [12] + + def _encode(self, num, guide=False): + """Encode for a UPC-A Barcode""" + self.text = EanBarcode.space(num[0:1], 3, num[1:6], 4, num[6:11], 3, num[11:]) + return self.enclose( + EanBarcode.encode_left(num[0:6]), + EanBarcode.encode_right(num[6:12]), + ) diff --git a/share/extensions/barcode/Upce.py b/share/extensions/barcode/Upce.py new file mode 100644 index 0000000..9404ca2 --- /dev/null +++ b/share/extensions/barcode/Upce.py @@ -0,0 +1,112 @@ +# coding=utf-8 +# +# Copyright (C) 2010 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Python barcode renderer for UPCE barcodes. Designed for use with Inkscape. +""" + +from .BaseEan import EanBarcode +from inkex.localization import inkex_gettext as _ + +# This is almost exactly the same as the standard FAMILIES +# But flipped around and with the first 111000 instead of 000000. +FAMS = [ + "111000", + "110100", + "110010", + "110001", + "101100", + "100110", + "100011", + "101010", + "101001", + "100101", +] + + +class Upce(EanBarcode): + """Generate EAN6/UPC-E barcode generator""" + + name = "upce" + font_size = 10 + lengths = [6, 11] + checks = [7, 12] + center_bar = "020" + + def _encode(self, num, guide=False): + """Generate a UPC-E Barcode""" + self.text = EanBarcode.space(["0"], 2, num[:6], 2, num[-1]) + code = EanBarcode.encode_interleaved(num[-1], num[:6], FAMS) + return self.enclose(code) + + def append_checksum(self, number): + """Generate a UPCE Checksum""" + if len(number) == 6: + number = Upce.convert_e2a(number) + result = self.get_checksum(number) + return Upce.convert_a2e(number) + result + + @staticmethod + def convert_a2e(number): + """Converting UPC-A to UPC-E, may cause errors.""" + # All UPC-E Numbers use number system 0 + if number[0] != "0" or len(number) != 11: + # If not then the code is invalid + raise ValueError(_("Invalid UPC Number")) + + # Most of the conversions deal + # with the specific code parts + maker = number[1:6] + product = number[6:11] + + # There are 4 cases to convert: + if maker[2:] == "000" or maker[2:] == "100" or maker[2:] == "200": + # Maximum number product code digits can be encoded + if product[:2] == "00": + return maker[:2] + product[2:] + maker[2] + elif maker[3:5] == "00": + # Now only 2 product code digits can be used + if product[:3] == "000": + return maker[:3] + product[3:] + "3" + elif maker[4] == "0": + # With even more maker code we have less room for product code + if product[:4] == "0000": + return maker[0:4] + product[4] + "4" + elif product[:4] == "0000" and int(product[4]) > 4: + # The last recorse is to try and squeeze it in the last 5 numbers + # so long as the product is 00005-00009 so as not to conflict with + # the 0-4 used above. + return maker + product[4] + # Invalid UPC-A Numbe + raise ValueError(_("Invalid UPC Number")) + + @staticmethod + def convert_e2a(number): + """Convert UPC-E to UPC-A by padding with zeros""" + # It's more likly to convert this without fault + # But we still must be mindful of the 4 conversions + if len(number) != 6: + return None + + if number[5] in ["0", "1", "2"]: + return "0" + number[:2] + number[5] + "0000" + number[2:5] + if number[5] == "3": + return "0" + number[:3] + "00000" + number[3:5] + if number[5] == "4": + return "0" + number[:4] + "00000" + number[4] + return "0" + number[:5] + "0000" + number[5] diff --git a/share/extensions/barcode/__init__.py b/share/extensions/barcode/__init__.py new file mode 100644 index 0000000..1bfe281 --- /dev/null +++ b/share/extensions/barcode/__init__.py @@ -0,0 +1,77 @@ +# coding=utf-8 +# +# Copyright (C) 2014 Martin Owens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# pylint: disable=no-self-use +""" +Renderer for barcodes, SVG extension for Inkscape. + +For supported barcodes see Barcode module directory. +""" + +from inkex.localization import inkex_gettext as _ + +# This lists all known Barcodes missing from this package +# ===== UPC-Based Extensions ====== # +# Code11 +# ========= Code25-Based ========== # +# Codabar +# Postnet +# ITF25 +# ========= Alpha-numeric ========= # +# Code39Mod +# USPS128 +# =========== 2D Based ============ # +# PDF417 +# PDF417-Macro +# PDF417-Truncated +# PDF417-GLI + + +class NoBarcode: + """Simple class for no barcode""" + + def __init__(self, msg): + self.msg = msg + + def encode(self, text): + """Encode the text into a barcode pattern""" + raise ValueError(_("No barcode encoder: {}").format(self.msg)) + + def generate(self): + """Generate actual svg from the barcode pattern""" + return None + + +def get_barcode(code, **kw): + """Gets a barcode from a list of available barcode formats""" + if not code: + return NoBarcode(_("No barcode format given.")) + + code = str(code).replace("-", "").strip() + module = "barcode." + code + lst = ["barcode"] + try: + return getattr(__import__(module, fromlist=lst), code)(kw) + except ImportError as err: + if code in str(err): + return NoBarcode(_("Invalid type of barcode: {}.{}").format(module, code)) + raise + except AttributeError: + return NoBarcode( + _("Barcode module is missing barcode class: {}.{}").format(module, code) + ) -- cgit v1.2.3