diff options
| author | Lex Neva <github.com@lexneva.name> | 2021-08-07 10:58:02 -0400 |
|---|---|---|
| committer | Lex Neva <github.com@lexneva.name> | 2021-08-07 10:58:02 -0400 |
| commit | 12ef0c84aa732623b210fdce1a7b8301aa435217 (patch) | |
| tree | 4c7fbb33c4840be4bf8d8fecfd7fe481d0e56895 /lib | |
| parent | c1e6558f7852def419adfbeb087b2194e6030a2c (diff) | |
| parent | d6e20fae8a03ac162ae0c863fff06b5bd8b77902 (diff) | |
Merge remote-tracking branch 'origin/main' into kaalleen/satin-patterns
Diffstat (limited to 'lib')
28 files changed, 344 insertions, 329 deletions
diff --git a/lib/api/server.py b/lib/api/server.py index 66087b4e..93b08ff1 100644 --- a/lib/api/server.py +++ b/lib/api/server.py @@ -11,7 +11,8 @@ import time from threading import Thread import requests -from flask import Flask, g, request +from flask import Flask, g +from werkzeug.serving import make_server from ..utils.json import InkStitchJSONEncoder from .install import install @@ -24,13 +25,14 @@ class APIServer(Thread): self.extension = args[0] Thread.__init__(self, *args[1:], **kwargs) self.daemon = True - self.shutting_down = False self.app = None self.host = None self.port = None self.ready = False self.__setup_app() + self.flask_server = None + self.server_thread = None def __setup_app(self): # noqa: C901 # Disable warning about using a development server in a production environment @@ -50,20 +52,13 @@ class APIServer(Thread): # this request g.extension = self.extension - @self.app.route('/shutdown', methods=['POST']) - def shutdown(): - self.shutting_down = True - request.environ.get('werkzeug.server.shutdown')() - return "shutting down" - @self.app.route('/ping') def ping(): return "pong" def stop(self): - # for whatever reason, shutting down only seems possible in - # the context of a flask request, so we'll just make one - requests.post("http://%s:%s/shutdown" % (self.host, self.port)) + self.flask_server.shutdown() + self.server_thread.join() def disable_logging(self): logging.getLogger('werkzeug').setLevel(logging.ERROR) @@ -76,7 +71,9 @@ class APIServer(Thread): while True: try: - self.app.run(self.host, self.port, threaded=True) + self.flask_server = make_server(self.host, self.port, self.app) + self.server_thread = Thread(target=self.flask_server.serve_forever) + self.server_thread.start() except socket.error as e: if e.errno == errno.EADDRINUSE: self.port += 1 diff --git a/lib/commands.py b/lib/commands.py index 265e7e23..73fea5ee 100644 --- a/lib/commands.py +++ b/lib/commands.py @@ -9,15 +9,13 @@ from copy import deepcopy from random import random import inkex -from lxml import etree from shapely import geometry as shgeo from .i18n import N_, _ from .svg import (apply_transforms, generate_unique_id, get_correction_transform, get_document, get_node_transform) from .svg.tags import (CONNECTION_END, CONNECTION_START, CONNECTOR_TYPE, - INKSCAPE_LABEL, INKSTITCH_ATTRIBS, SVG_DEFS_TAG, - SVG_GROUP_TAG, SVG_PATH_TAG, SVG_SYMBOL_TAG, + INKSCAPE_LABEL, INKSTITCH_ATTRIBS, SVG_SYMBOL_TAG, SVG_USE_TAG, XLINK_HREF) from .utils import Point, cache, get_bundled_dir @@ -258,42 +256,35 @@ def symbols_path(): @cache def symbols_svg(): with open(symbols_path()) as symbols_file: - return etree.parse(symbols_file) + return inkex.load_svg(symbols_file).getroot() @cache def symbol_defs(): - return get_defs(symbols_svg()) + return symbols_svg().defs @cache -def get_defs(document): - defs = document.find(SVG_DEFS_TAG) - - if defs is None: - defs = etree.SubElement(document, SVG_DEFS_TAG) - - return defs - - -def ensure_symbol(document, command): +def ensure_symbol(svg, command): """Make sure the command's symbol definition exists in the <svg:defs> tag.""" + # using @cache really just makes sure that we don't bother ensuring the + # same symbol is there twice, which would be wasted work + path = "./*[@id='inkstitch_%s']" % command - defs = get_defs(document) + defs = svg.defs if defs.find(path) is None: defs.append(deepcopy(symbol_defs().find(path))) def add_group(document, node, command): - group = etree.Element( - SVG_GROUP_TAG, - { - "id": generate_unique_id(document, "command_group"), - INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), - "transform": get_correction_transform(node) - }) - node.getparent().insert(node.getparent().index(node) + 1, group) + parent = node.getparent() + group = inkex.Group(attrib={ + "id": generate_unique_id(document, "command_group"), + INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), + "transform": get_correction_transform(node) + }) + parent.insert(parent.index(node) + 1, group) return group @@ -307,35 +298,36 @@ def add_connector(document, symbol, element): if element.node.get('id') is None: element.node.set('id', generate_unique_id(document, "object")) - path = etree.Element(SVG_PATH_TAG, - { - "id": generate_unique_id(document, "command_connector"), - "d": "M %s,%s %s,%s" % (start_pos[0], start_pos[1], end_pos.x, end_pos.y), - "style": "stroke:#000000;stroke-width:1px;stroke-opacity:0.5;fill:none;", - CONNECTION_START: "#%s" % symbol.get('id'), - CONNECTION_END: "#%s" % element.node.get('id'), - CONNECTOR_TYPE: "polyline", + path = inkex.PathElement(attrib={ + "id": generate_unique_id(document, "command_connector"), + "d": "M %s,%s %s,%s" % (start_pos[0], start_pos[1], end_pos.x, end_pos.y), + "style": "stroke:#000000;stroke-width:1px;stroke-opacity:0.5;fill:none;", + CONNECTION_START: "#%s" % symbol.get('id'), + CONNECTION_END: "#%s" % element.node.get('id'), + CONNECTOR_TYPE: "polyline", - # l10n: the name of the line that connects a command to the object it applies to - INKSCAPE_LABEL: _("connector") - }) + # l10n: the name of the line that connects a command to the object it applies to + INKSCAPE_LABEL: _("connector") + }) symbol.getparent().insert(0, path) def add_symbol(document, group, command, pos): - return etree.SubElement(group, SVG_USE_TAG, - { - "id": generate_unique_id(document, "command_use"), - XLINK_HREF: "#inkstitch_%s" % command, - "height": "100%", - "width": "100%", - "x": str(pos.x), - "y": str(pos.y), + symbol = inkex.Use(attrib={ + "id": generate_unique_id(document, "command_use"), + XLINK_HREF: "#inkstitch_%s" % command, + "height": "100%", + "width": "100%", + "x": str(pos.x), + "y": str(pos.y), + + # l10n: the name of a command symbol (example: scissors icon for trim command) + INKSCAPE_LABEL: _("command marker"), + }) + group.append(symbol) - # l10n: the name of a command symbol (example: scissors icon for trim command) - INKSCAPE_LABEL: _("command marker"), - }) + return symbol def get_command_pos(element, index, total): @@ -382,32 +374,31 @@ def remove_legacy_param(element, command): def add_commands(element, commands): - document = get_document(element.node) + svg = get_document(element.node) for i, command in enumerate(commands): - ensure_symbol(document, command) + ensure_symbol(svg, command) remove_legacy_param(element, command) - group = add_group(document, element.node, command) + group = add_group(svg, element.node, command) pos = get_command_pos(element, i, len(commands)) - symbol = add_symbol(document, group, command, pos) - add_connector(document, symbol, element) + symbol = add_symbol(svg, group, command, pos) + add_connector(svg, symbol, element) def add_layer_commands(layer, commands): - document = get_document(layer) + svg = layer.root correction_transform = get_correction_transform(layer) - for command in commands: - ensure_symbol(document, command) - etree.SubElement(layer, SVG_USE_TAG, - { - "id": generate_unique_id(document, "use"), - INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), - XLINK_HREF: "#inkstitch_%s" % command, - "height": "100%", - "width": "100%", - "x": "0", - "y": "-10", - "transform": correction_transform - }) + for i, command in enumerate(commands): + ensure_symbol(svg, command) + layer.append(inkex.Use(attrib={ + "id": generate_unique_id(svg, "use"), + INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), + XLINK_HREF: "#inkstitch_%s" % command, + "height": "100%", + "width": "100%", + "x": str(i * 20), + "y": "-10", + "transform": correction_transform + })) diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index 29ca3545..3c13a081 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -11,6 +11,7 @@ from shapely import geometry as shgeo from ..i18n import _ from ..stitches import auto_fill +from ..svg.tags import INKSCAPE_LABEL from ..utils import cache, version from .element import StitchGroup, param from .fill import Fill @@ -264,7 +265,8 @@ class AutoFill(Fill): def validation_warnings(self): if self.shape.area < 20: - yield SmallShapeWarning(self.shape.centroid) + label = self.node.get(INKSCAPE_LABEL) or self.node.get("id") + yield SmallShapeWarning(self.shape.centroid, label) if self.shrink_or_grow_shape(self.expand, True).is_empty: yield ExpandWarning(self.shape.centroid) diff --git a/lib/elements/element.py b/lib/elements/element.py index bcb59bf4..17ed9167 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -7,7 +7,6 @@ import sys from copy import deepcopy import inkex -import tinycss2 from inkex import bezier from ..commands import find_commands @@ -15,8 +14,7 @@ from ..i18n import _ from ..patterns import apply_patterns from ..svg import (PIXELS_PER_MM, apply_transforms, convert_length, get_node_transform) -from ..svg.tags import (EMBROIDERABLE_TAGS, INKSCAPE_LABEL, INKSTITCH_ATTRIBS, - SVG_GROUP_TAG, SVG_LINK_TAG, SVG_USE_TAG) +from ..svg.tags import INKSCAPE_LABEL, INKSTITCH_ATTRIBS from ..utils import Point, cache @@ -96,7 +94,7 @@ class EmbroideryElement(object): elif legacy_tie == "False": self.set_param('ties', 3) - # defaut setting for fill_underlay has changed + # default setting for fill_underlay has changed if legacy_attribs and not self.get_param('fill_underlay', ""): self.set_param('fill_underlay', False) @@ -166,37 +164,12 @@ class EmbroideryElement(object): self.node.set(param, str(value)) @cache - def parse_style(self, node=None): - if node is None: - node = self.node - element_style = node.get("style", "") - if element_style is None: - return None - declarations = tinycss2.parse_declaration_list(node.get("style", "")) - style = {declaration.lower_name: declaration.value[0].serialize() for declaration in declarations} - return style - - @cache - def _get_style_raw(self, style_name): - if self.node is None: - return None - if self.node.tag not in [SVG_GROUP_TAG, SVG_LINK_TAG, SVG_USE_TAG] and self.node.tag not in EMBROIDERABLE_TAGS: - return None - - style = self.parse_style() - if style: - style = style.get(style_name) or self.node.get(style_name) - parent = self.node.getparent() - # style not found, get inherited style elements - while not style and parent is not None: - style = self.parse_style(parent) - if style: - style = style.get(style_name) or parent.get(style_name) - parent = parent.getparent() - return style + def _get_specified_style(self): + # We want to cache this, because it's quite expensive to generate. + return self.node.specified_style() def get_style(self, style_name, default=None): - style = self._get_style_raw(style_name) or default + style = self._get_specified_style().get(style_name, default) if style == 'none': style = None return style diff --git a/lib/elements/empty_d_object.py b/lib/elements/empty_d_object.py index dbb43bc4..19fb58a4 100644 --- a/lib/elements/empty_d_object.py +++ b/lib/elements/empty_d_object.py @@ -4,6 +4,7 @@ # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. from ..i18n import _ +from ..svg.tags import INKSCAPE_LABEL from .element import EmbroideryElement from .validation import ObjectTypeWarning @@ -19,7 +20,8 @@ class EmptyD(ObjectTypeWarning): class EmptyDObject(EmbroideryElement): def validation_warnings(self): - yield EmptyD((0, 0)) + label = self.node.get(INKSCAPE_LABEL) or self.node.get("id") + yield EmptyD((0, 0), label) def to_patches(self, last_patch): return [] diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 3d0e7ff5..1f28cb45 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -790,7 +790,7 @@ class SatinColumn(EmbroideryElement): sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation) # "left" and "right" here are kind of arbitrary designations meaning - # a point from the first and second rail repectively + # a point from the first and second rail respectively for left, right in zip(*sides): patch.add_stitch(left) patch.add_stitch(right) diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py index edd5525a..76e80688 100644 --- a/lib/elements/stroke.py +++ b/lib/elements/stroke.py @@ -132,7 +132,7 @@ class Stroke(EmbroideryElement): # want to see if they set a running stitch length at all, and the property will apply # a default value. # - # Thsi is so tricky, and and intricate that's a major reason that we deprecated the + # This is so tricky, and and intricate that's a major reason that we deprecated the # 0.5 units rule. # Warn them the first time. diff --git a/lib/elements/validation.py b/lib/elements/validation.py index c14b634c..9ac8e745 100644 --- a/lib/elements/validation.py +++ b/lib/elements/validation.py @@ -25,11 +25,12 @@ class ValidationMessage(object): description = None steps_to_solve = [] - def __init__(self, position=None): + def __init__(self, position=None, label=""): if isinstance(position, ShapelyPoint): position = (position.x, position.y) self.position = InkstitchPoint(*position) + self.label = label class ValidationError(ValidationMessage): diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index 7996770d..83a522f2 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -9,6 +9,7 @@ from .auto_satin import AutoSatin from .break_apart import BreakApart from .cleanup import Cleanup from .convert_to_satin import ConvertToSatin +from .convert_to_stroke import ConvertToStroke from .cut_satin import CutSatin from .duplicate_params import DuplicateParams from .embroider_settings import EmbroiderSettings @@ -17,6 +18,7 @@ from .global_commands import GlobalCommands from .import_threadlist import ImportThreadlist from .input import Input from .install import Install +from .install_custom_palette import InstallCustomPalette from .layer_commands import LayerCommands from .lettering import Lettering from .lettering_custom_font_dir import LetteringCustomFontDir @@ -46,6 +48,7 @@ __all__ = extensions = [StitchPlanPreview, LayerCommands, GlobalCommands, ConvertToSatin, + ConvertToStroke, CutSatin, AutoSatin, Lettering, @@ -57,6 +60,7 @@ __all__ = extensions = [StitchPlanPreview, Cleanup, BreakApart, ImportThreadlist, + InstallCustomPalette, Simulator, Reorder, DuplicateParams, diff --git a/lib/extensions/base.py b/lib/extensions/base.py index 862d031e..476e4969 100644 --- a/lib/extensions/base.py +++ b/lib/extensions/base.py @@ -50,19 +50,7 @@ class InkStitchMetadata(MutableMapping): def __init__(self, document): self.document = document - self.metadata = self._get_or_create_metadata() - - def _get_or_create_metadata(self): - metadata = self.document.find(SVG_METADATA_TAG) - - if metadata is None: - metadata = etree.SubElement(self.document.getroot(), SVG_METADATA_TAG) - - # move it so that it goes right after the first element, sodipodi:namedview - self.document.getroot().remove(metadata) - self.document.getroot().insert(1, metadata) - - return metadata + self.metadata = document.metadata # Because this class inherints from MutableMapping, all we have to do is # implement these five methods and we get a full dict-like interface. @@ -118,15 +106,17 @@ class InkstitchExtension(inkex.Effect): if g.get(INKSCAPE_GROUPMODE) == "layer": g.set("style", "display:none") - def ensure_current_layer(self): + def get_current_layer(self): # if no layer is selected, inkex defaults to the root, which isn't # particularly useful - if self.svg.get_current_layer() is self.document.getroot(): + current_layer = self.svg.get_current_layer() + if current_layer is self.document.getroot(): try: - self.current_layer = self.document.xpath(".//svg:g[@inkscape:groupmode='layer']", namespaces=inkex.NSS)[0] + current_layer = self.document.xpath(".//svg:g[@inkscape:groupmode='layer']", namespaces=inkex.NSS)[0] except IndexError: # No layers at all?? Fine, we'll stick with the default. pass + return current_layer def no_elements_error(self): if self.svg.selected: @@ -135,7 +125,7 @@ class InkstitchExtension(inkex.Effect): else: inkex.errormsg(_("There are no objects in the entire document that Ink/Stitch knows how to work with.") + "\n") - inkex.errormsg(_("Tip: Select some objects and use Path -> Object to Path to convert them to paths.") + "\n") + inkex.errormsg(_("Tip: Run Extensions > Ink/Stitch > Troubleshoot > Troubleshoot Objects") + "\n") def descendants(self, node, selected=False, troubleshoot=False): # noqa: C901 nodes = [] @@ -148,7 +138,7 @@ class InkstitchExtension(inkex.Effect): if len(list(layer_commands(node, "ignore_layer"))): return [] - if element.has_style('display') and element.get_style('display') is None: + if (node.tag in EMBROIDERABLE_TAGS or node.tag == SVG_GROUP_TAG) and element.get_style('display', 'inline') is None: return [] if node.tag == SVG_DEFS_TAG: @@ -211,7 +201,7 @@ class InkstitchExtension(inkex.Effect): return patches def get_inkstitch_metadata(self): - return InkStitchMetadata(self.document) + return InkStitchMetadata(self.svg) def get_base_file_name(self): svg_filename = self.document.getroot().get(inkex.addNS('docname', 'sodipodi'), "embroidery.svg") diff --git a/lib/extensions/break_apart.py b/lib/extensions/break_apart.py index c2eb02d4..c645c7a1 100644 --- a/lib/extensions/break_apart.py +++ b/lib/extensions/break_apart.py @@ -141,7 +141,7 @@ class BreakApart(InkstitchExtension): el = copy(element.node) # Set fill-rule to evenodd - style = el.get('style', '').split(';') + style = el.get('style', ' ').split(';') style = [s for s in style if not s.startswith('fill-rule')] style.append('fill-rule:evenodd;') style = ';'.join(style) diff --git a/lib/extensions/convert_to_satin.py b/lib/extensions/convert_to_satin.py index 393ffd9e..f3b43366 100644 --- a/lib/extensions/convert_to_satin.py +++ b/lib/extensions/convert_to_satin.py @@ -9,16 +9,15 @@ from itertools import chain, groupby import inkex import numpy -from lxml import etree from numpy import diff, setdiff1d, sign from shapely import geometry as shgeo +from .base import InkstitchExtension from ..elements import Stroke from ..i18n import _ from ..svg import PIXELS_PER_MM, get_correction_transform -from ..svg.tags import INKSTITCH_ATTRIBS, SVG_PATH_TAG +from ..svg.tags import INKSTITCH_ATTRIBS from ..utils import Point -from .base import InkstitchExtension class SelfIntersectionError(Exception): @@ -317,11 +316,10 @@ class ConvertToSatin(InkstitchExtension): d += "%s,%s " % (x, y) d += " " - return etree.Element(SVG_PATH_TAG, - { - "id": self.uniqueId("path"), - "style": path_style, - "transform": correction_transform, - "d": d, - INKSTITCH_ATTRIBS['satin_column']: "true", - }) + return inkex.PathElement(attrib={ + "id": self.uniqueId("path"), + "style": path_style, + "transform": correction_transform, + "d": d, + INKSTITCH_ATTRIBS['satin_column']: "true", + }) diff --git a/lib/extensions/convert_to_stroke.py b/lib/extensions/convert_to_stroke.py new file mode 100644 index 00000000..dfaef615 --- /dev/null +++ b/lib/extensions/convert_to_stroke.py @@ -0,0 +1,58 @@ +# Authors: see git history +# +# Copyright (c) 2010 Authors +# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. + +import inkex +from lxml import etree + +from ..elements import SatinColumn +from ..i18n import _ +from ..svg import get_correction_transform +from ..svg.tags import SVG_PATH_TAG +from .base import InkstitchExtension + + +class ConvertToStroke(InkstitchExtension): + """Convert a satin column into a running stitch.""" + + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self, *args, **kwargs) + self.arg_parser.add_argument("-k", "--keep_satin", type=inkex.Boolean, default=False, dest="keep_satin") + + def effect(self): + if not self.svg.selected or not self.get_elements(): + inkex.errormsg(_("Please select at least one satin column to convert to a running stitch.")) + return + + if not any(isinstance(item, SatinColumn) for item in self.elements): + # L10N: Convert To Satin extension, user selected one or more objects that were not lines. + inkex.errormsg(_("Please select at least one satin column to convert to a running stitch.")) + return + + for element in self.elements: + if not isinstance(element, SatinColumn): + continue + + parent = element.node.getparent() + center_line = element.center_line.simplify(0.05) + + d = "M" + for x, y in center_line.coords: + d += "%s,%s " % (x, y) + d += " " + + stroke_element = etree.Element(SVG_PATH_TAG, + { + "id": self.uniqueId("path"), + "style": self.path_style(element), + "transform": get_correction_transform(element.node), + "d": d + }) + parent.insert(parent.index(element.node), stroke_element) + if not self.options.keep_satin: + parent.remove(element.node) + + def path_style(self, element): + color = element.get_style('stroke', '#000000') + return "stroke:%s;stroke-width:1px;stroke-dasharray:3, 1;fill:none" % (color) diff --git a/lib/extensions/input.py b/lib/extensions/input.py index a0861bbc..a8b8bee3 100644 --- a/lib/extensions/input.py +++ b/lib/extensions/input.py @@ -47,7 +47,7 @@ class Input(object): del stitch_plan.last_color_block[-1] extents = stitch_plan.extents - svg = etree.Element("svg", nsmap=inkex.NSS, attrib={ + svg = inkex.SvgDocumentElement("svg", nsmap=inkex.NSS, attrib={ "width": str(extents[0] * 2), "height": str(extents[1] * 2), "viewBox": "0 0 %s %s" % (extents[0] * 2, extents[1] * 2), diff --git a/lib/extensions/install_custom_palette.py b/lib/extensions/install_custom_palette.py new file mode 100644 index 00000000..da546cad --- /dev/null +++ b/lib/extensions/install_custom_palette.py @@ -0,0 +1,42 @@ +# Authors: see git history +# +# Copyright (c) 2021 Authors +# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. + +import os +import shutil + +import inkex + +from ..i18n import _ +from ..utils import guess_inkscape_config_path +from .base import InkstitchExtension + + +class InstallCustomPalette(InkstitchExtension): + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self, *args, **kwargs) + self.arg_parser.add_argument("-f", "--filepath", type=str, default="", dest="filepath") + + def effect(self): + gpl = self.options.filepath + if not os.path.isfile(gpl): + inkex.errormsg(_("File does not exist.")) + + palette_name = os.path.basename(gpl) + if not palette_name.endswith('.gpl'): + inkex.errormsg(_("Wrong file type. Ink/Stitch only accepts gpl color palettes.")) + + if not palette_name.startswith('InkStitch'): + palette_name = 'InkStitch %s' % palette_name + + palette_path = os.path.join(guess_inkscape_config_path(), 'palettes') + + if not os.path.isdir(palette_path): + inkex.errormsg(_("Ink/Stitch cannot find your palette folder automatically. Please install your palette manually.")) + dest = os.path.join(palette_path, palette_name) + shutil.copyfile(gpl, dest) + + if not os.path.isfile(dest): + inkex.errormsg("Something wwent wrong. Ink/Stitch wasn't able to copy your palette " + "file into the Inkscape palettes folder. Please do it manually.") diff --git a/lib/extensions/layer_commands.py b/lib/extensions/layer_commands.py index 2494e820..26f01fad 100644 --- a/lib/extensions/layer_commands.py +++ b/lib/extensions/layer_commands.py @@ -4,12 +4,9 @@ # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. import inkex -from lxml import etree -from ..commands import LAYER_COMMANDS, ensure_symbol, get_command_description +from ..commands import LAYER_COMMANDS, add_layer_commands from ..i18n import _ -from ..svg import get_correction_transform -from ..svg.tags import INKSCAPE_LABEL, SVG_USE_TAG, XLINK_HREF from .commands import CommandsExtension @@ -23,19 +20,4 @@ class LayerCommands(CommandsExtension): inkex.errormsg(_("Please choose one or more commands to add.")) return - correction_transform = get_correction_transform(self.svg.get_current_layer(), child=True) - - for i, command in enumerate(commands): - ensure_symbol(self.document, command) - - etree.SubElement(self.svg.get_current_layer(), SVG_USE_TAG, - { - "id": self.uniqueId("use"), - INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), - XLINK_HREF: "#inkstitch_%s" % command, - "height": "100%", - "width": "100%", - "x": str(i * 20), - "y": "-10", - "transform": correction_transform - }) + add_layer_commands(self.svg.get_current_layer(), commands) diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py index cf627fe5..312a47ce 100644 --- a/lib/extensions/lettering.py +++ b/lib/extensions/lettering.py @@ -12,7 +12,6 @@ import appdirs import inkex import wx import wx.adv -from lxml import etree from ..elements import nodes_to_elements from ..gui import PresetsPanel, SimulatorPreview, info_dialog @@ -21,7 +20,7 @@ from ..lettering import Font, FontError from ..svg import get_correction_transform from ..svg.tags import (INKSCAPE_LABEL, INKSTITCH_LETTERING, SVG_GROUP_TAG, SVG_PATH_TAG) -from ..utils import DotDict, cache, get_bundled_dir +from ..utils import DotDict, cache, get_bundled_dir, get_resource_dir from .commands import CommandsExtension from .lettering_custom_font_dir import get_custom_font_dir @@ -44,6 +43,9 @@ class LetteringFrame(wx.Frame): _("Ink/Stitch Lettering") ) + icon = wx.Icon(os.path.join(get_resource_dir("icons"), "inkstitch256x256.png")) + self.SetIcon(icon) + self.preview = SimulatorPreview(self, target_duration=1) self.presets_panel = PresetsPanel(self) @@ -142,10 +144,10 @@ class LetteringFrame(wx.Frame): for font_dir in font_dirs: font = Font(os.path.join(font_path, font_dir)) - if font.name == "" or font.id == "": + if font.marked_custom_font_name == "" or font.marked_custom_font_id == "": continue - self.fonts[font.name] = font - self.fonts_by_id[font.id] = font + self.fonts[font.marked_custom_font_name] = font + self.fonts_by_id[font.marked_custom_font_id] = font if len(self.fonts) == 0: info_dialog(self, _("Unable to find any fonts! Please try reinstalling Ink/Stitch.")) @@ -154,6 +156,7 @@ class LetteringFrame(wx.Frame): def set_font_list(self): for font in self.fonts.values(): image = font.preview_image + if image is not None: image = wx.Image(font.preview_image) """ @@ -169,16 +172,10 @@ class LetteringFrame(wx.Frame): """ # Windows requires all images to have the exact same size image.Rescale(300, 20, quality=wx.IMAGE_QUALITY_HIGH) - self.font_chooser.Append(font.name, wx.Bitmap(image)) + self.font_chooser.Append(font.marked_custom_font_name, wx.Bitmap(image)) else: self.font_chooser.Append(font.name) - def get_font_names(self): - font_names = [font.name for font in self.fonts.values()] - font_names.sort() - - return font_names - def get_font_descriptions(self): return {font.name: font.description for font in self.fonts.values()} @@ -189,9 +186,11 @@ class LetteringFrame(wx.Frame): '''A default font will be substituted.''' info_dialog(self, _(message) % font_id) try: - self.font_chooser.SetValue(self.fonts_by_id[font_id].name) + font = self.fonts_by_id[font_id].marked_custom_font_name except KeyError: - self.font_chooser.SetValue(self.default_font.name) + font = self.default_font.name + self.font_chooser.SetValue(font) + self.on_font_changed() @property @@ -208,7 +207,7 @@ class LetteringFrame(wx.Frame): def on_font_changed(self, event=None): font = self.fonts.get(self.font_chooser.GetValue(), self.default_font) - self.settings.font = font.id + self.settings.font = font.marked_custom_font_id self.scale_spinner.SetRange(int(font.min_scale * 100), int(font.max_scale * 100)) font_variants = [] @@ -260,12 +259,13 @@ class LetteringFrame(wx.Frame): if self.settings.scale == 100: destination_group = self.group else: - destination_group = etree.SubElement(self.group, SVG_GROUP_TAG, { + destination_group = inkex.Group(attrib={ # L10N The user has chosen to scale the text by some percentage # (50%, 200%, etc). If you need to use the percentage symbol, # make sure to double it (%%). INKSCAPE_LABEL: _("Text scale %s%%") % self.settings.scale }) + self.group.append(destination_group) font = self.fonts.get(self.font_chooser.GetValue(), self.default_font) try: @@ -416,11 +416,12 @@ class Lettering(CommandsExtension): else: return list(groups)[0] else: - self.ensure_current_layer() - return etree.SubElement(self.svg.get_current_layer(), SVG_GROUP_TAG, { + group = inkex.Group(attrib={ INKSCAPE_LABEL: _("Ink/Stitch Lettering"), - "transform": get_correction_transform(self.svg.get_current_layer(), child=True) + "transform": get_correction_transform(self.get_current_layer(), child=True) }) + self.get_current_layer().append(group) + return group def effect(self): app = wx.App() diff --git a/lib/extensions/params.py b/lib/extensions/params.py index 7775aed1..c96b9691 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -343,6 +343,10 @@ class SettingsFrame(wx.Frame): wx.Frame.__init__(self, None, wx.ID_ANY, _("Embroidery Params") ) + + icon = wx.Icon(os.path.join(get_resource_dir("icons"), "inkstitch256x256.png")) + self.SetIcon(icon) + self.notebook = wx.Notebook(self, wx.ID_ANY) self.tabs = self.tabs_factory(self.notebook) diff --git a/lib/extensions/print_pdf.py b/lib/extensions/print_pdf.py index a1a8c6c0..0facdf92 100644 --- a/lib/extensions/print_pdf.py +++ b/lib/extensions/print_pdf.py @@ -15,10 +15,10 @@ from datetime import date from threading import Thread import appdirs -import requests from flask import Flask, Response, jsonify, request, send_from_directory from jinja2 import Environment, FileSystemLoader, select_autoescape from lxml import etree +from werkzeug.serving import make_server from ..gui import open_url from ..i18n import get_languages @@ -66,7 +66,8 @@ class PrintPreviewServer(Thread): self.realistic_color_block_svgs = kwargs.pop('realistic_color_block_svgs') Thread.__init__(self, *args, **kwargs) self.daemon = True - self.shutting_down = False + self.flask_server = None + self.server_thread = None self.__setup_app() @@ -89,15 +90,9 @@ class PrintPreviewServer(Thread): def index(): return self.html - @self.app.route('/shutdown', methods=['POST']) - def shutdown(): - self.shutting_down = True - request.environ.get('werkzeug.server.shutdown')() - return "shutting down" - @self.app.route('/resources/<path:resource>', methods=['GET']) def resources(resource): - return send_from_directory(self.resources_path, resource, cache_timeout=1) + return send_from_directory(self.resources_path, resource, max_age=1) @self.app.route('/settings/<field_name>', methods=['POST']) def set_field(field_name): @@ -158,9 +153,8 @@ class PrintPreviewServer(Thread): return Response(self.realistic_overview_svg, mimetype='image/svg+xml') def stop(self): - # for whatever reason, shutting down only seems possible in - # the context of a flask request, so we'll just make one - requests.post("http://%s:%s/shutdown" % (self.host, self.port)) + self.flask_server.shutdown() + self.server_thread.join() def disable_logging(self): logging.getLogger('werkzeug').setLevel(logging.ERROR) @@ -173,7 +167,9 @@ class PrintPreviewServer(Thread): while True: try: - self.app.run(self.host, self.port, threaded=True) + self.flask_server = make_server(self.host, self.port, self.app) + self.server_thread = Thread(target=self.flask_server.serve_forever) + self.server_thread.start() except socket.error as e: if e.errno == errno.EADDRINUSE: self.port += 1 diff --git a/lib/extensions/troubleshoot.py b/lib/extensions/troubleshoot.py index 113a9383..bf7faf76 100644 --- a/lib/extensions/troubleshoot.py +++ b/lib/extensions/troubleshoot.py @@ -5,18 +5,15 @@ import textwrap -from inkex import errormsg -from lxml import etree +import inkex +from .base import InkstitchExtension from ..commands import add_layer_commands from ..elements.validation import (ObjectTypeWarning, ValidationError, ValidationWarning) from ..i18n import _ from ..svg.path import get_correction_transform -from ..svg.tags import (INKSCAPE_GROUPMODE, INKSCAPE_LABEL, SODIPODI_ROLE, - SVG_GROUP_TAG, SVG_PATH_TAG, SVG_TEXT_TAG, - SVG_TSPAN_TAG) -from .base import InkstitchExtension +from ..svg.tags import (INKSCAPE_GROUPMODE, INKSCAPE_LABEL, SODIPODI_ROLE) class Troubleshoot(InkstitchExtension): @@ -49,7 +46,7 @@ class Troubleshoot(InkstitchExtension): message += "\n\n" message += _("If you are still having trouble with a shape not being embroidered, " "check if it is in a layer with an ignore command.") - errormsg(message) + inkex.errormsg(message) def insert_pointer(self, problem): correction_transform = get_correction_transform(self.troubleshoot_layer) @@ -67,32 +64,28 @@ class Troubleshoot(InkstitchExtension): pointer_style = "stroke:#000000;stroke-width:0.2;fill:%s;" % (fill_color) text_style = "fill:%s;stroke:#000000;stroke-width:0.2;font-size:8px;text-align:center;text-anchor:middle" % (fill_color) - path = etree.Element( - SVG_PATH_TAG, - { - "id": self.uniqueId("inkstitch__invalid_pointer__"), - "d": "m %s,%s 4,20 h -8 l 4,-20" % (problem.position.x, problem.position.y), - "style": pointer_style, - INKSCAPE_LABEL: _('Invalid Pointer'), - "transform": correction_transform - } - ) + path = inkex.PathElement(attrib={ + "id": self.uniqueId("inkstitch__invalid_pointer__"), + "d": "m %s,%s 4,20 h -8 l 4,-20" % (problem.position.x, problem.position.y), + "style": pointer_style, + INKSCAPE_LABEL: _('Invalid Pointer'), + "transform": correction_transform + }) layer.insert(0, path) - text = etree.Element( - SVG_TEXT_TAG, - { - INKSCAPE_LABEL: _('Description'), - "x": str(problem.position.x), - "y": str(float(problem.position.y) + 30), - "transform": correction_transform, - "style": text_style - } - ) + text = inkex.TextElement(attrib={ + INKSCAPE_LABEL: _('Description'), + "x": str(problem.position.x), + "y": str(float(problem.position.y) + 30), + "transform": correction_transform, + "style": text_style + }) layer.append(text) - tspan = etree.Element(SVG_TSPAN_TAG) + tspan = inkex.Tspan() tspan.text = problem.name + if problem.label: + tspan.text += " (%s)" % problem.label text.append(tspan) def create_troubleshoot_layer(self): @@ -100,46 +93,34 @@ class Troubleshoot(InkstitchExtension): layer = svg.find(".//*[@id='__validation_layer__']") if layer is None: - layer = etree.Element( - SVG_GROUP_TAG, - { - 'id': '__validation_layer__', - INKSCAPE_LABEL: _('Troubleshoot'), - INKSCAPE_GROUPMODE: 'layer', - }) + layer = inkex.Group(attrib={ + 'id': '__validation_layer__', + INKSCAPE_LABEL: _('Troubleshoot'), + INKSCAPE_GROUPMODE: 'layer', + }) svg.append(layer) - else: # Clear out everything from the last run del layer[:] add_layer_commands(layer, ["ignore_layer"]) - error_group = etree.SubElement( - layer, - SVG_GROUP_TAG, - { - "id": '__validation_errors__', - INKSCAPE_LABEL: _("Errors"), - }) + error_group = inkex.Group(attrib={ + "id": '__validation_errors__', + INKSCAPE_LABEL: _("Errors"), + }) layer.append(error_group) - warning_group = etree.SubElement( - layer, - SVG_GROUP_TAG, - { - "id": '__validation_warnings__', - INKSCAPE_LABEL: _("Warnings"), - }) + warning_group = inkex.Group(attrib={ + "id": '__validation_warnings__', + INKSCAPE_LABEL: _("Warnings"), + }) layer.append(warning_group) - type_warning_group = etree.SubElement( - layer, - SVG_GROUP_TAG, - { - "id": '__validation_ignored__', - INKSCAPE_LABEL: _("Type Warnings"), - }) + type_warning_group = inkex.Group(attrib={ + "id": '__validation_ignored__', + INKSCAPE_LABEL: _("Type Warnings"), + }) layer.append(type_warning_group) self.troubleshoot_layer = layer @@ -151,14 +132,11 @@ class Troubleshoot(InkstitchExtension): svg = self.document.getroot() text_x = str(float(svg.get('viewBox', '0 0 800 0').split(' ')[2]) + 5.0) - text_container = etree.Element( - SVG_TEXT_TAG, - { - "x": text_x, - "y": str(5), - "style": "fill:#000000;font-size:5px;line-height:1;" - } - ) + text_container = inkex.TextElement(attrib={ + "x": text_x, + "y": str(5), + "style": "fill:#000000;font-size:5px;line-height:1;" + }) self.troubleshoot_layer.append(text_container) text = [ @@ -180,9 +158,8 @@ class Troubleshoot(InkstitchExtension): elif problem_type == "type_warning": text_color = "#ff9900" problem_type_header = _("Object Type Warnings") - problem_type_description = _("Ink/Stitch only knows how to works with paths and ignores everything else. " - "You might want these shapes to be ignored, but if you don't, " - "follow the instructions to change this behaviour.") + problem_type_description = _("These objects may not work properly with Ink/Stitch. " + "Follow the instructions to correct unwanted behaviour.") if problems: text.append([problem_type_header, "font-weight: bold; fill: %s; text-decoration: underline; font-size: 7px;" % text_color]) text.append(["", ""]) @@ -208,13 +185,10 @@ class Troubleshoot(InkstitchExtension): text = self.split_text(text) for text_line in text: - tspan = etree.Element( - SVG_TSPAN_TAG, - { - SODIPODI_ROLE: "line", - "style": text_line[1] - } - ) + tspan = inkex.Tspan(attrib={ + SODIPODI_ROLE: "line", + "style": text_line[1] + }) tspan.text = text_line[0] text_container.append(tspan) diff --git a/lib/lettering/font.py b/lib/lettering/font.py index d241bf05..ff726a56 100644 --- a/lib/lettering/font.py +++ b/lib/lettering/font.py @@ -7,14 +7,14 @@ import json import os from copy import deepcopy -from inkex import styles -from lxml import etree +import inkex from ..elements import nodes_to_elements from ..exceptions import InkstitchException +from ..extensions.lettering_custom_font_dir import get_custom_font_dir from ..i18n import _, get_languages from ..stitches.auto_satin import auto_satin -from ..svg.tags import INKSCAPE_LABEL, SVG_GROUP_TAG, SVG_PATH_TAG +from ..svg.tags import INKSCAPE_LABEL, SVG_PATH_TAG from ..utils import Point from .font_variant import FontVariant @@ -114,7 +114,7 @@ class Font(object): min_scale = font_metadata('min_scale', 1.0) max_scale = font_metadata('max_scale', 1.0) - # use values from SVG Font, exemple: + # use values from SVG Font, example: # <font horiz-adv-x="45" ... <glyph .... horiz-adv-x="49" glyph-name="A" /> ... <hkern ... k="3"g1="A" g2="B" /> .... /> # Example font.json : "horiz_adv_x": {"A":49}, @@ -158,6 +158,23 @@ class Font(object): raise FontError(_("The font '%s' has no variants.") % self.name) return font_variants + @property + def marked_custom_font_id(self): + if not self.is_custom_font(): + return self.id + else: + return self.id + '*' + + @property + def marked_custom_font_name(self): + if not self.is_custom_font(): + return self.name + else: + return self.name + '*' + + def is_custom_font(self): + return get_custom_font_dir() in self.path + def render_text(self, text, destination_group, variant=None, back_and_forth=True, trim=False): """Render text into an SVG group element.""" self._load_variants() @@ -190,7 +207,7 @@ class Font(object): for element in destination_group.iterdescendants(SVG_PATH_TAG): dash_array = "" stroke_width = "" - style = styles.Style(element.get('style')) + style = inkex.styles.Style(element.get('style')) if style.get('fill') == 'none': stroke_width = ";stroke-width:1px" @@ -224,7 +241,7 @@ class Font(object): An svg:g element containing the rendered text. """ - group = etree.Element(SVG_GROUP_TAG, { + group = inkex.Group(attrib={ INKSCAPE_LABEL: line }) diff --git a/lib/lettering/font_variant.py b/lib/lettering/font_variant.py index b1e38368..d9d8ed44 100644 --- a/lib/lettering/font_variant.py +++ b/lib/lettering/font_variant.py @@ -6,7 +6,6 @@ import os import inkex -from lxml import etree from ..svg.tags import INKSCAPE_GROUPMODE, INKSCAPE_LABEL from .glyph import Glyph @@ -61,8 +60,7 @@ class FontVariant(object): def _load_glyphs(self): svg_path = os.path.join(self.path, "%s.svg" % self.variant) - with open(svg_path, encoding="utf-8") as svg_file: - svg = etree.parse(svg_file) + svg = inkex.load_svg(svg_path) glyph_layers = svg.xpath(".//svg:g[starts-with(@inkscape:label, 'GlyphLayer-')]", namespaces=inkex.NSS) for layer in glyph_layers: @@ -76,14 +74,10 @@ class FontVariant(object): # glyph. del group.attrib[INKSCAPE_GROUPMODE] - style_text = group.get('style') - - if style_text: - # The layer may be marked invisible, so we'll clear the 'display' - # style. - style = dict(inkex.Style.parse_str(group.get('style'))) - style.pop('display') - group.set('style', str(inkex.Style(style))) + # The layer may be marked invisible, so we'll clear the 'display' + # style and presentation attribute. + group.style.pop('display', None) + group.attrib.pop('display', None) def __getitem__(self, character): if character in self.glyphs: diff --git a/lib/lettering/glyph.py b/lib/lettering/glyph.py index 3bedd7ed..047c12cf 100644 --- a/lib/lettering/glyph.py +++ b/lib/lettering/glyph.py @@ -8,7 +8,6 @@ from copy import copy from inkex import paths, transforms from ..svg import get_guides -from ..svg.path import get_correction_transform from ..svg.tags import SVG_GROUP_TAG, SVG_PATH_TAG @@ -53,9 +52,7 @@ class Glyph(object): node_copy = copy(node) if "d" in node.attrib: - transform = -transforms.Transform(get_correction_transform(node, True)) - path = paths.Path(node.get("d")).transform(transform).to_absolute() - node_copy.set("d", str(path)) + node_copy.path = node.path.transform(node.composed_transform()).to_absolute() # Delete transforms from paths and groups, since we applied # them to the paths already. diff --git a/lib/stitches/auto_satin.py b/lib/stitches/auto_satin.py index a047ea74..2b7f0906 100644 --- a/lib/stitches/auto_satin.py +++ b/lib/stitches/auto_satin.py @@ -8,7 +8,6 @@ from itertools import chain import inkex import networkx as nx -from lxml import etree from shapely import geometry as shgeo from shapely.geometry import Point as ShapelyPoint @@ -17,8 +16,7 @@ from ..elements import SatinColumn, Stroke from ..i18n import _ from ..svg import (PIXELS_PER_MM, generate_unique_id, get_correction_transform, line_strings_to_csp) -from ..svg.tags import (INKSCAPE_LABEL, INKSTITCH_ATTRIBS, SVG_GROUP_TAG, - SVG_PATH_TAG) +from ..svg.tags import (INKSCAPE_LABEL, INKSTITCH_ATTRIBS) from ..utils import Point as InkstitchPoint from ..utils import cache, cut @@ -219,14 +217,13 @@ class RunningStitch(object): original_element.node.get(INKSTITCH_ATTRIBS['contour_underlay_stitch_length_mm'], '') def to_element(self): - node = etree.Element(SVG_PATH_TAG) + node = inkex.PathElement() d = str(inkex.paths.CubicSuperPath(line_strings_to_csp([self.path]))) node.set("d", d) - style = self.original_element.parse_style() - style['stroke-dasharray'] = "0.5,0.5" - style = str(inkex.Style(style)) - node.set("style", style) + dasharray = inkex.Style("stroke-dasharray:0.5,0.5;") + style = inkex.Style(self.original_element.node.get('style', '')) + dasharray + node.set("style", str(style)) node.set(INKSTITCH_ATTRIBS['running_stitch_length_mm'], self.running_stitch_length) stroke = Stroke(node) @@ -658,7 +655,7 @@ def preserve_original_groups(elements, original_parent_nodes): def create_new_group(parent, insert_index): - group = etree.Element(SVG_GROUP_TAG, { + group = inkex.Group(attrib={ INKSCAPE_LABEL: _("Auto-Satin"), "transform": get_correction_transform(parent, child=True) }) diff --git a/lib/svg/path.py b/lib/svg/path.py index b503cc75..53cf80f2 100644 --- a/lib/svg/path.py +++ b/lib/svg/path.py @@ -4,7 +4,6 @@ # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. import inkex -from lxml import etree from .tags import SVG_GROUP_TAG, SVG_LINK_TAG from .units import get_viewbox_transform @@ -96,6 +95,6 @@ def point_lists_to_csp(point_lists): def line_strings_to_path(line_strings): csp = line_strings_to_csp(line_strings) - return etree.Element("path", { + return inkex.PathElement(attrib={ "d": str(inkex.paths.CubicSuperPath(csp)) }) diff --git a/lib/svg/rendering.py b/lib/svg/rendering.py index c28c7003..7be1e8b0 100644 --- a/lib/svg/rendering.py +++ b/lib/svg/rendering.py @@ -4,16 +4,14 @@ # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. import math +from math import pi import inkex -from lxml import etree -from math import pi +from .tags import (INKSCAPE_GROUPMODE, INKSCAPE_LABEL, INKSTITCH_ATTRIBS) +from .units import PIXELS_PER_MM, get_viewbox_transform from ..i18n import _ from ..utils import Point, cache -from .tags import (INKSCAPE_GROUPMODE, INKSCAPE_LABEL, INKSTITCH_ATTRIBS, - SVG_DEFS_TAG, SVG_GROUP_TAG, SVG_PATH_TAG) -from .units import PIXELS_PER_MM, get_viewbox_transform # The stitch vector path looks like this: # _______ @@ -174,7 +172,7 @@ def color_block_to_realistic_stitches(color_block, svg, destination): color = color_block.color.visible_on_white.darker.to_hex_str() start = point_list[0] for point in point_list[1:]: - destination.append(etree.Element(SVG_PATH_TAG, { + destination.append(inkex.PathElement(attrib={ 'style': "fill: %s; stroke: none; filter: url(#realistic-stitch-filter);" % color, 'd': realistic_stitch(start, point), 'transform': get_correction_transform(svg) @@ -200,7 +198,7 @@ def color_block_to_paths(color_block, svg, destination, visual_commands): color = color_block.color.visible_on_white.to_hex_str() - path = etree.Element(SVG_PATH_TAG, { + path = inkex.PathElement(attrib={ 'style': "stroke: %s; stroke-width: 0.4; fill: none;" % color, 'd': "M" + " ".join(" ".join(str(coord) for coord in point) for point in point_list), 'transform': get_correction_transform(svg), @@ -219,10 +217,11 @@ def color_block_to_paths(color_block, svg, destination, visual_commands): def render_stitch_plan(svg, stitch_plan, realistic=False, visual_commands=True): layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']") if layer is None: - layer = etree.Element(SVG_GROUP_TAG, - {'id': '__inkstitch_stitch_plan__', - INKSCAPE_LABEL: _('Stitch Plan'), - INKSCAPE_GROUPMODE: 'layer'}) + layer = inkex.Group(attrib={ + 'id': '__inkstitch_stitch_plan__', + INKSCAPE_LABEL: _('Stitch Plan'), + INKSCAPE_GROUPMODE: 'layer' + }) else: # delete old stitch plan del layer[:] @@ -233,19 +232,16 @@ def render_stitch_plan(svg, stitch_plan, realistic=False, visual_commands=True): svg.append(layer) for i, color_block in enumerate(stitch_plan): - group = etree.SubElement(layer, - SVG_GROUP_TAG, - {'id': '__color_block_%d__' % i, - INKSCAPE_LABEL: "color block %d" % (i + 1)}) + group = inkex.Group(attrib={ + 'id': '__color_block_%d__' % i, + INKSCAPE_LABEL: "color block %d" % (i + 1) + }) + layer.append(group) if realistic: color_block_to_realistic_stitches(color_block, svg, group) else: color_block_to_paths(color_block, svg, group, visual_commands) if realistic: - defs = svg.find(SVG_DEFS_TAG) - - if defs is None: - defs = etree.SubElement(svg, SVG_DEFS_TAG) - - defs.append(etree.fromstring(realistic_filter)) + filter_document = inkex.load_svg(realistic_filter) + svg.defs.append(filter_document.getroot()) diff --git a/lib/threads/catalog.py b/lib/threads/catalog.py index c3ccb0c3..c12ca1fe 100644 --- a/lib/threads/catalog.py +++ b/lib/threads/catalog.py @@ -75,7 +75,7 @@ class _ThreadCatalog(Sequence): Scans the catalog of color palettes and chooses one that seems most likely to be the one that the user used. A palette will only be - chosen if more tha 80% of the thread colors in the stitch plan are + chosen if more than 80% of the thread colors in the stitch plan are exact matches for threads in the palette. """ if not self.palettes: diff --git a/lib/utils/version.py b/lib/utils/version.py index 57ef11f0..2186ca23 100644 --- a/lib/utils/version.py +++ b/lib/utils/version.py @@ -18,5 +18,5 @@ def get_inkstitch_version(): with open(version, 'r') as v: inkstitch_version = _("Ink/Stitch Version: %s") % v.readline() else: - inkstitch_version = _("Ink/Stitch Version: unkown") + inkstitch_version = _("Ink/Stitch Version: unknown") return inkstitch_version |
