summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/commands.py32
-rw-r--r--lib/extensions/__init__.py10
-rw-r--r--lib/extensions/base.py5
-rw-r--r--lib/extensions/layer_commands.py6
-rw-r--r--lib/extensions/object_commands.py25
-rw-r--r--lib/i18n.py15
-rw-r--r--lib/inx/__init__.py1
-rwxr-xr-xlib/inx/extensions.py24
-rw-r--r--lib/inx/generate.py12
-rwxr-xr-xlib/inx/inputs.py18
-rw-r--r--lib/inx/outputs.py18
-rw-r--r--lib/inx/utils.py49
12 files changed, 205 insertions, 10 deletions
diff --git a/lib/commands.py b/lib/commands.py
index cadfa080..214b5f40 100644
--- a/lib/commands.py
+++ b/lib/commands.py
@@ -3,13 +3,42 @@ import cubicsuperpath
from .svg import apply_transforms
from .svg.tags import SVG_USE_TAG, SVG_SYMBOL_TAG, CONNECTION_START, CONNECTION_END, XLINK_HREF
+from .utils import cache
+from .i18n import _, N_
+COMMANDS = {
+ # L10N command attached to an object
+ "fill_start": N_("Fill stitch starting position"),
+
+ # L10N command attached to an object
+ "fill_end": N_("Fill stitch ending position"),
+
+ # L10N command attached to an object
+ "stop": N_("Stop (pause machine) after sewing this object"),
+
+ # L10N command attached to an object
+ "trim": N_("Trim thread after sewing this object"),
+
+ # L10N command attached to an object
+ "ignore_object": N_("Ignore this object (do not stitch)"),
+
+ # L10N command that affects entire layer
+ "ignore_layer": N_("Ignore layer (do not stitch any objects in this layer)")
+}
+
+OBJECT_COMMANDS = [ "fill_start", "fill_end", "stop", "trim", "ignore_object" ]
+LAYER_COMMANDS = [ "ignore_layer" ]
class CommandParseError(Exception):
pass
class BaseCommand(object):
+ @property
+ @cache
+ def description(self):
+ return get_command_description(self.command)
+
def parse_symbol(self):
if self.symbol.tag != SVG_SYMBOL_TAG:
raise CommandParseError("use points to non-symbol")
@@ -87,6 +116,9 @@ class StandaloneCommand(BaseCommand):
self.parse_symbol()
+def get_command_description(command):
+ return _(COMMANDS[command])
+
def find_commands(node):
"""Find the symbols this node is connected to and return them as Commands"""
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py
index 6c8db318..1606795c 100644
--- a/lib/extensions/__init__.py
+++ b/lib/extensions/__init__.py
@@ -10,3 +10,13 @@ from flip import Flip
from object_commands import ObjectCommands
from layer_commands import LayerCommands
from convert_to_satin import ConvertToSatin
+
+from base import InkstitchExtension
+import inspect
+
+extensions = []
+for item in locals().values():
+ if inspect.isclass(item) and \
+ issubclass(item, InkstitchExtension) and \
+ item is not InkstitchExtension:
+ extensions.append(item)
diff --git a/lib/extensions/base.py b/lib/extensions/base.py
index 571e3c2d..1794b68c 100644
--- a/lib/extensions/base.py
+++ b/lib/extensions/base.py
@@ -3,6 +3,7 @@ import re
import json
from copy import deepcopy
from collections import MutableMapping
+from stringcase import snakecase
from ..svg.tags import *
from ..elements import AutoFill, Fill, Stroke, SatinColumn, Polyline, EmbroideryElement
@@ -98,6 +99,10 @@ class InkStitchMetadata(MutableMapping):
class InkstitchExtension(inkex.Effect):
"""Base class for Inkstitch extensions. Not intended for direct use."""
+ @classmethod
+ def name(cls):
+ return snakecase(cls.__name__)
+
def hide_all_layers(self):
for g in self.document.getroot().findall(SVG_GROUP_TAG):
if g.get(INKSCAPE_GROUPMODE) == "layer":
diff --git a/lib/extensions/layer_commands.py b/lib/extensions/layer_commands.py
index 88170f66..576af044 100644
--- a/lib/extensions/layer_commands.py
+++ b/lib/extensions/layer_commands.py
@@ -3,13 +3,14 @@ import sys
import inkex
from .commands import CommandsExtension
+from ..commands import LAYER_COMMANDS, get_command_description
from ..i18n import _
-from ..svg.tags import SVG_USE_TAG, XLINK_HREF
+from ..svg.tags import *
from ..svg import get_correction_transform
class LayerCommands(CommandsExtension):
- COMMANDS = ["ignore_layer"]
+ COMMANDS = LAYER_COMMANDS
def ensure_current_layer(self):
# if no layer is selected, inkex defaults to the root, which isn't
@@ -37,6 +38,7 @@ class LayerCommands(CommandsExtension):
node = inkex.etree.SubElement(self.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%",
diff --git a/lib/extensions/object_commands.py b/lib/extensions/object_commands.py
index 27a07969..e9ee6063 100644
--- a/lib/extensions/object_commands.py
+++ b/lib/extensions/object_commands.py
@@ -7,14 +7,15 @@ from random import random
from shapely import geometry as shgeo
from .commands import CommandsExtension
+from ..commands import OBJECT_COMMANDS, get_command_description
from ..i18n import _
from ..elements import SatinColumn
-from ..svg.tags import SVG_GROUP_TAG, SVG_USE_TAG, SVG_PATH_TAG, INKSCAPE_GROUPMODE, XLINK_HREF, CONNECTION_START, CONNECTION_END, CONNECTOR_TYPE
+from ..svg.tags import *
from ..svg import get_correction_transform
class ObjectCommands(CommandsExtension):
- COMMANDS = ["fill_start", "fill_end", "stop", "trim", "ignore_object"]
+ COMMANDS = OBJECT_COMMANDS
def add_connector(self, symbol, element):
# I'd like it if I could position the connector endpoint nicely but inkscape just
@@ -27,14 +28,16 @@ class ObjectCommands(CommandsExtension):
"id": self.uniqueId("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;",
- "transform": get_correction_transform(symbol),
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")
}
)
- symbol.getparent().insert(symbol.getparent().index(symbol), path)
+ symbol.getparent().insert(0, path)
def get_command_pos(self, element, index, total):
# Put command symbols 30 pixels out from the shape, spaced evenly around it.
@@ -71,7 +74,15 @@ class ObjectCommands(CommandsExtension):
pos = self.get_command_pos(element, i, len(commands))
- symbol = inkex.etree.SubElement(element.node.getparent(), SVG_USE_TAG,
+ group = inkex.etree.SubElement(element.node.getparent(), SVG_GROUP_TAG,
+ {
+ "id": self.uniqueId("group"),
+ INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command),
+ "transform": get_correction_transform(element.node)
+ }
+ )
+
+ symbol = inkex.etree.SubElement(group, SVG_USE_TAG,
{
"id": self.uniqueId("use"),
XLINK_HREF: "#inkstitch_%s" % command,
@@ -79,7 +90,9 @@ class ObjectCommands(CommandsExtension):
"width": "100%",
"x": str(pos.x),
"y": str(pos.y),
- "transform": get_correction_transform(element.node)
+
+ # l10n: the name of a command symbol (example: scissors icon for trim command)
+ INKSCAPE_LABEL: _("command marker"),
}
)
diff --git a/lib/i18n.py b/lib/i18n.py
index d20f5d2f..419c03dc 100644
--- a/lib/i18n.py
+++ b/lib/i18n.py
@@ -1,21 +1,32 @@
import sys
import os
+from os.path import dirname, realpath
import gettext
_ = translation = None
+locale_dir = None
+
+# Use N_ to mark a string for translation but _not_ immediately translate it.
+# reference: https://docs.python.org/3/library/gettext.html#deferred-translations
+# Makefile configures pybabel to treat N_() the same as _()
+def N_(message): return message
+
+def _set_locale_dir():
+ global locale_dir
-def localize():
if getattr(sys, 'frozen', False):
# we are in a pyinstaller installation
locale_dir = sys._MEIPASS
else:
- locale_dir = os.path.dirname(__file__)
+ locale_dir = dirname(dirname(realpath(__file__)))
locale_dir = os.path.join(locale_dir, 'locales')
+def localize(languages=None):
global translation, _
translation = gettext.translation("inkstitch", locale_dir, fallback=True)
_ = translation.gettext
+_set_locale_dir()
localize()
diff --git a/lib/inx/__init__.py b/lib/inx/__init__.py
new file mode 100644
index 00000000..32b8bfae
--- /dev/null
+++ b/lib/inx/__init__.py
@@ -0,0 +1 @@
+from generate import generate_inx_files
diff --git a/lib/inx/extensions.py b/lib/inx/extensions.py
new file mode 100755
index 00000000..2b097440
--- /dev/null
+++ b/lib/inx/extensions.py
@@ -0,0 +1,24 @@
+import pyembroidery
+
+from .utils import build_environment, write_inx_file
+from .outputs import pyembroidery_output_formats
+from ..extensions import extensions, Input, Output
+
+
+def pyembroidery_debug_formats():
+ for format in pyembroidery.supported_formats():
+ if 'writer' in format and format['category'] != 'embroidery':
+ yield format['extension'], format['description']
+
+
+def generate_extension_inx_files():
+ env = build_environment()
+
+ for extension in extensions:
+ if extension is Input or extension is Output:
+ continue
+
+ name = extension.name()
+ template = env.get_template('%s.inx' % name)
+ write_inx_file(name, template.render(formats=pyembroidery_output_formats(),
+ debug_formats=pyembroidery_debug_formats()))
diff --git a/lib/inx/generate.py b/lib/inx/generate.py
new file mode 100644
index 00000000..f9ed799b
--- /dev/null
+++ b/lib/inx/generate.py
@@ -0,0 +1,12 @@
+import os
+
+from .inputs import generate_input_inx_files
+from .outputs import generate_output_inx_files
+from .extensions import generate_extension_inx_files
+from .utils import iterate_inx_locales, inx_path
+
+def generate_inx_files():
+ for locale in iterate_inx_locales():
+ generate_input_inx_files()
+ generate_output_inx_files()
+ generate_extension_inx_files()
diff --git a/lib/inx/inputs.py b/lib/inx/inputs.py
new file mode 100755
index 00000000..d40ffeaf
--- /dev/null
+++ b/lib/inx/inputs.py
@@ -0,0 +1,18 @@
+import pyembroidery
+
+from .utils import build_environment, write_inx_file
+
+
+def pyembroidery_input_formats():
+ for format in pyembroidery.supported_formats():
+ if 'reader' in format and format['category'] == 'embroidery':
+ yield format['extension'], format['description']
+
+
+def generate_input_inx_files():
+ env = build_environment()
+ template = env.get_template('input.inx')
+
+ for format, description in pyembroidery_input_formats():
+ name = "input_%s" % format.upper()
+ write_inx_file(name, template.render(format=format, description=description))
diff --git a/lib/inx/outputs.py b/lib/inx/outputs.py
new file mode 100644
index 00000000..aef0c8b5
--- /dev/null
+++ b/lib/inx/outputs.py
@@ -0,0 +1,18 @@
+import pyembroidery
+
+from .utils import build_environment, write_inx_file
+
+
+def pyembroidery_output_formats():
+ for format in pyembroidery.supported_formats():
+ if 'writer' in format and format['category'] == 'embroidery':
+ yield format['extension'], format['description']
+
+
+def generate_output_inx_files():
+ env = build_environment()
+ template = env.get_template('output.inx')
+
+ for format, description in pyembroidery_output_formats():
+ name = "output_%s" % format.upper()
+ write_inx_file(name, template.render(format=format, description=description))
diff --git a/lib/inx/utils.py b/lib/inx/utils.py
new file mode 100644
index 00000000..6103f360
--- /dev/null
+++ b/lib/inx/utils.py
@@ -0,0 +1,49 @@
+import os
+import gettext
+from os.path import dirname
+from jinja2 import Environment, FileSystemLoader
+
+from ..i18n import translation as default_translation, locale_dir, _, N_
+
+
+_top_path = dirname(dirname(dirname(os.path.realpath(__file__))))
+inx_path = os.path.join(_top_path, "inx")
+template_path = os.path.join(_top_path, "templates")
+
+current_translation = default_translation
+current_locale = "en_US"
+
+def build_environment():
+ env = Environment(
+ loader = FileSystemLoader(template_path),
+ autoescape = True,
+ extensions=['jinja2.ext.i18n']
+ )
+
+ env.install_gettext_translations(current_translation)
+ env.globals["locale"] = current_locale
+
+ return env
+
+def write_inx_file(name, contents):
+ inx_file_name = "inkstitch_%s_%s.inx" % (name, current_locale)
+ with open(os.path.join(inx_path, inx_file_name), 'w') as inx_file:
+ print >> inx_file, contents
+
+def iterate_inx_locales():
+ global current_translation, current_locale
+
+ locales = sorted(os.listdir(locale_dir))
+ for locale in locales:
+ translation = gettext.translation("inkstitch", locale_dir, languages=[locale], fallback=True)
+
+ # L10N If you translate this string, that will tell Ink/Stitch to
+ # generate menu items for this language in Inkscape's "Extensions"
+ # menu.
+ magic_string = N_("Generate INX files")
+ translated_magic_string = translation.gettext(magic_string)
+
+ if translated_magic_string != magic_string or locale == "en_US":
+ current_translation = translation
+ current_locale = locale
+ yield locale