Skip to content

Instantly share code, notes, and snippets.

@ryancdotorg
Created August 21, 2025 10:30
Show Gist options
  • Save ryancdotorg/73299cdd28e2182f2b42997153fd034c to your computer and use it in GitHub Desktop.
Save ryancdotorg/73299cdd28e2182f2b42997153fd034c to your computer and use it in GitHub Desktop.
#!/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