diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/api/install.py | 3 | ||||
| -rw-r--r-- | lib/elements/element.py | 41 | ||||
| -rw-r--r-- | lib/extensions/base.py | 6 | ||||
| -rw-r--r-- | lib/extensions/lettering.py | 16 | ||||
| -rw-r--r-- | lib/extensions/lettering_custom_font_dir.py | 2 | ||||
| -rw-r--r-- | lib/extensions/lettering_generate_json.py | 32 | ||||
| -rw-r--r-- | lib/extensions/lettering_remove_kerning.py | 12 | ||||
| -rw-r--r-- | lib/extensions/params.py | 26 | ||||
| -rw-r--r-- | lib/extensions/zip.py | 2 | ||||
| -rw-r--r-- | lib/gui/simulator.py | 3 | ||||
| -rw-r--r-- | lib/lettering/font.py | 5 | ||||
| -rw-r--r-- | lib/lettering/kerning.py | 25 | ||||
| -rw-r--r-- | lib/stitch_plan/stitch.py | 23 | ||||
| -rw-r--r-- | lib/stitch_plan/stitch_plan.py | 6 | ||||
| -rw-r--r-- | lib/stitch_plan/ties.py | 7 | ||||
| -rw-r--r-- | lib/threads/palette.py | 2 |
16 files changed, 134 insertions, 77 deletions
diff --git a/lib/api/install.py b/lib/api/install.py index 242b55b4..654f8171 100644 --- a/lib/api/install.py +++ b/lib/api/install.py @@ -30,8 +30,11 @@ def palettes(): if sys.platform == "win32": # If we try to just use shutil.copy it says the operation requires elevation. def copy_files(files, dest): + import pythoncom import winutils + pythoncom.CoInitialize() + if not os.path.exists(dest): os.makedirs(dest) diff --git a/lib/elements/element.py b/lib/elements/element.py index b45604d2..9b894d89 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -22,11 +22,12 @@ from ..utils import Point, cache class Patch: """A raw collection of stitches with attached instructions.""" - def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False, stitch_as_is=False): + def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False, tie_modus=0, stitch_as_is=False): self.color = color self.stitches = stitches or [] self.trim_after = trim_after self.stop_after = stop_after + self.tie_modus = tie_modus self.stitch_as_is = stitch_as_is def __add__(self, other): @@ -47,7 +48,8 @@ class Patch: class Param(object): - def __init__(self, name, description, unit=None, values=[], type=None, group=None, inverse=False, default=None, tooltip=None, sort_index=0): + def __init__(self, name, description, unit=None, values=[], type=None, group=None, inverse=False, + options=[], default=None, tooltip=None, sort_index=0): self.name = name self.description = description self.unit = unit @@ -55,6 +57,7 @@ class Param(object): self.type = type self.group = group self.inverse = inverse + self.options = options self.default = default self.tooltip = tooltip self.sort_index = sort_index @@ -79,14 +82,21 @@ class EmbroideryElement(object): def __init__(self, node): self.node = node + # update legacy embroider_ attributes to namespaced attributes legacy_attribs = False for attrib in self.node.attrib: if attrib.startswith('embroider_'): - # update embroider_ attributes to namespaced attributes self.replace_legacy_param(attrib) legacy_attribs = True + # convert legacy tie setting + legacy_tie = self.get_param('ties', None) + if legacy_tie == "True": + self.set_param('ties', 0) + elif legacy_tie == "False": + self.set_param('ties', 3) + + # defaut setting for fill_underlay has changed if legacy_attribs and not self.get_param('fill_underlay', ""): - # defaut setting for fill_underlay has changed self.set_param('fill_underlay', False) @property @@ -239,14 +249,17 @@ class EmbroideryElement(object): @property @param('ties', - _('Ties'), - tooltip=_('Add ties. Manual stitch will not add ties.'), - type='boolean', - default=True, + _('Allow lock stitches'), + tooltip=_('Tie thread at the beginning and/or end of this object. Manual stitch will not add lock stitches.'), + type='dropdown', + # Ties: 0 = Both | 1 = Before | 2 = After | 3 = Neither + # L10N options to allow lock stitch before and after objects + options=[_("Both"), _("Before"), _("After"), _("Neither")], + default=0, sort_index=4) @cache def ties(self): - return self.get_boolean_param("ties", True) + return self.get_int_param("ties", 0) @property def path(self): @@ -312,11 +325,8 @@ class EmbroideryElement(object): def get_command(self, command): commands = self.get_commands(command) - if len(commands) == 1: + if commands: return commands[0] - elif len(commands) > 1: - raise ValueError(_("%(id)s has more than one command of type '%(command)s' linked to it") % - dict(id=self.node.get('id'), command=command)) else: return None @@ -353,9 +363,8 @@ class EmbroideryElement(object): patches = self.to_patches(last_patch) - if not self.ties: - for patch in patches: - patch.stitch_as_is = True + for patch in patches: + patch.tie_modus = self.ties if patches: patches[-1].trim_after = self.has_command("trim") or self.trim_after diff --git a/lib/extensions/base.py b/lib/extensions/base.py index 1d83be59..44c7ec88 100644 --- a/lib/extensions/base.py +++ b/lib/extensions/base.py @@ -155,7 +155,7 @@ class InkstitchExtension(inkex.Effect): # command connectors with a fill color set, will glitch into the elements list if is_command(node) or node.get(CONNECTOR_TYPE): - return[] + return [] if self.svg.selected: if node.get("id") in self.svg.selected: @@ -168,7 +168,9 @@ class InkstitchExtension(inkex.Effect): nodes.extend(self.descendants(child, selected, troubleshoot)) if selected: - if getattr(node, "get_path", None): + if node.tag == SVG_GROUP_TAG: + pass + elif getattr(node, "get_path", None): nodes.append(node) elif troubleshoot and (node.tag in NOT_EMBROIDERABLE_TAGS or node.tag in EMBROIDERABLE_TAGS or is_clone(node)): nodes.append(node) diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py index 8fc3168a..c57348ac 100644 --- a/lib/extensions/lettering.py +++ b/lib/extensions/lettering.py @@ -6,6 +6,7 @@ import json import os import sys +from base64 import b64decode import appdirs import inkex @@ -91,12 +92,17 @@ class LetteringFrame(wx.Frame): "scale": 100 }) - try: - if INKSTITCH_LETTERING in self.group.attrib: + if INKSTITCH_LETTERING in self.group.attrib: + try: self.settings.update(json.loads(self.group.get(INKSTITCH_LETTERING))) - return - except (TypeError, ValueError): - pass + except json.decoder.JSONDecodeError: + # legacy base64 encoded (changed in v2.0) + try: + self.settings.update(json.loads(b64decode(self.group.get(INKSTITCH_LETTERING)))) + except (TypeError, ValueError): + pass + except (TypeError, ValueError): + pass def apply_settings(self): """Make the settings in self.settings visible in the UI.""" diff --git a/lib/extensions/lettering_custom_font_dir.py b/lib/extensions/lettering_custom_font_dir.py index e2af3d47..b73d23a6 100644 --- a/lib/extensions/lettering_custom_font_dir.py +++ b/lib/extensions/lettering_custom_font_dir.py @@ -33,6 +33,8 @@ class LetteringCustomFontDir(InkstitchExtension): config_path = appdirs.user_config_dir('inkstitch') except ImportError: config_path = os.path.expanduser('~/.inkstitch') + if not os.path.exists(config_path): + os.makedirs(config_path) config_path = os.path.join(config_path, 'custom_dirs.json') with open(config_path, 'w', encoding="utf8") as font_data: diff --git a/lib/extensions/lettering_generate_json.py b/lib/extensions/lettering_generate_json.py index fd1eab30..3822b0c2 100644 --- a/lib/extensions/lettering_generate_json.py +++ b/lib/extensions/lettering_generate_json.py @@ -25,9 +25,12 @@ class LetteringGenerateJson(InkstitchExtension): self.arg_parser.add_argument("-s", "--auto-satin", type=Boolean, default="true", dest="auto_satin") self.arg_parser.add_argument("-r", "--reversible", type=Boolean, default="true", dest="reversible") self.arg_parser.add_argument("-g", "--default-glyph", type=str, default="", dest="default_glyph") - self.arg_parser.add_argument("-i", "--min-scale", type=float, default=1, dest="min_scale") - self.arg_parser.add_argument("-a", "--max-scale", type=float, default=1, dest="max_scale") + self.arg_parser.add_argument("-i", "--min-scale", type=float, default=1.0, dest="min_scale") + self.arg_parser.add_argument("-a", "--max-scale", type=float, default=1.0, dest="max_scale") + self.arg_parser.add_argument("-c", "--use-custom-leading", type=Boolean, default="false", dest="use_custom_leading") + self.arg_parser.add_argument("-b", "--use-custom-spacing", type=Boolean, default="false", dest="use_custom_spacing") self.arg_parser.add_argument("-l", "--leading", type=int, default=0, dest="leading") + self.arg_parser.add_argument("-w", "--word-spacing", type=int, default=26, dest="word_spacing") self.arg_parser.add_argument("-p", "--font-file", type=str, default="", dest="path") def effect(self): @@ -43,19 +46,18 @@ class LetteringGenerateJson(InkstitchExtension): horiz_adv_x = kerning.horiz_adv_x() hkern = kerning.hkern() + custom_leading = self.options.use_custom_leading + custom_spacing = self.options.use_custom_spacing word_spacing = kerning.word_spacing() + # use user input in case that the default word spacing is not defined + # in the svg file or the user forces custom values + if custom_spacing or not word_spacing: + word_spacing = self.options.word_spacing letter_spacing = kerning.letter_spacing() - units_per_em = kerning.units_per_em() - # missing_glyph_spacing = kerning.missing_glyph_spacing() - - # if letter spacing returns 0, it hasn't been specified in the font file - # Ink/Stitch will calculate the width of each letter automatically - if letter_spacing == 0: - letter_spacing = None - - # if leading (line height) is set to 0, the font author wants Ink/Stitch to use units_per_em - # if units_per_em is not defined in the font file a default value will be returned - if self.options.leading == 0: + units_per_em = kerning.units_per_em() or self.options.leading + # use units_per_em for leading (line height) if defined in the font file, + # unless the user wishes to overwrite the value + if units_per_em and not custom_leading: leading = units_per_em else: leading = self.options.leading @@ -67,8 +69,8 @@ class LetteringGenerateJson(InkstitchExtension): 'auto_satin': self.options.auto_satin, 'reversible': self.options.reversible, 'default_glyph': self.options.default_glyph, - 'min_scale': self.options.min_scale, - 'max_scale': self.options.max_scale, + 'min_scale': round(self.options.min_scale, 1), + 'max_scale': round(self.options.max_scale, 1), 'horiz_adv_x_default': letter_spacing, 'horiz_adv_x_space': word_spacing, 'units_per_em': units_per_em, diff --git a/lib/extensions/lettering_remove_kerning.py b/lib/extensions/lettering_remove_kerning.py index 1e30e872..21a8eda3 100644 --- a/lib/extensions/lettering_remove_kerning.py +++ b/lib/extensions/lettering_remove_kerning.py @@ -28,8 +28,10 @@ class LetteringRemoveKerning(InkstitchExtension): with open(path, 'r+', encoding="utf-8") as fontfile: svg = etree.parse(fontfile) xpath = ".//svg:font[1]" - kerning = svg.xpath(xpath, namespaces=NSS)[0] - kerning.getparent().remove(kerning) - fontfile.seek(0) - fontfile.write(etree.tostring(svg).decode('utf-8')) - fontfile.truncate() + kerning = svg.xpath(xpath, namespaces=NSS) + if kerning: + kerning = kerning[0] + kerning.getparent().remove(kerning) + fontfile.seek(0) + fontfile.write(etree.tostring(svg).decode('utf-8')) + fontfile.truncate() diff --git a/lib/extensions/params.py b/lib/extensions/params.py index 87b9c3bf..10cc6e3c 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -14,7 +14,7 @@ from itertools import groupby import wx from wx.lib.scrolledpanel import ScrolledPanel -from ..commands import is_command +from ..commands import is_command, is_command_symbol from ..elements import (AutoFill, Clone, EmbroideryElement, Fill, Polyline, SatinColumn, Stroke) from ..elements.clone import is_clone @@ -158,7 +158,11 @@ class ParamsTab(ScrolledPanel): for name, input in self.param_inputs.items(): if input in self.changed_inputs and input != self.toggle_checkbox: - values[name] = input.GetValue() + try: + values[name] = input.GetValue() + except AttributeError: + # dropdown + values[name] = input.GetSelection() return values @@ -188,7 +192,10 @@ class ParamsTab(ScrolledPanel): for name, value in preset_data.items(): if name in self.param_inputs: - self.param_inputs[name].SetValue(value) + try: + self.param_inputs[name].SetValue(value) + except AttributeError: + self.param_inputs[name].SetSelection(int(value)) self.changed_inputs.add(self.param_inputs[name]) self.update_toggle_state() @@ -276,6 +283,10 @@ class ParamsTab(ScrolledPanel): input.SetValue(param.values[0]) input.Bind(wx.EVT_CHECKBOX, self.changed) + elif param.type == 'dropdown': + input = wx.Choice(self, wx.ID_ANY, choices=param.options) + input.SetSelection(int(param.values[0])) + input.Bind(wx.EVT_CHOICE, self.changed) elif len(param.values) > 1: input = wx.ComboBox(self, wx.ID_ANY, choices=sorted(str(value) for value in param.values), style=wx.CB_DROPDOWN) input.Bind(wx.EVT_COMBOBOX, self.changed) @@ -454,6 +465,13 @@ class SettingsFrame(wx.Frame): sizer_1.Add(sizer_3, 0, wx.ALIGN_RIGHT, 0) self.SetSizer(sizer_1) sizer_1.Fit(self) + + # prevent the param dialog to become smaller than 500*400 + # otherwise the scrollbar jumps to the side and produces a + # deprecation warning in wxpythons scrolledpanel.py line 225 - + # which is expecting an integer, but uses previously a division + self.SetSizeHints(500, 400) + self.Layout() # end wxGlade @@ -471,7 +489,7 @@ class Params(InkstitchExtension): element = EmbroideryElement(node) classes = [] - if not is_command(node): + if not is_command(node) and not is_command_symbol(node): if node.tag == SVG_POLYLINE_TAG: classes.append(Polyline) elif is_clone(node): diff --git a/lib/extensions/zip.py b/lib/extensions/zip.py index d0c56ba2..605b4573 100644 --- a/lib/extensions/zip.py +++ b/lib/extensions/zip.py @@ -60,7 +60,7 @@ class Zip(InkstitchExtension): svg.write(etree.tostring(document).decode('utf-8')) elif format == 'threadlist': output_file = os.path.join(path, "%s_%s.txt" % (base_file_name, _("threadlist"))) - output = open(output_file, 'w') + output = open(output_file, 'w', encoding='utf-8') output.write(self.get_threadlist(stitch_plan, base_file_name)) output.close() else: diff --git a/lib/gui/simulator.py b/lib/gui/simulator.py index c4100804..27b9a97e 100644 --- a/lib/gui/simulator.py +++ b/lib/gui/simulator.py @@ -11,10 +11,10 @@ from threading import Event, Thread import wx from wx.lib.intctrl import IntCtrl +from .dialogs import info_dialog from ..i18n import _ from ..stitch_plan import patches_to_stitch_plan, stitch_plan_from_file from ..svg import PIXELS_PER_MM -from .dialogs import info_dialog # L10N command label at bottom of simulator window COMMAND_NAMES = [_("STITCH"), _("JUMP"), _("TRIM"), _("STOP"), _("COLOR CHANGE")] @@ -686,7 +686,6 @@ class EmbroiderySimulator(wx.Frame): stitch_plan = kwargs.pop('stitch_plan', None) stitches_per_second = kwargs.pop('stitches_per_second', 16) target_duration = kwargs.pop('target_duration', None) - size = kwargs.get('size', (0, 0)) wx.Frame.__init__(self, *args, **kwargs) self.statusbar = self.CreateStatusBar(2) self.statusbar.SetStatusWidths([250, -1]) diff --git a/lib/lettering/font.py b/lib/lettering/font.py index 7b479072..b1979067 100644 --- a/lib/lettering/font.py +++ b/lib/lettering/font.py @@ -14,7 +14,6 @@ from ..elements import nodes_to_elements from ..exceptions import InkstitchException from ..i18n import _, get_languages from ..stitches.auto_satin import auto_satin -from ..svg import PIXELS_PER_MM from ..svg.tags import INKSCAPE_LABEL, SVG_GROUP_TAG, SVG_PATH_TAG from ..utils import Point from .font_variant import FontVariant @@ -108,7 +107,7 @@ class Font(object): name = localized_font_metadata('name', '') description = localized_font_metadata('description', '') default_glyph = font_metadata('defalt_glyph', "�") - leading = font_metadata('leading', 5, multiplier=PIXELS_PER_MM) + leading = font_metadata('leading', 100) kerning_pairs = font_metadata('kerning_pairs', {}) auto_satin = font_metadata('auto_satin', True) min_scale = font_metadata('min_scale', 1.0) @@ -124,7 +123,7 @@ class Font(object): horiz_adv_x_default = font_metadata('horiz_adv_x_default') # Define by <glyph glyph-name="space" unicode=" " horiz-adv-x="22" />, Example font.json : "horiz_adv_x_space":22, - word_spacing = font_metadata('horiz_adv_x_space', 0) + word_spacing = font_metadata('horiz_adv_x_space', 20) reversible = font_metadata('reversible', True) diff --git a/lib/lettering/kerning.py b/lib/lettering/kerning.py index 6db9ba1e..6380f1a6 100644 --- a/lib/lettering/kerning.py +++ b/lib/lettering/kerning.py @@ -12,7 +12,7 @@ class FontKerning(object): This class reads kerning information from an SVG file """ def __init__(self, path): - with open(path) as svg: + with open(path, 'r', encoding="utf-8") as svg: self.svg = etree.parse(svg) # horiz_adv_x defines the wdith of specific letters (distance to next letter) @@ -51,21 +51,30 @@ class FontKerning(object): # the space character def word_spacing(self): xpath = "string(.//svg:glyph[@glyph-name='space'][1]/@*[name()='horiz-adv-x'])" - word_spacing = self.svg.xpath(xpath, namespaces=NSS) or 26 - return int(word_spacing) + word_spacing = self.svg.xpath(xpath, namespaces=NSS) + try: + return int(word_spacing) + except ValueError: + return None # default letter spacing def letter_spacing(self): xpath = "string(.//svg:font[@horiz-adv-x][1]/@*[name()='horiz-adv-x'])" - letter_spacing = self.svg.xpath(xpath, namespaces=NSS) or 0 - return int(letter_spacing) + letter_spacing = self.svg.xpath(xpath, namespaces=NSS) + try: + return int(letter_spacing) + except ValueError: + return None # this value will be saved into the json file to preserve it for later font edits # additionally it serves to automatically define the line height (leading) - def units_per_em(self, default=100): + def units_per_em(self): xpath = "string(.//svg:font-face[@units-per-em][1]/@*[name()='units-per-em'])" - units_per_em = self.svg.xpath(xpath, namespaces=NSS) or default - return int(units_per_em) + units_per_em = self.svg.xpath(xpath, namespaces=NSS) + try: + return int(units_per_em) + except ValueError: + return None """ def missing_glyph_spacing(self): diff --git a/lib/stitch_plan/stitch.py b/lib/stitch_plan/stitch.py index a5938c7b..ae6fa480 100644 --- a/lib/stitch_plan/stitch.py +++ b/lib/stitch_plan/stitch.py @@ -7,7 +7,7 @@ from ..utils.geometry import Point class Stitch(Point): - def __init__(self, x, y=None, color=None, jump=False, stop=False, trim=False, color_change=False, no_ties=False): + def __init__(self, x, y=None, color=None, jump=False, stop=False, trim=False, color_change=False, tie_modus=0, no_ties=False): self.x = x self.y = y self.color = color @@ -15,6 +15,7 @@ class Stitch(Point): self.trim = trim self.stop = stop self.color_change = color_change + self.tie_modus = tie_modus self.no_ties = no_ties # Allow creating a Stitch from a Point @@ -24,18 +25,18 @@ class Stitch(Point): self.y = point.y def __repr__(self): - return "Stitch(%s, %s, %s, %s, %s, %s, %s, %s)" % (self.x, - self.y, - self.color, - "JUMP" if self.jump else " ", - "TRIM" if self.trim else " ", - "STOP" if self.stop else " ", - "NO TIES" if self.no_ties else " ", - "COLOR CHANGE" if self.color_change else " " - ) + return "Stitch(%s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.x, + self.y, + self.color, + "JUMP" if self.jump else " ", + "TRIM" if self.trim else " ", + "STOP" if self.stop else " ", + "TIE MODUS" if self.tie_modus else " ", + "NO TIES" if self.no_ties else " ", + "COLOR CHANGE" if self.color_change else " ") def copy(self): - return Stitch(self.x, self.y, self.color, self.jump, self.stop, self.trim, self.color_change, self.no_ties) + return Stitch(self.x, self.y, self.color, self.jump, self.stop, self.trim, self.color_change, self.tie_modus, self.no_ties) def __json__(self): return vars(self) diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py index 47e8b203..01463aba 100644 --- a/lib/stitch_plan/stitch_plan.py +++ b/lib/stitch_plan/stitch_plan.py @@ -19,7 +19,9 @@ def patches_to_stitch_plan(patches, collapse_len=None, disable_ties=False): * adds jump-stitches between patches if necessary """ - collapse_len = (collapse_len or 3.0) * PIXELS_PER_MM + if collapse_len is None: + collapse_len = 3.0 + collapse_len = collapse_len * PIXELS_PER_MM stitch_plan = StitchPlan() color_block = stitch_plan.new_color_block(color=patches[0].color) @@ -45,7 +47,7 @@ def patches_to_stitch_plan(patches, collapse_len=None, disable_ties=False): if len(color_block) and (patch.stitches[0] - color_block.stitches[-1]).length() > collapse_len: color_block.add_stitch(patch.stitches[0], jump=True) - color_block.add_stitches(patch.stitches, no_ties=patch.stitch_as_is) + color_block.add_stitches(stitches=patch.stitches, tie_modus=patch.tie_modus, no_ties=patch.stitch_as_is) if patch.trim_after: color_block.add_stitch(trim=True) diff --git a/lib/stitch_plan/ties.py b/lib/stitch_plan/ties.py index d54b0f0f..c649ee44 100644 --- a/lib/stitch_plan/ties.py +++ b/lib/stitch_plan/ties.py @@ -36,11 +36,14 @@ def add_tie(stitches, tie_path): def add_tie_off(stitches): - add_tie(stitches, stitches[-1:-3:-1]) + # tie_modus: 0 = both | 1 = before | 2 = after | 3 = neither + if stitches[-1].tie_modus not in [1, 3]: + add_tie(stitches, stitches[-1:-3:-1]) def add_tie_in(stitches, upcoming_stitches): - add_tie(stitches, upcoming_stitches) + if stitches[0].tie_modus not in [2, 3]: + add_tie(stitches, upcoming_stitches) def add_ties(stitch_plan): diff --git a/lib/threads/palette.py b/lib/threads/palette.py index f1ff6cb4..29d2a6dd 100644 --- a/lib/threads/palette.py +++ b/lib/threads/palette.py @@ -62,7 +62,7 @@ class ThreadPalette(Set): thread = ThreadColor(thread_color, thread_name, thread_number, manufacturer=self.name) self.threads[thread] = convert_color(sRGBColor(*thread_color, is_upscaled=True), LabColor) - except ValueError: + except (ValueError, IndexError): continue def __contains__(self, thread): |
