# 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