summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLex Neva <github.com@lexneva.name>2021-08-07 10:58:02 -0400
committerLex Neva <github.com@lexneva.name>2021-08-07 10:58:02 -0400
commit12ef0c84aa732623b210fdce1a7b8301aa435217 (patch)
tree4c7fbb33c4840be4bf8d8fecfd7fe481d0e56895 /lib
parentc1e6558f7852def419adfbeb087b2194e6030a2c (diff)
parentd6e20fae8a03ac162ae0c863fff06b5bd8b77902 (diff)
Merge remote-tracking branch 'origin/main' into kaalleen/satin-patterns
Diffstat (limited to 'lib')
-rw-r--r--lib/api/server.py21
-rw-r--r--lib/commands.py121
-rw-r--r--lib/elements/auto_fill.py4
-rw-r--r--lib/elements/element.py39
-rw-r--r--lib/elements/empty_d_object.py4
-rw-r--r--lib/elements/satin_column.py2
-rw-r--r--lib/elements/stroke.py2
-rw-r--r--lib/elements/validation.py3
-rw-r--r--lib/extensions/__init__.py4
-rw-r--r--lib/extensions/base.py28
-rw-r--r--lib/extensions/break_apart.py2
-rw-r--r--lib/extensions/convert_to_satin.py20
-rw-r--r--lib/extensions/convert_to_stroke.py58
-rw-r--r--lib/extensions/input.py2
-rw-r--r--lib/extensions/install_custom_palette.py42
-rw-r--r--lib/extensions/layer_commands.py22
-rw-r--r--lib/extensions/lettering.py39
-rw-r--r--lib/extensions/params.py4
-rw-r--r--lib/extensions/print_pdf.py22
-rw-r--r--lib/extensions/troubleshoot.py124
-rw-r--r--lib/lettering/font.py29
-rw-r--r--lib/lettering/font_variant.py16
-rw-r--r--lib/lettering/glyph.py5
-rw-r--r--lib/stitches/auto_satin.py15
-rw-r--r--lib/svg/path.py3
-rw-r--r--lib/svg/rendering.py38
-rw-r--r--lib/threads/catalog.py2
-rw-r--r--lib/utils/version.py2
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