#!/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='')