diff options
Diffstat (limited to 'share/extensions/barcode')
30 files changed, 1340 insertions, 0 deletions
diff --git a/share/extensions/barcode/Base.py b/share/extensions/barcode/Base.py new file mode 100644 index 0000000..a4680a7 --- /dev/null +++ b/share/extensions/barcode/Base.py @@ -0,0 +1,166 @@ +# 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 + +(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..d1c4352 --- /dev/null +++ b/share/extensions/barcode/BaseEan.py @@ -0,0 +1,158 @@ +# 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 + +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' + + def intarray(self, number): + """Convert a string of digits into an array of ints""" + return [int(i) for i in number] + + def encode_interleaved(self, family, number, fams=FAMILIES): + """Encode any side of the barcode, interleaved""" + result = [] + encset = self.intarray(fams[family]) + for i in range(len(number)): + thismap = MAPPING[encset[i]] + result.append(thismap[number[i]]) + return result + + def encode_right(self, 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 + + def encode_left(self, number): + """Encode the left side of the barcode, non-interleaved""" + result = [] + for num in number: + result.append(MAPPING[0][num]) + return result + + def space(self, *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, code): + """Encode any EAN barcode""" + code = code.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(self.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..20876b3 --- /dev/null +++ b/share/extensions/barcode/Code128.py @@ -0,0 +1,158 @@ +# coding=utf-8 +# +# Authored by Martin Owens <doctormo@gmail.com> +# 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, 125)) # 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(self.best_block(block)) + block = '' + + return self.encode_blocks(blocks) + + def best_block(self, block): + """If this has lower case then select B over A""" + if block.upper() == block: + return ['A', block] + return ['B', block] + + def encode_blocks(self, 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 == 'A' or b_set == '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..e655f6c --- /dev/null +++ b/share/extensions/barcode/Code25i.py @@ -0,0 +1,69 @@ +# 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 + +# 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, number): + if not number.isdigit(): + return self.error(number, "CODE25 can only encode numbers.") + + # 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..a0ee2e2 --- /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 = colour == '1' and '0' or '1' + + return encoded diff --git a/share/extensions/barcode/Code39Ext.py b/share/extensions/barcode/Code39Ext.py new file mode 100644 index 0000000..e9e804a --- /dev/null +++ b/share/extensions/barcode/Code39Ext.py @@ -0,0 +1,68 @@ +# 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') + +map = {} + +i = 0 +for char in encode: + map[char] = i + i += 1 + + +# Extended encoding maps for full ASCII Code93 +def getMap(array): + result = {} + y = 0 + for x in array: + result[chr(x)] = encode[y] + y += 1 + + return result + + +# MapA is eclectic, but B, C, D are all ASCII ranges +mapA = getMap([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 = getMap(range(1, 26)) # $ +mapC = getMap(range(33, 58)) # / +mapD = getMap(range(97, 122)) # + + + +class Code39Ext(Code39): + def encode(self, text): + # We are only going to extend the Code39 barcodes + result = '' + for char in text: + if char in mapA: + char = '%' + mapA[char] + elif char in mapB: + char = '$' + mapB[char] + elif char in mapC: + char = '/' + mapC[char] + elif char in mapD: + char = '+' + mapD[char] + result = result + char + + 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..e7af072 --- /dev/null +++ b/share/extensions/barcode/Code93.py @@ -0,0 +1,116 @@ +# 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): + def encode(self, text): + # start marker + bits = ENCODE[MAP.get('MARKER', -1)] + + # Extend to ASCII charset ( return Array ) + text = self.encode_ascii(text) + + # Calculate the checksums + text.append(self.checksum(text, 20)) # C + text.append(self.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' + + def checksum(self, 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] + + # Some characters need re-ENCODE into the code93 specification + def encode_ascii(self, text): + 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..2f41a9c --- /dev/null +++ b/share/extensions/barcode/Ean13.py @@ -0,0 +1,43 @@ +# 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 = self.space(num[0:1], 4, num[1:7], 5, num[7:], 7) + if guide: + self.text = self.text[:-4] + '>' + return self.enclose( + self.encode_interleaved(num[0], num[1:7]), + self.encode_right(num[7:]) + ) diff --git a/share/extensions/barcode/Ean2.py b/share/extensions/barcode/Ean2.py new file mode 100644 index 0000000..08c990a --- /dev/null +++ b/share/extensions/barcode/Ean2.py @@ -0,0 +1,39 @@ +# 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(self.space(num)) + family = ((num[0] * 10) + num[1]) % 4 + return START + '01'.join(self.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..074c970 --- /dev/null +++ b/share/extensions/barcode/Ean5.py @@ -0,0 +1,39 @@ +# 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(self.space(num)) + family = sum([int(n) * int(m) for n, m in zip(num, '39393')]) % 10 + return START + '01'.join(self.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..d8fb2d8 --- /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 = self.space(num[:4], 3, num[4:]) + return self.enclose( + self.encode_left(num[:4]), + self.encode_right(num[4:]) + ) diff --git a/share/extensions/barcode/Rm4scc.py b/share/extensions/barcode/Rm4scc.py new file mode 100644 index 0000000..5687f63 --- /dev/null +++ b/share/extensions/barcode/Rm4scc.py @@ -0,0 +1,136 @@ +# 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 + +map = { + '(': '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): + default_height = 18 + + def encode(self, text): + result = '' + + text = text.upper() + text.replace('(', '') + text.replace(')', '') + + text = '(' + text + self.checksum(text) + ')' + + i = 0 + for char in text: + if char in map: + result = result + map[char] + i += 1 + + return result + + # given a string of data, return the check character + def checksum(self, text): + total_lower = 0 + total_upper = 0 + for char in text: + if char in map: + bars = map[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..77cb415 --- /dev/null +++ b/share/extensions/barcode/Upca.py @@ -0,0 +1,39 @@ +# 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 = self.space(num[0:1], 3, num[1:6], 4, num[6:11], 3, num[11:]) + return self.enclose( + self.encode_left(num[0:6]), + self.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..e341bdd --- /dev/null +++ b/share/extensions/barcode/Upce.py @@ -0,0 +1,100 @@ +# 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 + +# 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 = self.space(['0'], 2, num[:6], 2, num[-1]) + code = self.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 = self.convert_e2a(number) + result = self.get_checksum(number) + return self.convert_a2e(number) + result + + def convert_a2e(self, 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] + else: + # Invalid UPC-A Numbe + raise ValueError("Invalid UPC Number") + + def convert_e2a(self, 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] + elif number[5] == '3': + return '0' + number[:3] + '00000' + number[3:5] + elif number[5] == '4': + return '0' + number[:4] + '00000' + number[4] + else: + 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..f68d947 --- /dev/null +++ b/share/extensions/barcode/__init__.py @@ -0,0 +1,73 @@ +# 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. +""" + + +# 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(object): + """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)) diff --git a/share/extensions/barcode/__pycache__/Base.cpython-39.pyc b/share/extensions/barcode/__pycache__/Base.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..aa8da7a --- /dev/null +++ b/share/extensions/barcode/__pycache__/Base.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/BaseEan.cpython-39.pyc b/share/extensions/barcode/__pycache__/BaseEan.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..28ad0ad --- /dev/null +++ b/share/extensions/barcode/__pycache__/BaseEan.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Code128.cpython-39.pyc b/share/extensions/barcode/__pycache__/Code128.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..da22eb5 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Code128.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Code25i.cpython-39.pyc b/share/extensions/barcode/__pycache__/Code25i.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..fc11498 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Code25i.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Code39.cpython-39.pyc b/share/extensions/barcode/__pycache__/Code39.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..fd4bab3 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Code39.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Code39Ext.cpython-39.pyc b/share/extensions/barcode/__pycache__/Code39Ext.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..dac67f3 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Code39Ext.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Code93.cpython-39.pyc b/share/extensions/barcode/__pycache__/Code93.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..d03fac4 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Code93.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Ean13.cpython-39.pyc b/share/extensions/barcode/__pycache__/Ean13.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..e69a658 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Ean13.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Ean2.cpython-39.pyc b/share/extensions/barcode/__pycache__/Ean2.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..d8e9cf9 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Ean2.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Ean5.cpython-39.pyc b/share/extensions/barcode/__pycache__/Ean5.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..9d24001 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Ean5.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Ean8.cpython-39.pyc b/share/extensions/barcode/__pycache__/Ean8.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..8dad384 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Ean8.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Rm4scc.cpython-39.pyc b/share/extensions/barcode/__pycache__/Rm4scc.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..bbc1a35 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Rm4scc.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Upca.cpython-39.pyc b/share/extensions/barcode/__pycache__/Upca.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..74d648e --- /dev/null +++ b/share/extensions/barcode/__pycache__/Upca.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/Upce.cpython-39.pyc b/share/extensions/barcode/__pycache__/Upce.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..888e4a7 --- /dev/null +++ b/share/extensions/barcode/__pycache__/Upce.cpython-39.pyc diff --git a/share/extensions/barcode/__pycache__/__init__.cpython-39.pyc b/share/extensions/barcode/__pycache__/__init__.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..3b09d89 --- /dev/null +++ b/share/extensions/barcode/__pycache__/__init__.cpython-39.pyc |