diff options
Diffstat (limited to 'lib/extensions')
| -rw-r--r-- | lib/extensions/__init__.py | 2 | ||||
| -rw-r--r-- | lib/extensions/lettering_fill_composed_glyphs.py | 573 | ||||
| -rw-r--r-- | lib/extensions/lettering_svg_font_to_layers.py | 46 |
3 files changed, 600 insertions, 21 deletions
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index 22da70a7..4308d316 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -35,6 +35,7 @@ from .lettering import Lettering from .lettering_along_path import LetteringAlongPath from .lettering_custom_font_dir import LetteringCustomFontDir from .lettering_edit_json import LetteringEditJson +from .lettering_fill_composed_glyphs import LetteringFillComposedGlyphs from .lettering_font_sample import LetteringFontSample from .lettering_force_lock_stitches import LetteringForceLockStitches from .lettering_generate_json import LetteringGenerateJson @@ -112,6 +113,7 @@ extensions = [ LetteringAlongPath, LetteringCustomFontDir, LetteringEditJson, + LetteringFillComposedGlyphs, LetteringFontSample, LetteringForceLockStitches, LetteringGenerateJson, diff --git a/lib/extensions/lettering_fill_composed_glyphs.py b/lib/extensions/lettering_fill_composed_glyphs.py new file mode 100644 index 00000000..edc1dd81 --- /dev/null +++ b/lib/extensions/lettering_fill_composed_glyphs.py @@ -0,0 +1,573 @@ +# Authors: see git history +# +# Copyright (c) 2025 Authors +# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. +import unicodedata +from inkex import NSS, Group, Layer, errormsg +from copy import deepcopy +from ..svg.tags import (INKSCAPE_GROUPMODE, INKSCAPE_LABEL, SODIPODI_INSENSITIVE, SVG_GROUP_TAG) +from .base import InkstitchExtension +from ..i18n import _ + + +class LetteringFillComposedGlyphs(InkstitchExtension): + """_summary_ + The goal of this extension is to help the font digitizer with steps to organize its work. + At each step a group of glyphs is brought to the top of the object stack, and the font + digitizer should digitize these glyphs before going to the next step. + Steps are organized in such a way as to break the work into smaller chunks and maximize reuse of already digitized letters. + + unicodedata is used to decomposed letters into pieces. + Furthermore the extension use some additional information, such as "i" and "j" usually reuse + the digitalization of "." + """ + + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self, *args, **kwargs) + self.arg_parser.add_argument("--tabs") + self.arg_parser.add_argument("-c", "--action", dest="action", type=str, default="none") + self._glyphs_layers = [] # list of all Layers () + self._all_glyphs = [] # list of chr, (one per layer) + # constructed using unicodedata.decomposition + self._decomposition = {} # keys are glyphs, value are the list of pieces in the decomposition + self._used_in_decompositions = {} # reverse dictionary + self._normalization = {} # constructed using unicodedata.normalize('NFD', glyph) + + self._pieces = [] # all the pieces for all decomposable glyphs + self._missing = [] # pieces not already among _all_glyphs + + self._category_name = self._category_name() + + def _remove_empty_groups(self): + for group in self.svg.iterdescendants(SVG_GROUP_TAG): + if len(group.getchildren()) == 0: + group.delete() + + def _update_glyphs_layers(self): + self._glyphs_layers = self.svg.xpath('.//svg:g[starts-with(@inkscape:label, "GlyphLayer-")]', namespaces=NSS) + + def _category_name(self): + category_name = {} + category_name["Lu"] = _("Upper Case Letters") + category_name["Ll"] = _('Lower Case Letters') + category_name["Lo"] = _('Other Letters') + category_name["Nd"] = _("Digits") + category_name["Sc"] = _('Symbols') + category_name["Pc"] = _('Punctuation') + category_name["Pe"] = _('Closing Punctuation') + category_name["Mn"] = _('Diacritics') + category_name["Co"] = _('Special') + + return category_name + + def _update_all_glyphs(self): + # only consider GlyphLayer we know the unicode they represent, that means their name is a single letter + self._update_glyphs_layers() + for layer in self._glyphs_layers: + name = layer.attrib[INKSCAPE_LABEL] + name = name.replace("GlyphLayer-", "", 1) + if len(name) == 1: + self._all_glyphs.append(name) + + def _fill_decompose_lists(self): + # NFD normalization decomposes 'Ṓ' into three characters, letter O, macron accent and acute accent, + # unicodedata.decomposition(Ṓ) splits it into two entry points, one for 'Ō' and one for the acute accent + # NFD normalization of 'a' is simply 'a', while unicodedata.decomposition('a') is an empty string. + # unicodedata.decomposition also splits a subscript letter into a keyword for subscript and the entry point + # of the corresponding letter, this is just an example, the normalization of the subscript letter being + # the subscript letter itself. We do need both! + + for glyph in self._all_glyphs: + normalization = [char for char in unicodedata.normalize('NFD', glyph)] + decomposition = [] + for code in unicodedata.decomposition(glyph).split(' '): + try: + piece = chr(int(code, 16)) # convert entry point into unicode character + # we don't need the space separator nor the other separators as pieces, as there + # is nothing to render for them + if unicodedata.category(piece)[0] != 'Z': + decomposition.append(piece) + except ValueError: # this will eliminate keywords as they do not convert to int + pass + if decomposition != []: + self._decomposition[glyph] = decomposition + if unicodedata.decomposition(glyph) == "": + decomposition = [glyph] + self._normalization[glyph] = normalization + for piece in decomposition: + if piece not in self._used_in_decompositions: + self._used_in_decompositions[piece] = [glyph] + else: + self._used_in_decompositions[piece].append(glyph) + self._pieces = [piece for piece in self._used_in_decompositions] + self._missing = [piece for piece in self._pieces + if piece not in self._all_glyphs and len(self._used_in_decompositions[piece]) > 1] + + def _find_layer(self, char): + for layer in self._glyphs_layers: + label = layer.attrib[INKSCAPE_LABEL] + label = label.replace("GlyphLayer-", "", 1) + if SODIPODI_INSENSITIVE in layer.attrib: + layer.attrib.pop(SODIPODI_INSENSITIVE) + if len(label) == 1 and label == char: + return layer + return None + + def _remove(self, char): + char_layer = self._find_layer(char) + if char_layer is not None: + char_layer.delete() + + def _create_empty_glyph(self, char): + new_layer = self.svg.add(Layer.new("GlyphLayer-" + char)) + new_layer.set("style", "display:none") + return new_layer + + # Step 0: check for duplicate and remove unwanted layer + + def _look_for_duplicate(self, verbose=False): + if len(self._all_glyphs) != len(set(self._all_glyphs)): + duplicated_glyphs = " ".join( + [glyph for glyph in set(self._all_glyphs) if self._all_glyphs.count(glyph) > 1] + ) + errormsg(_("Found duplicated glyphs in font file: {duplicated_glyphs}").format(duplicated_glyphs=duplicated_glyphs)) + + for letter in duplicated_glyphs: + errormsg((unicodedata.name(letter))) + else: + if verbose: + errormsg(_("No duplicated glyph found")) + + def _is_valid(self, char): + # sometimes one grabs a non rendering char in the ttf file, and it results in + # an invalid glyph (d='') + if char == "": + return True + category = unicodedata.category(char) + if category[0] in ['Z', 'C'] and category != 'Co': + return False + return True + + def _remove_invalid_glyphs(self): + for layer in self._glyphs_layers: + name = layer.attrib[INKSCAPE_LABEL] + name = name.replace("GlyphLayer-", "", 1) + if name == "" or name == ".null" or (len(name) == 1 and not self._is_valid(name)): + layer.delete() + + # Step1 time to digitize comma, hyphen, and period: + # move comma, hyphen and period on top + # Lock all other glyphs + + def _lock_and_hide_all_layers(self): + self._update_glyphs_layers() + for layer in self._glyphs_layers: + layer.set(SODIPODI_INSENSITIVE, True) + layer.set("style", "display:none") + + def _move_on_top(self, layer): + if SODIPODI_INSENSITIVE in layer.attrib: + layer.pop(SODIPODI_INSENSITIVE) + copy_layer = deepcopy(layer) + layer.delete() + self.svg.append(copy_layer) + + def _move_char_on_top(self, char): + warning = False + layer_char = self._find_layer(char) + if layer_char is None: + warning = True + self._create_empty_glyph(char) + else: + self._move_on_top(layer_char) + return warning + + def _add_chars(self, char_list): + self._lock_and_hide_all_layers() + added = "" + for char in char_list: + if self._move_char_on_top(char): + added = added + char + " " + if added != "": + added_char__warning = _( + "This or these glyphs have been added:\n" + "{added_char}\n" + "Either fill them or delete them").format(added_char=added) + errormsg(added_char__warning) + + # Step 2 + # Find all non-composed letters (no use of diacritic allowed) + # Group them by category, all upper cases, lower cases and other + + def _create_empty_group(self, group_name): + new_group = Group() + new_group.label = group_name + return new_group + + def _do_in_first_steps(self, glyph): + # check if a glyph can be digitalize without waiting for some of its piece to be digitalized first + if unicodedata.decomposition(glyph) == "": + return True + # There is a decomposition, but the decomposition is in only one piece, and this piece + # is not used for anything else. + if len(self._decomposition[glyph]) == 1: + piece = self._decomposition[glyph][0] + if len(self._used_in_decompositions[piece]) == 1: + return True + return False + + def _create_and_fill_group(self, unicode_categories, excepting=[], adding=[], also_composed=False): + + group_name = self._category_name[unicode_categories[0]] + new_group = self._create_empty_group(group_name) + glyphs = self._all_glyphs + for glyph in glyphs: + if glyph not in excepting: + if unicodedata.category(glyph) in unicode_categories: + if self._do_in_first_steps(glyph) or also_composed: + glyph_layer = self._find_layer(glyph) + if glyph_layer is not None: + new_group.add(glyph_layer) + for glyph in adding: + if self._do_in_first_steps(glyph) or also_composed: + glyph_layer = self._find_layer(glyph) + if glyph_layer is not None: + new_group.add(glyph_layer) + if len(new_group) > 0: + self.svg.append(new_group) + + def _add_first_in_second(self, glyph_one, glyph_two): + layer_one = self._find_layer(glyph_one) + layer_two = self._find_layer(glyph_two) + if layer_one is not None and layer_two is not None: + layer_to_insert = deepcopy(layer_one) + layer_to_insert.attrib[INKSCAPE_LABEL] = ' ' + glyph_one + layer_to_insert.pop(INKSCAPE_GROUPMODE) + layer_to_insert.set("style", "display:inline") + layer_two.append(layer_to_insert) + + def _all_non_composed_letters_by_category(self): + self._create_and_fill_group(['Lo', 'Lt', 'Lm']) + self._create_and_fill_group(['Ll']) + self._create_and_fill_group(['Lu']) + + # Step 3 + # Find all non-composed digits and symbols, also find some punctuation signs + + def _add_usually_used(self, usually_use): + for B in usually_use: + for A in usually_use[B]: + self._add_first_in_second(A, B) + + def _digit_symbols_non_closing_punctuation(self): + + usually_use = {} + usually_use[";"] = [",", "."] + usually_use[":"] = [".", "."] + usually_use["!"] = ["."] + usually_use["?"] = ["."] + usually_use["!"] = ["."] + usually_use["_"] = ["-"] + usually_use["¨"] = [".", "."] + usually_use["÷"] = [".", "."] + usually_use["%"] = [".", "."] + usually_use['0'] = ['O'] + usually_use['1'] = ['l', 'I'] + usually_use['÷'] = ['.', '.'] + usually_use['='] = ['-', '-'] + usually_use['±'] = ['-'] + usually_use['$'] = ['S'] + usually_use["'"] = [','] + usually_use["·"] = ["."] + usually_use['"'] = [",", ","] + + self._add_usually_used(usually_use) + self._create_and_fill_group(['Nd', 'Nl', 'No']) + self._create_and_fill_group(['Sc', 'Sm', 'Sk', 'So'], excepting=[">"]) + self._create_and_fill_group(['Pc', 'Pd', 'Ps', 'Pi', 'Po'], excepting=["¿", "¡", "/"]) + + # Step 4 + # Punctuation + + def _closing_punctuation(self): + usually_use = {} + usually_use["¿"] = ["?"] + usually_use["¡"] = ["!"] + usually_use[">"] = ["<"] + usually_use[")"] = ["("] + usually_use["}"] = ["{"] + usually_use["]"] = ["["] + usually_use["»"] = ["«"] + usually_use['”'] = ['“'] + usually_use["’"] = ["‘"] + usually_use["/"] = ["\\"] + + self._add_usually_used(usually_use) + self._create_and_fill_group(['Pe', 'Pf'], excepting=[], adding=["¿", "¡", ">", "/"]) + # Step 5 + # There are several sorts of apostrophes and quotes depending on the used language. + # If there is at least one, let us make sure that we have all those in ["'","’", "ʼ"] + # Same for quotes + + def _find_representative(self, equivalence): + use_to_represent = None + for item in equivalence: + if item in self._all_glyphs: + use_to_represent = item + break + return use_to_represent + + def _deal_with_equivalences(self): + apostrophes = ["'", "’", "ʼ"] + quotes_opening = ['"', "«", '“'] + quotes_closing = ['"', "»", '”'] + equivalences = [apostrophes, quotes_opening, quotes_closing] + use_A_in_B = {} + group_name = _("Additional Punctuation") + new_group = self._create_empty_group(group_name) + for equivalence in equivalences: + use_to_represent = self._find_representative(equivalence) + if use_to_represent is not None: + for item in equivalence: + if item not in self._all_glyphs: + item_layer = self._create_empty_glyph(item) + new_group.add(item_layer) + if use_to_represent not in use_A_in_B: + use_A_in_B[use_to_represent] = [item] + else: + use_A_in_B[use_to_represent].append(item) + + if len(new_group) > 0: + self.svg.append(new_group) + self._update_glyphs_layers() + for use_to_represent in use_A_in_B: + for char in use_A_in_B[use_to_represent]: + self._add_first_in_second(use_to_represent, char) + + # To fill the composed glyphs we need diacritics (COMBINING ACCENT mostly) + # We may already have some of them already digitized, as a COMBINING ACCENT (Mark category) + # has sometimes an homoglyph MODIFIER LETTER ACCENT in the letter category and or an homoglyph ACCENT in the + # symmbol category. + # At this step we want only diacritics without positioning or doubling info. For instance, we want the font digitizer + # to create COMBINING ACUTE ACCENT, but to wait till next step for COMBININIG ACUTE ACCENT BELOW + # COMBINIG ACCENT ABOVE and COMBINING DOUBLE ACUTE ACCENT, not to do the same work several times. + # create the missing diacritics. If the same drawing letter is here, we will fill the diacritic + # with it. Many diacritics are the same, except for the positioning. For instance, for COMBINING ACUTE ACCENT + # has a corresponding letter MODIFIER LETTER ACUTE ACCENT + # If (for instance) COMBINING ACUTE ACCENT is in the glyphs,we simply brinng it to the new group + # of letters to be digitized. + # If it is not here but we have the corresponding MODIFIER LETTER, we create an empty glyph that + # contains the already digitized letter. If there is no such corresponding LETTER or SYMBOL, we fill + # the empty glyph with a letter that uses the accent, so that the font digitizer knows what this + # diacritics is supposed to look like + def _simplify_name(self, glyph): + name = unicodedata.name(glyph) + words = ["DOUBLE", "BELOW", "ABOVE", "INVERTED", "TURNED", "REVERSED"] + simplified_name = name + for word in words: + simplified_name = simplified_name.replace(word, "") + + return simplified_name + + def _has_simple_name(self, glyph): + + words = ["DOUBLE", "BELOW", "ABOVE", "INVERTED", "TURNED", "REVERSED"] + for word in words: + if word in unicodedata.name(glyph): + return False + return True + + def _use_modifier_letter_instead(self, missing_char): + substitute = None + if unicodedata.name(missing_char).startswith("COMBINING"): + letter_name = unicodedata.name(missing_char).replace("COMBINING", "MODIFIER LETTER") + symbol_name = unicodedata.name(missing_char).replace("COMBINING ", "") + for glyph in self._all_glyphs: + if unicodedata.name(glyph) == letter_name or unicodedata.name(glyph) == symbol_name: + substitute = glyph + break + return substitute + + def _add_letter_using_piece(self, piece): + try: + for char in self._used_in_decompositions[piece]: + if unicodedata.category(char)[0] == 'L': + self._add_first_in_second(char, piece) + break + except KeyError: + pass + + def _add_simple_diacritics(self): + missing_group_name = _("Simple Diacritics") + new_group = self._create_empty_group(missing_group_name) + fill_now = [] + for glyph in self._all_glyphs: + if unicodedata.category(glyph) == 'Mn': + fill_now.append(glyph) + for glyph in fill_now: + glyph_layer = self._find_layer(glyph) + new_group.add(glyph_layer) + + for glyph in self._missing: + if self._has_simple_name(glyph): + glyph_layer = self._create_empty_glyph(glyph) + new_group.add(glyph_layer) + self.svg.append(new_group) + self._update_glyphs_layers() + for glyph in self._missing: + if self._has_simple_name(glyph): + substitute = self._use_modifier_letter_instead(glyph) + if substitute is not None: + self._add_first_in_second(substitute, glyph) + else: + self._add_letter_using_piece(glyph) + # Step 6 + # at this step we deal with other diacritics. + # if the diacritic is not present, we prefill the created layer with one copy or two of the + # corresponding simple diacritic, and additionally one letter that does use the diacritics so that the font + # digitizer can move the simple diacritics to its right position (and then delete the additional letter) + + def _find_substitute(self, glyph): + simplified_name = self._simplify_name(glyph) + substitute = None + for candidate in self._all_glyphs: + if simplified_name.replace(" ", "") == unicodedata.name(candidate).replace(" ", ""): + substitute = candidate + break + if substitute is None: + if "COMMA" in unicodedata.name(glyph): + substitute = ',' + if "DOT" in unicodedata.name(glyph): + substitute = "." + return substitute + + def _add_other_diacritics(self): + if self._missing == []: + errormsg(_("nothing to do, you are ready for next step")) + else: + missing_group_name = _("Other Diacritics") + new_group = self._create_empty_group(missing_group_name) + for glyph in self._missing: + glyph_layer = self._create_empty_glyph(glyph) + new_group.add(glyph_layer) + self.svg.append(new_group) + self._update_glyphs_layers() + for glyph in self._missing: + self._add_letter_using_piece(glyph) + substitute = self._find_substitute(glyph) + if substitute is not None: + self._add_first_in_second(substitute, glyph) + if "DOUBLE" in unicodedata.name(glyph): + self._add_first_in_second(substitute, glyph) + + # Step 7 + # Proceed with letters with decomposition of length 2 + + def _fill_two_pieces_letters(self): + glyphs_to_add = [glyph for glyph in self._all_glyphs if len(self._normalization[glyph]) == 2] + also_take = [glyph for glyph in self._decomposition + if len(self._normalization[glyph]) == 1] + + if glyphs_to_add == [] and also_take == []: + errormsg(_("nothing to do, you are ready for next step")) + else: + group_name = _("Two pieces letters") + new_group = self._create_empty_group(group_name) + for glyph in glyphs_to_add: + glyph_layer = self._find_layer(glyph) + new_group.add(glyph_layer) + for piece in self._normalization[glyph][::-1]: + self._add_first_in_second(piece, glyph) + + for glyph in also_take: + glyph_layer = self._find_layer(glyph) + new_group.add(glyph_layer) + for piece in self._decomposition[glyph][::-1]: + self._add_first_in_second(piece, glyph) + self.svg.append(new_group) + + # Step 8 + # Proceed with letters with decomposition of length 3 + def _fill_other_letters(self): + glyphs_to_add = [glyph for glyph in self._all_glyphs if len(self._normalization[glyph]) == 3] + also_take = [glyph for glyph in self._all_glyphs + if len(self._normalization[glyph]) > 3] + + if glyphs_to_add == [] and also_take == []: + errormsg(_("nothing to do, you are ready for next step")) + else: + group_name = _("Other composed letters") + new_group = self._create_empty_group(group_name) + for glyph in glyphs_to_add: + glyph_layer = self._find_layer(glyph) + new_group.add(glyph_layer) + for piece in self._decomposition[glyph]: + self._add_first_in_second(piece, glyph) + for glyph in also_take: + glyph_layer = self._find_layer(glyph) + new_group.add(glyph_layer) + for piece in self._normalization[glyph]: + self._add_first_in_second(piece, glyph) + self.svg.append(new_group) + + def _sort_by_category(self): + self._create_and_fill_group(['Co', 'Cf', 'Cc', 'Cs'], [], [], also_composed=True) + self._create_and_fill_group(['Mn', 'Mc', 'Me'], [], [], also_composed=True) + self._create_and_fill_group(['Nd', 'Nl', 'No'], [], [], also_composed=True) + self._create_and_fill_group(['Sc', 'Sm', 'Sk', 'So'], [], [], also_composed=True) + self._create_and_fill_group(['Pc', 'Pd', 'Ps', 'Pi', 'Po', 'Pe', 'Pf'], [], [], also_composed=True) + self._create_and_fill_group(['Lo', 'Lt', 'Lm'], [], [], also_composed=True) + self._create_and_fill_group(['Ll'], [], [], also_composed=True) + self._create_and_fill_group(['Lu'], [], [], also_composed=True) + + def _additional_actions(self): + # These last actions may be used any time on any font file + + if self.options.action == 'duplicate': + self._look_for_duplicate(verbose=True) + + if self.options.action == 'sort': + self._sort_by_category() + self._remove_empty_groups() + + def effect(self): + self.svg = self.document.getroot() + self._update_glyphs_layers() + self._update_all_glyphs() + self._fill_decompose_lists() + + if self.options.action == 'step1': + self._remove_invalid_glyphs() + self._look_for_duplicate() + self._add_chars([',', '.', '-']) + + if self.options.action == 'step2': + self._all_non_composed_letters_by_category() + self._remove_empty_groups() + + if self.options.action == 'step3': + self._digit_symbols_non_closing_punctuation() + self._remove_empty_groups() + + if self.options.action == 'step4': + self._closing_punctuation() + self._remove_empty_groups() + + if self.options.action == 'step5': + self._deal_with_equivalences() + self._add_simple_diacritics() + self._remove_empty_groups() + + if self.options.action == 'step6': + self._add_other_diacritics() + self._remove_empty_groups() + + if self.options.action == 'step7': + self._fill_two_pieces_letters() + self._remove_empty_groups() + + if self.options.action == 'step8': + self._fill_other_letters() + self._remove_empty_groups() + + self._additional_actions() diff --git a/lib/extensions/lettering_svg_font_to_layers.py b/lib/extensions/lettering_svg_font_to_layers.py index 81586195..0bcd7003 100644 --- a/lib/extensions/lettering_svg_font_to_layers.py +++ b/lib/extensions/lettering_svg_font_to_layers.py @@ -28,6 +28,8 @@ from .base import InkstitchExtension from ..svg import PIXELS_PER_MM +import unicodedata + class LetteringSvgFontToLayers(InkstitchExtension): """ @@ -165,33 +167,35 @@ class LetteringSvgFontToLayers(InkstitchExtension): Converts a single glyph into a new SVG layer, applying scaling and coordinate transformation. """ unicode_char = glyph.get("unicode") + if unicode_char is None or unicodedata.category(unicode_char[0])[0] != "Z": + # all unicode with category starting with 'Z' is not rendered, no need to create layers with path.d ="" + + glyph_name = glyph.get("glyph-name").split('.') + if unicode_char is None: + if len(glyph_name) == 2: + unicode_char = glyph_name[0] + else: + return - glyph_name = glyph.get("glyph-name").split('.') - if unicode_char is None: + typographic_feature = '' if len(glyph_name) == 2: - unicode_char = glyph_name[0] + typographic_feature = glyph_name[1] else: - return - - typographic_feature = '' - if len(glyph_name) == 2: - typographic_feature = glyph_name[1] - else: - arabic_form = glyph.get('arabic-form', None) - if arabic_form is not None and len(arabic_form) > 4: - typographic_feature = arabic_form[:4] - if typographic_feature: - typographic_feature = f".{typographic_feature}" + arabic_form = glyph.get('arabic-form', None) + if arabic_form is not None and len(arabic_form) > 4: + typographic_feature = arabic_form[:4] + if typographic_feature: + typographic_feature = f".{typographic_feature}" - layer = self.svg.add(Layer.new(f"GlyphLayer-{unicode_char}{typographic_feature}")) + layer = self.svg.add(Layer.new(f"GlyphLayer-{unicode_char}{typographic_feature}")) - # glyph layers (except the first one) are initially hidden - if hide_layer: - layer.style["display"] = "none" + # glyph layers (except the first one) are initially hidden + if hide_layer: + layer.style["display"] = "none" - # Using curve description in d attribute of svg:glyph - path = layer.add(PathElement()) - path.path = self.flip_cordinate_system(glyph, emsize, baseline, scale_by) + # Using curve description in d attribute of svg:glyph + path = layer.add(PathElement()) + path.path = self.flip_cordinate_system(glyph, emsize, baseline, scale_by) def effect(self) -> None: # Current code only reads the first svgfont instance |
