diff options
Diffstat (limited to 'share/extensions/barcode/BaseEan.py')
-rw-r--r-- | share/extensions/barcode/BaseEan.py | 158 |
1 files changed, 158 insertions, 0 deletions
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] |