diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/extensions/__init__.py | 2 | ||||
| -rw-r--r-- | lib/extensions/lettering.py | 88 | ||||
| -rw-r--r-- | lib/extensions/lettering_generate_json.py | 36 | ||||
| -rw-r--r-- | lib/extensions/lettering_update_json_glyphlist.py | 41 | ||||
| -rwxr-xr-x | lib/inx/extensions.py | 2 | ||||
| -rw-r--r-- | lib/lettering/categories.py | 30 | ||||
| -rw-r--r-- | lib/lettering/font.py | 2 | ||||
| -rw-r--r-- | lib/lettering/font_info.py (renamed from lib/lettering/kerning.py) | 14 |
8 files changed, 183 insertions, 32 deletions
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index 25d3214c..d0900f2d 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -32,6 +32,7 @@ from .lettering_custom_font_dir import LetteringCustomFontDir from .lettering_force_lock_stitches import LetteringForceLockStitches from .lettering_generate_json import LetteringGenerateJson from .lettering_remove_kerning import LetteringRemoveKerning +from .lettering_update_json_glyphlist import LetteringUpdateJsonGlyphlist from .letters_to_font import LettersToFont from .object_commands import ObjectCommands from .object_commands_toggle_visibility import ObjectCommandsToggleVisibility @@ -84,6 +85,7 @@ __all__ = extensions = [StitchPlanPreview, AutoRun, Lettering, LetteringGenerateJson, + LetteringUpdateJsonGlyphlist, LetteringRemoveKerning, LetteringCustomFontDir, LetteringForceLockStitches, diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py index fec48100..0879c30f 100644 --- a/lib/extensions/lettering.py +++ b/lib/extensions/lettering.py @@ -18,13 +18,14 @@ from ..elements import nodes_to_elements from ..gui import PresetsPanel, SimulatorPreview, info_dialog from ..i18n import _ from ..lettering import Font, FontError +from ..lettering.categories import FONT_CATEGORIES, FontCategory 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, get_resource_dir +from ..utils.threading import ExitThread from .commands import CommandsExtension from .lettering_custom_font_dir import get_custom_font_dir -from ..utils.threading import ExitThread class LetteringFrame(wx.Frame): @@ -52,19 +53,32 @@ class LetteringFrame(wx.Frame): self.font_chooser = wx.adv.BitmapComboBox(self, wx.ID_ANY, style=wx.CB_READONLY | wx.CB_SORT) self.font_chooser.Bind(wx.EVT_COMBOBOX, self.on_font_changed) - self.font_filter = fs.FloatSpin(self, min_val=0, max_val=None, increment=1, value="0") - self.font_filter.SetFormat("%f") - self.font_filter.SetDigits(2) - self.font_filter.Bind(fs.EVT_FLOATSPIN, self.on_filter_changed) - self.font_filter.SetToolTip(_("Font size filter (mm). 0 for all sizes.")) - - self.update_font_list() - self.set_font_list() + self.font_size_filter = fs.FloatSpin(self, min_val=0, max_val=None, increment=1, value="0") + self.font_size_filter.SetFormat("%f") + self.font_size_filter.SetDigits(2) + self.font_size_filter.Bind(fs.EVT_FLOATSPIN, self.on_filter_changed) + self.font_size_filter.SetToolTip(_("Font size filter (mm). 0 for all sizes.")) + + self.font_glyph_filter = wx.CheckBox(self, label=_("Glyphs")) + self.font_glyph_filter.Bind(wx.EVT_CHECKBOX, self.on_filter_changed) + self.font_glyph_filter.SetToolTip(_("Filter fonts by available glyphs.")) + + self.font_category_filter = wx.ComboBox(self, wx.ID_ANY, choices=[], style=wx.CB_DROPDOWN | wx.CB_READONLY) + unfiltered = FontCategory('unfiltered', "---") + self.font_category_filter.Append(unfiltered.name, unfiltered) + for category in FONT_CATEGORIES: + self.font_category_filter.Append(category.name, category) + self.font_category_filter.SetToolTip(_("Filter fonts by category.")) + self.font_category_filter.SetSelection(0) + self.font_category_filter.Bind(wx.EVT_COMBOBOX, self.on_filter_changed) # font details self.font_description = wx.StaticText(self, wx.ID_ANY) self.Bind(wx.EVT_SIZE, self.resize) + # font filter + self.filter_box = wx.StaticBox(self, wx.ID_ANY, label=_("Font Filter")) + # options self.options_box = wx.StaticBox(self, wx.ID_ANY, label=_("Options")) @@ -95,6 +109,10 @@ class LetteringFrame(wx.Frame): self.apply_button = wx.Button(self, wx.ID_ANY, _("Apply and Quit")) self.apply_button.Bind(wx.EVT_BUTTON, self.apply) + # set font list + self.update_font_list() + self.set_font_list() + self.__do_layout() self.load_settings() @@ -165,10 +183,26 @@ class LetteringFrame(wx.Frame): self.fonts = {} self.fonts_by_id = {} - filter_size = self.font_filter.GetValue() + # font size filter value + filter_size = self.font_size_filter.GetValue() + filter_glyph = self.font_glyph_filter.GetValue() + filter_category = self.font_category_filter.GetSelection() - 1 + + # glyph filter string without spaces + glyphs = [*self.text_editor.GetValue().replace(" ", "").replace("\n", "")] + for font in self.font_list: + if filter_glyph and glyphs and not set(glyphs).issubset(font.available_glyphs): + continue + + if filter_category != -1: + category = FONT_CATEGORIES[filter_category].id + if category not in font.keywords: + continue + if filter_size != 0 and (filter_size < font.size * font.min_scale or filter_size > font.size * font.max_scale): continue + self.fonts[font.marked_custom_font_name] = font self.fonts_by_id[font.marked_custom_font_id] = font @@ -208,7 +242,7 @@ class LetteringFrame(wx.Frame): try: font = self.fonts_by_id[font_id].marked_custom_font_name except KeyError: - font = self.default_font.name + font = self.default_font.marked_custom_font_name self.font_chooser.SetValue(font) self.on_font_changed() @@ -222,6 +256,8 @@ class LetteringFrame(wx.Frame): def on_change(self, attribute, event): self.settings[attribute] = event.GetEventObject().GetValue() + if attribute == "text" and self.font_glyph_filter.GetValue() is True: + self.on_filter_changed() self.preview.update() def on_trim_option_change(self, event=None): @@ -232,7 +268,7 @@ class LetteringFrame(wx.Frame): font = self.fonts.get(self.font_chooser.GetValue(), self.default_font) self.settings.font = font.marked_custom_font_id - filter_size = self.font_filter.GetValue() + filter_size = self.font_size_filter.GetValue() self.scale_spinner.SetRange(int(font.min_scale * 100), int(font.max_scale * 100)) if filter_size != 0: self.scale_spinner.SetValue(int(filter_size / font.size * 100)) @@ -245,7 +281,7 @@ class LetteringFrame(wx.Frame): pass # Update font description - color = (0, 0, 0) + color = wx.NullColour description = font.description if len(font_variants) == 0: color = (255, 0, 0) @@ -271,17 +307,17 @@ class LetteringFrame(wx.Frame): if not self.fonts: # No fonts for filtered size self.font_chooser.Clear() - self.filter_label.SetForegroundColour("red") + self.filter_box.SetForegroundColour("red") return else: - self.filter_label.SetForegroundColour("black") + self.filter_box.SetForegroundColour(wx.NullColour) - filter_size = self.font_filter.GetValue() + filter_size = self.font_size_filter.GetValue() previous_font = self.font_chooser.GetValue() self.set_font_list() font = self.fonts.get(previous_font, self.default_font) - self.font_chooser.SetValue(font.name) - if font.name != previous_font: + self.font_chooser.SetValue(font.marked_custom_font_name) + if font.marked_custom_font_name != previous_font: self.on_font_changed() elif filter_size != 0: self.scale_spinner.SetValue(int(filter_size / font.size * 100)) @@ -396,19 +432,27 @@ class LetteringFrame(wx.Frame): font_selector_sizer = wx.StaticBoxSizer(self.font_selector_box, wx.VERTICAL) font_selector_box = wx.BoxSizer(wx.HORIZONTAL) font_selector_box.Add(self.font_chooser, 4, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.RIGHT, 10) - self.filter_label = wx.StaticText(self, wx.ID_ANY, _("Filter")) - font_selector_box.Add(self.filter_label, 0, wx.LEFT | wx.ALIGN_CENTRE_VERTICAL, 0) - font_selector_box.Add(self.font_filter, 1, wx.LEFT | wx.ALIGN_CENTRE_VERTICAL, 5) font_selector_sizer.Add(font_selector_box, 0, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT, 10) font_selector_sizer.Add(self.font_description, 1, wx.EXPAND | wx.ALL, 10) outer_sizer.Add(font_selector_sizer, 0, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT, 10) + # filter fon list + filter_sizer = wx.StaticBoxSizer(self.filter_box, wx.HORIZONTAL) + filter_size_label = wx.StaticText(self, wx.ID_ANY, _("Size")) + filter_sizer.Add(filter_size_label, 0, wx.LEFT | wx.TOP | wx.BOTTOM, 10) + filter_sizer.AddSpacer(5) + filter_sizer.Add(self.font_size_filter, 1, wx.RIGHT | wx.TOP | wx.BOTTOM, 10) + filter_sizer.AddSpacer(5) + filter_sizer.Add(self.font_glyph_filter, 1, wx.RIGHT | wx.TOP | wx.BOTTOM, 10) + filter_sizer.Add(self.font_category_filter, 1, wx.RIGHT | wx.TOP | wx.BOTTOM, 10) + outer_sizer.Add(filter_sizer, 0, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT, 10) + # options left_option_sizer = wx.BoxSizer(wx.VERTICAL) left_option_sizer.Add(self.back_and_forth_checkbox, 1, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT, 5) trim_option_sizer = wx.BoxSizer(wx.HORIZONTAL) - trim_option_sizer.Add(wx.StaticText(self, wx.ID_ANY, _("Add trims")), 0, wx.LEFT | wx.ALIGN_CENTRE_VERTICAL, 5) + trim_option_sizer.Add(wx.StaticText(self, wx.ID_ANY, _("Add trims")), 0, wx.LEFT | wx.ALIGN_TOP, 5) trim_option_sizer.Add(self.trim_option_choice, 1, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT | wx.BOTTOM, 5) trim_option_sizer.Add(self.use_trim_symbols, 1, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT | wx.BOTTOM, 5) left_option_sizer.Add(trim_option_sizer, 0, wx.ALIGN_LEFT, 5) diff --git a/lib/extensions/lettering_generate_json.py b/lib/extensions/lettering_generate_json.py index 85c918b2..a884ccac 100644 --- a/lib/extensions/lettering_generate_json.py +++ b/lib/extensions/lettering_generate_json.py @@ -10,7 +10,8 @@ import sys from inkex import Boolean from ..i18n import _ -from ..lettering.kerning import FontKerning +from ..lettering.categories import FONT_CATEGORIES +from ..lettering.font_info import FontFileInfo from .base import InkstitchExtension @@ -20,6 +21,11 @@ class LetteringGenerateJson(InkstitchExtension): ''' def __init__(self, *args, **kwargs): InkstitchExtension.__init__(self, *args, **kwargs) + self.arg_parser.add_argument("--options") + self.arg_parser.add_argument("--general") + self.arg_parser.add_argument("--settings") + self.arg_parser.add_argument("--kerning") + self.arg_parser.add_argument("-n", "--font-name", type=str, default="Font", dest="font_name") self.arg_parser.add_argument("-d", "--font-description", type=str, default="Description", dest="font_description") self.arg_parser.add_argument("-s", "--auto-satin", type=Boolean, default="true", dest="auto_satin") @@ -35,6 +41,9 @@ class LetteringGenerateJson(InkstitchExtension): 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") + for category in FONT_CATEGORIES: + self.arg_parser.add_argument(f"--{category.id}", type=Boolean, default="false", dest=category.id) + def effect(self): # file paths path = self.options.path @@ -43,20 +52,20 @@ class LetteringGenerateJson(InkstitchExtension): return output_path = os.path.join(os.path.dirname(path), 'font.json') - # kerning - kerning = FontKerning(path) + # font info (kerning, glyphs) + font_info = FontFileInfo(path) - horiz_adv_x = kerning.horiz_adv_x() - hkern = kerning.hkern() + horiz_adv_x = font_info.horiz_adv_x() + hkern = font_info.hkern() custom_leading = self.options.use_custom_leading custom_spacing = self.options.use_custom_spacing - word_spacing = kerning.word_spacing() + word_spacing = font_info.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() or self.options.leading + letter_spacing = font_info.letter_spacing() + units_per_em = font_info.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: @@ -64,9 +73,17 @@ class LetteringGenerateJson(InkstitchExtension): else: leading = self.options.leading + glyphs = font_info.glyph_list() + + keywords = [] + for category in FONT_CATEGORIES: + if getattr(self.options, category.id): + keywords.append(category.id) + # collect data data = {'name': self.options.font_name, 'description': self.options.font_description, + 'keywords': keywords, 'leading': leading, 'auto_satin': self.options.auto_satin, 'reversible': self.options.reversible, @@ -79,7 +96,8 @@ class LetteringGenerateJson(InkstitchExtension): 'horiz_adv_x_space': word_spacing, 'units_per_em': units_per_em, 'horiz_adv_x': horiz_adv_x, - 'kerning_pairs': hkern + 'kerning_pairs': hkern, + 'glyphs': glyphs } # write data to font.json into the same directory as the font file diff --git a/lib/extensions/lettering_update_json_glyphlist.py b/lib/extensions/lettering_update_json_glyphlist.py new file mode 100644 index 00000000..4dea6d51 --- /dev/null +++ b/lib/extensions/lettering_update_json_glyphlist.py @@ -0,0 +1,41 @@ +# 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 json +import os +import sys + +from ..i18n import _ +from ..lettering.font_info import FontFileInfo +from .base import InkstitchExtension + + +class LetteringUpdateJsonGlyphlist(InkstitchExtension): + ''' + This extension helps font creators to generate the json file for the lettering tool + ''' + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self, *args, **kwargs) + self.arg_parser.add_argument("-f", "--font-file", type=str, default="", dest="font_file") + self.arg_parser.add_argument("-j", "--json-file", type=str, default="", dest="json_file") + + def effect(self): + # file paths + font_file = self.options.font_file + json_file = self.options.json_file + if not os.path.isfile(font_file) or not os.path.isfile(json_file): + print(_("Please verify file locations."), file=sys.stderr) + return + + glyphs = FontFileInfo(font_file).glyph_list() + + with open(json_file, 'r') as font_data: + data = json.load(font_data) + + data['glyphs'] = glyphs + + # write data to font.json into the same directory as the font file + with open(json_file, 'w', encoding="utf8") as font_data: + json.dump(data, font_data, indent=4, ensure_ascii=False) diff --git a/lib/inx/extensions.py b/lib/inx/extensions.py index 0ff3e889..30fca4da 100755 --- a/lib/inx/extensions.py +++ b/lib/inx/extensions.py @@ -8,6 +8,7 @@ import pyembroidery from ..commands import (COMMANDS, GLOBAL_COMMANDS, LAYER_COMMANDS, OBJECT_COMMANDS) from ..extensions import Input, Output, extensions +from ..lettering.categories import FONT_CATEGORIES from ..threads import ThreadCatalog from .outputs import pyembroidery_output_formats from .utils import build_environment, write_inx_file @@ -51,6 +52,7 @@ def generate_extension_inx_files(): write_inx_file(name, template.render(formats=pyembroidery_output_formats(), debug_formats=pyembroidery_debug_formats(), threadcatalog=threadcatalog(), + font_categories=FONT_CATEGORIES, layer_commands=layer_commands(), object_commands=object_commands(), global_commands=global_commands())) diff --git a/lib/lettering/categories.py b/lib/lettering/categories.py new file mode 100644 index 00000000..40b41529 --- /dev/null +++ b/lib/lettering/categories.py @@ -0,0 +1,30 @@ +# Authors: see git history +# +# Copyright (c) 2023 Authors +# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. + +from ..i18n import _ + + +class FontCategory: + def __init__(self, cat_id=None, name=None): + self.id: str = cat_id + self.name: str = name + + def __repr__(self): + return "FontCategory(%s, %s)" % (self.id, self.name) + + +FONT_CATEGORIES = [ + FontCategory('applique', _("Applique")), + FontCategory('crossstitch', _("Crossstitch")), + FontCategory('display', _('Display')), + FontCategory('handwriting', _("Handwriting")), + FontCategory('italic', _("Italic")), + FontCategory('monogram', _("Monogram")), + FontCategory('multicolor', _('Multicolor')), + FontCategory('running_stitch', _('Running Stitch')), + FontCategory('sans_serif', _("Sans Serif")), + FontCategory('serif', _("Serif")), + FontCategory('tiny', _("Tiny")) +] diff --git a/lib/lettering/font.py b/lib/lettering/font.py index 77f17e7f..fb17f760 100644 --- a/lib/lettering/font.py +++ b/lib/lettering/font.py @@ -111,6 +111,7 @@ class Font(object): name = localized_font_metadata('name', '') description = localized_font_metadata('description', '') + keywords = font_metadata('keywords', '') letter_case = font_metadata('letter_case', '') default_glyph = font_metadata('default_glyph', "�") leading = font_metadata('leading', 100) @@ -119,6 +120,7 @@ class Font(object): min_scale = font_metadata('min_scale', 1.0) max_scale = font_metadata('max_scale', 1.0) size = font_metadata('size', 0) + available_glyphs = font_metadata('glyphs', []) # 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" /> .... /> diff --git a/lib/lettering/kerning.py b/lib/lettering/font_info.py index 5596ce8a..398786a4 100644 --- a/lib/lettering/kerning.py +++ b/lib/lettering/font_info.py @@ -5,9 +5,10 @@ from inkex import NSS from lxml import etree +from ..svg.tags import INKSCAPE_LABEL -class FontKerning(object): +class FontFileInfo(object): """ This class reads kerning information from an SVG file """ @@ -123,3 +124,14 @@ class FontKerning(object): xpath = "string(.//svg:missing-glyph/@*[name()='horiz-adv-x'])" return float(self.svg.xpath(xpath, namespaces=NSS)) """ + + def glyph_list(self): + """ + Returns a list of available glyphs in the font file + """ + glyphs = [] + glyph_layers = self.svg.xpath(".//svg:g[starts-with(@inkscape:label, 'GlyphLayer-')]", namespaces=NSS) + for layer in glyph_layers: + glyph_name = layer.attrib[INKSCAPE_LABEL].replace("GlyphLayer-", "", 1) + glyphs.append(glyph_name) + return glyphs |
