summaryrefslogtreecommitdiffstats
path: root/share/extensions/barcode/Base.py
diff options
context:
space:
mode:
Diffstat (limited to 'share/extensions/barcode/Base.py')
-rw-r--r--share/extensions/barcode/Base.py171
1 files changed, 171 insertions, 0 deletions
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