diff options
| author | Lex Neva <github.com@lexneva.name> | 2018-05-01 21:21:07 -0400 |
|---|---|---|
| committer | Lex Neva <github.com@lexneva.name> | 2018-05-01 21:21:07 -0400 |
| commit | 05daffb7e01db55879eb24f3c00532324a5d41af (patch) | |
| tree | 43ff5d954e035e0e8b5a507b9c1bf9d6b4d3338d /lib/svg | |
| parent | 1b31806423c8fec4040fed6d1009db016860b763 (diff) | |
refactor everything out of lib/__init__.py
Diffstat (limited to 'lib/svg')
| -rw-r--r-- | lib/svg/__init__.py | 2 | ||||
| -rw-r--r-- | lib/svg/svg.py | 81 | ||||
| -rw-r--r-- | lib/svg/tags.py | 12 | ||||
| -rw-r--r-- | lib/svg/units.py | 105 |
4 files changed, 200 insertions, 0 deletions
diff --git a/lib/svg/__init__.py b/lib/svg/__init__.py new file mode 100644 index 00000000..1895bba4 --- /dev/null +++ b/lib/svg/__init__.py @@ -0,0 +1,2 @@ +from .svg import color_block_to_point_lists, render_stitch_plan +from .units import * diff --git a/lib/svg/svg.py b/lib/svg/svg.py new file mode 100644 index 00000000..3bc546e7 --- /dev/null +++ b/lib/svg/svg.py @@ -0,0 +1,81 @@ +import simpletransform, simplestyle, inkex + +from .units import get_viewbox_transform +from .tags import SVG_GROUP_TAG, INKSCAPE_LABEL, INKSCAPE_GROUPMODE, SVG_PATH_TAG +from ..i18n import _ +from ..utils import cache + + +def color_block_to_point_lists(color_block): + point_lists = [[]] + + for stitch in color_block: + if stitch.trim: + if point_lists[-1]: + point_lists.append([]) + continue + + if not stitch.jump and not stitch.stop: + point_lists[-1].append(stitch.as_tuple()) + + return point_lists + + +@cache +def get_correction_transform(svg): + transform = get_viewbox_transform(svg) + + # we need to correct for the viewbox + transform = simpletransform.invertTransform(transform) + transform = simpletransform.formatTransform(transform) + + return transform + + +def color_block_to_paths(color_block, svg): + paths = [] + # We could emit just a single path with one subpath per point list, but + # emitting multiple paths makes it easier for the user to manipulate them. + for point_list in color_block_to_point_lists(color_block): + color = color_block.color.visible_on_white.to_hex_str() + paths.append(inkex.etree.Element( + SVG_PATH_TAG, + {'style': simplestyle.formatStyle( + {'stroke': color, + 'stroke-width': "0.4", + 'fill': 'none'}), + 'd': "M" + " ".join(" ".join(str(coord) for coord in point) for point in point_list), + 'transform': get_correction_transform(svg), + 'embroider_manual_stitch': 'true', + 'embroider_trim_after': 'true', + })) + + # no need to trim at the end of a thread color + if paths: + paths[-1].attrib.pop('embroider_trim_after') + + return paths + + +def render_stitch_plan(svg, stitch_plan): + layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']") + if layer is None: + layer = inkex.etree.Element(SVG_GROUP_TAG, + {'id': '__inkstitch_stitch_plan__', + INKSCAPE_LABEL: _('Stitch Plan'), + INKSCAPE_GROUPMODE: 'layer'}) + else: + # delete old stitch plan + del layer[:] + + # make sure the layer is visible + layer.set('style', 'display:inline') + + for i, color_block in enumerate(stitch_plan): + group = inkex.etree.SubElement(layer, + SVG_GROUP_TAG, + {'id': '__color_block_%d__' % i, + INKSCAPE_LABEL: "color block %d" % (i + 1)}) + group.extend(color_block_to_paths(color_block, svg)) + + svg.append(layer) diff --git a/lib/svg/tags.py b/lib/svg/tags.py new file mode 100644 index 00000000..fee59957 --- /dev/null +++ b/lib/svg/tags.py @@ -0,0 +1,12 @@ +import inkex + + +SVG_PATH_TAG = inkex.addNS('path', 'svg') +SVG_POLYLINE_TAG = inkex.addNS('polyline', 'svg') +SVG_DEFS_TAG = inkex.addNS('defs', 'svg') +SVG_GROUP_TAG = inkex.addNS('g', 'svg') + +INKSCAPE_LABEL = inkex.addNS('label', 'inkscape') +INKSCAPE_GROUPMODE = inkex.addNS('groupmode', 'inkscape') + +EMBROIDERABLE_TAGS = (SVG_PATH_TAG, SVG_POLYLINE_TAG) diff --git a/lib/svg/units.py b/lib/svg/units.py new file mode 100644 index 00000000..015da60e --- /dev/null +++ b/lib/svg/units.py @@ -0,0 +1,105 @@ +import simpletransform + +from ..utils import cache + +# modern versions of Inkscape use 96 pixels per inch as per the CSS standard +PIXELS_PER_MM = 96 / 25.4 + +# cribbed from inkscape-silhouette +def parse_length_with_units( str ): + + ''' + Parse an SVG value which may or may not have units attached + This version is greatly simplified in that it only allows: no units, + units of px, mm, and %. Everything else, it returns None for. + There is a more general routine to consider in scour.py if more + generality is ever needed. + ''' + + u = 'px' + s = str.strip() + if s[-2:] == 'px': + s = s[:-2] + elif s[-2:] == 'mm': + u = 'mm' + s = s[:-2] + elif s[-2:] == 'pt': + u = 'pt' + s = s[:-2] + elif s[-2:] == 'pc': + u = 'pc' + s = s[:-2] + elif s[-2:] == 'cm': + u = 'cm' + s = s[:-2] + elif s[-2:] == 'in': + u = 'in' + s = s[:-2] + elif s[-1:] == '%': + u = '%' + s = s[:-1] + try: + v = float( s ) + except: + raise ValueError(_("parseLengthWithUnits: unknown unit %s") % s) + + return v, u + + +def convert_length(length): + value, units = parse_length_with_units(length) + + if not units or units == "px": + return value + + if units == 'pt': + value /= 72 + units = 'in' + + if units == 'pc': + value /= 6 + units = 'in' + + if units == 'cm': + value *= 10 + units = 'mm' + + if units == 'mm': + value = value / 25.4 + units = 'in' + + if units == 'in': + # modern versions of Inkscape use CSS's 96 pixels per inch. When you + # open an old document, inkscape will add a viewbox for you. + return value * 96 + + raise ValueError(_("Unknown unit: %s") % units) + + +@cache +def get_doc_size(svg): + doc_width = convert_length(svg.get('width')) + doc_height = convert_length(svg.get('height')) + + return doc_width, doc_height + +@cache +def get_viewbox_transform(node): + # somewhat cribbed from inkscape-silhouette + doc_width, doc_height = get_doc_size(node) + + viewbox = node.get('viewBox').strip().replace(',', ' ').split() + + dx = -float(viewbox[0]) + dy = -float(viewbox[1]) + transform = simpletransform.parseTransform("translate(%f, %f)" % (dx, dy)) + + try: + sx = doc_width / float(viewbox[2]) + sy = doc_height / float(viewbox[3]) + scale_transform = simpletransform.parseTransform("scale(%f, %f)" % (sx, sy)) + transform = simpletransform.composeTransform(transform, scale_transform) + except ZeroDivisionError: + pass + + return transform |
