Created
August 21, 2025 10:30
-
-
Save ryancdotorg/73299cdd28e2182f2b42997153fd034c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| from sys import argv, exit, stdin, stdout, stderr, version_info | |
| from functools import partial | |
| eprint = partial(print, file=stderr) | |
| # Python standard library imports | |
| import os | |
| from io import BytesIO, TextIOWrapper | |
| # Third party library imports | |
| # End imports | |
| width_shape = [None, None, 416, 456, 500, 548, 600, 658, 720] | |
| width_menu = [1, 2, 3, 4, 5, 6, 7, 8, 9] | |
| width_name = [ | |
| 'ultracondensed', 'extracondensed', 'condensed', | |
| 'semicondensed', 'normal', 'semiextended', | |
| 'extended', 'extraextended', 'ultraextended', | |
| ] | |
| width_css = [ | |
| 'ultra-condensed', 'extra-condensed', 'condensed', | |
| 'semi-condensed', 'normal', 'semi-expanded', | |
| 'expanded', 'extra-expanded', 'ultra-expanded', | |
| ] | |
| def default(v, d): | |
| if v is not None: return v | |
| elif callable(d): return d() | |
| else: return d | |
| class BuildPlan: | |
| def __init__(self, label, family, *, buffer=None, **kw): | |
| self.bin = default(buffer, BytesIO) | |
| self.txt = TextIOWrapper(self.bin, write_through=True) | |
| self.label = label | |
| self.section() | |
| kw.pop('family', None) | |
| self.value('family', family) | |
| self.value('spacing', kw.pop('spacing', 'normal')) | |
| self.value('serifs', kw.pop('serifs', 'sans')) | |
| for k, v in kw.items(): | |
| self.value(k, v) | |
| def section(self, *a, **kw): | |
| pos = self.bin.tell() | |
| self.bin.seek(-2, os.SEEK_END) | |
| last = self.bin.read(2) | |
| self.bin.seek(pos, os.SEEK_SET) | |
| if last not in (b'', b'\n\n'): | |
| self.print() | |
| self.print(f'[buildPlans.{".".join([self.label, *a])}]') | |
| for k, v in kw.items(): | |
| self.value(k, v) | |
| def value(self, k, v): | |
| k = k.replace('_', '-') | |
| if v is True: v = 'true' | |
| elif v is False: v = 'false' | |
| elif isinstance(v, str): v = f'"{v}"' | |
| self.print(f'{k} = {v}') | |
| def print(self, *a, **kw): | |
| kw['file'] = self.txt | |
| kw['flush'] = True | |
| print(*a, **kw) | |
| def slope(self, name, *, shape=None, menu=None, css=None, angle=None): | |
| if name == 'upright': | |
| angle = default(menu, 0) | |
| shape = default(shape, name) | |
| menu = default(menu, name) | |
| css = default(css, 'normal') | |
| elif name in ('italic', 'oblique'): | |
| angle = default(menu, 9.4) | |
| css = default(css, name) | |
| shape = default(shape, name) | |
| menu = default(menu, name) | |
| css = default(css, name) | |
| if angle is None: raise ValueError('must specify `angle` parameter') | |
| self.section( | |
| 'slopes', name, | |
| angle=angle, | |
| shape=shape, | |
| menu=menu, | |
| css=css, | |
| ) | |
| def weight(self, name, *, shape=None, menu=None, css=None): | |
| if name == 'thin': shape = default(shape, 100) | |
| elif name == 'extralight': shape = default(shape, 200) | |
| elif name == 'light': shape = default(shape, 300) | |
| elif name == 'semilight': shape = default(shape, 350) | |
| elif name == 'regular': shape = default(shape, 400) | |
| elif name == 'book': shape = default(shape, 450) | |
| elif name == 'medium': shape = default(shape, 500) | |
| elif name == 'semibold': shape = default(shape, 600) | |
| elif name == 'bold': shape = default(shape, 700) | |
| elif name == 'extrabold': shape = default(shape, 800) | |
| elif name == 'heavy': shape = default(shape, 900) | |
| menu = default(menu, shape) | |
| css = default(css, menu) | |
| if shape is None: raise ValueError('must specify `shape` parameter') | |
| self.section( | |
| 'weights', name, | |
| shape=shape, | |
| menu=menu, | |
| css=css, | |
| ) | |
| def width(self, name, *, shape=None, menu=None, css=None, adjust=0): | |
| index = width_name.index(name) if name in width_name else -1 | |
| if index != -1: | |
| shape = default(shape, width_shape[index+adjust]) | |
| menu = default(menu, width_menu[index]) | |
| css = default(css, width_css[index]) | |
| if shape is None: raise ValueError('must specify `shape` parameter') | |
| if menu is None: raise ValueError('must specify `menu` parameter') | |
| if css is None: raise ValueError('must specify `css` parameter') | |
| self.section( | |
| 'widths', name, | |
| shape=shape, | |
| menu=menu, | |
| css=css, | |
| ) | |
| def variants(self, name='design', **kw): | |
| self.section('variants', name, **kw) | |
| class PersistantBytesIO(BytesIO): | |
| def close(self): pass | |
| def _close(self): super().close() | |
| bio = PersistantBytesIO() | |
| p = BuildPlan( | |
| 'iosevka-hex', 'Iosevka Hex', buffer=bio, | |
| noCvSs=True, noLigation=True, | |
| ) | |
| p.variants( | |
| capital_a = 'curly-serifless', | |
| capital_b = 'more-asymmetric-serifless', | |
| capital_d = 'more-rounded-serifless', | |
| zero = 'unslashed', | |
| one = 'base', | |
| two = 'curly-neck-serifless', | |
| four = 'open-serifless', | |
| ) | |
| p.weight('regular') | |
| p.slope('upright') | |
| p = BuildPlan('template-sans', 'Template Sans', buffer=bio, serifs='sans') | |
| p.variants( | |
| capital_a = 'curly-serifless', | |
| capital_b = 'more-asymmetric-serifless', | |
| capital_d = 'more-rounded-serifless', | |
| capital_g = 'toothless-corner-serifless-hooked', | |
| g = 'double-storey', | |
| i = 'hooky', | |
| l = 'serifed-semi-tailed', | |
| m = 'short-leg-serifless', | |
| w = 'straight-serifless', | |
| x = 'straight-serifless', | |
| y = 'straight-turn-serifless', | |
| long_s = 'flat-hook-middle-serifed', | |
| eszet = 'longs-s-lig-serifless', | |
| capital_delta = 'curly', | |
| lower_iota = 'serifed-semi-tailed', | |
| capital_lambda = 'curly-serifless', | |
| lower_lambda = 'curly-turn', | |
| lower_tau = 'short-tailed', | |
| cyrl_capital_u = 'straight-turn-serifless', | |
| cyrl_u = 'straight-turn-serifless', | |
| cyrl_ef = 'split-serifless', | |
| zero = 'tall-slashed', | |
| one = 'base', | |
| two = 'curly-neck-serifless', | |
| three = 'two-arcs', | |
| four = 'closed-serifless', | |
| five = 'upright-arched-serifless', | |
| six = 'closed-contour', | |
| seven = 'straight-serifless', | |
| eight = 'crossing-asymmetric', | |
| nine = 'closed-contour', | |
| tilde = 'low', | |
| asterisk = 'penta-low', | |
| underscore = 'above-baseline', | |
| caret = 'low', | |
| paren = 'normal', | |
| brace = 'curly', | |
| guillemet = 'straight', | |
| number_sign = 'upright', | |
| ampersand = 'upper-open', | |
| at = 'threefold', | |
| dollar = 'through-cap', | |
| percent = 'rings-continuous-slash', | |
| bar = 'natural-slope', | |
| ascii_single_quote = 'straight', | |
| ascii_grave = 'straight', | |
| question = 'smooth', | |
| pilcrow = 'high', | |
| cent = 'through-cap', | |
| micro_sign = 'tailed-serifless', | |
| ) | |
| p = BuildPlan( | |
| 'iosevka-ryanc', 'Iosevka RyanC', buffer=bio, | |
| buildTextureFeature=True, | |
| exportGlyphNames=True, | |
| noCvSs=True, | |
| ) | |
| for x in ('upright', 'italic'): p.slope(x) | |
| p.section('variants', inherits='buildPlans.template-sans') | |
| p.variants('italic', | |
| a = 'single-storey-tailed', | |
| b = 'toothed-serifless', | |
| c = 'serifless', | |
| d = 'tailed-serifless', | |
| e = 'rounded', | |
| f = 'tailed', | |
| g = 'single-storey-serifless', | |
| h = 'tailed-serifless', | |
| i = 'tailed-serifed', | |
| j = 'serifless', | |
| k = 'cursive-serifless', | |
| l = 'tailed-serifed', | |
| m = 'short-leg-tailed-serifless', | |
| n = 'tailed-serifless', | |
| p = 'eared-serifless', | |
| q = 'tailed-serifless', | |
| r = 'serifless', | |
| s = 'serifless', | |
| t = 'bent-hook', | |
| u = 'tailed-serifless', | |
| v = 'cursive-serifless', | |
| w = 'cursive-serifless', | |
| x = 'semi-chancery-curly-serifless', | |
| y = 'cursive-serifless', | |
| z = 'curly-serifless', | |
| long_s = 'flat-hook-tailed-middle-serifed', | |
| eszet = 'longs-s-lig-tailed-serifless', | |
| lower_iota = 'serifed-semi-tailed', | |
| cyrl_zhe = 'cursive', | |
| cyrl_el = 'tailed', | |
| cyrl_en = 'tailed-serifless', | |
| cyrl_u = 'cursive-serifless', | |
| cyrl_ef = 'split-cursive', | |
| cyrl_che = 'tailed', | |
| cyrl_yeri = 'cursive', | |
| cyrl_yery = 'cursive-tailed', | |
| cyrl_ya = 'straight-tailed-serifless', | |
| ) | |
| p = BuildPlan( | |
| 'iosevka-ryanc-web', 'Iosevka RyanC Web', buffer=bio, | |
| buildTextureFeature=True, | |
| exportGlyphNames=False, | |
| noCvSs=True, | |
| ) | |
| for x in ('upright', 'italic'): p.slope(x) | |
| for x in ('regular', 'bold'): p.weight(x) | |
| p.width('normal', adjust=0) | |
| p.section('variants', inherits='buildPlans.iosevka-ryanc') | |
| p = BuildPlan( | |
| 'iosevka-ryanc-aile', 'Iosevka RyanC Aile', buffer=bio, | |
| spacing='quasi-proportional', serifs='sans', | |
| exportGlyphNames=False, | |
| noCvSs=True, | |
| ) | |
| for x in ('upright', 'italic'): p.slope(x) | |
| for x in ('regular', 'bold'): p.weight(x) | |
| p.width('normal', adjust=2) | |
| p.section('variants', inherits='buildPlans.template-sans') | |
| p.variants( | |
| capital_i = 'short-serifed', | |
| capital_j = 'serifless', | |
| f = 'flat-hook-serifless', | |
| i = 'serifless', | |
| j = 'flat-hook-serifless', | |
| l = 'hooky', | |
| r = 'compact-serifless', | |
| t = 'flat-hook', | |
| at = 'fourfold', | |
| ) | |
| p = BuildPlan( | |
| 'iosevka-ryanc-etoile', 'Iosevka RyanC Etoile', buffer=bio, | |
| spacing='quasi-proportional', serifs='slab', | |
| exportGlyphNames=False, | |
| noCvSs=True, | |
| ) | |
| for x in ('upright', 'italic'): p.slope(x) | |
| for x in ('regular', 'bold'): p.weight(x) | |
| p.width('normal', adjust=2) | |
| p.variants( | |
| capital_a = 'curly-base-serifed', | |
| capital_b = 'more-asymmetric-bilateral-serifed', | |
| capital_d = 'more-rounded-bilateral-serifed', | |
| capital_g = 'toothless-corner-serifed-capped', | |
| capital_i = 'serifed', | |
| capital_m = 'flat-bottom-serifed', | |
| capital_w = 'straight-flat-top-serifed', | |
| f = 'flat-hook-serifed', | |
| i = 'serifed-asymmetric', | |
| j = 'flat-hook-serifed', | |
| l = 'serifed-asymmetric', | |
| t = 'flat-hook', | |
| w = 'straight-flat-top-serifed', | |
| long_s = 'flat-hook-double-serifed', | |
| eszet = 'longs-s-lig-bottom-serifed', | |
| capital_delta = 'curly', | |
| lower_iota = 'serifed-flat-tailed', | |
| capital_lambda = 'curly-base-serifed', | |
| lower_lambda = 'curly-turn', | |
| lower_mu = 'tailed-serifed', | |
| lower_tau = 'flat-tailed', | |
| cyrl_em = 'flat-bottom-serifed', | |
| zero = 'tall-slashed', | |
| one = 'base', | |
| two = 'curly-neck-serifless', | |
| three = 'two-arcs', | |
| four = 'closed-serifless', | |
| five = 'upright-arched-serifless', | |
| six = 'closed-contour', | |
| seven = 'straight-serifed', | |
| eight = 'crossing-asymmetric', | |
| nine = 'closed-contour', | |
| diacritic_dot = 'square', | |
| punctuation_dot = 'square', | |
| tilde = 'low', | |
| asterisk = 'penta-low', | |
| underscore = 'above-baseline', | |
| caret = 'low', | |
| paren = 'normal', | |
| brace = 'curly', | |
| guillemet = 'straight', | |
| number_sign = 'upright', | |
| ampersand = 'upper-open', | |
| at = 'fourfold', | |
| dollar = 'through-cap', | |
| percent = 'rings-continuous-slash', | |
| bar = 'natural-slope', | |
| ascii_single_quote = 'straight', | |
| ascii_grave = 'straight', | |
| question = 'smooth', | |
| pilcrow = 'high', | |
| cent = 'through-cap', | |
| micro_sign = 'tailed-serifed', | |
| ) | |
| p.variants('italic', | |
| b = 'toothed-motion-serifed', | |
| f = 'flat-hook-tailed', | |
| h = 'tailed-motion-serifed', | |
| i = 'serifed-flat-tailed', | |
| l = 'serifed-flat-tailed', | |
| m = 'tailed-top-left-serifed', | |
| n = 'tailed-motion-serifed', | |
| p = 'eared-motion-serifed', | |
| q = 'tailed-motion-serifed', | |
| v = 'cursive-serifed', | |
| w = 'cursive-serifed', | |
| long_s = 'flat-hook-tailed-middle-serifed', | |
| eszet = 'longs-s-lig-tailed-serifless', | |
| ) | |
| for spacing, name in (('term', 'Term'), ('fixed', 'Fixed')): | |
| p = BuildPlan( | |
| f'iosevka-ryanc-{spacing}', f'Iosevka RyanC {name}', buffer=bio, | |
| spacing=spacing, | |
| buildTextureFeature=True, | |
| exportGlyphNames=True, | |
| noCvSs=True, | |
| ) | |
| for x in ('upright', 'italic'): p.slope(x) | |
| for x in ('extralight', 'light', 'semilight', 'regular', 'medium', 'semibold', 'bold', 'extrabold'): p.weight(x) | |
| p.width('normal') | |
| p.section('variants', inherits='buildPlans.iosevka-ryanc') | |
| p = BuildPlan( | |
| f'iosevka-ryanc-{spacing}-ext', f'Iosevka RyanC {name}Ext', buffer=bio, | |
| spacing=spacing, | |
| exportGlyphNames=True, | |
| noCvSs=True, | |
| ) | |
| for x in ('upright', 'italic'): p.slope(x) | |
| for x in ('extralight', 'light', 'semilight', 'regular', 'medium', 'semibold', 'bold', 'extrabold'): p.weight(x) | |
| #for x in ('normal', 'condensed'): p.width(x, adjust=2) | |
| p.width('normal', adjust=2) | |
| p.section('variants', inherits='buildPlans.iosevka-ryanc') | |
| print(bio.getvalue().decode(), end='') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment