summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaalleen <36401965+kaalleen@users.noreply.github.com>2021-10-09 18:25:29 +0200
committerGitHub <noreply@github.com>2021-10-09 18:25:29 +0200
commit5a1ad7e4e8bebb31a679674ed8b4ca526138695c (patch)
tree254f906a6524abd3cb9a685975e563f09790bbaa
parent0224794a0815f22a719d2880e175077b77214513 (diff)
Letters to font extension (#1312)
-rw-r--r--lib/extensions/__init__.py2
-rw-r--r--lib/extensions/input.py63
-rw-r--r--lib/extensions/lettering.py2
-rw-r--r--lib/extensions/letters_to_font.py81
-rw-r--r--lib/lettering/font.py38
-rw-r--r--lib/lettering/font_variant.py29
-rw-r--r--lib/lettering/glyph.py55
-rw-r--r--lib/stitch_plan/__init__.py7
-rw-r--r--lib/stitch_plan/generate_stitch_plan.py74
-rw-r--r--lib/svg/guides.py16
-rw-r--r--lib/svg/tags.py1
-rw-r--r--templates/letters_to_font.xml40
12 files changed, 323 insertions, 85 deletions
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py
index 83a522f2..ec21b592 100644
--- a/lib/extensions/__init__.py
+++ b/lib/extensions/__init__.py
@@ -24,6 +24,7 @@ from .lettering import Lettering
from .lettering_custom_font_dir import LetteringCustomFontDir
from .lettering_generate_json import LetteringGenerateJson
from .lettering_remove_kerning import LetteringRemoveKerning
+from .letters_to_font import LettersToFont
from .object_commands import ObjectCommands
from .output import Output
from .params import Params
@@ -55,6 +56,7 @@ __all__ = extensions = [StitchPlanPreview,
LetteringGenerateJson,
LetteringRemoveKerning,
LetteringCustomFontDir,
+ LettersToFont,
Troubleshoot,
RemoveEmbroiderySettings,
Cleanup,
diff --git a/lib/extensions/input.py b/lib/extensions/input.py
index a8b8bee3..066b9003 100644
--- a/lib/extensions/input.py
+++ b/lib/extensions/input.py
@@ -3,70 +3,13 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-import os
-import sys
-
-import inkex
from lxml import etree
-import pyembroidery
-
-from ..i18n import _
-from ..stitch_plan import StitchPlan
-from ..svg import PIXELS_PER_MM, render_stitch_plan
-from ..svg.tags import INKSCAPE_LABEL
+from ..stitch_plan import generate_stitch_plan
class Input(object):
def run(self, args):
embroidery_file = args[0]
- self.validate_file_path(embroidery_file)
-
- pattern = pyembroidery.read(embroidery_file)
- stitch_plan = StitchPlan()
- color_block = None
-
- for raw_stitches, thread in pattern.get_as_colorblocks():
- color_block = stitch_plan.new_color_block(thread)
- for x, y, command in raw_stitches:
- if command == pyembroidery.STITCH:
- color_block.add_stitch(x * PIXELS_PER_MM / 10.0, y * PIXELS_PER_MM / 10.0)
- if len(color_block) > 0:
- if command == pyembroidery.TRIM:
- color_block.add_stitch(trim=True)
- elif command == pyembroidery.STOP:
- color_block.add_stitch(stop=True)
- color_block = stitch_plan.new_color_block(thread)
-
- stitch_plan.delete_empty_color_blocks()
-
- if stitch_plan.last_color_block:
- if stitch_plan.last_color_block.last_stitch:
- if stitch_plan.last_color_block.last_stitch.stop:
- # ending with a STOP command is redundant, so remove it
- del stitch_plan.last_color_block[-1]
-
- extents = stitch_plan.extents
- 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),
- })
- render_stitch_plan(svg, stitch_plan)
-
- # rename the Stitch Plan layer so that it doesn't get overwritten by Embroider
- layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']")
- layer.set(INKSCAPE_LABEL, os.path.basename(embroidery_file))
- layer.attrib.pop('id')
-
- # Shift the design so that its origin is at the center of the canvas
- # Note: this is NOT the same as centering the design in the canvas!
- layer.set('transform', 'translate(%s,%s)' % (extents[0], extents[1]))
-
- print(etree.tostring(svg).decode('utf-8'))
-
- def validate_file_path(self, path):
- # Check if the file exists
- if not os.path.isfile(path):
- inkex.errormsg(_('File does not exist and cannot be opened. Please correct the file path and try again.\r%s') % path)
- sys.exit(1)
+ stitch_plan = generate_stitch_plan(embroidery_file)
+ print(etree.tostring(stitch_plan).decode('utf-8'))
diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py
index 312a47ce..35509eb6 100644
--- a/lib/extensions/lettering.py
+++ b/lib/extensions/lettering.py
@@ -174,7 +174,7 @@ class LetteringFrame(wx.Frame):
image.Rescale(300, 20, quality=wx.IMAGE_QUALITY_HIGH)
self.font_chooser.Append(font.marked_custom_font_name, wx.Bitmap(image))
else:
- self.font_chooser.Append(font.name)
+ self.font_chooser.Append(font.marked_custom_font_name)
def get_font_descriptions(self):
return {font.name: font.description for font in self.fonts.values()}
diff --git a/lib/extensions/letters_to_font.py b/lib/extensions/letters_to_font.py
new file mode 100644
index 00000000..158d0d9f
--- /dev/null
+++ b/lib/extensions/letters_to_font.py
@@ -0,0 +1,81 @@
+# 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
+from pathlib import Path
+
+import inkex
+from inkex import errormsg
+
+from ..commands import ensure_symbol
+from ..i18n import _
+from ..stitch_plan import generate_stitch_plan
+from ..svg import get_correction_transform
+from ..svg.tags import INKSCAPE_GROUPMODE, INKSCAPE_LABEL, SVG_PATH_TAG
+from .base import InkstitchExtension
+
+
+class LettersToFont(InkstitchExtension):
+ '''
+ This extension will create a json file to store a custom directory path for additional user fonts
+ '''
+ def __init__(self, *args, **kwargs):
+ InkstitchExtension.__init__(self, *args, **kwargs)
+ self.arg_parser.add_argument("-d", "--font-dir", type=str, default="", dest="font_dir")
+ self.arg_parser.add_argument("-f", "--file-format", type=str, default="", dest="file_format")
+ self.arg_parser.add_argument("-c", "--import-commands", type=inkex.Boolean, default=False, dest="import_commands")
+
+ def effect(self):
+ font_dir = self.options.font_dir
+ file_format = self.options.file_format
+
+ if not os.path.isdir(font_dir):
+ errormsg(_("Font directory not found. Please specify an existing directory."))
+
+ glyphs = list(Path(font_dir).rglob(file_format))
+ if not glyphs:
+ glyphs = list(Path(font_dir).rglob(file_format.lower()))
+
+ document = self.document.getroot()
+ for glyph in glyphs:
+ letter = self.get_glyph_element(glyph)
+ label = "GlyphLayer-%s" % letter.get(INKSCAPE_LABEL, ' ').split('.')[0][-1]
+ group = inkex.Group(attrib={
+ INKSCAPE_LABEL: label,
+ INKSCAPE_GROUPMODE: "layer",
+ "transform": get_correction_transform(document, child=True)
+ })
+
+ # remove color block groups if we import without commands
+ # there will only be one object per color block anyway
+ if not self.options.import_commands:
+ for element in letter.iter(SVG_PATH_TAG):
+ group.insert(0, element)
+ else:
+ group.insert(0, letter)
+
+ document.insert(0, group)
+ group.set('style', 'display:none')
+
+ # users may be confused if they get an empty document
+ # make last letter visible again
+ group.set('style', None)
+
+ # In most cases trims are inserted with the imported letters.
+ # Let's make sure the trim symbol exists in the defs section
+ ensure_symbol(document, 'trim')
+
+ self.insert_baseline(document)
+
+ def get_glyph_element(self, glyph):
+ stitch_plan = generate_stitch_plan(str(glyph), self.options.import_commands)
+ # we received a stitch plan wrapped in an svg document, we only need the stitch_plan group
+ # this group carries the name of the file, so we can search for it.
+ stitch_plan = stitch_plan.xpath('.//*[@inkscape:label="%s"]' % os.path.basename(glyph), namespaces=inkex.NSS)[0]
+ stitch_plan.attrib.pop(INKSCAPE_GROUPMODE)
+ return stitch_plan
+
+ def insert_baseline(self, document):
+ document.namedview.new_guide(position=0.0, name="baseline")
diff --git a/lib/lettering/font.py b/lib/lettering/font.py
index ff726a56..cd94e3a7 100644
--- a/lib/lettering/font.py
+++ b/lib/lettering/font.py
@@ -6,15 +6,18 @@
import json
import os
from copy import deepcopy
+from random import randint
import inkex
+from ..commands import ensure_symbol
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_PATH_TAG
+from ..svg.tags import (CONNECTION_END, CONNECTION_START, INKSCAPE_LABEL,
+ SVG_PATH_TAG, SVG_USE_TAG, XLINK_HREF)
from ..utils import Point
from .font_variant import FontVariant
@@ -220,6 +223,8 @@ class Font(object):
element.set('style', '%s%s%s' % (style.to_str(), stroke_width, dash_array))
+ self._ensure_command_symbols(destination_group)
+
return destination_group
def get_variant(self, variant):
@@ -303,8 +308,39 @@ class Font(object):
position.x += self.horiz_adv_x.get(character, horiz_adv_x_default) - glyph.min_x
+ self._update_commands(node, glyph)
+
return node
+ def _update_commands(self, node, glyph):
+ for element, connectors in glyph.commands.items():
+ # update element
+ el = node.find(".//*[@id='%s']" % element)
+ # we cannot get a unique id from the document at this point
+ # so let's create a random id which will most probably work as well
+ new_element_id = "%s_%s" % (element, randint(0, 9999))
+ el.set_id(new_element_id)
+ for connector, symbol in connectors:
+ # update symbol
+ new_symbol_id = "%s_%s" % (symbol, randint(0, 9999))
+ s = node.find(".//*[@id='%s']" % symbol)
+ s.set_id(new_symbol_id)
+ # update connector
+ c = node.find(".//*[@id='%s']" % connector)
+ c.set(CONNECTION_END, "#%s" % new_element_id)
+ c.set(CONNECTION_START, "#%s" % new_symbol_id)
+
+ def _ensure_command_symbols(self, group):
+ # collect commands
+ commands = set()
+ for element in group.iterdescendants(SVG_USE_TAG):
+ xlink = element.get(XLINK_HREF, ' ')
+ if xlink.startswith('#inkstitch_'):
+ commands.add(xlink[11:])
+ # make sure all necessary command symbols are in the document
+ for command in commands:
+ ensure_symbol(group.getroottree().getroot(), command)
+
def _apply_auto_satin(self, group, trim):
"""Apply Auto-Satin to an SVG XML node tree with an svg:g at its root.
diff --git a/lib/lettering/font_variant.py b/lib/lettering/font_variant.py
index d9d8ed44..a7f353fe 100644
--- a/lib/lettering/font_variant.py
+++ b/lib/lettering/font_variant.py
@@ -7,7 +7,8 @@ import os
import inkex
-from ..svg.tags import INKSCAPE_GROUPMODE, INKSCAPE_LABEL
+from ..svg.tags import (INKSCAPE_GROUPMODE, INKSCAPE_LABEL, SVG_GROUP_TAG,
+ SVG_PATH_TAG, SVG_USE_TAG)
from .glyph import Glyph
@@ -60,7 +61,8 @@ class FontVariant(object):
def _load_glyphs(self):
svg_path = os.path.join(self.path, "%s.svg" % self.variant)
- svg = inkex.load_svg(svg_path)
+ svg = inkex.load_svg(svg_path).getroot()
+ svg = self._apply_transforms(svg)
glyph_layers = svg.xpath(".//svg:g[starts-with(@inkscape:label, 'GlyphLayer-')]", namespaces=inkex.NSS)
for layer in glyph_layers:
@@ -79,6 +81,29 @@ class FontVariant(object):
group.style.pop('display', None)
group.attrib.pop('display', None)
+ def _apply_transforms(self, svg):
+ # apply transforms to paths and use tags
+ for element in svg.iterdescendants((SVG_PATH_TAG, SVG_USE_TAG)):
+ transform = element.composed_transform()
+ if element.tag == SVG_PATH_TAG:
+ path = element.path.transform(transform)
+ element.set_path(path)
+ element.attrib.pop("transform", None)
+
+ if element.tag == SVG_USE_TAG:
+ oldx = element.get('x', 0)
+ oldy = element.get('y', 0)
+ newx, newy = transform.apply_to_point((oldx, oldy))
+ element.set('x', newx)
+ element.set('y', newy)
+ element.attrib.pop("transform", None)
+
+ # remove transforms after they have been applied
+ for group in svg.iterdescendants(SVG_GROUP_TAG):
+ group.attrib.pop('transform', None)
+
+ return svg
+
def __getitem__(self, character):
if character in self.glyphs:
return self.glyphs[character]
diff --git a/lib/lettering/glyph.py b/lib/lettering/glyph.py
index 047c12cf..f50d3bb4 100644
--- a/lib/lettering/glyph.py
+++ b/lib/lettering/glyph.py
@@ -5,10 +5,11 @@
from copy import copy
-from inkex import paths, transforms
+from inkex import paths, transforms, units
-from ..svg import get_guides
-from ..svg.tags import SVG_GROUP_TAG, SVG_PATH_TAG
+from ..svg import get_correction_transform, get_guides
+from ..svg.tags import (CONNECTION_END, SVG_GROUP_TAG, SVG_PATH_TAG,
+ SVG_USE_TAG, XLINK_HREF)
class Glyph(object):
@@ -38,6 +39,7 @@ class Glyph(object):
self.node = self._process_group(group)
self._process_bbox()
self._move_to_origin()
+ self._process_commands()
def _process_group(self, group):
new_group = copy(group)
@@ -50,13 +52,21 @@ class Glyph(object):
new_group.append(self._process_group(node))
else:
node_copy = copy(node)
+ transform = -transforms.Transform(get_correction_transform(node, True))
if "d" in node.attrib:
- 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.
- node_copy.attrib.pop('transform', None)
+ node_copy.path = node.path.transform(transform).to_absolute()
+
+ if not node.tag == SVG_USE_TAG:
+ # Delete transforms from paths and groups, since we applied
+ # them to the paths already.
+ node_copy.attrib.pop('transform', None)
+ else:
+ oldx = node.get('x', 0)
+ oldy = node.get('y', 0)
+ x, y = transform.apply_to_point((oldx, oldy))
+ node_copy.set('x', x)
+ node_copy.set('y', y)
new_group.append(node_copy)
@@ -72,11 +82,30 @@ class Glyph(object):
self.baseline = 0
def _process_bbox(self):
- bbox = [paths.Path(node.get("d")).bounding_box() for node in self.node.iterdescendants(SVG_PATH_TAG)]
+ bbox = [paths.Path(node.get("d")).bounding_box() for node in self.node.iterdescendants(SVG_PATH_TAG) if not node.get(CONNECTION_END, None)]
left, right = min([box.left for box in bbox]), max([box.right for box in bbox])
self.width = right - left
self.min_x = left
+ def _process_commands(self):
+ # Save object ids with commmands in a dictionary: {object_id: [connector_id, symbol_id]}
+ self.commands = {}
+
+ for node in self.node.iter(SVG_USE_TAG):
+ xlink = node.get(XLINK_HREF, ' ')
+ if not xlink.startswith('#inkstitch_'):
+ continue
+
+ try:
+ connector = self.node.xpath(".//*[@inkscape:connection-start='#%s']" % node.get('id', ' '))[0]
+ command_object = connector.get(CONNECTION_END)[1:]
+ try:
+ self.commands[command_object].append([connector.get_id(), node.get_id()])
+ except KeyError:
+ self.commands[command_object] = [[connector.get_id(), node.get_id()]]
+ except IndexError:
+ pass
+
def _move_to_origin(self):
translate_x = -self.min_x
translate_y = -self.baseline
@@ -87,3 +116,11 @@ class Glyph(object):
path = path.transform(transform)
node.set('d', str(path))
node.attrib.pop('transform', None)
+
+ # Move commands as well
+ for node in self.node.iter(SVG_USE_TAG):
+ oldx = units.convert_unit(node.get("x", 0), 'px', node.unit)
+ oldy = units.convert_unit(node.get("y", 0), 'px', node.unit)
+ x, y = transform.apply_to_point((oldx, oldy))
+ node.set('x', x)
+ node.set('y', y)
diff --git a/lib/stitch_plan/__init__.py b/lib/stitch_plan/__init__.py
index d4b43ace..9764e66a 100644
--- a/lib/stitch_plan/__init__.py
+++ b/lib/stitch_plan/__init__.py
@@ -3,8 +3,9 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-from .stitch_plan import stitch_groups_to_stitch_plan, StitchPlan
from .color_block import ColorBlock
-from .stitch_group import StitchGroup
-from .stitch import Stitch
+from .generate_stitch_plan import generate_stitch_plan
from .read_file import stitch_plan_from_file
+from .stitch import Stitch
+from .stitch_group import StitchGroup
+from .stitch_plan import StitchPlan, stitch_groups_to_stitch_plan
diff --git a/lib/stitch_plan/generate_stitch_plan.py b/lib/stitch_plan/generate_stitch_plan.py
new file mode 100644
index 00000000..2d8ceeff
--- /dev/null
+++ b/lib/stitch_plan/generate_stitch_plan.py
@@ -0,0 +1,74 @@
+# 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 os
+import sys
+
+import inkex
+
+import pyembroidery
+
+from ..i18n import _
+from ..svg import PIXELS_PER_MM, render_stitch_plan
+from ..svg.tags import INKSCAPE_LABEL
+from .stitch import Stitch
+from .stitch_plan import StitchPlan
+
+
+def generate_stitch_plan(embroidery_file, import_commands=True): # noqa: C901
+ validate_file_path(embroidery_file)
+ pattern = pyembroidery.read(embroidery_file)
+ stitch_plan = StitchPlan()
+ color_block = None
+
+ for raw_stitches, thread in pattern.get_as_colorblocks():
+ color_block = stitch_plan.new_color_block(thread)
+ for x, y, command in raw_stitches:
+ if command == pyembroidery.STITCH:
+ color_block.add_stitch(Stitch(x * PIXELS_PER_MM / 10.0, y * PIXELS_PER_MM / 10.0))
+ if len(color_block) > 0:
+ if not import_commands and command in [pyembroidery.TRIM, pyembroidery.STOP]:
+ # Importing commands is not wanted:
+ # start a new color block without inserting the command
+ color_block = stitch_plan.new_color_block(thread)
+ elif command == pyembroidery.TRIM:
+ color_block.add_stitch(trim=True)
+ elif command == pyembroidery.STOP:
+ color_block.add_stitch(stop=True)
+ color_block = stitch_plan.new_color_block(thread)
+
+ stitch_plan.delete_empty_color_blocks()
+
+ if stitch_plan.last_color_block:
+ if stitch_plan.last_color_block.last_stitch:
+ if stitch_plan.last_color_block.last_stitch.stop:
+ # ending with a STOP command is redundant, so remove it
+ del stitch_plan.last_color_block[-1]
+
+ extents = stitch_plan.extents
+ 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),
+ })
+ render_stitch_plan(svg, stitch_plan)
+
+ # rename the Stitch Plan layer so that it doesn't get overwritten by Embroider
+ layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']")
+ layer.set(INKSCAPE_LABEL, os.path.basename(embroidery_file))
+ layer.attrib.pop('id')
+
+ # Shift the design so that its origin is at the center of the canvas
+ # Note: this is NOT the same as centering the design in the canvas!
+ layer.set('transform', 'translate(%s,%s)' % (extents[0], extents[1]))
+
+ return svg
+
+
+def validate_file_path(path):
+ # Check if the file exists
+ if not os.path.isfile(path):
+ inkex.errormsg(_('File does not exist and cannot be opened. Please correct the file path and try again.\r%s') % path)
+ sys.exit(1)
diff --git a/lib/svg/guides.py b/lib/svg/guides.py
index e492a90d..3329940e 100644
--- a/lib/svg/guides.py
+++ b/lib/svg/guides.py
@@ -3,11 +3,10 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-from inkex import transforms
+from inkex.units import convert_unit
from ..utils import Point, cache, string_to_floats
from .tags import INKSCAPE_LABEL, SODIPODI_GUIDE, SODIPODI_NAMEDVIEW
-from .units import get_doc_size, get_viewbox_transform
class InkscapeGuide(object):
@@ -20,16 +19,15 @@ class InkscapeGuide(object):
def _parse(self):
self.label = self.node.get(INKSCAPE_LABEL, "")
- doc_size = list(get_doc_size(self.svg))
-
- # convert the size from viewbox-relative to real-world pixels
- viewbox_transform = get_viewbox_transform(self.svg)
- viewbox_transform = transforms.Transform(-transforms.Transform(viewbox_transform)).apply_to_point(doc_size)
+ doc_size = self.svg.get_page_bbox()
+ # inkscape's Y axis is reversed from SVG's, and the guide is in inkscape coordinates
self.position = Point(*string_to_floats(self.node.get('position')))
+ self.position.y = doc_size.y.size - self.position.y
- # inkscape's Y axis is reversed from SVG's, and the guide is in inkscape coordinates
- self.position.y = doc_size[1] - self.position.y
+ # convert units to px
+ unit = self.svg.unit
+ self.position.y = convert_unit(self.position.y, 'px', unit)
# This one baffles me. I think inkscape might have gotten the order of
# their vector wrong?
diff --git a/lib/svg/tags.py b/lib/svg/tags.py
index 3130bc16..0dc027ea 100644
--- a/lib/svg/tags.py
+++ b/lib/svg/tags.py
@@ -28,6 +28,7 @@ INKSCAPE_GROUPMODE = inkex.addNS('groupmode', 'inkscape')
CONNECTION_START = inkex.addNS('connection-start', 'inkscape')
CONNECTION_END = inkex.addNS('connection-end', 'inkscape')
CONNECTOR_TYPE = inkex.addNS('connector-type', 'inkscape')
+INKSCAPE_DOCUMENT_UNITS = inkex.addNS('document-units', 'inkscape')
XLINK_HREF = inkex.addNS('href', 'xlink')
diff --git a/templates/letters_to_font.xml b/templates/letters_to_font.xml
new file mode 100644
index 00000000..e1efd856
--- /dev/null
+++ b/templates/letters_to_font.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+ <name>Letters to font</name>
+ <id>org.inkstitch.letters_to_font</id>
+ <param name="extension" type="string" gui-hidden="true">letters_to_font</param>
+ <effect needs-live-preview="false">
+ <object-type>all</object-type>
+ <effects-menu>
+ <submenu name="Ink/Stitch">
+ <submenu name="Font Management" />
+ </submenu>
+ </effects-menu>
+ </effect>
+ <param name="header" type="description" appearance="header" indent="1" >
+ Includes all letters of a predigitized embroidery font (one file for each letter) into the document in order to make it available for the Ink/Stitch lettering system.
+ </param>
+ <param name="file-description" type="description" indent="1" >
+ Embroidery files need to have the name of the letter right before the file extension. E.g. A.dst or Example_Font_A.dst will be recognized as the letter A.
+ </param>
+ <separator />
+ <spacer />
+ <param name="file-format" type="optiongroup" appearance="combo" gui-text="File format" indent="1">
+ {% for format, description, mimetype, category in formats %}
+ <option value="*.{{ format | upper }}">{{ format | upper }}</option>
+ {% endfor %}
+ </param>
+ <param type="path" name="font-dir" gui-text="Font directory" indent="1" mode="folder" filetypes="svg"/>
+ <spacer />
+ <param type="boolean" name="import-commands" gui-text="Import commands" indent="1">false</param>
+ <spacer />
+ <separator />
+ <param name="file-description" type="description" indent="1" >
+ &#9888; After running this function, drag the baseline into the desired position and place the letters accordingly.
+ Save your font in a separate folder. Then generate the json file (with "Autoroute Satin" unchecked).
+ </param>
+ <separator />
+ <script>
+ {{ command_tag | safe }}
+ </script>
+</inkscape-extension>