From e28c9a0718536786502c626159d2ada3745df8e2 Mon Sep 17 00:00:00 2001 From: mishamyrt Date: Sun, 4 Aug 2024 01:59:25 +0300 Subject: [PATCH] feat: rework build system, fix tables --- scripts/font.py | 80 ++++++++-------- scripts/liblilex/__init__.py | 4 +- scripts/liblilex/build/__init__.py | 4 +- scripts/liblilex/build/build.py | 67 +++++++++++++ scripts/liblilex/build/const.py | 13 --- scripts/liblilex/build/constants.py | 9 ++ scripts/liblilex/build/fontmake.py | 45 +++++++++ scripts/liblilex/build/make.py | 89 ------------------ scripts/liblilex/build/path.py | 14 +++ scripts/liblilex/build/post_process.py | 66 +++++++++++++ scripts/liblilex/features/__init__.py | 3 + .../liblilex/{generator => features}/const.py | 0 scripts/liblilex/features/features_loader.py | 94 +++++++++++++++++++ .../{generator => features}/ligatures.py | 0 .../{generator => features}/spacers.py | 0 scripts/liblilex/generator/__init__.py | 3 - scripts/liblilex/glyphs_font.py | 36 +------ scripts/utils/__init__.py | 1 - scripts/utils/files.py | 49 ---------- sources/Lilex.glyphs | 10 +- sources/STAT.yaml | 53 ++++------- sources/family_config.yaml | 8 +- .../_classes}/acc_comb_bottom.cls | 0 .../_classes}/acc_comb_case.cls | 0 .../_classes}/acc_comb_dflt.cls | 0 .../_classes}/acc_comb_top.cls | 0 .../_classes}/lca_alt1.fea | 0 .../_classes}/lca_cyrl_alt1.fea | 0 .../_classes}/lca_cyrl_dflt.fea | 0 .../_classes}/lca_dflt.fea | 0 .../_classes}/lcg_alt1.fea | 0 .../_classes}/lcg_dflt.fea | 0 .../_classes}/numbers_dflt.fea | 0 .../_classes}/numbers_dnom.fea | 0 .../_classes}/numbers_hex.fea | 0 .../_classes}/numbers_numr.fea | 0 .../_classes}/numbers_onum.fea | 0 .../_classes}/numbers_sinf.fea | 0 .../_classes}/numbers_sups.fea | 0 .../_classes}/uc_basic.cls | 0 sources/features/aalt.fea | 2 + 41 files changed, 372 insertions(+), 278 deletions(-) create mode 100644 scripts/liblilex/build/build.py delete mode 100644 scripts/liblilex/build/const.py create mode 100644 scripts/liblilex/build/constants.py create mode 100644 scripts/liblilex/build/fontmake.py delete mode 100644 scripts/liblilex/build/make.py create mode 100644 scripts/liblilex/build/path.py create mode 100644 scripts/liblilex/build/post_process.py rename scripts/liblilex/{generator => features}/const.py (100%) create mode 100644 scripts/liblilex/features/features_loader.py rename scripts/liblilex/{generator => features}/ligatures.py (100%) rename scripts/liblilex/{generator => features}/spacers.py (100%) delete mode 100644 scripts/liblilex/generator/__init__.py delete mode 100644 scripts/utils/files.py rename sources/{classes => features/_classes}/acc_comb_bottom.cls (100%) rename sources/{classes => features/_classes}/acc_comb_case.cls (100%) rename sources/{classes => features/_classes}/acc_comb_dflt.cls (100%) rename sources/{classes => features/_classes}/acc_comb_top.cls (100%) rename sources/{classes => features/_classes}/lca_alt1.fea (100%) rename sources/{classes => features/_classes}/lca_cyrl_alt1.fea (100%) rename sources/{classes => features/_classes}/lca_cyrl_dflt.fea (100%) rename sources/{classes => features/_classes}/lca_dflt.fea (100%) rename sources/{classes => features/_classes}/lcg_alt1.fea (100%) rename sources/{classes => features/_classes}/lcg_dflt.fea (100%) rename sources/{classes => features/_classes}/numbers_dflt.fea (100%) rename sources/{classes => features/_classes}/numbers_dnom.fea (100%) rename sources/{classes => features/_classes}/numbers_hex.fea (100%) rename sources/{classes => features/_classes}/numbers_numr.fea (100%) rename sources/{classes => features/_classes}/numbers_onum.fea (100%) rename sources/{classes => features/_classes}/numbers_sinf.fea (100%) rename sources/{classes => features/_classes}/numbers_sups.fea (100%) rename sources/{classes => features/_classes}/uc_basic.cls (100%) diff --git a/scripts/font.py b/scripts/font.py index 886b0c6f..72f782d1 100755 --- a/scripts/font.py +++ b/scripts/font.py @@ -1,16 +1,21 @@ #!/usr/bin/env python3 """Lilex helper entrypoint""" -import sys import os +import sys from argparse import BooleanOptionalAction -import yaml +from typing import TypedDict -from multiprocessing import Process +import yaml from arrrgs import arg, command, global_args, run -from glyphsLib import GSFeature, GSFont -from liblilex import DEFAULT_FORMATS, GlyphsFont, generate_spacers, render_ligatures -from utils import read_classes, read_features, read_files -from typing import TypedDict +from glyphsLib import GSFont +from liblilex import ( + FontFormat, + GlyphsFont, + OpenTypeFeatures, + build_family, + generate_spacers, + render_ligatures, +) CLASSES_DIR = "sources/classes" FEATURES_DIR = "sources/features" @@ -54,34 +59,28 @@ def generate(args, config: AppConfig): print("🟢 Font source successfully regenerated") @command( - arg("formats", nargs="*", help="Format list", default=DEFAULT_FORMATS), + arg("formats", nargs="*", help="Format list", default=['ttf', 'variable']), arg("--store_temp", "-s", action=BooleanOptionalAction, help="Not to delete the temporary folder after build") ) -def build(args, config: AppConfig): +async def build(args, config: AppConfig): """Builds a binary font file""" if not os.path.exists(config["output"]): os.makedirs(config["output"]) - proc = [] - for font in config["fonts"]: - p = Process(target=font.build, args=[ - args.formats, - config["output"], - args.store_temp - ]) - p.start() - proc.append(p) - for p in proc: - p.join() - if p.exitcode != 0: - print("Failed to build font") - sys.exit(1) + + formats = [] + for fmt in args.formats: + formats.append(FontFormat(fmt)) + + print("Building font binaries...") + fonts = [font.file for font in config["fonts"]] + await build_family(fonts, config["output"], formats) print("🟢 Font binaries successfully built") -def generate_calt(font: GlyphsFont) -> GSFeature: - glyphs = font.ligatures() - code = render_ligatures(glyphs) + read_files(f"{FEATURES_DIR}/calt") - return GSFeature("calt", code) +# def generate_calt(font: GlyphsFont) -> GSFeature: +# glyphs = font.ligatures() +# code = render_ligatures(glyphs) + read_files(f"{FEATURES_DIR}/calt") +# return GSFeature("calt", code) def move_to_calt(font: GSFont, features: list[str]): for fea in features: @@ -98,26 +97,29 @@ def move_to_calt(font: GSFont, features: list[str]): aalt = font.features["aalt"] aalt.code = aalt.code.replace(f"feature {fea};\n", "") -def set_features(font: GlyphsFont, cls: list[str], fea: list[GSFeature]): - features = fea.copy() - calt = generate_calt(font) - features.append(calt) - font.set_classes(cls) - font.set_features(features) - def load_font(args): with open(args.config, mode="r", encoding="utf-8") as file: config_file = yaml.safe_load(file) source_dir = config_file["source"] - cls = read_classes(os.path.join(source_dir, "classes")) - fea = read_features(os.path.join(source_dir, "features")) + features = OpenTypeFeatures(source_dir) config = AppConfig(output=config_file["output"], fonts=[]) for file in config_file["family"]: + font_config = config_file["family"][file] font = GlyphsFont(os.path.join(source_dir, file)) - config["fonts"].append(font) + skips = [] + if font_config is not None and "skip-features" in font_config: + skips = font_config["skip-features"] + feats, cls = features.items( + ignore_features=skips, + data={ + "calt": render_ligatures(font.ligatures()), + } + ) generate_spacers(font.ligatures(), font.file.glyphs) - if config_file["family"][file] == "all": - set_features(font, cls, fea) + font.set_classes(cls) + font.set_features(feats) + font.set_fea_names() + config["fonts"].append(font) return args, config if __name__ == "__main__": diff --git a/scripts/liblilex/__init__.py b/scripts/liblilex/__init__.py index 62ce444b..836bf4fd 100644 --- a/scripts/liblilex/__init__.py +++ b/scripts/liblilex/__init__.py @@ -1,4 +1,4 @@ """Lilex font library""" -from .build import DEFAULT_FORMATS, SUPPORTED_FORMATS -from .generator import generate_spacers, render_ligatures +from .build import FontFormat, build_family +from .features import OpenTypeFeatures, generate_spacers, render_ligatures from .glyphs_font import GlyphsFont diff --git a/scripts/liblilex/build/__init__.py b/scripts/liblilex/build/__init__.py index 2951d3f1..63575c97 100644 --- a/scripts/liblilex/build/__init__.py +++ b/scripts/liblilex/build/__init__.py @@ -1,3 +1,3 @@ """Lilex font builder module""" -from .const import DEFAULT_FORMATS, SUPPORTED_FORMATS -from .make import make +from .build import build_family +from .constants import FontFormat diff --git a/scripts/liblilex/build/build.py b/scripts/liblilex/build/build.py new file mode 100644 index 00000000..fe5fde62 --- /dev/null +++ b/scripts/liblilex/build/build.py @@ -0,0 +1,67 @@ +import asyncio +import os +from shutil import rmtree +from tempfile import mkdtemp + +from glyphsLib import GSFont, build_masters +from glyphsLib.builder.axes import find_base_style + +from .constants import FontFormat +from .fontmake import fontmake +from .post_process import post_process + + +def _build_design_space(font: GSFont) -> str: + """Creates temporary designspace""" + temp_dir = mkdtemp(prefix="lilex") + glyphs_file = os.path.join(temp_dir, "source.glyphs") + ufo_dir = os.path.join(temp_dir, "master_ufo") + file_name = font.familyName + base_style = find_base_style(font.masters) + if base_style != "": + file_name = f"{file_name}-{base_style}" + ds_file = os.path.join(ufo_dir, f"{file_name}.designspace") + font.save(glyphs_file) + build_masters( + glyphs_file, + ufo_dir, + write_skipexportglyphs=True, + designspace_path=ds_file, + ) + return (temp_dir, ds_file) + +async def _build_font( + font: GSFont, + output_dir: str, + formats: list[FontFormat] +) -> list[str]: + """Builds a font format. Returns a list of output files""" + temp_dir, ds_file = _build_design_space(font) + format_tasks = [] + for fmt in formats: + out_dir = os.path.join(output_dir, fmt.value) + format_tasks.append(fontmake(ds_file, out_dir, fmt)) + files = await asyncio.gather(*format_tasks) + rmtree(temp_dir) + return files + +def _group_by_format(output: list[list[tuple[str, list[str]]]]) -> dict[FontFormat, list[str]]: + result = {} + for design_space in output: + for fmt, files in design_space: + if fmt not in result: + result[fmt] = [] + result[fmt].extend(files) + return result + +async def build_family( + fonts: list[GSFont], + output_dir: str, + formats: list[FontFormat]): + """Builds a font family""" + tasks = [] + for font in fonts: + tasks.append(_build_font(font, output_dir, formats)) + files = await asyncio.gather(*tasks) + await post_process(_group_by_format(files)) + return files diff --git a/scripts/liblilex/build/const.py b/scripts/liblilex/build/const.py deleted file mode 100644 index 82934b35..00000000 --- a/scripts/liblilex/build/const.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Lilex builder constants""" - -SUPPORTED_FORMATS = [ - "ttf", - "otf", - "variable" -] - -DEFAULT_FORMATS = [ - "ttf", - "variable" -] - diff --git a/scripts/liblilex/build/constants.py b/scripts/liblilex/build/constants.py new file mode 100644 index 00000000..cbb64568 --- /dev/null +++ b/scripts/liblilex/build/constants.py @@ -0,0 +1,9 @@ +"""Lilex builder constants""" +from enum import Enum + + +class FontFormat(Enum): + """Font format enum""" + TTF = "ttf" + OTF = "otf" + VARIABLE = "variable" diff --git a/scripts/liblilex/build/fontmake.py b/scripts/liblilex/build/fontmake.py new file mode 100644 index 00000000..7eb6830e --- /dev/null +++ b/scripts/liblilex/build/fontmake.py @@ -0,0 +1,45 @@ +"""Make helpers""" +import asyncio +import os +import re + +from .constants import FontFormat +from .path import which + + +def _format_variable_path(font_dir: str, family_name: str, axis: list[str]) -> str: + return f"{font_dir}/{family_name}[{','.join(axis)}].ttf" + +async def fontmake( + design_space_path: str, + out_dir: str, + fmt: FontFormat, + axis: list[str] = None +): + """Wrapper for fontmake""" + if axis is None: + axis = ["wght"] + cmd = [ + which("fontmake"), + f'-m "{design_space_path}"', + f'-o "{fmt.value}"', + "--flatten-components", + "--autohint", + "--filter DecomposeTransformedComponentsFilter" + ] + font_name = os.path.basename(design_space_path).split(".")[0] + if fmt == FontFormat.VARIABLE: + cmd.append(f'--output-path "{_format_variable_path(out_dir, font_name, axis)}"') + else: + cmd.append("--interpolate") + cmd.append(f'--output-dir "{out_dir}"') + proc = await asyncio.create_subprocess_shell(" ".join(cmd), + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE) + _, stderr = await proc.communicate() + file_re = re.escape(out_dir) + r"/(.*)" + matches = re.findall(file_re, stderr.decode(), flags=re.MULTILINE) + files = [] + for match in matches: + files.append(os.path.join(out_dir, match)) + return fmt, list(set(files)) diff --git a/scripts/liblilex/build/make.py b/scripts/liblilex/build/make.py deleted file mode 100644 index 9392b655..00000000 --- a/scripts/liblilex/build/make.py +++ /dev/null @@ -1,89 +0,0 @@ -"""Make helpers""" -import subprocess as sp -from os import listdir -from shutil import which - -STAT_CONFIG = 'sources/STAT.yaml' -VARIABLE_SUFFIX = '[wght]' - -def _format_variable_path(font_dir: str, family_name: str) -> str: - return f"{font_dir}/{family_name}{VARIABLE_SUFFIX}.ttf" - -def _run(*args: str) -> bool: - with sp.Popen(" ".join(args), shell=True, stdout=sp.PIPE) as child: - child.communicate() - return child.returncode == 0 - -def _which(cmd: str) -> str: - """shutil.which that throws on None""" - result = which(cmd) - if result is None: - raise ValueError(f""" - Can't find {cmd}. Make sure you have venv configured with - `make configure` and activated. Alternatively, install {cmd} - externally and check by running `which {cmd}`. - """) - return result - -def _gftools(subcommand: str, *args: str) -> bool: - """Runs gftools subcommand""" - return _run(_which("gftools"), subcommand, *args) - -def _fix_variable(font_dir, family_name) -> bool: - """Generate STAT table for variable ttf""" - file_path = _format_variable_path(font_dir, family_name) - return _gftools( - "fix-font", - "--include-source-fixes", - f'--out "{file_path}"', - f'"{file_path}"' - ) and _gftools( - "gen-stat", - "--inplace", - f'--src "{STAT_CONFIG}"', - f'"{file_path}"' - ) - -def _fix_ttf(font_dir, _) -> bool: - """Fix bold fsSelection and macStyle""" - files = listdir(font_dir) - print(files) - for file in files: - file_path = f'{font_dir}/{file}' - success = _gftools( - "fix-font", - "--include-source-fixes", - f'--out "{file_path}"', - file_path - ) - if not success: - return False - return True - -POST_FIXES = { - "ttf": _fix_ttf, - "variable": _fix_variable -} - -def make(family_name: str, ds_path: str, fmt: str, out_dir: str) -> bool: - """Wrapper for fontmake""" - cmd = [ - _which("fontmake"), - f'-m "{ds_path}"', - f'-o "{fmt}"', - "--flatten-components", - "--autohint", - "--filter DecomposeTransformedComponentsFilter" - ] - if fmt == "variable": - cmd.append(f'--output-path "{_format_variable_path(out_dir, family_name)}"') - else: - cmd.append("--interpolate") - cmd.append(f'--output-dir "{out_dir}"') - success = _run(*cmd) - if not success: - return False - if fmt in POST_FIXES: - print(f'Running fixes for {fmt}') - success = success and POST_FIXES[fmt](out_dir, family_name) - return success diff --git a/scripts/liblilex/build/path.py b/scripts/liblilex/build/path.py new file mode 100644 index 00000000..a3586c5a --- /dev/null +++ b/scripts/liblilex/build/path.py @@ -0,0 +1,14 @@ +"""Path helpers""" +import shutil + + +def which(cmd: str) -> str: + """shutil.which that throws on None""" + result = shutil.which(cmd) + if result is None: + raise ValueError(f""" + Can't find {cmd}. Make sure you have venv configured with + `make configure` and activated. Alternatively, install {cmd} + externally and check by running `which {cmd}`. + """) + return result diff --git a/scripts/liblilex/build/post_process.py b/scripts/liblilex/build/post_process.py new file mode 100644 index 00000000..3340a931 --- /dev/null +++ b/scripts/liblilex/build/post_process.py @@ -0,0 +1,66 @@ +"""Fonts post processing""" +import asyncio +import os + +from fontTools.ttLib import TTFont +from gftools.stat import gen_stat_tables_from_config +from yaml import SafeLoader, load + +from .constants import FontFormat +from .path import which + +STAT_CONFIG = 'sources/STAT.yaml' + +async def _gftools(subcommand: str, *args: str): + """Wrapper for fontmake""" + cmd = [which("gftools"), subcommand, *args] + proc = await asyncio.create_subprocess_shell(" ".join(cmd), + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE) + await proc.communicate() + if proc.returncode != 0: + raise ChildProcessError(f"gftools {subcommand} failed") + +async def _fix_font(file: str): + await _gftools( + "fix-font", + "--include-source-fixes", + f'--out "{file}"', + f'"{file}"' + ) + + +async def _fix_variable(files: list[str]): + """Generate STAT table for variable ttf""" + fix_tasks = [] + for file in files: + fix_tasks.append(_fix_font(file)) + await asyncio.gather(*fix_tasks) + config = load(open(STAT_CONFIG, encoding="utf-8"), Loader=SafeLoader) + fonts = [TTFont(f) for f in files] + gen_stat_tables_from_config(config, fonts, has_italic=True) + for font in fonts: + dst = font.reader.file.name + if os.path.isfile(dst): + os.remove(dst) + font.save(dst) + +async def _fix_ttf(files: list[str]): + """Fix bold fsSelection and macStyle""" + fix_tasks = [] + for file in files: + fix_tasks.append(_fix_font(file)) + await asyncio.gather(*fix_tasks) + +POST_FIXES = { + FontFormat.TTF: _fix_ttf, + FontFormat.VARIABLE: _fix_variable +} + +async def post_process(font_map: dict[FontFormat, list[str]]): + """Run post fixes""" + tasks = [] + for fmt, files in font_map.items(): + if fmt in POST_FIXES: + tasks.append(POST_FIXES[fmt](files)) + await asyncio.gather(*tasks) diff --git a/scripts/liblilex/features/__init__.py b/scripts/liblilex/features/__init__.py index cf6de00a..fb35d876 100644 --- a/scripts/liblilex/features/__init__.py +++ b/scripts/liblilex/features/__init__.py @@ -1,3 +1,6 @@ """OpenType features utils""" +from .features_loader import OpenTypeFeatures +from .ligatures import render_ligatures from .name import feature_prefix, name_from_code +from .spacers import generate_spacers from .tpl import NAME_TPL diff --git a/scripts/liblilex/generator/const.py b/scripts/liblilex/features/const.py similarity index 100% rename from scripts/liblilex/generator/const.py rename to scripts/liblilex/features/const.py diff --git a/scripts/liblilex/features/features_loader.py b/scripts/liblilex/features/features_loader.py new file mode 100644 index 00000000..a81637c3 --- /dev/null +++ b/scripts/liblilex/features/features_loader.py @@ -0,0 +1,94 @@ +"""OpenType code loader""" +from __future__ import annotations + +import os +from os import listdir +from os.path import basename, isdir, isfile, join, splitext +from typing import TypeVar + +from glyphsLib import GSClass, GSFeature + +FEATURES_DIR = "features" +CLASSES_DIR = f"{FEATURES_DIR}/_classes" +FEATURE_EXT = ".fea" +CLASS_EXT = ".cls" + +T = TypeVar("T") + +def list_files(dir_path: str, ext: str = None) -> list[str]: + """List all files in the directory""" + files = [] + for file in listdir(dir_path): + name = splitext(file)[0] + if splitext(file)[0].endswith(".disabled"): + print(f'WARN: "{splitext(name)[0]}" is ignored') + continue + file_path = join(dir_path, file) + if isfile(file_path) and not file.startswith('.'): + files.append(file_path) + elif isdir(file_path): + files.extend(list_files(file_path)) + if ext is not None: + files = filter(lambda x: x.endswith(ext) == ext, files) + return sorted(files) + +def _read_classes(dir_path: str) -> list[GSClass]: + classes = [] + for path in list_files(dir_path): + cls = _read_gs_file(path, GSClass) + classes.append(cls) + return classes + +def _read_gs_file(path: str, constructor: T) -> T: + name = basename(path).split('.')[0] + with open(path, mode="r", encoding="utf-8") as file: + return constructor(name, file.read()) + +def _read_features(features_path: str) -> dict[str, GSFeature]: + feature_paths = list_files(features_path) + features = {} + for path in feature_paths: + if CLASSES_DIR in path: + continue + name = path.removeprefix(features_path + "/").removesuffix(FEATURE_EXT) + features[name] = _read_gs_file(path, GSFeature) + return features + +class OpenTypeFeatures: + """Utility class for loading OpenType code files. Can filter features fo sub-font.""" + + _path: str + _features: dict[str, GSFeature] + _classes: list[GSClass] + + def __init__(self, sources_dir: str): + fea_dir = os.path.join(sources_dir, FEATURES_DIR) + cls_dir = os.path.join(sources_dir, CLASSES_DIR) + self._path = sources_dir + self._features = _read_features(fea_dir) + self._classes = _read_classes(cls_dir) + + def items( + self, + ignore_features: list[str] = None, + data: dict[str, str] = None + ) -> tuple[list[GSFeature], list[GSClass]]: + """Returns a lists of features and classes""" + feat_map: dict[str, str] = {} + for name, feature in self._features.items(): + if name in ignore_features: + continue + fea_name = name + if "/" in name: + fea_name = name.split("/")[0] + if fea_name not in feat_map: + if data is not None and fea_name in data: + feat_map[fea_name] = data[fea_name] + else: + feat_map[fea_name] = feature.code + else: + feat_map[fea_name] += "\n" + feature.code + feats = [] + for name, code in feat_map.items(): + feats.append(GSFeature(name, code)) + return feats, self._classes diff --git a/scripts/liblilex/generator/ligatures.py b/scripts/liblilex/features/ligatures.py similarity index 100% rename from scripts/liblilex/generator/ligatures.py rename to scripts/liblilex/features/ligatures.py diff --git a/scripts/liblilex/generator/spacers.py b/scripts/liblilex/features/spacers.py similarity index 100% rename from scripts/liblilex/generator/spacers.py rename to scripts/liblilex/features/spacers.py diff --git a/scripts/liblilex/generator/__init__.py b/scripts/liblilex/generator/__init__.py deleted file mode 100644 index 87112165..00000000 --- a/scripts/liblilex/generator/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Lilex font generator""" -from .ligatures import render_ligatures -from .spacers import generate_spacers diff --git a/scripts/liblilex/glyphs_font.py b/scripts/liblilex/glyphs_font.py index c7e8b8cd..f598926c 100644 --- a/scripts/liblilex/glyphs_font.py +++ b/scripts/liblilex/glyphs_font.py @@ -2,8 +2,6 @@ from __future__ import annotations from pathlib import Path -from shutil import rmtree -from tempfile import mkdtemp from typing import Callable from glyphsLib import ( @@ -11,10 +9,8 @@ GSFeature, GSFont, GSGlyph, - build_masters, ) -from .build import SUPPORTED_FORMATS, make from .features import NAME_TPL, feature_prefix, name_from_code GlyphFilter = Callable[[GSGlyph], bool] @@ -92,23 +88,7 @@ def set_version(self, version: str): def clear_opened_files(self): self._font.DisplayStrings = "" - def build(self, formats: list[str], out_dir: str, store_temp=False) -> bool: - print("Preparing build environment") - temp_dir, ds_file = self._prepare_build() - success = True - for fmt in formats: - if fmt not in SUPPORTED_FORMATS: - print(f'Unsupported format "{fmt}"') - break - fmt_dir = f'{out_dir}/{fmt}' - success = success and make(self._name, ds_file, fmt, fmt_dir) - if store_temp: - print(f'Build directory: {temp_dir}') - else: - rmtree(temp_dir) - return success - - def _set_fea_names(self): + def set_fea_names(self): for fea in self._font.features: prefix = feature_prefix(fea.name) if prefix in NAME_TPL: @@ -116,17 +96,3 @@ def _set_fea_names(self): name = name_from_code(feature.code) if name is not None: feature.code = NAME_TPL[prefix].replace("$NAME", name) + feature.code - - def _prepare_build(self) -> str: - self._set_fea_names() - temp_dir = mkdtemp(prefix="LilexBuild") - glyphs_file = f'{temp_dir}/{self._font.familyName}.glyphs' - ufo_dir = f'{temp_dir}/master_ufo' - ds_file = f"{ufo_dir}/{self._name}.designspace" - self.save_to(glyphs_file) - build_masters( - glyphs_file, - ufo_dir, - write_skipexportglyphs=True - ) - return (temp_dir, ds_file) diff --git a/scripts/utils/__init__.py b/scripts/utils/__init__.py index e6f5c47c..244ab78e 100644 --- a/scripts/utils/__init__.py +++ b/scripts/utils/__init__.py @@ -1,3 +1,2 @@ """Lilex utilities module""" from .cli import print_gs, print_warn -from .files import read_classes, read_features, read_files diff --git a/scripts/utils/files.py b/scripts/utils/files.py deleted file mode 100644 index 893a53fa..00000000 --- a/scripts/utils/files.py +++ /dev/null @@ -1,49 +0,0 @@ -"""File utilities""" -from __future__ import annotations - -from os import listdir -from os.path import basename, isfile, join, splitext -from typing import TypeVar - -from glyphsLib import GSClass, GSFeature - -T = TypeVar("T") - -def list_files(dir_path: str) -> list[str]: - files = [] - for file in listdir(dir_path): - name = splitext(file)[0] - if splitext(file)[0].endswith(".disabled"): - print(f'WARN: "{splitext(name)[0]}" is ignored') - continue - file_path = join(dir_path, file) - if isfile(file_path) and not file.startswith('.'): - files.append(file_path) - return sorted(files) - -def read_classes(dir_path: str) -> list[GSClass]: - classes = [] - for path in list_files(dir_path): - cls = _read_gs_file(path, GSClass) - classes.append(cls) - return classes - -def read_features(dir_path: str) -> list[GSFeature]: - features = [] - for path in list_files(dir_path): - fea = _read_gs_file(path, GSFeature) - features.append(fea) - return features - -def _read_gs_file(path: str, constructor: T) -> T: - name = basename(path).split('.')[0] - with open(path, mode="r", encoding="utf-8") as file: - return constructor(name, file.read()) - -def read_files(dir_path: str) -> str: - """Reads all files in the directory and returns a summing string""" - result = "" - for path in list_files(dir_path): - with open(path, mode="r", encoding="utf-8") as file: - result += file.read() + "\n" - return result diff --git a/sources/Lilex.glyphs b/sources/Lilex.glyphs index f766ae93..ffbe8fa8 100644 --- a/sources/Lilex.glyphs +++ b/sources/Lilex.glyphs @@ -2,7 +2,8 @@ .appVersion = "3306"; .formatVersion = 3; DisplayStrings = ( -"₲" +"₲", +"/greater_equal.liga" ); axes = ( { @@ -94128,7 +94129,7 @@ width = 600; }, { glyphname = greater_equal.liga; -lastChange = "2024-08-01 19:48:23 +0000"; +lastChange = "2024-08-03 17:06:50 +0000"; layers = ( { guides = ( @@ -94206,11 +94207,6 @@ name = hr00; (247,186,l), (-287,-69,l) ); -}, -{ -pos = (-386,67); -ref = greater; -scale = (1.2362,0.9969); } ); width = 600; diff --git a/sources/STAT.yaml b/sources/STAT.yaml index cd395a36..d5f38f12 100644 --- a/sources/STAT.yaml +++ b/sources/STAT.yaml @@ -1,36 +1,17 @@ -"Lilex[wght].ttf": - - tag: wght - name: Weight - values: - - name: Thin - value: 100 - - name: ExtraLight - value: 200 - - name: Light - value: 300 - - name: Regular - value: 400 - - name: Medium - value: 500 - - name: SemiBold - value: 600 - - name: Bold - value: 700 -"Lilex-Italic[wght].ttf": - - tag: wght - name: Weight - values: - - name: Thin - value: 100 - - name: ExtraLight - value: 200 - - name: Light - value: 300 - - name: Regular - value: 400 - - name: Medium - value: 500 - - name: SemiBold - value: 600 - - name: Bold - value: 700 +- tag: wght + name: Weight + values: + - name: Thin + value: 100 + - name: ExtraLight + value: 200 + - name: Light + value: 300 + - name: Regular + value: 400 + - name: Medium + value: 500 + - name: SemiBold + value: 600 + - name: Bold + value: 700 \ No newline at end of file diff --git a/sources/family_config.yaml b/sources/family_config.yaml index 2319257b..5600a7f5 100644 --- a/sources/family_config.yaml +++ b/sources/family_config.yaml @@ -2,6 +2,10 @@ source: ./sources output: ./build family: Lilex.glyphs: - features: all Lilex-Italic.glyphs: - features: skip + skip-features: + - aalt + - ss01 + - calt/equal-arrows + - calt/hyphen-arrows + - calt/underscores diff --git a/sources/classes/acc_comb_bottom.cls b/sources/features/_classes/acc_comb_bottom.cls similarity index 100% rename from sources/classes/acc_comb_bottom.cls rename to sources/features/_classes/acc_comb_bottom.cls diff --git a/sources/classes/acc_comb_case.cls b/sources/features/_classes/acc_comb_case.cls similarity index 100% rename from sources/classes/acc_comb_case.cls rename to sources/features/_classes/acc_comb_case.cls diff --git a/sources/classes/acc_comb_dflt.cls b/sources/features/_classes/acc_comb_dflt.cls similarity index 100% rename from sources/classes/acc_comb_dflt.cls rename to sources/features/_classes/acc_comb_dflt.cls diff --git a/sources/classes/acc_comb_top.cls b/sources/features/_classes/acc_comb_top.cls similarity index 100% rename from sources/classes/acc_comb_top.cls rename to sources/features/_classes/acc_comb_top.cls diff --git a/sources/classes/lca_alt1.fea b/sources/features/_classes/lca_alt1.fea similarity index 100% rename from sources/classes/lca_alt1.fea rename to sources/features/_classes/lca_alt1.fea diff --git a/sources/classes/lca_cyrl_alt1.fea b/sources/features/_classes/lca_cyrl_alt1.fea similarity index 100% rename from sources/classes/lca_cyrl_alt1.fea rename to sources/features/_classes/lca_cyrl_alt1.fea diff --git a/sources/classes/lca_cyrl_dflt.fea b/sources/features/_classes/lca_cyrl_dflt.fea similarity index 100% rename from sources/classes/lca_cyrl_dflt.fea rename to sources/features/_classes/lca_cyrl_dflt.fea diff --git a/sources/classes/lca_dflt.fea b/sources/features/_classes/lca_dflt.fea similarity index 100% rename from sources/classes/lca_dflt.fea rename to sources/features/_classes/lca_dflt.fea diff --git a/sources/classes/lcg_alt1.fea b/sources/features/_classes/lcg_alt1.fea similarity index 100% rename from sources/classes/lcg_alt1.fea rename to sources/features/_classes/lcg_alt1.fea diff --git a/sources/classes/lcg_dflt.fea b/sources/features/_classes/lcg_dflt.fea similarity index 100% rename from sources/classes/lcg_dflt.fea rename to sources/features/_classes/lcg_dflt.fea diff --git a/sources/classes/numbers_dflt.fea b/sources/features/_classes/numbers_dflt.fea similarity index 100% rename from sources/classes/numbers_dflt.fea rename to sources/features/_classes/numbers_dflt.fea diff --git a/sources/classes/numbers_dnom.fea b/sources/features/_classes/numbers_dnom.fea similarity index 100% rename from sources/classes/numbers_dnom.fea rename to sources/features/_classes/numbers_dnom.fea diff --git a/sources/classes/numbers_hex.fea b/sources/features/_classes/numbers_hex.fea similarity index 100% rename from sources/classes/numbers_hex.fea rename to sources/features/_classes/numbers_hex.fea diff --git a/sources/classes/numbers_numr.fea b/sources/features/_classes/numbers_numr.fea similarity index 100% rename from sources/classes/numbers_numr.fea rename to sources/features/_classes/numbers_numr.fea diff --git a/sources/classes/numbers_onum.fea b/sources/features/_classes/numbers_onum.fea similarity index 100% rename from sources/classes/numbers_onum.fea rename to sources/features/_classes/numbers_onum.fea diff --git a/sources/classes/numbers_sinf.fea b/sources/features/_classes/numbers_sinf.fea similarity index 100% rename from sources/classes/numbers_sinf.fea rename to sources/features/_classes/numbers_sinf.fea diff --git a/sources/classes/numbers_sups.fea b/sources/features/_classes/numbers_sups.fea similarity index 100% rename from sources/classes/numbers_sups.fea rename to sources/features/_classes/numbers_sups.fea diff --git a/sources/classes/uc_basic.cls b/sources/features/_classes/uc_basic.cls similarity index 100% rename from sources/classes/uc_basic.cls rename to sources/features/_classes/uc_basic.cls diff --git a/sources/features/aalt.fea b/sources/features/aalt.fea index 0e5b680b..5da60ee0 100644 --- a/sources/features/aalt.fea +++ b/sources/features/aalt.fea @@ -1,3 +1,5 @@ +# Name: All alternatives + feature frac; feature locl; feature numr;