diff --git a/Makefile b/Makefile index a4a584f2..856c5d2c 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ UFO=$(STYLES:%=$(UFODIR)/$(FAMILY)-%.ufo) \ $(UFODIR)/$(FAMILY)-Color-Black.ufo \ $(UFODIR)/$(FAMILY)-Arrows-Color.ufo -.PHONY: $(STYLES) ufo clean glyphs build proofs autobuild +.PHONY: $(STYLES) ufo clean glyphs build proofs autobuild update-deps init update default: build @@ -151,7 +151,7 @@ $(FONTSDIR)/%/otf-variable : %.designspace $(PY) tools/stat.py $* $@/*.otf # Create Variable Color fonts-OTF from variable font. The variable font will be colrv0 already -# We just rename as per naminc conventions. Then build colrv1 from it. +# We just rename as per naming conventions. Then build colrv1 from it. $(FONTSDIR)/%/otf-color : $(FONTSDIR)/%/otf-variable @mkdir -p $@ @mv $ $(FONTSDIR)/Nupuram-Display.zip.sha256 md5sum $(FONTSDIR)/Nupuram-Display.zip > $(FONTSDIR)/Nupuram-Display.zip.md5 + +init: + pip-sync + pip check + +update: update-deps init + +update-deps: + export PIP_REQUIRE_VIRTUALENV=true + pip install --upgrade pip-tools pip wheel + pip-compile --upgrade --extra=tests,dev -o requirements.txt pyproject.toml diff --git a/requirements.in b/requirements.in deleted file mode 100644 index 657cdaec..00000000 --- a/requirements.in +++ /dev/null @@ -1,9 +0,0 @@ -fontmake -fontFeatures -pyyaml -fontbakery[freetype] -munch -svgpathtools -ufonormalizer -Brotli -uharfbuzz \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b406a000..530de72d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile --output-file=requirements.txt requirements.in +# pip-compile --extra=tests,dev --output-file=requirements.txt pyproject.toml # appdirs==1.4.4 # via fs @@ -10,7 +10,7 @@ attrs==23.1.0 # via # fontmake # ufolib2 -axisregistry==0.4.2 +axisregistry==0.4.3 # via fontbakery babelfont==3.0.1 # via @@ -23,22 +23,32 @@ beziers==0.5.0 # fontbakery # fontfeatures # glyphtools +black==23.7.0 + # via nupuram (pyproject.toml) booleanoperations==0.9.0 # via ufo2ft brotli==1.0.9 - # via -r requirements.in + # via nupuram (pyproject.toml) +cachetools==5.3.1 + # via tox certifi==2023.5.7 # via requests cffi==1.15.1 # via cmarkgfm cffsubr==0.2.9.post1 # via ufo2ft -charset-normalizer==3.1.0 +chardet==5.1.0 + # via tox +charset-normalizer==3.2.0 # via requests +click==8.1.5 + # via black cmarkgfm==2022.10.27 # via fontbakery collidoscope==0.6.5 # via fontbakery +colorama==0.4.6 + # via tox commandlines==0.4.1 # via ufolint compreffor==0.5.3 @@ -51,19 +61,25 @@ defcon==0.10.2 # glyphsets dehinter==4.0.0 # via fontbakery +distlib==0.3.6 + # via virtualenv +filelock==3.12.2 + # via + # tox + # virtualenv font-v==2.1.0 # via fontbakery fontbakery[freetype]==0.8.13 - # via -r requirements.in + # via nupuram (pyproject.toml) fontfeatures==1.7.4 # via - # -r requirements.in # babelfont + # nupuram (pyproject.toml) fontmake==3.6.0 - # via -r requirements.in + # via nupuram (pyproject.toml) fontmath==0.9.3 # via fontmake -fonttools[lxml,ufo,unicode]==4.40.0 +fonttools[lxml,ufo,unicode]==4.41.0 # via # axisregistry # babelfont @@ -96,11 +112,11 @@ gflanguages==0.8.9 # via fontbakery gitdb==4.0.10 # via gitpython -gitpython==3.1.31 +gitpython==3.1.32 # via font-v -glyphsets==0.6.1 +glyphsets==0.6.2 # via fontbakery -glyphslib==6.2.2 +glyphslib==6.2.5 # via # babelfont # fontmake @@ -110,9 +126,11 @@ glyphtools==0.8.0 # via fontfeatures idna==3.4 # via requests +isort==5.12.0 + # via nupuram (pyproject.toml) kurbopy==0.9.0 # via collidoscope -lxml==4.9.2 +lxml==4.9.3 # via # fontbakery # fontfeatures @@ -121,15 +139,14 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -munch==3.0.0 - # via -r requirements.in +munch==4.0.0 + # via nupuram (pyproject.toml) munkres==1.1.4 # via fontbakery -numpy==1.24.3 - # via - # glyphtools - # scipy - # svgpathtools +mypy-extensions==1.0.0 + # via black +numpy==1.25.1 + # via glyphtools openstep-plist==0.3.0.post1 # via # babelfont @@ -138,12 +155,25 @@ opentype-sanitizer==9.1.0 # via fontbakery opentypespec==1.9.1 # via fontbakery -orjson==3.9.1 +orjson==3.9.2 # via babelfont packaging==23.1 - # via fontbakery + # via + # black + # fontbakery + # pyproject-api + # tox +pathspec==0.11.1 + # via black pip-api==0.0.30 # via fontbakery +platformdirs==3.8.1 + # via + # black + # tox + # virtualenv +pluggy==1.2.0 + # via tox protobuf==3.20.3 # via # axisregistry @@ -157,22 +187,22 @@ pycparser==2.21 # via cffi pygments==2.15.1 # via rich +pyproject-api==1.5.3 + # via tox pyyaml==6.0 # via - # -r requirements.in # fontbakery + # nupuram (pyproject.toml) requests==2.31.0 # via fontbakery rich==13.4.2 # via fontbakery rstr==3.2.1 # via stringbrewer -scipy==1.10.1 - # via svgpathtools +ruff==0.0.278 + # via nupuram (pyproject.toml) six==1.16.0 - # via - # fs - # munch + # via fs skia-pathops==0.8.0 # via collidoscope smmap==5.0.0 @@ -183,12 +213,10 @@ sre-yield==1.2 # via stringbrewer stringbrewer==0.0.1 # via fontbakery -svgpathtools==1.6.1 - # via -r requirements.in -svgwrite==1.4.3 - # via svgpathtools toml==0.10.2 # via fontbakery +tox==4.6.4 + # via nupuram (pyproject.toml) tqdm==4.65.0 # via collidoscope ufo2ft[compreffor]==2.32.0 @@ -203,11 +231,11 @@ ufolib2==0.14.0 ufolint==1.2.0 # via fontbakery ufonormalizer==0.6.1 - # via -r requirements.in + # via nupuram (pyproject.toml) uharfbuzz==0.37.0 # via - # -r requirements.in # collidoscope + # nupuram (pyproject.toml) # vharfbuzz unicodedata2==15.0.0 # via @@ -217,6 +245,8 @@ urllib3==2.0.3 # via requests vharfbuzz==0.2.0 # via fontbakery +virtualenv==20.23.1 + # via tox # The following packages are considered to be unsafe in a requirements file: # pip diff --git a/specimen/css/main.css b/specimen/css/main.css index ba8c9cfa..b87863d6 100644 --- a/specimen/css/main.css +++ b/specimen/css/main.css @@ -36,6 +36,7 @@ body { hyphens: auto; -webkit-hyphenate-character: ""; + hyphenate-character: ""; font-family: "Lato"; font-size: 100%; background-color: var(--body-background-color); diff --git a/tools/build_color_v0.py b/tools/build_color_v0.py index 931d550a..d6ef90f3 100644 --- a/tools/build_color_v0.py +++ b/tools/build_color_v0.py @@ -1,17 +1,16 @@ -from ufoLib2.objects import Font, Layer -from munch import DefaultMunch -from operator import itemgetter import copy -import yaml -import ufo2ft -import sys import logging +import sys +from operator import itemgetter + +import ufo2ft +import yaml +from munch import DefaultMunch +from ufoLib2.objects import Font, Layer log = logging.getLogger(__name__) -colorConfig = DefaultMunch.fromDict( - yaml.load(open("config.yaml"), Loader=yaml.FullLoader) -) +colorConfig = DefaultMunch.fromDict(yaml.load(open("config.yaml"), Loader=yaml.FullLoader)) config = colorConfig.colorfonts[sys.argv[1]] @@ -20,7 +19,7 @@ def hex_to_rgba(hexcolor): hexcolor = hexcolor.lstrip("#") if len(hexcolor) == 6: hexcolor = hexcolor + "FF" - return tuple(int(hexcolor[i:i + 2], 16) for i in (0, 2, 4, 6)) + return tuple(int(hexcolor[i : i + 2], 16) for i in (0, 2, 4, 6)) layer_mapping = [] @@ -31,9 +30,7 @@ def hex_to_rgba(hexcolor): font = Font().open(config["layers"][layer_name]["source"]) else: if not font: - raise ValueError( - "Default font not found. Define default source as first item in layers" - ) + raise ValueError("Default font not found. Define default source as first item in layers") layer: Layer = font.newLayer(layer_name) layer_font: Font = Font().open(config["layers"][layer_name]["source"]) for base_glyph in layer_font: diff --git a/tools/build_color_v1.py b/tools/build_color_v1.py index 7ec8ebfa..e59d6ffb 100644 --- a/tools/build_color_v1.py +++ b/tools/build_color_v1.py @@ -1,11 +1,11 @@ -from fontTools import ttLib -from fontTools.colorLib import builder -from fontTools.ttLib.tables import otTables as ot -import sys import logging +import sys from io import BytesIO import uharfbuzz as hb +from fontTools import ttLib +from fontTools.colorLib import builder +from fontTools.ttLib.tables import otTables as ot log = logging.getLogger(__name__) @@ -34,12 +34,12 @@ def getGlyphBounds(font, glyphName): h += y # Since the shadow extends beyond width and height, increase them by a factor. # Make the box slightly bigger by reducing xMin, yMin toog - return x-50, y-50, w*1.5, h*1.5 + return x - 50, y - 50, w * 1.5, h * 1.5 for glyph_name, layers in colr0.ColorLayers.items(): # Remove udatta and anudatta from color font. They skew the clibbox a lot - if glyph_name in ['uni952', 'uni951']: + if glyph_name in ["uni952", "uni951"]: continue v1_layers = [] colrv1_map[glyph_name] = (ot.PaintFormat.PaintColrLayers, v1_layers) @@ -48,15 +48,17 @@ def getGlyphBounds(font, glyphName): # Match COLRv0 fill if cpal.numPaletteEntries == 2: - v1_layers.append({ - "Format": ot.PaintFormat.PaintGlyph, - "Paint": { - "Format": ot.PaintFormat.PaintSolid, - "PaletteIndex": layer.colorID, - "Alpha": 1, - }, - "Glyph": layer.name, - }) + v1_layers.append( + { + "Format": ot.PaintFormat.PaintGlyph, + "Paint": { + "Format": ot.PaintFormat.PaintSolid, + "PaletteIndex": layer.colorID, + "Alpha": 1, + }, + "Glyph": layer.name, + } + ) else: if layer.colorID == 0: # Shadow c1 = 0 @@ -64,23 +66,22 @@ def getGlyphBounds(font, glyphName): if layer.colorID == 1: # Main c1 = 2 c2 = 1 - v1_layers.append({ - "Format": ot.PaintFormat.PaintGlyph, - "Paint": { - "Format": ot.PaintFormat.PaintLinearGradient, - "ColorLine": { - "ColorStop": [(0.0, c1), (1.0, c2)], - "Extend": "reflect" + v1_layers.append( + { + "Format": ot.PaintFormat.PaintGlyph, + "Paint": { + "Format": ot.PaintFormat.PaintLinearGradient, + "ColorLine": {"ColorStop": [(0.0, c1), (1.0, c2)], "Extend": "reflect"}, + "x0": 0, + "y0": 0, + "x1": 0, + "y1": 400, + "x2": 100, + "y2": 0, }, - "x0": 0, - "y0": 0, - "x1": 0, - "y1": 400, - "x2": 100, - "y2": 0, - }, - "Glyph": layer.name, - }) + "Glyph": layer.name, + } + ) if glyph_name not in clipBoxes: clipBoxes[glyph_name] = getGlyphBounds(font, glyph_name) diff --git a/tools/builder.py b/tools/builder.py index 15687c05..3718e515 100644 --- a/tools/builder.py +++ b/tools/builder.py @@ -1,12 +1,11 @@ - -from munch import DefaultMunch import argparse import logging -import sys -import yaml import os +import sys +import yaml from malayalamfont import MalayalamFont +from munch import DefaultMunch log = logging.getLogger(__name__) @@ -19,22 +18,19 @@ def dir_path(path): if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Build a UFO formatted font", add_help=True) - parser.add_argument( - "-c", "--config", help="The font information and configuraion", - default="config.yaml", type=argparse.FileType('r')) - parser.add_argument( - "-s", "--source", help="SVG sources", - type=dir_path) + parser = argparse.ArgumentParser(description="Build a UFO formatted font", add_help=True) parser.add_argument( - "-o", "--output", help="Output UFO File") - parser.add_argument('-t', '--style', default='Regular', - required=False, help="Set style") - parser.add_argument('-w', '--weight', default=400, - required=False, help="Set weight") - parser.add_argument('-l', '--log-level', default='INFO', - required=False, help="Set log level") + "-c", + "--config", + help="The font information and configuraion", + default="config.yaml", + type=argparse.FileType("r"), + ) + parser.add_argument("-s", "--source", help="SVG sources", type=dir_path) + parser.add_argument("-o", "--output", help="Output UFO File") + parser.add_argument("-t", "--style", default="Regular", required=False, help="Set style") + parser.add_argument("-w", "--weight", default=400, required=False, help="Set weight") + parser.add_argument("-l", "--log-level", default="INFO", required=False, help="Set log level") options = parser.parse_args() try: @@ -43,14 +39,12 @@ def dir_path(path): logging.error("Invalid log level: {}".format(options.log_level)) sys.exit(1) - config = DefaultMunch.fromDict( - yaml.load(options.config, Loader=yaml.FullLoader)) + config = DefaultMunch.fromDict(yaml.load(options.config, Loader=yaml.FullLoader)) # This is not the best way to do it, anyway.. - if options.style in ["Arrows", "Color", "Shadow", "Calligraphy", "Dots", "Outline"]: + if options.style in ["Arrows", "Color", "Shadow", "Calligraphy", "Dots", "Outline"]: config.name = f"{config.name} {options.style}" options.style = "Regular" - font: MalayalamFont = MalayalamFont( - config, style=options.style, weight=options.weight) + font: MalayalamFont = MalayalamFont(config, style=options.style, weight=options.weight) font.build(options.source) font.buildFeatures() font.setFontInfo() diff --git a/tools/fix_font.py b/tools/fix_font.py index ac3ac40f..eee2dd32 100644 --- a/tools/fix_font.py +++ b/tools/fix_font.py @@ -1,7 +1,8 @@ +import logging +import sys + from fontTools import ttLib from fontTools.ttLib.tables import ttProgram -import sys -import logging log = logging.getLogger(__name__) @@ -15,19 +16,19 @@ def fix_font(fontFile): remove_aat(ttFont) ttFont.save(fontFile) + def fix_unhinted_font(ttFont: ttLib.TTFont): """Improve the appearance of an unhinted font on Win platforms by: - - Add a new GASP table with a newtable that has a single - range which is set to smooth. - - Add a new prep table which is optimized for unhinted fonts. + - Add a new GASP table with a newtable that has a single + range which is set to smooth. + - Add a new prep table which is optimized for unhinted fonts. """ gasp = ttLib.newTable("gasp") # Set GASP so all sizes are smooth gasp.gaspRange = {0xFFFF: 15} program = ttProgram.Program() - assembly = ["PUSHW[]", "511", "SCANCTRL[]", - "PUSHB[]", "4", "SCANTYPE[]"] + assembly = ["PUSHW[]", "511", "SCANCTRL[]", "PUSHB[]", "4", "SCANTYPE[]"] program.fromAssembly(assembly) prep = ttLib.newTable("prep") @@ -36,6 +37,7 @@ def fix_unhinted_font(ttFont: ttLib.TTFont): ttFont["gasp"] = gasp ttFont["prep"] = prep + def fix_fs_type(ttFont: ttLib.TTFont): """Set the OS/2 table's fsType flag to 0 (Installable embedding). Args: @@ -45,16 +47,36 @@ def fix_fs_type(ttFont: ttLib.TTFont): ttFont["OS/2"].fsType = 0 return old != 0 + def remove_aat(ttFont: ttLib.TTFont): """Unwanted AAT tables were found in the font and should be removed . Args: ttFont: a TTFont instance """ unwanted_tables = [ - 'EBSC', 'Zaph', 'acnt', 'ankr', 'bdat', 'bhed', 'bloc', - 'bmap', 'bsln', 'fdsc', 'feat', 'fond', 'gcid', 'just', - 'kerx', 'lcar', 'ltag', 'mort', 'morx', 'opbd', 'prop', - 'trak', 'xref' + "EBSC", + "Zaph", + "acnt", + "ankr", + "bdat", + "bhed", + "bloc", + "bmap", + "bsln", + "fdsc", + "feat", + "fond", + "gcid", + "just", + "kerx", + "lcar", + "ltag", + "mort", + "morx", + "opbd", + "prop", + "trak", + "xref", ] for unwanted in unwanted_tables: if unwanted in ttFont: @@ -74,6 +96,7 @@ def add_dummy_dsig(ttFont: ttLib.TTFont) -> None: newDSIG.signatureRecords = [] ttFont.tables["DSIG"] = newDSIG + if __name__ == "__main__": for arg in sys.argv[1:]: - fix_font(arg) \ No newline at end of file + fix_font(arg) diff --git a/tools/gen_webfonts.py b/tools/gen_webfonts.py index 88555049..d72bdf49 100644 --- a/tools/gen_webfonts.py +++ b/tools/gen_webfonts.py @@ -1,5 +1,6 @@ import os import sys + from fontTools.ttLib import TTFont if __name__ == "__main__": @@ -7,5 +8,5 @@ f = TTFont(filepath) f.flavor = "woff2" fontname = os.path.splitext(os.path.basename(filepath))[0] + ".woff2" - dest = os.path.join(os.path.dirname(filepath), '../', 'webfonts', fontname) + dest = os.path.join(os.path.dirname(filepath), "../", "webfonts", fontname) f.save(dest) diff --git a/tools/malayalamfont.py b/tools/malayalamfont.py index 0b5c201d..b3d829e4 100644 --- a/tools/malayalamfont.py +++ b/tools/malayalamfont.py @@ -1,21 +1,19 @@ - import logging import os import re from datetime import datetime -from typing import List from itertools import product -from ufoLib2.objects import Font, Glyph, Component -from fontFeatures import (Chaining, FontFeatures, Positioning, Routine, Attachment, - Substitution, ValueRecord) -from fontTools import agl +from typing import List +from fontFeatures import Attachment, Chaining, FontFeatures, Positioning, Routine, Substitution, ValueRecord +from fontTools import agl from svgglyph import SVGGlyph +from ufoLib2.objects import Component, Font, Glyph log = logging.getLogger(__name__) -LANGUAGE_MALAYALAM = [('mlm2', 'dflt')] -LANGUAGE_LATIN = [('DFLT', 'dflt'), ('latn', 'dflt')] +LANGUAGE_MALAYALAM = [("mlm2", "dflt")] +LANGUAGE_LATIN = [("DFLT", "dflt"), ("latn", "dflt")] class MalayalamFont(Font): @@ -26,13 +24,12 @@ def __init__(self, options, style="", weight=400): self.weight = weight self.fontFeatures = FontFeatures() self.available_svgs = [] - self.salts={} #Stylistic Alternates - logging.basicConfig(level='WARN') + self.salts = {} # Stylistic Alternates + logging.basicConfig(level="WARN") def build_glyph_classes(self): for gclass in self.options.glyphs.classes: - glyph_names = [SVGGlyph.get_glyph_name( - g) for g in self.get_glyphs_from_named_classes(gclass)] + glyph_names = [SVGGlyph.get_glyph_name(g) for g in self.get_glyphs_from_named_classes(gclass)] glyph_names = [g for g in glyph_names if g in self] self.fontFeatures.namedClasses[gclass] = glyph_names @@ -43,7 +40,7 @@ def get_glyphs_from_named_classes(self, *argv): if isinstance(glyphs, list): all_glyphs = all_glyphs + glyphs else: - all_glyphs = all_glyphs + [g for g in glyphs] + all_glyphs = all_glyphs + list(glyphs) return all_glyphs def get_glyph_names_from_named_classes(self, class_name): @@ -53,12 +50,13 @@ def get_glyph_names_from_named_classes(self, class_name): def build_latin_ligatures(self): feature = "liga" name = "latin_ligatures" - ligatures = self.get_glyphs_from_named_classes('LATIN_LIGATURES') + ligatures = self.get_glyphs_from_named_classes("LATIN_LIGATURES") rules = [] for ligature in ligatures: - sub = Substitution([[SVGGlyph.get_glyph_name(l)] for l in ligature], - replacement=[[SVGGlyph.get_glyph_name(ligature)]]) + sub = Substitution( + [[SVGGlyph.get_glyph_name(l)] for l in ligature], replacement=[[SVGGlyph.get_glyph_name(ligature)]] + ) rules.append(sub) routine = Routine(rules=rules, name=name, languages=LANGUAGE_LATIN) self.fontFeatures.addFeature(feature, [routine]) @@ -71,8 +69,8 @@ def build_kern(self): rules = [] for kern_def in self.options.glyphs.kern[script]: lhs_def = kern_def[0] - if '@' in lhs_def: - lhs=[lhs_def] + if "@" in lhs_def: + lhs = [lhs_def] else: if isinstance(lhs_def, list): lhs = [SVGGlyph.get_glyph_name(l) for l in lhs_def] @@ -80,36 +78,33 @@ def build_kern(self): lhs = [SVGGlyph.get_glyph_name(lhs_def)] rhs_def = kern_def[1] - if '@' in rhs_def: - rhs=[rhs_def] + if "@" in rhs_def: + rhs = [rhs_def] else: if isinstance(rhs_def, list): rhs = [SVGGlyph.get_glyph_name(l) for l in rhs_def] else: - rhs=[SVGGlyph.get_glyph_name(rhs_def)] + rhs = [SVGGlyph.get_glyph_name(rhs_def)] xAdvance = kern_def[2] # Flatten the class to avoid the issue of class overlaps and rules # getting ignored for l, r in product(lhs, rhs): - rules.append( - Positioning([[l], [r]], - [ValueRecord(xAdvance=xAdvance), ValueRecord()]) - ) + rules.append(Positioning([[l], [r]], [ValueRecord(xAdvance=xAdvance), ValueRecord()])) routine = Routine(rules=rules, name=name, languages=languages) self.fontFeatures.addFeature(feature, [routine]) def build_chillus(self): feature = "akhn" name = "zwj_chillus" - chillus = ["ന്\u200d", "ര്\u200d", "ല്\u200d", - "ള്\u200d", "ണ്\u200d", "ഴ്\u200d", "ക്\u200d"] + chillus = ["ന്\u200d", "ര്\u200d", "ല്\u200d", "ള്\u200d", "ണ്\u200d", "ഴ്\u200d", "ക്\u200d"] rules = [] for chillu in chillus: rules.append( - Substitution([[SVGGlyph.get_glyph_name(l)] for l in chillu], - replacement=[[SVGGlyph.get_glyph_name(chillu)]]) + Substitution( + [[SVGGlyph.get_glyph_name(l)] for l in chillu], replacement=[[SVGGlyph.get_glyph_name(chillu)]] + ) ) routine = Routine(rules=rules, name=name, languages=LANGUAGE_MALAYALAM) self.fontFeatures.addFeature(feature, [routine]) @@ -119,10 +114,11 @@ def build_cons_la(self): name = "cons_la" rules = [] - for conjunct in self.get_glyphs_from_named_classes('ML_LA_CONJUNCTS'): + for conjunct in self.get_glyphs_from_named_classes("ML_LA_CONJUNCTS"): rules.append( - Substitution([[SVGGlyph.get_glyph_name(l)] for l in conjunct], - replacement=[[SVGGlyph.get_glyph_name(conjunct)]]) + Substitution( + [[SVGGlyph.get_glyph_name(l)] for l in conjunct], replacement=[[SVGGlyph.get_glyph_name(conjunct)]] + ) ) routine = Routine(rules=rules, name=name, languages=LANGUAGE_MALAYALAM) self.fontFeatures.addFeature(feature, [routine]) @@ -134,8 +130,10 @@ def build_cons_signs(self): rules = [] for cons_sign in cons_signs: rules.append( - Substitution([[SVGGlyph.get_glyph_name(l)] for l in cons_sign], - replacement=[[SVGGlyph.get_glyph_name(cons_sign)]]) + Substitution( + [[SVGGlyph.get_glyph_name(l)] for l in cons_sign], + replacement=[[SVGGlyph.get_glyph_name(cons_sign)]], + ) ) routine = Routine(rules=rules, name=name, languages=LANGUAGE_MALAYALAM) self.fontFeatures.addFeature(feature, [routine]) @@ -148,11 +146,12 @@ def build_cons_signs_order_fix(self): rules = [] for cons_sign in cons_signs: rules.append( - Substitution([[SVGGlyph.get_glyph_name(cons_sign)]], - replacement=[[SVGGlyph.get_glyph_name(l)] for l in cons_sign]) + Substitution( + [[SVGGlyph.get_glyph_name(cons_sign)]], + replacement=[[SVGGlyph.get_glyph_name(l)] for l in cons_sign], + ) ) - split_cons_signs = Routine( - rules=rules, name='split_cons_signs', languages=LANGUAGE_MALAYALAM) + split_cons_signs = Routine(rules=rules, name="split_cons_signs", languages=LANGUAGE_MALAYALAM) rules = [ # Avoid യ + ് + ര ligature @@ -172,9 +171,9 @@ def build_cons_signs_order_fix(self): [[SVGGlyph.get_glyph_name(reph)]], precontext=[[SVGGlyph.get_glyph_name(cons_signs[1])]], lookups=[[split_cons_signs]], - )] - split_cons_signs = Routine( - rules=rules, name=name, languages=LANGUAGE_MALAYALAM) + ), + ] + split_cons_signs = Routine(rules=rules, name=name, languages=LANGUAGE_MALAYALAM) self.fontFeatures.addFeature(feature, [split_cons_signs]) @@ -186,22 +185,22 @@ def build_ra_sign(self): # designed reph forms. For all other conjuncts, reph should be virama+ra # Example -> ക + ് +ര -> ക + ്ര # ഖ + ് + ര -> ഖ + ് + ര - glyph_names_with_reph=[] - for ligature in self.get_glyphs_from_named_classes('ML_REPH_CONJUNCTS'): - ligature = ligature.replace(reph, '') + glyph_names_with_reph = [] + for ligature in self.get_glyphs_from_named_classes("ML_REPH_CONJUNCTS"): + ligature = ligature.replace(reph, "") ligature_glyph_name = SVGGlyph.get_glyph_name(ligature) if ligature_glyph_name in self: glyph_names_with_reph.append(ligature_glyph_name) # To allow independent reph sign rendering, use zwj + reph - glyph_names_with_reph.append('zwj') + glyph_names_with_reph.append("zwj") - rule = Substitution([[SVGGlyph.get_glyph_name(l)] for l in reph], - replacement=[[SVGGlyph.get_glyph_name(reph)]], - precontext=[glyph_names_with_reph], + rule = Substitution( + [[SVGGlyph.get_glyph_name(l)] for l in reph], + replacement=[[SVGGlyph.get_glyph_name(reph)]], + precontext=[glyph_names_with_reph], ) - routine = Routine(rules=[rule], name=name, - languages=LANGUAGE_MALAYALAM) + routine = Routine(rules=[rule], name=name, languages=LANGUAGE_MALAYALAM) self.fontFeatures.addFeature(feature, [routine]) def build_conjuncts(self): @@ -218,33 +217,32 @@ def build_conjuncts(self): "ല്പ": "്പ", } conditional_stack_lookup = None - #TODO Can this dictionary be moved to config? - for conjunct in self.get_glyphs_from_named_classes('ML_CONS_CONJUNCTS'): + # TODO Can this dictionary be moved to config? + for conjunct in self.get_glyphs_from_named_classes("ML_CONS_CONJUNCTS"): conjunct_glyph_name = SVGGlyph.get_glyph_name(conjunct) if conjunct_glyph_name not in self: continue - sub = Substitution([[SVGGlyph.get_glyph_name(l)] for l in conjunct], - replacement=[[conjunct_glyph_name]]) + sub = Substitution([[SVGGlyph.get_glyph_name(l)] for l in conjunct], replacement=[[conjunct_glyph_name]]) if conjunct in prevent_stack_contexts.keys(): if conditional_stack_lookup: conditional_stack_lookup.addRule(sub) else: conditional_stack_lookup = Routine( - rules=[sub], - name='conditional_stack_lookup', - languages=LANGUAGE_MALAYALAM + rules=[sub], name="conditional_stack_lookup", languages=LANGUAGE_MALAYALAM ) - rules.extend([ - Chaining( - [[SVGGlyph.get_glyph_name(l)] for l in conjunct], - postcontext=[[SVGGlyph.get_glyph_name(l)] for l in prevent_stack_contexts.get(conjunct)], - lookups=[] - ), # Produces ignore sub rule. - Chaining( - [[SVGGlyph.get_glyph_name(l)] for l in conjunct], - lookups=[[ self.fontFeatures.referenceRoutine(conditional_stack_lookup)], None, None ] - ) - ]) + rules.extend( + [ + Chaining( + [[SVGGlyph.get_glyph_name(l)] for l in conjunct], + postcontext=[[SVGGlyph.get_glyph_name(l)] for l in prevent_stack_contexts.get(conjunct)], + lookups=[], + ), # Produces ignore sub rule. + Chaining( + [[SVGGlyph.get_glyph_name(l)] for l in conjunct], + lookups=[[self.fontFeatures.referenceRoutine(conditional_stack_lookup)], None, None], + ), + ] + ) # THANKS to https://github.com/simoncozens/fontFeatures/issues/58 for the above method else: rules.append(sub) @@ -256,15 +254,15 @@ def build_cons_ra_substitutions(self): name = "pres_reph" reph = "്ര" rules = [] - for ligature in self.get_glyphs_from_named_classes('ML_REPH_CONJUNCTS'): - ligature = ligature.replace(reph, '') - ligature_glyph_name = SVGGlyph.get_glyph_name(ligature+reph) + for ligature in self.get_glyphs_from_named_classes("ML_REPH_CONJUNCTS"): + ligature = ligature.replace(reph, "") + ligature_glyph_name = SVGGlyph.get_glyph_name(ligature + reph) if ligature_glyph_name not in self: continue sub = Substitution( - [[SVGGlyph.get_glyph_name(reph)], [ - SVGGlyph.get_glyph_name(ligature)]], - replacement=[[ligature_glyph_name]]) + [[SVGGlyph.get_glyph_name(reph)], [SVGGlyph.get_glyph_name(ligature)]], + replacement=[[ligature_glyph_name]], + ) rules.append(sub) routine = Routine(rules=rules, name=name, languages=LANGUAGE_MALAYALAM) @@ -274,19 +272,20 @@ def build_cons_conj_vowel_signs(self): feature = "psts" name = "psts_vowel_signs" rules = [] - vowel_signs = self.options.glyphs.classes['ML_VOWEL_SIGNS_CONJOINING'] + vowel_signs = self.options.glyphs.classes["ML_VOWEL_SIGNS_CONJOINING"] ligatures = sorted( - self.get_glyphs_from_named_classes('ML_CONSONANTS') + - self.get_glyphs_from_named_classes('ML_REPH_CONJUNCTS') + - self.get_glyphs_from_named_classes('ML_CONS_CONJUNCTS') + - self.get_glyphs_from_named_classes('ML_LA_CONJUNCTS')+ - ["്യ"] + self.get_glyphs_from_named_classes("ML_CONSONANTS") + + self.get_glyphs_from_named_classes("ML_REPH_CONJUNCTS") + + self.get_glyphs_from_named_classes("ML_CONS_CONJUNCTS") + + self.get_glyphs_from_named_classes("ML_LA_CONJUNCTS") + + ["്യ"] ) for ligature in ligatures: for vowel_sign in vowel_signs: - replacement_ligature = SVGGlyph.get_glyph_name( - ligature)+SVGGlyph.get_glyph_name(vowel_sign, prefix="_") + replacement_ligature = SVGGlyph.get_glyph_name(ligature) + SVGGlyph.get_glyph_name( + vowel_sign, prefix="_" + ) ligature_glyph_name = SVGGlyph.get_glyph_name(ligature) if ligature_glyph_name not in self: continue @@ -295,10 +294,10 @@ def build_cons_conj_vowel_signs(self): # add conditional stacking rule continue sub = Substitution( - [[ligature_glyph_name], [ - SVGGlyph.get_glyph_name(vowel_sign)]], + [[ligature_glyph_name], [SVGGlyph.get_glyph_name(vowel_sign)]], replacement=[[replacement_ligature]], - flags=8) + flags=8, + ) rules.append(sub) routine = Routine(rules=rules, name=name, languages=LANGUAGE_MALAYALAM) self.fontFeatures.addFeature(feature, [routine]) @@ -306,10 +305,10 @@ def build_cons_conj_vowel_signs(self): def build_conjuncts_fixup(self): feature = "akhn" name = "akhn_conjuncts_fixup" - vowel_signs = self.options.glyphs.classes['ML_VOWEL_SIGNS_CONJOINING'] + vowel_signs = self.options.glyphs.classes["ML_VOWEL_SIGNS_CONJOINING"] ligatures = sorted( - self.get_glyphs_from_named_classes('ML_CONS_CONJUNCTS') + - self.get_glyphs_from_named_classes('ML_LA_CONJUNCTS') + self.get_glyphs_from_named_classes("ML_CONS_CONJUNCTS") + + self.get_glyphs_from_named_classes("ML_LA_CONJUNCTS") ) rules = [] split_cons_conj = None @@ -320,35 +319,34 @@ def build_conjuncts_fixup(self): missing_vowels = [] missing_vowels_glyph_names = [] for vowel_sign in vowel_signs: - ligature_vowel = ligature+vowel_sign + ligature_vowel = ligature + vowel_sign replacement_ligature_glyphname = SVGGlyph.get_glyph_name(ligature_vowel) if replacement_ligature_glyphname in self: continue missing_vowels.append(vowel_sign) missing_vowels_glyph_names.append(SVGGlyph.get_glyph_name(vowel_sign)) - parts=[] - seq="" - largest_seq=seq + parts = [] + seq = "" + largest_seq = seq for char in reversed(ligature): - seq = char+seq + seq = char + seq # FIXME We are basing missing_vowels[0] here to find largest available ligature # with that vowel. But this is not flexible to accommodate the nature of other vowel # conjoining. If we are doing that for each missing vowel, we cannot group this splitting # under split_cons_conj lookup. We will lookups for each vowelsign. # But this is rare; Having a -uu glyph without _u glyph is rare. - if SVGGlyph.get_glyph_name(seq+missing_vowels[0]) in self: + if SVGGlyph.get_glyph_name(seq + missing_vowels[0]) in self: largest_seq = seq # Split everything from the starting part of ligature_vowel. - for l in ligature[0:len(ligature)-len(largest_seq)]: + for l in ligature[0 : len(ligature) - len(largest_seq)]: parts.append(l) if len(largest_seq): parts.append(largest_seq) - sub = Substitution([[ligature_glyph_name]],replacement=[[SVGGlyph.get_glyph_name(l)] for l in parts]) + sub = Substitution([[ligature_glyph_name]], replacement=[[SVGGlyph.get_glyph_name(l)] for l in parts]) if split_cons_conj: split_cons_conj.addRule(sub) else: - split_cons_conj = Routine( - rules=[sub], name='split_cons_conj', languages=LANGUAGE_MALAYALAM) + split_cons_conj = Routine(rules=[sub], name="split_cons_conj", languages=LANGUAGE_MALAYALAM) rules.append( Chaining( [[SVGGlyph.get_glyph_name(ligature)]], @@ -362,19 +360,18 @@ def build_conjuncts_fixup(self): def build_abvm(self): feature = "abvm" name = "abvm_topmarks" - top_mark_glyphs = [SVGGlyph.get_glyph_name( - c) for c in self.get_glyphs_from_named_classes('ML_TOP_MARKS')] + top_mark_glyphs = [SVGGlyph.get_glyph_name(c) for c in self.get_glyphs_from_named_classes("ML_TOP_MARKS")] anchors = {} visual_center_anchor_name = "vc" lettersWithMarks = sorted( - self.get_glyphs_from_named_classes('ML_CONSONANTS') + - self.get_glyphs_from_named_classes('ML_REPH_CONJUNCTS') + - self.get_glyphs_from_named_classes('ML_CONS_CONJUNCTS') + - self.get_glyphs_from_named_classes('ML_LA_CONJUNCTS') + - self.get_glyphs_from_named_classes('ML_TOP_MARKS')+ - ['\u25cc'] # Dotted circle. + self.get_glyphs_from_named_classes("ML_CONSONANTS") + + self.get_glyphs_from_named_classes("ML_REPH_CONJUNCTS") + + self.get_glyphs_from_named_classes("ML_CONS_CONJUNCTS") + + self.get_glyphs_from_named_classes("ML_LA_CONJUNCTS") + + self.get_glyphs_from_named_classes("ML_TOP_MARKS") + + ["\u25cc"] # Dotted circle. ) - vowel_signs = self.options.glyphs.classes['ML_VOWEL_SIGNS_CONJOINING'] + vowel_signs = self.options.glyphs.classes["ML_VOWEL_SIGNS_CONJOINING"] # FIXME, should do this for alt glyphs too for l in lettersWithMarks: glyph_name = SVGGlyph.get_glyph_name(l) @@ -386,12 +383,10 @@ def build_abvm(self): anchors[glyph.name] = {anchor.name: (anchor.x, anchor.y)} if glyph.name not in anchors: - anchors[glyph.name] = { - visual_center_anchor_name: (glyph.width/2, 0)} + anchors[glyph.name] = {visual_center_anchor_name: (glyph.width / 2, 0)} for vowel_sign in vowel_signs: - letterWithVowel = SVGGlyph.get_glyph_name( - l)+SVGGlyph.get_glyph_name(vowel_sign, prefix="_") + letterWithVowel = SVGGlyph.get_glyph_name(l) + SVGGlyph.get_glyph_name(vowel_sign, prefix="_") if letterWithVowel not in self: continue glyphWithVowel = self[letterWithVowel] @@ -409,23 +404,18 @@ def build_abvm(self): else: top_bases[glyphname] = position - tops = Attachment(visual_center_anchor_name, - visual_center_anchor_name, top_bases, top_marks) - routine = Routine(name=name, rules=[ - tops], languages=LANGUAGE_MALAYALAM) + tops = Attachment(visual_center_anchor_name, visual_center_anchor_name, top_bases, top_marks) + routine = Routine(name=name, rules=[tops], languages=LANGUAGE_MALAYALAM) self.fontFeatures.addFeature(feature, [routine]) - def build_blwm(self): feature = "blwm" name = "blwm_bottommarks" - bottom_mark_glyphs = [SVGGlyph.get_glyph_name( - c) for c in self.get_glyphs_from_named_classes('ML_BOTTOM_MARKS')] + bottom_mark_glyphs = [SVGGlyph.get_glyph_name(c) for c in self.get_glyphs_from_named_classes("ML_BOTTOM_MARKS")] anchors = {} bbvm_anchor_name = "bottom" lettersWithMarks = sorted( - self.get_glyphs_from_named_classes('ML_CONSONANTS')+ - self.get_glyphs_from_named_classes('ML_BOTTOM_MARKS') + self.get_glyphs_from_named_classes("ML_CONSONANTS") + self.get_glyphs_from_named_classes("ML_BOTTOM_MARKS") ) for l in lettersWithMarks: glyph_name = SVGGlyph.get_glyph_name(l) @@ -437,8 +427,7 @@ def build_blwm(self): anchors[glyph.name] = {anchor.name: (anchor.x, anchor.y)} if glyph.name not in anchors: - anchors[glyph.name] = { - bbvm_anchor_name: (glyph.width,0)} + anchors[glyph.name] = {bbvm_anchor_name: (glyph.width, 0)} self.fontFeatures.anchors = anchors bottom_bases = {} @@ -451,10 +440,8 @@ def build_blwm(self): else: bottom_bases[glyphname] = position - bottoms = Attachment(bbvm_anchor_name, - bbvm_anchor_name, bottom_bases, bottom_marks) - routine = Routine(name=name, rules=[ - bottoms], languages=LANGUAGE_MALAYALAM) + bottoms = Attachment(bbvm_anchor_name, bbvm_anchor_name, bottom_bases, bottom_marks) + routine = Routine(name=name, rules=[bottoms], languages=LANGUAGE_MALAYALAM) self.fontFeatures.addFeature(feature, [routine]) def build_gpos(self): @@ -463,23 +450,23 @@ def build_gpos(self): def build_gdef(self): ligatures = sorted( - self.get_glyphs_from_named_classes('ML_CONSONANTS') + - self.get_glyphs_from_named_classes('ML_REPH_CONJUNCTS') + - self.get_glyphs_from_named_classes('ML_CONS_CONJUNCTS') + - self.get_glyphs_from_named_classes('ML_LA_CONJUNCTS') + - self.get_glyphs_from_named_classes('LATIN_EXTRA') + self.get_glyphs_from_named_classes("ML_CONSONANTS") + + self.get_glyphs_from_named_classes("ML_REPH_CONJUNCTS") + + self.get_glyphs_from_named_classes("ML_CONS_CONJUNCTS") + + self.get_glyphs_from_named_classes("ML_LA_CONJUNCTS") + + self.get_glyphs_from_named_classes("LATIN_EXTRA") ) for ligature in ligatures: ligature_glyph_name = SVGGlyph.get_glyph_name(ligature) if ligature_glyph_name in self: self.fontFeatures.glyphclasses[ligature_glyph_name] = "ligature" - for c in self.get_glyphs_from_named_classes('LC_ALL') + self.get_glyphs_from_named_classes('UC_ALL'): + for c in self.get_glyphs_from_named_classes("LC_ALL") + self.get_glyphs_from_named_classes("UC_ALL"): c_glyph_name = SVGGlyph.get_glyph_name(c) if c_glyph_name in self: self.fontFeatures.glyphclasses[c_glyph_name] = "base" - for cons in self.get_glyphs_from_named_classes('ML_CONSONANTS'): + for cons in self.get_glyphs_from_named_classes("ML_CONSONANTS"): cons_glyph_name = SVGGlyph.get_glyph_name(cons) if cons_glyph_name in self: self.fontFeatures.glyphclasses[cons_glyph_name] = "base" @@ -487,38 +474,37 @@ def build_gdef(self): # FIXME: Use unicode instead of glyph names so that glyph names are always # calculated marks = [ - 'acutecmb', # U+0301 - 'brevecmb', # U+0306 - 'caroncmb', # U+030C - 'cedillacmb', # U+0327 - 'circumflexcmb', # U+0302 - 'commaaccent', # U+0326 - 'commaturnedabovecmb', # U+0312 - 'dieresiscmb', # U+0308 - 'dotaccentcmb', # U+0307 - 'gravecmb', # U+0300 - 'hungarumlautcmb', # U+030B - 'macroncmb', # U+0304 - 'ml_virama', # U+0D4D - 'ml_candrabindu', # U+0D01 - 'ml_circular_virama', # U+0D3C - 'ml_combining_anusvara_above', # U+0D00 + "acutecmb", # U+0301 + "brevecmb", # U+0306 + "caroncmb", # U+030C + "cedillacmb", # U+0327 + "circumflexcmb", # U+0302 + "commaaccent", # U+0326 + "commaturnedabovecmb", # U+0312 + "dieresiscmb", # U+0308 + "dotaccentcmb", # U+0307 + "gravecmb", # U+0300 + "hungarumlautcmb", # U+030B + "macroncmb", # U+0304 + "ml_virama", # U+0D4D + "ml_candrabindu", # U+0D01 + "ml_circular_virama", # U+0D3C + "ml_combining_anusvara_above", # U+0D00 # 'ml_sign_u', # U+0D41 # 'ml_sign_uu', # U+0D42 # 'ml_sign_vocalic_l', # U+0D62 # 'ml_sign_vocalic_ll', # U+0D63 # 'ml_sign_vocalic_r', # U+0D43 # 'ml_sign_vocalic_rr', # U+0D44 - 'ml_vertical_bar_virama', # U+0D3B - 'ml_dot_reph', # U+0D4E - 'ogonekcmb', # U+0328 - 'ringcmb', # U+030A - 'tildecmb', # U+0303 + "ml_vertical_bar_virama", # U+0D3B + "ml_dot_reph", # U+0D4E + "ogonekcmb", # U+0328 + "ringcmb", # U+030A + "tildecmb", # U+0303 ] for mark in marks: self.fontFeatures.glyphclasses[mark] = "mark" - def build_calt(self): feature = "calt" for script in self.options.glyphs.calts: @@ -528,27 +514,27 @@ def build_calt(self): for calt_def in self.options.glyphs.calts[script]: precontext = SVGGlyph.get_glyph_name(calt_def[0]) base = SVGGlyph.get_glyph_name(calt_def[1]) - replacement = base+"."+calt_def[2] + replacement = base + "." + calt_def[2] rules.append(Substitution([[base]], [[replacement]], precontext=[[precontext]])) routine = Routine(name=name, rules=rules, languages=languages) self.fontFeatures.addFeature(feature, [routine]) def build_salt(self): feature = "salt" - name="salts_lookup" - rules=[] + name = "salts_lookup" + rules = [] for base, alts in self.salts.items(): rules.append(Substitution([[base]], [[base] + alts])) - routine = Routine(name=name, rules=rules ) + routine = Routine(name=name, rules=rules) self.fontFeatures.addFeature(feature, [routine]) def build_aalt(self): feature = "aalt" - name="aalts_lookup" - rules=[] + name = "aalts_lookup" + rules = [] for base, alts in self.salts.items(): rules.append(Substitution([[base]], [alts])) - routine = Routine(name=name, rules=rules ) + routine = Routine(name=name, rules=rules) self.fontFeatures.addFeature(feature, [routine]) def getFeatures(self): @@ -578,26 +564,26 @@ def buildFeatures(self): def build(self, design_dir): empty_glyphs = { - '.null': 0, - 'nonmarkingreturn': 0, - 'uni00A0': 0x00A0, # NBSP - 'uni00AD': 0x00AD, # Soft hyphen - 'zwnj': 0x200C, - 'zwj': 0x200D, + ".null": 0, + "nonmarkingreturn": 0, + "uni00A0": 0x00A0, # NBSP + "uni00AD": 0x00AD, # Soft hyphen + "zwnj": 0x200C, + "zwj": 0x200D, } for g, u in empty_glyphs.items(): glyph = Glyph(name=g) - if u !=0 : + if u != 0: glyph.unicodes = [u] self.addGlyph(glyph) # Add space - space = Glyph(name='space') + space = Glyph(name="space") space.width = 260 space.unicodes = [0x0020] self.addGlyph(space) # NBSP width must be same as Space width - self['uni00A0'].width = space.width + self["uni00A0"].width = space.width for f in sorted(os.listdir(design_dir)): if not f.endswith(".svg"): @@ -608,7 +594,7 @@ def build(self, design_dir): self.addGlyph(svg_glyph.glif) if svg_glyph.alt: if svg_glyph.glyph_name not in self.salts: - self.salts[svg_glyph.glyph_name]=[] + self.salts[svg_glyph.glyph_name] = [] self.salts[svg_glyph.glyph_name].append(svg_glyph.glif.name) @@ -628,7 +614,7 @@ def build(self, design_dir): for b, c in horizontally_flippables.items(): compositename = SVGGlyph.get_glyph_name(c) basename = SVGGlyph.get_glyph_name(b) - if not basename in self: + if basename not in self: log.warn(f"{basename} glyph not found for horizontal flipping") continue self.newGlyph(compositename) @@ -668,114 +654,110 @@ def build(self, design_dir): composite.width = 0 for part in parts: basename = SVGGlyph.get_glyph_name(part) - if not basename in self: + if basename not in self: log.warn(f"{basename} glyph not found for doubling") continue baseGlyph = self[basename] component: Component = composite.instantiateComponent() - component.move((composite.width ,0)) + component.move((composite.width, 0)) component.baseGlyph = basename composite.width = composite.width + baseGlyph.width composite.appendComponent(component) diacritics = [ - '\u02c6', # 02c6 Circumflex accent - '\u00b4', # 00b4 Acute accent - '\u00b8', # 00b8 cedilla - '˚', # 02da Ring above - '¯', # 00af macron - '`', # 0060 grave accent - 'ˇ', # 02c7 caron - '¨', # 00a8 Diaeresis - '˙', # 02d9 dot above - '\u02dc', # 02dc Small tilde - '\u02d8', # 02d8 Breve - '\u02db', # 02db Ogonek - '\u02dd', # 02dd Double acute accent - '\u0326', # 0326 COMBINING COMMA BELOW + "\u02c6", # 02c6 Circumflex accent + "\u00b4", # 00b4 Acute accent + "\u00b8", # 00b8 cedilla + "˚", # 02da Ring above + "¯", # 00af macron + "`", # 0060 grave accent + "ˇ", # 02c7 caron + "¨", # 00a8 Diaeresis + "˙", # 02d9 dot above + "\u02dc", # 02dc Small tilde + "\u02d8", # 02d8 Breve + "\u02db", # 02db Ogonek + "\u02dd", # 02dd Double acute accent + "\u0326", # 0326 COMBINING COMMA BELOW ] for diacritic in diacritics: - for base in self.get_glyphs_from_named_classes('LC_ALL')+self.get_glyphs_from_named_classes('UC_ALL'): + for base in self.get_glyphs_from_named_classes("LC_ALL") + self.get_glyphs_from_named_classes("UC_ALL"): base_name = SVGGlyph.get_glyph_name(base) diacritc_name = SVGGlyph.get_glyph_name(diacritic) items = [base_name, diacritc_name] - composite_glyph_name = ''.join(items) + composite_glyph_name = "".join(items) if composite_glyph_name in agl.AGL2UV: composite_unicode = agl.AGL2UV[composite_glyph_name] else: continue - if chr(composite_unicode) not in self.get_glyphs_from_named_classes('LATIN_EXTRA'): + if chr(composite_unicode) not in self.get_glyphs_from_named_classes("LATIN_EXTRA"): continue assert diacritc_name in self - if base_name in ['i', 'j']: - base_name = 'dotless' + base_name - if base_name == 't' and diacritc_name == 'caron': - diacritc_name = 'quotesingle' - if base_name == 'L' and diacritc_name == 'cedilla': - diacritc_name = 'commaaccent' - if base_name == 'L' and diacritc_name == 'caron': - diacritc_name = 'quoteright' - if base_name == 'l' and diacritc_name == 'caron': - diacritc_name = 'quoteright' - if base_name == 'n' and diacritc_name == 'cedilla': - diacritc_name = 'commaaccent' - if base_name == 'g' and diacritc_name == 'cedilla': - diacritc_name = 'commaturnedmod' + if base_name in ["i", "j"]: + base_name = "dotless" + base_name + if base_name == "t" and diacritc_name == "caron": + diacritc_name = "quotesingle" + if base_name == "L" and diacritc_name == "cedilla": + diacritc_name = "commaaccent" + if base_name == "L" and diacritc_name == "caron": + diacritc_name = "quoteright" + if base_name == "l" and diacritc_name == "caron": + diacritc_name = "quoteright" + if base_name == "n" and diacritc_name == "cedilla": + diacritc_name = "commaaccent" + if base_name == "g" and diacritc_name == "cedilla": + diacritc_name = "commaturnedmod" items = [base_name, diacritc_name] assert base_name in self log.debug( - f"Compose {chr(composite_unicode)} - {composite_glyph_name} : {'+'.join(items)} : {composite_unicode}") - self.buildComposite(composite_glyph_name, - composite_unicode, items) + f"Compose {chr(composite_unicode)} - {composite_glyph_name} : {'+'.join(items)} : {composite_unicode}" + ) + self.buildComposite(composite_glyph_name, composite_unicode, items) - for base in self.get_glyphs_from_named_classes('ML_CONSONANTS'): + for base in self.get_glyphs_from_named_classes("ML_CONSONANTS"): base_glyph_name = SVGGlyph.get_glyph_name(base) if base_glyph_name not in self: continue - if base+'്ല' not in self.get_glyphs_from_named_classes('ML_LA_CONJUNCTS'): + if base + "്ല" not in self.get_glyphs_from_named_classes("ML_LA_CONJUNCTS"): continue - la_glyph_name = SVGGlyph.get_glyph_name(base+'്ല') - la_sign_glyph_name = SVGGlyph.get_glyph_name('്ല') - log.debug( - f"Compose {la_glyph_name} : {base_glyph_name}+{la_sign_glyph_name}") - self.buildComposite(la_glyph_name, None, [ - base_glyph_name, la_sign_glyph_name]) + la_glyph_name = SVGGlyph.get_glyph_name(base + "്ല") + la_sign_glyph_name = SVGGlyph.get_glyph_name("്ല") + log.debug(f"Compose {la_glyph_name} : {base_glyph_name}+{la_sign_glyph_name}") + self.buildComposite(la_glyph_name, None, [base_glyph_name, la_sign_glyph_name]) base_for_u = ( - self.get_glyphs_from_named_classes('ML_CONSONANTS') + - self.get_glyphs_from_named_classes('ML_CONS_CONJUNCTS') + - self.get_glyphs_from_named_classes('ML_LA_CONJUNCTS') + - self.get_glyphs_from_named_classes('ML_REPH_CONJUNCTS')+ - ["്യ"] + self.get_glyphs_from_named_classes("ML_CONSONANTS") + + self.get_glyphs_from_named_classes("ML_CONS_CONJUNCTS") + + self.get_glyphs_from_named_classes("ML_LA_CONJUNCTS") + + self.get_glyphs_from_named_classes("ML_REPH_CONJUNCTS") + + ["്യ"] ) for base in base_for_u: base_glyph_name = SVGGlyph.get_glyph_name(base) if base_glyph_name not in self: continue - u_glyph_name = SVGGlyph.get_glyph_name(base+'ു') - uu_glyph_name = SVGGlyph.get_glyph_name(base+'ൂ') - - if u_glyph_name not in self and u_glyph_name not in self.get_glyph_names_from_named_classes('ML_PREVENT_CONJUNCTS'): - log.debug( - f"Compose {u_glyph_name} : {base_glyph_name}+uu_drop_sign") - self.buildComposite(u_glyph_name, None, [ - base_glyph_name, 'u_drop_sign']) + u_glyph_name = SVGGlyph.get_glyph_name(base + "ു") + uu_glyph_name = SVGGlyph.get_glyph_name(base + "ൂ") + + if u_glyph_name not in self and u_glyph_name not in self.get_glyph_names_from_named_classes( + "ML_PREVENT_CONJUNCTS" + ): + log.debug(f"Compose {u_glyph_name} : {base_glyph_name}+uu_drop_sign") + self.buildComposite(u_glyph_name, None, [base_glyph_name, "u_drop_sign"]) if base_glyph_name in self.salts: - u_glyph_name_alt = u_glyph_name.replace(base_glyph_name, self.salts[base_glyph_name][0] ) - self.buildComposite(u_glyph_name_alt, None, [ - self.salts[base_glyph_name][0], 'u_drop_sign']) - self.salts[u_glyph_name]=[u_glyph_name_alt] - if uu_glyph_name not in self and uu_glyph_name not in self.get_glyph_names_from_named_classes('ML_PREVENT_CONJUNCTS'): - log.debug( - f"Compose {u_glyph_name} : {base_glyph_name}+uu_drop_sign") - self.buildComposite(uu_glyph_name, None, [ - base_glyph_name, 'uu_drop_sign']) + u_glyph_name_alt = u_glyph_name.replace(base_glyph_name, self.salts[base_glyph_name][0]) + self.buildComposite(u_glyph_name_alt, None, [self.salts[base_glyph_name][0], "u_drop_sign"]) + self.salts[u_glyph_name] = [u_glyph_name_alt] + if uu_glyph_name not in self and uu_glyph_name not in self.get_glyph_names_from_named_classes( + "ML_PREVENT_CONJUNCTS" + ): + log.debug(f"Compose {u_glyph_name} : {base_glyph_name}+uu_drop_sign") + self.buildComposite(uu_glyph_name, None, [base_glyph_name, "uu_drop_sign"]) if base_glyph_name in self.salts: - uu_glyph_name_alt = uu_glyph_name.replace(base_glyph_name, self.salts[base_glyph_name][0] ) - self.buildComposite(uu_glyph_name_alt, None, [ - self.salts[base_glyph_name][0], 'u_drop_sign']) - self.salts[uu_glyph_name]=[uu_glyph_name_alt] + uu_glyph_name_alt = uu_glyph_name.replace(base_glyph_name, self.salts[base_glyph_name][0]) + self.buildComposite(uu_glyph_name_alt, None, [self.salts[base_glyph_name][0], "u_drop_sign"]) + self.salts[uu_glyph_name] = [uu_glyph_name_alt] log.debug(f"Total glyph count: {len(self)}") @@ -806,8 +788,7 @@ def buildComposite(self, glyph_name: str, unicode, items: List): baseAnchors = baseGlyph.anchors currentGlyph = self[glyphName] glyphAnchors = currentGlyph.anchors - commonAnchorName = MalayalamFont.commonAnchor( - baseAnchors, glyphAnchors) + commonAnchorName = MalayalamFont.commonAnchor(baseAnchors, glyphAnchors) component = Component(composite) component.baseGlyph = glyphName @@ -828,7 +809,7 @@ def buildComposite(self, glyph_name: str, unicode, items: List): y = anchor["y"] - _anchor["y"] component.move((x, y)) composite.components.append(component) - composite.lib['public.markColor'] = '0.92, 0.93, 0.94, 1.0' # grey + composite.lib["public.markColor"] = "0.92, 0.93, 0.94, 1.0" # grey # Now current glyph is base glyph for next one, if any baseGlyph = currentGlyph @@ -838,12 +819,10 @@ def updateFontVersion(self): versionMajor, versionMinor = [int(num) for num in version.split(".")] self.info.versionMajor = versionMajor self.info.versionMinor = versionMinor - self.info.openTypeNameVersion = "Version %d.%03d" % ( - versionMajor, versionMinor) - psFamily = re.sub(r'\s', '', self.options.name) - psStyle = re.sub(r'\s', '', self.style) - self.info.openTypeNameUniqueID = "%s-%s:%d" % ( - psFamily, psStyle, now.year) + self.info.openTypeNameVersion = "Version %d.%03d" % (versionMajor, versionMinor) + psFamily = re.sub(r"\s", "", self.options.name) + psStyle = re.sub(r"\s", "", self.style) + self.info.openTypeNameUniqueID = "%s-%s:%d" % (psFamily, psStyle, now.year) self.info.openTypeHeadCreated = now.strftime("%Y/%m/%d %H:%M:%S") def setFontInfo(self): @@ -891,9 +870,9 @@ def setFontInfo(self): self.info.openTypeOS2Type = [] # The TypoAscender minus the TypoDescender should equal the unit square. # the height of the ascenders in units - self.info.openTypeOS2TypoAscender = round(self.info.ascender*1.2) + self.info.openTypeOS2TypoAscender = round(self.info.ascender * 1.2) # the depth of the descenders in units (negative value) - self.info.openTypeOS2TypoDescender = -round(self.info.descender*1.2) + self.info.openTypeOS2TypoDescender = -round(self.info.descender * 1.2) self.info.openTypeOS2TypoLineGap = 0 self.info.openTypeOS2UnicodeRanges = [0, 1, 2, 3, 23] self.info.openTypeOS2WeightClass = int(self.weight) @@ -903,9 +882,9 @@ def setFontInfo(self): # table's yMax, abs(yMin) values. If they are less than these values, # clipping can occur on Windows platforms # the top extremum of the font rendering box - self.info.openTypeOS2WinAscent = 1223 # As calculated by Fontbakery + self.info.openTypeOS2WinAscent = 1223 # As calculated by Fontbakery # the bottom extremum of the font rendering box (positive value) - self.info.openTypeOS2WinDescent = 919 # As calculated by Fontbakery + self.info.openTypeOS2WinDescent = 919 # As calculated by Fontbakery # When the win Metrics are significantly greater than the upm, the # linespacing can appear too loose. To counteract this, enabling the OS/2 # fsSelection bit 7 (Use_Typo_Metrics), will force Windows to use the OS/2 @@ -920,9 +899,9 @@ def setFontInfo(self): # - Mac OS X uses the hhea values. # - Windows uses OS/2 or Win, depending on the OS or fsSelection bit value. # The height of the ascenders in units - self.info.openTypeHheaAscender = self.info.openTypeOS2TypoAscender + self.info.openTypeHheaAscender = self.info.openTypeOS2TypoAscender # The depth of the descenders in units (negative value) - self.info.openTypeHheaDescender = self.info.openTypeOS2TypoDescender + self.info.openTypeHheaDescender = self.info.openTypeOS2TypoDescender self.info.openTypeHheaLineGap = 0 # postscript metrics diff --git a/tools/read_config.py b/tools/read_config.py index 28160fb8..dc382520 100644 --- a/tools/read_config.py +++ b/tools/read_config.py @@ -1,7 +1,8 @@ import sys + import yaml -config = yaml.load(open('config.yaml'), Loader=yaml.BaseLoader) +config = yaml.load(open("config.yaml"), Loader=yaml.BaseLoader) if __name__ == "__main__": key = sys.argv[1] diff --git a/tools/stat.py b/tools/stat.py index 1e4b071b..976d4089 100644 --- a/tools/stat.py +++ b/tools/stat.py @@ -1,12 +1,13 @@ """ Generate STAT tables for a Variable Font Family """ +import logging +import sys + +import yaml from fontTools import ttLib from fontTools.otlLib.builder import buildStatTable from munch import DefaultMunch -import yaml -import sys -import logging log = logging.getLogger(__name__) diff --git a/tools/svgglyph.py b/tools/svgglyph.py index 627371f5..51f1ba2b 100644 --- a/tools/svgglyph.py +++ b/tools/svgglyph.py @@ -3,59 +3,59 @@ import unicodedata import xml.etree.ElementTree as etree -from ufoLib2.objects import Glyph from fontTools import agl from fontTools.svgLib import SVGPath from fontTools.ufoLib import UFOLibError +from ufoLib2.objects import Glyph - -AGL2UV_EXTRA={ - 'onesuperior': 0x00B9, - 'twosuperior': 0x00B2, - 'threesuperior': 0x00B3, - 'foursuperior': 0x2074, - 'quotedblbasereversed': 0x2E42, - 'Acaron': 0x01CD, - 'acaron': 0x01CE, - 'Lcedilla': 0x013B, - 'lcedilla': 0x013C, - 'Kcedilla': 0x0136, - 'kcedilla': 0x0137, - 'Ncedilla': 0x0145, - 'Gcedilla': 0x0122, - 'gcedilla': 0x0123, - 'ncedilla': 0x0146, - 'Rcedilla': 0x0156, - 'rcedilla': 0x0157, - 'ringcmb': 0x030A, - 'tildecmb': 0x0303, - 'hungarumlautcmb': 0x030B, - 'cedillacmb': 0x0327, - 'acutecmb': 0x0301, - 'ogonekcmb': 0x0328, - 'dieresiscmb': 0x0308, - 'dotaccentcmb': 0x0307, - 'circumflexcmb': 0x0302, - 'commaturnedabovecmb': 0x0312, - 'caroncmb': 0x030C, - 'brevecmb': 0x0306, - 'macroncmb': 0x0304, - 'gravecmb': 0x0300, - 'commaturnedmod': 0x02BB, - 'commaaccent': 0x0326, - 'scommaaccent': 0x0219, - 'Scommaaccent': 0x0218, - 'Tcommaaccent': 0x021A, - 'tcommaaccent': 0x021B, - 'dotlessj': 0x0237, - 'jcaron': 0x01F0, - 'notdef': 0 +AGL2UV_EXTRA = { + "onesuperior": 0x00B9, + "twosuperior": 0x00B2, + "threesuperior": 0x00B3, + "foursuperior": 0x2074, + "quotedblbasereversed": 0x2E42, + "Acaron": 0x01CD, + "acaron": 0x01CE, + "Lcedilla": 0x013B, + "lcedilla": 0x013C, + "Kcedilla": 0x0136, + "kcedilla": 0x0137, + "Ncedilla": 0x0145, + "Gcedilla": 0x0122, + "gcedilla": 0x0123, + "ncedilla": 0x0146, + "Rcedilla": 0x0156, + "rcedilla": 0x0157, + "ringcmb": 0x030A, + "tildecmb": 0x0303, + "hungarumlautcmb": 0x030B, + "cedillacmb": 0x0327, + "acutecmb": 0x0301, + "ogonekcmb": 0x0328, + "dieresiscmb": 0x0308, + "dotaccentcmb": 0x0307, + "circumflexcmb": 0x0302, + "commaturnedabovecmb": 0x0312, + "caroncmb": 0x030C, + "brevecmb": 0x0306, + "macroncmb": 0x0304, + "gravecmb": 0x0300, + "commaturnedmod": 0x02BB, + "commaaccent": 0x0326, + "scommaaccent": 0x0219, + "Scommaaccent": 0x0218, + "Tcommaaccent": 0x021A, + "tcommaaccent": 0x021B, + "dotlessj": 0x0237, + "jcaron": 0x01F0, + "notdef": 0, } -UV2AGL_EXTRA={ - 0x0326: 'commaaccent', - 0x0328: 'ogonekcmb', +UV2AGL_EXTRA = { + 0x0326: "commaaccent", + 0x0328: "ogonekcmb", } + class SVGGlyph: def __init__(self, svg_file_path): self.svg_file_path = svg_file_path @@ -68,16 +68,15 @@ def __init__(self, svg_file_path): self.glyph_width = 0 self.glyph_height = 1000 self.glif = None - self.transform = '1 0 0 -1 0 0' + self.transform = "1 0 0 -1 0 0" + # Fill missing AGL2UV, UV2AGL values in agl # Fill missing AGL2UV, UV2AGL values in agl - # Fill missing AGL2UV, UV2AGL values in agl agl.AGL2UV.update(AGL2UV_EXTRA) agl.UV2AGL.update(UV2AGL_EXTRA) @staticmethod - def svg2glif(svg_file, name, width=0, height=0, unicodes=None, transform=None, - version=3, anchors=None): - """ Convert an SVG outline to a UFO glyph with given 'name', advance + def svg2glif(svg_file, name, width=0, height=0, unicodes=None, transform=None, version=3, anchors=None): + """Convert an SVG outline to a UFO glyph with given 'name', advance 'width' and 'height' (int), and 'unicodes' (list of int). Return the resulting string in GLIF format (default: version 2). If 'transform' is provided, apply a transformation matrix before the @@ -104,9 +103,8 @@ def transform_list(arg): def parse(self): svgObj = etree.parse(self.svg_file_path).getroot() parent_map = {c: p for p in svgObj.iter() for c in p} - self.svg_width = float(svgObj.get('width', '1000').replace("px", " ")) - self.svg_height = float(svgObj.get( - 'height', '1000').replace("px", " ")) + self.svg_width = float(svgObj.get("width", "1000").replace("px", " ")) + self.svg_height = float(svgObj.get("height", "1000").replace("px", " ")) # Filename without extension filename = os.path.splitext(os.path.basename(self.svg_file_path))[0] self.name = filename.split(".")[0] @@ -124,17 +122,17 @@ def parse(self): self.unicode = None self.glyph_name = SVGGlyph.get_glyph_name(self.name) if not self.glyph_name: - raise UFOLibError( - f"Could not calculate glyph name for {self.name}") + raise UFOLibError(f"Could not calculate glyph name for {self.name}") - prefix_map = {"sodipodi": "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd", - "inkscape": "http://www.inkscape.org/namespaces/inkscape"} - widthGuide = svgObj.find( - ".//sodipodi:guide/[@inkscape:label='width']", prefix_map) + prefix_map = { + "sodipodi": "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd", + "inkscape": "http://www.inkscape.org/namespaces/inkscape", + } + widthGuide = svgObj.find(".//sodipodi:guide/[@inkscape:label='width']", prefix_map) width = self.svg_width - if widthGuide != None: - width = int(float(widthGuide.get('position').split(',')[0])) + if widthGuide is not None: + width = int(float(widthGuide.get("position").split(",")[0])) # + int(svg_config['left']) + int(svg_config['right']) self.glyph_width = width @@ -144,108 +142,105 @@ def parse(self): # Calculate the transformation to do transform = SVGGlyph.transform_list(self.transform) - baseGuide = svgObj.find( - ".//sodipodi:guide/[@inkscape:label='base']", prefix_map) + baseGuide = svgObj.find(".//sodipodi:guide/[@inkscape:label='base']", prefix_map) base = -200 - if baseGuide != None: - base = int(float(baseGuide.get('position').split(',')[1])) * -1 + if baseGuide is not None: + base = int(float(baseGuide.get("position").split(",")[1])) * -1 transform[5] += self.svg_height + base # Y offset - anchorEls = svgObj.findall( - './/{http://www.w3.org/2000/svg}text', prefix_map) + anchorEls = svgObj.findall(".//{http://www.w3.org/2000/svg}text", prefix_map) anchors = [] - vc_guide = svgObj.find( - ".//sodipodi:guide/[@inkscape:label='vc']", prefix_map) - if vc_guide != None: - anchors.append({ - "x": float(vc_guide.get('position').split(',')[0]), - "y": 0, - "name": "vc" - }) + vc_guide = svgObj.find(".//sodipodi:guide/[@inkscape:label='vc']", prefix_map) + if vc_guide is not None: + anchors.append({"x": float(vc_guide.get("position").split(",")[0]), "y": 0, "name": "vc"}) - anchor_names=[] + anchor_names = [] try: for anchorEl in anchorEls: - x=0 - y=0 - name="" + x = 0 + y = 0 + name = "" parentEl = parent_map.get(anchorEl) if parentEl and "transform" in parentEl.attrib: transformstr = parentEl.get("transform") if "translate" in transformstr: - [x, y] = transformstr.strip().replace("translate(","").replace(")","").split(" ") + [x, y] = transformstr.strip().replace("translate(", "").replace(")", "").split(" ") if "matrix" in transformstr: - [lhs, y] = transformstr.strip().replace("matrix(","").replace(")","").split(" ") + [lhs, y] = transformstr.strip().replace("matrix(", "").replace(")", "").split(" ") x = lhs.split(",")[-1] x = float(x) y = float(y) - if 'x' in anchorEl.attrib: + if "x" in anchorEl.attrib: x = float(anchorEl.get("x")) - if 'y' in anchorEl.attrib: + if "y" in anchorEl.attrib: y = float(anchorEl.get("y")) if "{http://www.inkscape.org/namespaces/inkscape}label" in anchorEl.attrib: name = anchorEl.attrib["{http://www.inkscape.org/namespaces/inkscape}label"] else: name = anchorEl.text - anchor={ - "x": x, - "y": self.svg_height + base - y, - "name": name - } + anchor = {"x": x, "y": self.svg_height + base - y, "name": name} # print(anchor) anchor_names.append(name) - if name=='vc': - anchor['y']=0 + if name == "vc": + anchor["y"] = 0 anchors.append(anchor) except: print(f"Error while processing {self.__dict__}") traceback.print_exc() pass if "right" not in anchor_names: - anchors.append({ - "x": self.glyph_width, - "y": 0, - "name": "right" - }) + anchors.append({"x": self.glyph_width, "y": 0, "name": "right"}) try: glif_name = self.glyph_name if self.alt: glif_name = self.glyph_name + "." + self.alt - self.glif = SVGGlyph.svg2glif(self.svg_file_path, - name=glif_name, - width=self.glyph_width, - height=self.glyph_height, - unicodes=self.unicode, - transform=transform, - anchors=anchors, - version=3) + self.glif = SVGGlyph.svg2glif( + self.svg_file_path, + name=glif_name, + width=self.glyph_width, + height=self.glyph_height, + unicodes=self.unicode, + transform=transform, + anchors=anchors, + version=3, + ) except Exception: print(f"Error while processing {self.__dict__}") traceback.print_exc() @staticmethod def name_from_uc(char): - return unicodedata.name(char).replace('MALAYALAM SIGN', '').replace('MALAYALAM', '').replace('LETTER', '').replace('VOWEL', '').lower().strip().replace(' ', '_', -1).replace('-', '_') + return ( + unicodedata.name(char) + .replace("MALAYALAM SIGN", "") + .replace("MALAYALAM", "") + .replace("LETTER", "") + .replace("VOWEL", "") + .lower() + .strip() + .replace(" ", "_", -1) + .replace("-", "_") + ) @staticmethod def get_glyph_name(name: str, prefix="ml_") -> str: - name_parts = name.split('_') + name_parts = name.split("_") name = name_parts[0] if len(name_parts) > 1: - ext = '_'+'_'.join(name_parts[1:]) + ext = "_" + "_".join(name_parts[1:]) else: - ext = '' + ext = "" codepoint = ord(name[0]) if codepoint == 8205: - return 'zwj' - if name == 'notdef': - return '.notdef' + return "zwj" + if name == "notdef": + return ".notdef" if codepoint >= 3328 and codepoint <= 3455: if len(name) > 1: chillu_normalize_map = { - "ന്\u200d": 'ൻ', + "ന്\u200d": "ൻ", "ര്\u200d": "ർ", "ല്\u200d": "ൽ", "ള്\u200d": "ൾ", @@ -254,10 +249,10 @@ def get_glyph_name(name: str, prefix="ml_") -> str: "ക്\u200d": "ൿ", } name = chillu_normalize_map.get(name, name) - return prefix + "_".join(SVGGlyph.name_from_uc(c) for c in name)+ext + return prefix + "_".join(SVGGlyph.name_from_uc(c) for c in name) + ext else: - return prefix + SVGGlyph.name_from_uc(name)+ext + return prefix + SVGGlyph.name_from_uc(name) + ext if len(name) == 1: - return agl.UV2AGL.get(ord(name), f"uni{hex(codepoint).replace('0x','').upper()}")+ext - return agl.UV2AGL.get(name, name)+ext + return agl.UV2AGL.get(ord(name), f"uni{hex(codepoint).replace('0x','').upper()}") + ext + return agl.UV2AGL.get(name, name) + ext