├── website ├── .prettierignore ├── src │ ├── env.d.ts │ ├── utils │ │ ├── text-animation │ │ │ ├── web-components │ │ │ │ ├── index.ts │ │ │ │ └── waving-glyphs │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── waving-glyphs-element.ts │ │ │ │ │ └── wave-animator.ts │ │ │ ├── index.ts │ │ │ ├── variants.ts │ │ │ ├── axisLoop.ts │ │ │ ├── renderers │ │ │ │ ├── magnifying-glass.ts │ │ │ │ └── weight.ts │ │ │ ├── nodes.ts │ │ │ └── geometry.ts │ │ └── environment.ts │ ├── styles │ │ ├── theme │ │ │ ├── transitions.scss │ │ │ ├── border-radius.scss │ │ │ ├── spaces.scss │ │ │ ├── colors.scss │ │ │ └── typography.scss │ │ └── styles.scss │ ├── dom.d.ts │ ├── stores │ │ ├── superpowers.ts │ │ └── glyphs.ts │ ├── components │ │ ├── FloatingActions │ │ │ ├── index.ts │ │ │ ├── FloatingPanel.astro │ │ │ ├── FloatingAction.astro │ │ │ └── FloatingToggle.astro │ │ ├── GlyphAnimations │ │ │ ├── AnimatedGlyphs.astro │ │ │ └── WavingGlyphs.astro │ │ ├── LayoutBlock.astro │ │ ├── Link.astro │ │ ├── ReleaseLinks.astro │ │ ├── Button.astro │ │ ├── Stack.astro │ │ ├── Slider.astro │ │ ├── Typography.astro │ │ ├── Toggle.astro │ │ ├── TextArea.astro │ │ └── Powerline.astro │ ├── pages │ │ ├── preview.astro │ │ └── index.astro │ ├── layouts │ │ └── Layout.astro │ └── blocks │ │ ├── Hero │ │ ├── InformationColumn.astro │ │ ├── ReleaseLinks.astro │ │ └── Hero.astro │ │ ├── Glyphs │ │ ├── PreviewRow.astro │ │ ├── BetaTag.astro │ │ └── WeightSlider.astro │ │ └── Superpowers │ │ ├── PreviewLigatures.astro │ │ ├── PreviewWeight.astro │ │ ├── PreviewPowerline.astro │ │ ├── PreviewAligns.astro │ │ ├── FeatureRow.astro │ │ └── Superpowers.astro ├── tsconfig.json ├── .vscode │ ├── extensions.json │ └── launch.json ├── .gitignore ├── public │ ├── favicon.svg │ └── icons │ │ └── github.svg ├── package.json ├── astro.config.mjs └── file-linker.mjs ├── CODE_OF_CONDUCT.md ├── images ├── main@2x.png ├── arrows@2x.png ├── italics@2x.png ├── styles@2x.png ├── alternatives@2x.png ├── alt_ligatures@2x.png ├── character-set@2x.png └── logo.svg ├── sources ├── opentype_features │ ├── _classes │ │ ├── lca_cyrl_dflt.fea │ │ ├── numbers_hex.fea │ │ ├── lcg_dflt.fea │ │ ├── lca_cyrl_alt1.fea │ │ ├── acc_comb_bottom.cls │ │ ├── numbers_dflt.fea │ │ ├── lcg_alt1.fea │ │ ├── numbers_onum.fea │ │ ├── numbers_dnom.fea │ │ ├── numbers_numr.fea │ │ ├── uc_basic.cls │ │ ├── numbers_sinf.fea │ │ ├── numbers_sups.fea │ │ ├── acc_comb_dflt.cls │ │ ├── acc_comb_case.cls │ │ ├── lca_dflt.fea │ │ ├── acc_comb_top.cls │ │ └── lca_alt1.fea │ ├── cv03.fea │ ├── cv02.fea │ ├── dnom.fea │ ├── numr.fea │ ├── sups.fea │ ├── cv10.fea │ ├── ordn.fea │ ├── sinf.fea │ ├── onum.fea │ ├── cv05.fea │ ├── cv06.fea │ ├── cv12.fea │ ├── zero.fea │ ├── cv04.fea │ ├── cv08.fea │ ├── cv11.fea │ ├── calt │ │ ├── multiply.fea │ │ ├── numbersigns.fea │ │ ├── colon.fea │ │ ├── underscores.fea │ │ ├── hyphen-arrows.fea │ │ └── equal-arrows.fea │ ├── cv01.fea │ ├── ss03.fea │ ├── cv07.fea │ ├── ss04.fea │ ├── cv09.fea │ ├── ss02.fea │ ├── aalt.fea │ ├── ss01.fea │ ├── frac.fea │ ├── case.fea │ ├── ccmp.fea │ ├── locl.fea │ └── mark.fea ├── lilexgen_config.yaml ├── LilexDuo │ └── config.yaml ├── Lilex │ └── config.yaml └── README.md ├── fonts ├── otf │ ├── Lilex-Bold.otf │ ├── Lilex-Italic.otf │ ├── Lilex-Light.otf │ ├── Lilex-Medium.otf │ ├── Lilex-Thin.otf │ ├── Lilex-Regular.otf │ ├── Lilex-SemiBold.otf │ ├── Lilex-BoldItalic.otf │ ├── Lilex-ExtraLight.otf │ ├── Lilex-LightItalic.otf │ ├── Lilex-ThinItalic.otf │ ├── Lilex-MediumItalic.otf │ ├── Lilex-ExtraLightItalic.otf │ └── Lilex-SemiBoldItalic.otf ├── ttf │ ├── Lilex-Bold.ttf │ ├── Lilex-Italic.ttf │ ├── Lilex-Light.ttf │ ├── Lilex-Medium.ttf │ ├── Lilex-Thin.ttf │ ├── Lilex-Regular.ttf │ ├── Lilex-SemiBold.ttf │ ├── Lilex-BoldItalic.ttf │ ├── Lilex-ExtraLight.ttf │ ├── Lilex-LightItalic.ttf │ ├── Lilex-ThinItalic.ttf │ ├── Lilex-MediumItalic.ttf │ ├── Lilex-ExtraLightItalic.ttf │ └── Lilex-SemiBoldItalic.ttf ├── variable │ ├── Lilex[wght].ttf │ └── Lilex-Italic[wght].ttf └── webfonts │ ├── Lilex-Bold.woff2 │ ├── Lilex-Thin.woff2 │ ├── Lilex-Italic.woff2 │ ├── Lilex-Light.woff2 │ ├── Lilex-Medium.woff2 │ ├── Lilex[wght].woff2 │ ├── Lilex-Regular.woff2 │ ├── Lilex-SemiBold.woff2 │ ├── Lilex-BoldItalic.woff2 │ ├── Lilex-ExtraLight.woff2 │ ├── Lilex-LightItalic.woff2 │ ├── Lilex-ThinItalic.woff2 │ ├── Lilex-Italic[wght].woff2 │ ├── Lilex-MediumItalic.woff2 │ ├── Lilex-SemiBoldItalic.woff2 │ └── Lilex-ExtraLightItalic.woff2 ├── scripts ├── lilexgen │ ├── __init__.py │ ├── opentype_features │ │ ├── __init__.py │ │ ├── spacers.py │ │ ├── ligatures.py │ │ └── const.py │ ├── config.py │ └── generate.py ├── generate.py ├── website_env.py ├── ansi_features.py ├── changelog.py ├── porting.py └── normalize_names.py ├── .editorconfig ├── .gitignore ├── .github ├── workflows │ ├── qa.yaml │ ├── website_master.yaml │ ├── build.yaml │ └── release.yaml ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.yaml ├── AUTHORS ├── lefthook.yml ├── SECURITY.md ├── pyproject.toml ├── CONTRIBUTING.md ├── OFL.txt ├── README.md └── Makefile /website/.prettierignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Be awesome for each other 2 | -------------------------------------------------------------------------------- /website/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /website/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict" 3 | } 4 | -------------------------------------------------------------------------------- /images/main@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/images/main@2x.png -------------------------------------------------------------------------------- /sources/opentype_features/_classes/lca_cyrl_dflt.fea: -------------------------------------------------------------------------------- 1 | a-cy adieresis-cy abreve-cy 2 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/numbers_hex.fea: -------------------------------------------------------------------------------- 1 | @numbers_dflt a b c d e f A B C D E F -------------------------------------------------------------------------------- /sources/opentype_features/cv03.fea: -------------------------------------------------------------------------------- 1 | # Name: Low stem g 2 | 3 | sub g by g.alt02; 4 | -------------------------------------------------------------------------------- /images/arrows@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/images/arrows@2x.png -------------------------------------------------------------------------------- /images/italics@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/images/italics@2x.png -------------------------------------------------------------------------------- /images/styles@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/images/styles@2x.png -------------------------------------------------------------------------------- /sources/opentype_features/cv02.fea: -------------------------------------------------------------------------------- 1 | # Name: Open g 2 | 3 | sub @lcg_dflt by @lcg_alt1; 4 | -------------------------------------------------------------------------------- /website/src/utils/text-animation/web-components/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./waving-glyphs"; 2 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/lcg_dflt.fea: -------------------------------------------------------------------------------- 1 | g gbreve gcircumflex gcommaaccent gdotaccent 2 | -------------------------------------------------------------------------------- /sources/opentype_features/dnom.fea: -------------------------------------------------------------------------------- 1 | # Denominators 2 | 3 | sub @numbers_dflt by @numbers_dnom; 4 | -------------------------------------------------------------------------------- /sources/opentype_features/numr.fea: -------------------------------------------------------------------------------- 1 | # Name: numerators 2 | sub @numbers_dflt by @numbers_numr; 3 | -------------------------------------------------------------------------------- /sources/opentype_features/sups.fea: -------------------------------------------------------------------------------- 1 | # Name: Superiors 2 | sub @numbers_dflt by @numbers_sups; 3 | -------------------------------------------------------------------------------- /fonts/otf/Lilex-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-Bold.otf -------------------------------------------------------------------------------- /fonts/otf/Lilex-Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-Italic.otf -------------------------------------------------------------------------------- /fonts/otf/Lilex-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-Light.otf -------------------------------------------------------------------------------- /fonts/otf/Lilex-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-Medium.otf -------------------------------------------------------------------------------- /fonts/otf/Lilex-Thin.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-Thin.otf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-Bold.ttf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-Italic.ttf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-Light.ttf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-Medium.ttf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-Thin.ttf -------------------------------------------------------------------------------- /images/alternatives@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/images/alternatives@2x.png -------------------------------------------------------------------------------- /sources/opentype_features/_classes/lca_cyrl_alt1.fea: -------------------------------------------------------------------------------- 1 | a-cy.alt01 adieresis-cy.alt01 abreve-cy.alt01 2 | -------------------------------------------------------------------------------- /sources/opentype_features/cv10.fea: -------------------------------------------------------------------------------- 1 | # Name: High asterisk 2 | 3 | sub asterisk by asterisk.cv10; 4 | -------------------------------------------------------------------------------- /sources/opentype_features/ordn.fea: -------------------------------------------------------------------------------- 1 | # Name: Ordinals 2 | sub [a o] by [ordfeminine ordmasculine]; 3 | -------------------------------------------------------------------------------- /fonts/otf/Lilex-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-Regular.otf -------------------------------------------------------------------------------- /fonts/otf/Lilex-SemiBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-SemiBold.otf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-Regular.ttf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-SemiBold.ttf -------------------------------------------------------------------------------- /images/alt_ligatures@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/images/alt_ligatures@2x.png -------------------------------------------------------------------------------- /images/character-set@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/images/character-set@2x.png -------------------------------------------------------------------------------- /sources/opentype_features/_classes/acc_comb_bottom.cls: -------------------------------------------------------------------------------- 1 | dotbelowcomb commaaccentcomb cedillacomb ogonekcomb -------------------------------------------------------------------------------- /sources/opentype_features/_classes/numbers_dflt.fea: -------------------------------------------------------------------------------- 1 | zero one two three four five six seven eight nine 2 | -------------------------------------------------------------------------------- /sources/opentype_features/sinf.fea: -------------------------------------------------------------------------------- 1 | # Name: Scientific inferiors 2 | sub @numbers_dflt by @numbers_sinf; 3 | -------------------------------------------------------------------------------- /website/src/styles/theme/transitions.scss: -------------------------------------------------------------------------------- 1 | .lilexTheme { 2 | --transition-slow: 0.3s ease-out; 3 | } 4 | -------------------------------------------------------------------------------- /fonts/otf/Lilex-BoldItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-BoldItalic.otf -------------------------------------------------------------------------------- /fonts/otf/Lilex-ExtraLight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-ExtraLight.otf -------------------------------------------------------------------------------- /fonts/otf/Lilex-LightItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-LightItalic.otf -------------------------------------------------------------------------------- /fonts/otf/Lilex-ThinItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-ThinItalic.otf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-ExtraLight.ttf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-ThinItalic.ttf -------------------------------------------------------------------------------- /fonts/variable/Lilex[wght].ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/variable/Lilex[wght].ttf -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-Bold.woff2 -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-Thin.woff2 -------------------------------------------------------------------------------- /sources/opentype_features/onum.fea: -------------------------------------------------------------------------------- 1 | # Name: Old-style numerals 2 | 3 | sub @numbers_dflt by @numbers_onum; 4 | -------------------------------------------------------------------------------- /fonts/otf/Lilex-MediumItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-MediumItalic.otf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-MediumItalic.ttf -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-Italic.woff2 -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-Light.woff2 -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-Medium.woff2 -------------------------------------------------------------------------------- /fonts/webfonts/Lilex[wght].woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex[wght].woff2 -------------------------------------------------------------------------------- /website/src/utils/text-animation/web-components/waving-glyphs/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./waving-glyphs-element"; 2 | -------------------------------------------------------------------------------- /fonts/otf/Lilex-ExtraLightItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-ExtraLightItalic.otf -------------------------------------------------------------------------------- /fonts/otf/Lilex-SemiBoldItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/otf/Lilex-SemiBoldItalic.otf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /fonts/ttf/Lilex-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/ttf/Lilex-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-Regular.woff2 -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-SemiBold.woff2 -------------------------------------------------------------------------------- /sources/opentype_features/cv05.fea: -------------------------------------------------------------------------------- 1 | # Name: 16th-century lowercase eszett 2 | 3 | sub germandbls by germandbls.cv05; 4 | -------------------------------------------------------------------------------- /fonts/variable/Lilex-Italic[wght].ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/variable/Lilex-Italic[wght].ttf -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-BoldItalic.woff2 -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-ExtraLight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-ExtraLight.woff2 -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-LightItalic.woff2 -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-ThinItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-ThinItalic.woff2 -------------------------------------------------------------------------------- /sources/opentype_features/_classes/lcg_alt1.fea: -------------------------------------------------------------------------------- 1 | g.alt01 gbreve.alt01 gcircumflex.alt01 gcommaaccent.alt01 gdotaccent.alt01 2 | -------------------------------------------------------------------------------- /sources/opentype_features/cv06.fea: -------------------------------------------------------------------------------- 1 | # Name: Elongated tilde 2 | # Example: ~ 3 | 4 | sub asciitilde by asciitilde.original; 5 | -------------------------------------------------------------------------------- /sources/opentype_features/cv12.fea: -------------------------------------------------------------------------------- 1 | # Name: blackletter-adapted lowercase eszett 2 | 3 | sub germandbls by germandbls.cv12; 4 | -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-Italic[wght].woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-Italic[wght].woff2 -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-MediumItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-MediumItalic.woff2 -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-SemiBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-SemiBoldItalic.woff2 -------------------------------------------------------------------------------- /sources/opentype_features/zero.fea: -------------------------------------------------------------------------------- 1 | # Name: Slashed zero 2 | 3 | sub zero by zero.alt01; 4 | sub zero.tosf by zero.tosf.alt01; 5 | -------------------------------------------------------------------------------- /website/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /fonts/webfonts/Lilex-ExtraLightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mishamyrt/Lilex/HEAD/fonts/webfonts/Lilex-ExtraLightItalic.woff2 -------------------------------------------------------------------------------- /sources/opentype_features/cv04.fea: -------------------------------------------------------------------------------- 1 | # Name: Plain number zero 2 | 3 | sub zero by zero.alt02; 4 | sub zero.tosf by zero.tosf.alt02; 5 | -------------------------------------------------------------------------------- /scripts/lilexgen/__init__.py: -------------------------------------------------------------------------------- 1 | """Lilex font generator""" 2 | 3 | from .config import LilexGeneratorConfig 4 | from .generate import generate_sources 5 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/numbers_onum.fea: -------------------------------------------------------------------------------- 1 | zero.tosf one.tosf two.tosf three.tosf four.tosf five.tosf six.tosf seven.tosf eight.tosf nine.tosf -------------------------------------------------------------------------------- /sources/opentype_features/_classes/numbers_dnom.fea: -------------------------------------------------------------------------------- 1 | zero.dnom one.dnom two.dnom three.dnom four.dnom five.dnom six.dnom seven.dnom eight.dnom nine.dnom 2 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/numbers_numr.fea: -------------------------------------------------------------------------------- 1 | zero.numr one.numr two.numr three.numr four.numr five.numr six.numr seven.numr eight.numr nine.numr 2 | -------------------------------------------------------------------------------- /website/src/styles/theme/border-radius.scss: -------------------------------------------------------------------------------- 1 | .lilexTheme { 2 | --border-radius-s: 5px; 3 | --border-radius-m: 9px; 4 | --border-radius-l: 14px; 5 | } 6 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/uc_basic.cls: -------------------------------------------------------------------------------- 1 | A AE Aogonek B C Ccedilla D E Eogonek F G H I Iogonek J K L M N O OE Ohorn Oslash P Q R S Schwa T U Uhorn Uogonek V W X Y Z -------------------------------------------------------------------------------- /sources/opentype_features/cv08.fea: -------------------------------------------------------------------------------- 1 | # Name: Classical greater/less then 2 | 3 | sub greater_equal.liga by greater_equal.liga.cv08; 4 | sub less_equal.liga by less_equal.liga.cv08; 5 | -------------------------------------------------------------------------------- /sources/opentype_features/cv11.fea: -------------------------------------------------------------------------------- 1 | # Name: Connected bar with less or greater 2 | 3 | sub bar_greater.liga by bar_greater.liga.cv11; 4 | sub less_bar.liga by less_bar.liga.cv11; 5 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/numbers_sinf.fea: -------------------------------------------------------------------------------- 1 | zeroinferior oneinferior twoinferior threeinferior fourinferior fiveinferior sixinferior seveninferior eightinferior nineinferior 2 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/numbers_sups.fea: -------------------------------------------------------------------------------- 1 | zerosuperior onesuperior twosuperior threesuperior foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperior 2 | -------------------------------------------------------------------------------- /website/src/dom.d.ts: -------------------------------------------------------------------------------- 1 | type TagName = keyof HTMLElementTagNameMap; 2 | 3 | type HTMLElementProps = Partial< 4 | Omit 5 | >; 6 | -------------------------------------------------------------------------------- /sources/opentype_features/calt/multiply.fea: -------------------------------------------------------------------------------- 1 | # Multiply align 2 | # Examples: 0xDEADBEEF, 10x2 3 | sub zero x' @numbers_hex by multiply; 4 | sub @numbers_dflt x' @numbers_dflt by multiply; 5 | -------------------------------------------------------------------------------- /sources/opentype_features/cv01.fea: -------------------------------------------------------------------------------- 1 | # Name: One storey a 2 | 3 | sub @lca_dflt by @lca_alt1; 4 | sub @lca_cyrl_dflt by @lca_cyrl_alt1; 5 | sub alpha by alpha.alt01; 6 | sub alphatonos by alphatonos.alt01; 7 | -------------------------------------------------------------------------------- /website/src/utils/text-animation/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./geometry"; 2 | export * from "./nodes"; 3 | export * from "./renderers/magnifying-glass"; 4 | export * from "./web-components/waving-glyphs/wave-animator"; 5 | -------------------------------------------------------------------------------- /sources/opentype_features/ss03.fea: -------------------------------------------------------------------------------- 1 | # Name: Thin backslash 2 | # Example: \n \\ 3 | 4 | lookup backslash_thin { 5 | ignore sub backslash.ss03 backslash'; 6 | sub backslash' by backslash.ss03; 7 | } backslash_thin; 8 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/acc_comb_dflt.cls: -------------------------------------------------------------------------------- 1 | breveacute brevegrave brevehook brevetilde circumflexacute circumflexbreve circumflexgrave circumflexhook circumflextilde dieresisacute dieresiscaron dieresisgrave dieresismacron hookabovecomb -------------------------------------------------------------------------------- /sources/opentype_features/cv07.fea: -------------------------------------------------------------------------------- 1 | # Name: Vertically extended clock arrows 2 | # Example: ↻ ↺ 3 | 4 | sub clockwiseOpenCircleArrow by clockwiseOpenCircleArrow.alt01; 5 | sub anticlockwiseOpenCircleArrow by anticlockwiseOpenCircleArrow.alt01; 6 | -------------------------------------------------------------------------------- /sources/opentype_features/ss04.fea: -------------------------------------------------------------------------------- 1 | # Name: Broken number signs 2 | 3 | sub numbersign_start.seq by numbersign_start.ss04.seq; 4 | sub numbersign_middle.seq by numbersign_middle.ss04.seq; 5 | sub numbersign_end.seq by numbersign_end.ss04.seq; 6 | -------------------------------------------------------------------------------- /website/src/stores/superpowers.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "nanostores"; 2 | 3 | export const $isSuperpowersEnabled = atom(true); 4 | 5 | export function setSuperpowersEnabled(enabled: boolean) { 6 | $isSuperpowersEnabled.set(enabled); 7 | } 8 | -------------------------------------------------------------------------------- /website/src/styles/theme/spaces.scss: -------------------------------------------------------------------------------- 1 | .lilexTheme { 2 | --space-xxs: 4px; 3 | --space-xs: 6px; 4 | --space-s: 9px; 5 | --space-s_plus: 12px; 6 | --space-m: 18px; 7 | --space-m_plus: 24px; 8 | --space-l: 36px; 9 | --space-xl: 72px; 10 | } 11 | -------------------------------------------------------------------------------- /sources/lilexgen_config.yaml: -------------------------------------------------------------------------------- 1 | featuresDir: ./opentype_features 2 | sources: 3 | Lilex: 4 | Lilex.glyphs: 5 | Lilex-Italic.glyphs: 6 | 7 | LilexDuo: 8 | LilexDuo.glyphs: 9 | source: Lilex.glyphs 10 | patch: LilexDuo.patch.glyphs 11 | -------------------------------------------------------------------------------- /website/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /website/src/components/FloatingActions/index.ts: -------------------------------------------------------------------------------- 1 | import FloatingPanel from "./FloatingPanel.astro"; 2 | import FloatingAction from "./FloatingAction.astro"; 3 | import FloatingToggle from "./FloatingToggle.astro"; 4 | 5 | export { FloatingPanel, FloatingAction, FloatingToggle }; 6 | -------------------------------------------------------------------------------- /website/src/components/GlyphAnimations/AnimatedGlyphs.astro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*.{js,jsx,ts,tsx,json,astro}] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_size = 2 10 | indent_style = tab 11 | insert_final_newline = true 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /sources/opentype_features/cv09.fea: -------------------------------------------------------------------------------- 1 | # Name: Empty dollar and cent 2 | 3 | sub cent by cent.cv09; 4 | sub dollar by dollar.cv09; 5 | sub dollar_greater.liga by dollar_greater.liga.cv09; 6 | sub less_dollar.liga by less_dollar.liga.cv09; 7 | sub less_dollar_greater.liga by less_dollar_greater.liga.cv09; 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /build/ 3 | /venv/ 4 | /.vscode 5 | * (Autosaved).glyphs 6 | *.glyphspackage 7 | __pycache__ 8 | .ruff_cache 9 | reports/ 10 | .netlify 11 | /preview/.env 12 | 13 | # gftools build artifacts 14 | /sources/instance_ufos 15 | /sources/.ninja_log 16 | /sources/build.ninja 17 | -------------------------------------------------------------------------------- /scripts/lilexgen/opentype_features/__init__.py: -------------------------------------------------------------------------------- 1 | """OpenType features utils""" 2 | 3 | from .const import NAME_TPL 4 | from .features_loader import OpenTypeFeatures 5 | from .ligatures import render_ligature_lookups 6 | # from .name import feature_prefix, name_from_code 7 | # from .spacers import generate_spacers 8 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/acc_comb_case.cls: -------------------------------------------------------------------------------- 1 | breveacute.case brevegrave.case brevehook.case brevetilde.case circumflexacute.case circumflexbreve.case circumflexgrave.case circumflexhook.case circumflextilde.case dieresisacute.case dieresiscaron.case dieresisgrave.case dieresismacron.case hookabovecomb.case -------------------------------------------------------------------------------- /sources/opentype_features/_classes/lca_dflt.fea: -------------------------------------------------------------------------------- 1 | a aacute abreve acircumflex adieresis adotbelow agrave ahookabove amacron aogonek aring aringacute atilde abreveacute abrevedotbelow abrevegrave abrevehookabove abrevetilde acircumflexacute acircumflexdotbelow acircumflexgrave acircumflexhookabove acircumflextilde 2 | -------------------------------------------------------------------------------- /.github/workflows/qa.yaml: -------------------------------------------------------------------------------- 1 | name: Quality Assurance 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | tags: 8 | - "!**" 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | uses: ./.github/workflows/build.yaml 14 | with: 15 | check: true 16 | website: true 17 | -------------------------------------------------------------------------------- /sources/opentype_features/ss02.fea: -------------------------------------------------------------------------------- 1 | # Name: Broken equals ligatures 2 | # Example: != !== 3 | sub equal_equal.liga by equal_equal.liga.ss02; 4 | sub equal_equal_equal.liga by equal_equal_equal.liga.ss02; 5 | sub exclam_equal.liga by exclam_equal.liga.ss02; 6 | sub exclam_equal_equal.liga by exclam_equal_equal.liga.ss02; 7 | -------------------------------------------------------------------------------- /website/src/pages/preview.astro: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | 5 | 6 | 7 | 8 | 9 | Font preview 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /website/src/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @use "./theme/border-radius.scss"; 2 | @use "./theme/colors.scss"; 3 | @use "./theme/spaces.scss"; 4 | @use "./theme/transitions.scss"; 5 | @use "./theme/typography.scss"; 6 | 7 | body { 8 | margin: 0; 9 | overflow-x: hidden; 10 | min-width: 410px; 11 | } 12 | 13 | * { 14 | box-sizing: border-box; 15 | } 16 | -------------------------------------------------------------------------------- /website/src/utils/environment.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if the browser is Safari 3 | */ 4 | export function checkIsSafari(): boolean { 5 | return ( 6 | Boolean(navigator.userAgent.match(/safari/i)) && 7 | !navigator.userAgent.match(/chrome/i) && 8 | typeof document.body.style.webkitFilter === "string" && 9 | !("chrome" in window) 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /website/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/Layout.astro"; 3 | import Hero from "../blocks/Hero/Hero.astro"; 4 | import Superpowers from "../blocks/Superpowers/Superpowers.astro"; 5 | import Glyphs from "../blocks/Glyphs/Glyphs.astro"; 6 | --- 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of project authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS.txt file. 3 | # See the latter for an explanation. 4 | # 5 | # Names should be added to this file as: 6 | # Name or Organization 7 | 8 | Bold Monday (https://www.boldmonday.com/support/contact/) 9 | Mikhael Khrustik (misha@myrt.co) github.com/mishamyrt 10 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/acc_comb_top.cls: -------------------------------------------------------------------------------- 1 | acutecomb breveacute brevecomb brevegrave brevehook brevetilde caroncomb commaaboverightcomb circumflexacute circumflexbreve circumflexcomb circumflexgrave circumflexhook circumflextilde commaturnedabovecomb dieresisacute dieresiscaron dieresiscomb dieresisgrave dieresismacron dotaccentcomb gravecomb hookabovecomb horncomb hungarumlautcomb macroncomb ringcomb tildecomb -------------------------------------------------------------------------------- /sources/opentype_features/calt/numbersigns.fea: -------------------------------------------------------------------------------- 1 | # Generative hashtag ligature 2 | # Example: ################## 3 | lookup numbersigns { 4 | sub [numbersign_start.seq numbersign_middle.seq] numbersign' numbersign by numbersign_middle.seq; 5 | 6 | sub [numbersign_start.seq numbersign_middle.seq] numbersign' by numbersign_end.seq; 7 | 8 | sub numbersign' numbersign by numbersign_start.seq; 9 | } numbersigns; 10 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | 23 | # jetbrains setting folder 24 | .idea/ 25 | 26 | # fonts 27 | public/fonts 28 | 29 | -------------------------------------------------------------------------------- /sources/opentype_features/_classes/lca_alt1.fea: -------------------------------------------------------------------------------- 1 | a.alt01 aacute.alt01 abreve.alt01 acircumflex.alt01 adieresis.alt01 adotbelow.alt01 agrave.alt01 ahookabove.alt01 amacron.alt01 aogonek.alt01 aring.alt01 aringacute.alt01 atilde.alt01 abreveacute.alt01 abrevedotbelow.alt01 abrevegrave.alt01 abrevehookabove.alt01 abrevetilde.alt01 acircumflexacute.alt01 acircumflexdotbelow.alt01 acircumflexgrave.alt01 acircumflexhookabove.alt01 acircumflextilde.alt01 2 | -------------------------------------------------------------------------------- /sources/opentype_features/aalt.fea: -------------------------------------------------------------------------------- 1 | # Name: All alternatives 2 | 3 | feature frac; 4 | feature locl; 5 | feature numr; 6 | feature ordn; 7 | feature sinf; 8 | feature ss01; 9 | feature ss02; 10 | feature ss03; 11 | feature sups; 12 | feature zero; 13 | feature cv01; 14 | # feature cv02; Ignore cv02 because zero will be used 15 | feature cv03; 16 | feature cv04; 17 | feature cv05; 18 | feature cv06; 19 | feature cv07; 20 | feature cv08; 21 | feature cv09; 22 | feature cv10; 23 | feature calt; 24 | feature subs; 25 | feature dnom; 26 | feature case; 27 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | parallel: true 3 | jobs: 4 | - name: regenerate font sources 5 | stage_fixed: true 6 | glob: 7 | - sources/**/* 8 | - sources/* 9 | run: make generate 10 | 11 | - name: lint (fix) python code 12 | stage_fixed: true 13 | glob: 14 | - scripts/**/*.py 15 | - scripts/*.py 16 | run: make scripts-format 17 | 18 | - name: lint (fix) preview website code 19 | stage_fixed: true 20 | glob: 21 | - website/**/* 22 | - website/* 23 | run: make website-format -------------------------------------------------------------------------------- /website/public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | title: string; 4 | } 5 | 6 | const { title } = Astro.props; 7 | --- 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {title} 18 | 19 | 20 | 21 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /website/src/blocks/Hero/InformationColumn.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Typography from '../../components/Typography.astro'; 3 | 4 | 5 | export type Props = { 6 | title: string; 7 | class?: string; 8 | } 9 | 10 | const { title, class: className } = Astro.props; 11 | --- 12 | 13 |
14 |
15 | {title} 16 | 17 |
18 |
19 | 20 | 29 | -------------------------------------------------------------------------------- /website/src/blocks/Glyphs/PreviewRow.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import TextArea from "../../components/TextArea.astro"; 3 | import Typography from "../../components/Typography.astro"; 4 | 5 | export type Props = { 6 | title: string; 7 | text: string; 8 | }; 9 | 10 | const { title, text } = Astro.props; 11 | --- 12 | 13 |
14 | 15 | {title} 16 | 17 | 20 | 21 | 91 | 92 | 126 | -------------------------------------------------------------------------------- /scripts/lilexgen/config.py: -------------------------------------------------------------------------------- 1 | """Lilex font config module""" 2 | 3 | from __future__ import annotations 4 | 5 | from enum import Enum 6 | from pathlib import Path 7 | from typing import TypedDict 8 | 9 | import yaml 10 | 11 | 12 | class PatchParams(TypedDict): 13 | """Patch font params""" 14 | 15 | # Basic source font name 16 | source: str 17 | # Patch file name 18 | patch: str 19 | 20 | 21 | FontSourceParams = PatchParams | None 22 | 23 | 24 | class RawLilexGenConfig(TypedDict): 25 | """Raw Lilex font config""" 26 | 27 | featuresDir: str 28 | # font_name -> output_source_name -> PatchDescriptor | None 29 | sources: dict[str, dict[str, FontSourceParams]] 30 | 31 | 32 | class SourceType(Enum): 33 | """Font source type""" 34 | 35 | SOURCE = "source" 36 | PATCH_SOURCE = "patch_source" 37 | PATCH_TARGET = "patch_target" 38 | 39 | 40 | class FontDescriptor: 41 | """Font source descriptor""" 42 | 43 | path: Path 44 | type: SourceType 45 | params: FontSourceParams 46 | 47 | def __init__(self, path: Path, source_type: SourceType, params: FontSourceParams): 48 | self.path = path 49 | self.type = source_type 50 | self.params = params 51 | 52 | @property 53 | def dir(self) -> Path: 54 | """Directory""" 55 | return self.path.parent 56 | 57 | 58 | class LilexGeneratorConfig: 59 | """Font family config""" 60 | 61 | _path: Path 62 | _dir: Path 63 | _raw: RawLilexGenConfig 64 | _descriptors: dict[str, FontDescriptor] 65 | 66 | def __init__(self, raw_config: RawLilexGenConfig, root_dir: Path): 67 | """Initializes Lilex generator config""" 68 | self._raw = raw_config 69 | self._dir = root_dir 70 | self._descriptors = {} 71 | 72 | for font, sources in self._raw["sources"].items(): 73 | for file_name, params in sources.items(): 74 | if params is None: 75 | source_path = self._dir / font / file_name 76 | self._descriptors[file_name] = FontDescriptor( 77 | source_path, SourceType.SOURCE, params 78 | ) 79 | elif "source" in params and "patch" in params: 80 | source_path = self._dir / font / params["patch"] 81 | self._descriptors[params["patch"]] = FontDescriptor( 82 | source_path, SourceType.PATCH_SOURCE, params 83 | ) 84 | target_path = self._dir / font / file_name 85 | self._descriptors[file_name] = FontDescriptor( 86 | target_path, SourceType.PATCH_TARGET, params 87 | ) 88 | else: 89 | raise ValueError(f"Invalid source params: {params}") 90 | 91 | @staticmethod 92 | def from_file(path: str | Path) -> LilexGeneratorConfig: 93 | """Loads a config from a file""" 94 | if isinstance(path, str): 95 | path = Path(path) 96 | with path.open(encoding="utf-8") as file: 97 | raw_config = yaml.safe_load(file) 98 | return LilexGeneratorConfig(raw_config, path.parent) 99 | 100 | @property 101 | def dir(self) -> Path: 102 | """Font family directory""" 103 | return self._dir 104 | 105 | @property 106 | def features_dir(self) -> Path: 107 | """Features directory""" 108 | return self._dir / self._raw.get("featuresDir") 109 | 110 | @property 111 | def descriptors(self) -> list[FontDescriptor]: 112 | """Source descriptors""" 113 | return list(self._descriptors.values()) 114 | 115 | def get_descriptor(self, path: Path) -> FontDescriptor: 116 | """Gets a source descriptor by path""" 117 | return self._descriptors[path] 118 | -------------------------------------------------------------------------------- /scripts/lilexgen/generate.py: -------------------------------------------------------------------------------- 1 | """Font loader""" 2 | 3 | from __future__ import annotations 4 | 5 | from copy import deepcopy 6 | from pathlib import Path 7 | 8 | from glyphsLib import GSFont 9 | 10 | from .config import FontDescriptor, LilexGeneratorConfig, SourceType 11 | from .opentype_features import OpenTypeFeatures 12 | 13 | 14 | def generate_sources( 15 | config: LilexGeneratorConfig, 16 | forced_features: list[str] = None, 17 | version: str = None, 18 | ): 19 | """Regenerates the sources for a font family""" 20 | loader = FontLoader(config, forced_features) 21 | version_major = None 22 | version_minor = None 23 | if version: 24 | (version_major, version_minor) = _parse_version(version) 25 | for descriptor in config.descriptors: 26 | font = loader.load(descriptor) 27 | if version and descriptor.type != SourceType.PATCH_SOURCE: 28 | font.versionMajor = version_major 29 | font.versionMinor = version_minor 30 | font.save(descriptor.path.as_posix()) 31 | 32 | 33 | class FontLoader: 34 | """Font loader""" 35 | 36 | _cache: dict[Path, GSFont] 37 | _config: LilexGeneratorConfig 38 | _features: OpenTypeFeatures 39 | 40 | def __init__( 41 | self, 42 | config: LilexGeneratorConfig, 43 | forced_features: list[str] = None, 44 | ): 45 | """Loads fonts from a config""" 46 | self._cache = {} 47 | self._features = OpenTypeFeatures(config.features_dir, forced_features) 48 | self._config = config 49 | 50 | def load(self, descriptor: FontDescriptor) -> GSFont: 51 | """Loads a font from a descriptor""" 52 | if descriptor.path in self._cache: 53 | return self._cache[descriptor.path] 54 | 55 | if descriptor.type == SourceType.SOURCE: 56 | font = GSFont(descriptor.path.as_posix()) 57 | self._features.inject(font) 58 | elif descriptor.type == SourceType.PATCH_SOURCE: 59 | font = GSFont(descriptor.path.as_posix()) 60 | elif descriptor.type == SourceType.PATCH_TARGET: 61 | source_descriptor = self._config.get_descriptor(descriptor.params["source"]) 62 | source_font = self.load(source_descriptor) 63 | patch_descriptor = self._config.get_descriptor(descriptor.params["patch"]) 64 | patch_font = GSFont(patch_descriptor.path.as_posix()) 65 | font = _merge_fonts(source_font, patch_font) 66 | else: 67 | raise ValueError(f"Invalid source type: {descriptor.type}") 68 | 69 | self._cache[descriptor.path] = font 70 | return font 71 | 72 | 73 | def _merge_fonts(base: GSFont, patch: GSFont) -> GSFont: 74 | """Merges two fonts""" 75 | font = deepcopy(base) 76 | font.familyName = patch.familyName 77 | # Merge glyphs 78 | for glyph in patch.glyphs: 79 | glyph_idx = _find_glyph_index(font, glyph.name) 80 | if glyph_idx == -1: 81 | font.glyphs.append(glyph) 82 | elif glyph.export: 83 | font.glyphs[glyph_idx] = glyph 84 | # Merge custom parameters 85 | for param in patch.customParameters: 86 | if param.name in font.customParameters: 87 | font.customParameters[param.name] = param.value 88 | else: 89 | font.customParameters.append(param) 90 | return font 91 | 92 | 93 | def _find_glyph_index(font: GSFont, glyph_name: str) -> int: 94 | """Finds the index of a glyph in a font""" 95 | for idx, glyph in enumerate(font.glyphs): 96 | if glyph.name == glyph_name: 97 | return idx 98 | return -1 99 | 100 | 101 | GSFontVersion = tuple[int, int] 102 | 103 | 104 | def _parse_version(version: str) -> GSFontVersion: 105 | """Parses a version string into a tuple of major and minor version numbers. 106 | Example: 107 | "2.700" -> (2, 700) 108 | "2.700-beta" -> (2, 700) 109 | """ 110 | if "-" in version: 111 | print( 112 | f"Warning: Version {version} contains a hyphen, using only the first part" 113 | ) 114 | version = version.split("-")[0] 115 | parts = version.split(".") 116 | assert len(parts) == 2 117 | return (int(parts[0]), int(parts[1])) 118 | -------------------------------------------------------------------------------- /website/src/blocks/Superpowers/FeatureRow.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Typography from "../../components/Typography.astro"; 3 | 4 | export type Props = { 5 | title: string; 6 | alignExample?: "block" | "description"; 7 | }; 8 | 9 | const { title, alignExample = "block" } = Astro.props; 10 | --- 11 | 12 |
13 | 21 | {title} 22 | 23 |
24 | 25 |
26 |
32 | 33 |
34 |
35 | 36 | 76 | 77 | 190 | -------------------------------------------------------------------------------- /scripts/porting.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """A utility for developing pairings to fonts. 3 | I want Italic to have all the same features as a roman font, 4 | this script will allow to control progress 5 | """ 6 | 7 | from __future__ import annotations 8 | 9 | import json 10 | from pathlib import Path 11 | 12 | from arrrgs import arg, command, global_args, run 13 | from glyphsLib import GSFont 14 | 15 | ROMAN_FONT_PATH = "sources/Lilex.glyphs" 16 | ITALIC_FONT_PATH = "sources/Lilex-Italic.glyphs" 17 | 18 | GLYPH_GROUPS = [ 19 | ("Ligatures", ".liga"), 20 | ("Sequences", ".seq"), 21 | ("Bulgarian forms", ".loclBGR"), 22 | ("Spacers", ".spacer"), 23 | ("Miscellaneous", None), 24 | ] 25 | 26 | 27 | global_args( 28 | arg("--snapshot-dir", "-s", default="__snapshots__", help="Snapshot directory"), 29 | ) 30 | 31 | 32 | @command( 33 | arg("--markdown", "-m", action="store_true", help="Markdown output"), 34 | arg("--download-url", "-d", help="Download URL"), 35 | ) 36 | def progress(args): 37 | """Finds missing ligatures""" 38 | missing_glyphs = _find_missing_glyphs() 39 | snapshot_path = f"{args.snapshot_dir}/missing_glyphs.json" 40 | 41 | with Path(snapshot_path).open("r", encoding="utf-8") as file: 42 | stored_glyphs = json.load(file) 43 | 44 | diff = len(stored_glyphs) - len(missing_glyphs) 45 | progress_value = diff / len(stored_glyphs) 46 | if not args.markdown: 47 | for glyph in missing_glyphs: 48 | print(f"- {glyph}") 49 | print(f"Glyphs coverage: {(progress_value * 100):.2f}%") 50 | return 51 | 52 | groups = { 53 | "Miscellaneous": [], 54 | } 55 | for glyph in stored_glyphs: 56 | for group, suffix in GLYPH_GROUPS: 57 | if suffix is None: 58 | groups[group].append(glyph) 59 | continue 60 | if suffix in glyph: 61 | if group not in groups: 62 | groups[group] = [] 63 | groups[group].append(glyph) 64 | break 65 | 66 | print("## Glyphs porting progress") 67 | print(_progress_badge(progress_value)) 68 | print() 69 | 70 | for group, _ in GLYPH_GROUPS: 71 | glyphs = groups[group] 72 | coverage = _group_coverage(glyphs, missing_glyphs) 73 | coverage_url = _progress_url(coverage) 74 | counts = f"{int(len(glyphs) * coverage)} of {len(glyphs)}" 75 | print("
") 76 | print("") 77 | print(f'

{group} ({counts})

  ') 78 | print("
") 79 | print() 80 | for glyph in glyphs: 81 | if glyph in missing_glyphs: 82 | print(f"- [ ] {glyph}") 83 | else: 84 | print(f"- [x] {glyph}") 85 | print() 86 | print("
") 87 | print() 88 | 89 | if args.download_url: 90 | print(f"**[Download]({args.download_url})** CI build") 91 | 92 | 93 | @command() 94 | def snapshot(args): 95 | """Dumps missing glyphs""" 96 | glyphs = _find_missing_glyphs() 97 | Path(args.snapshot_dir).mkdir(parents=True, exist_ok=True) 98 | with Path(f"{args.snapshot_dir}/missing_glyphs.json").open( 99 | "w", encoding="utf-8" 100 | ) as file: 101 | json.dump(glyphs, file) 102 | print(f"Missing glyphs saved to {args.snapshot_dir}/missing_glyphs.json") 103 | 104 | 105 | def _group_coverage(glyphs: list[str], missing_glyphs: list[str]) -> list[str]: 106 | """Finds group coverage. Returns number in range 0-1""" 107 | missing_in_group = list(filter(lambda glyph: glyph in missing_glyphs, glyphs)) 108 | return 1 - (len(missing_in_group) / len(glyphs)) 109 | 110 | 111 | def _progress_badge(value: float) -> str: 112 | """Generates progress badge""" 113 | return f"![]({_progress_url(value)})" 114 | 115 | 116 | def _progress_url(value: float) -> str: 117 | """Generates progress URL""" 118 | return f"https://geps.dev/progress/{int(value * 100):.0f}" 119 | 120 | 121 | def _find_missing_glyphs() -> list[str]: 122 | """Finds missing glyphs""" 123 | target_font = GSFont(ROMAN_FONT_PATH) 124 | source_font = GSFont(ITALIC_FONT_PATH) 125 | missing_glyphs = [] 126 | for glyph in target_font.glyphs: 127 | if glyph.export and glyph.name not in source_font.glyphs: 128 | missing_glyphs.append(glyph.name) 129 | return missing_glyphs 130 | 131 | 132 | if __name__ == "__main__": 133 | run() 134 | -------------------------------------------------------------------------------- /OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2019 The Lilex Project Authors (https://github.com/mishamyrt/Lilex) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | https://openfontlicense.org 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /sources/opentype_features/calt/equal-arrows.fea: -------------------------------------------------------------------------------- 1 | # Arbitrary-length equal arrows 2 | # Ported from FiraCode 3 | 4 | lookup equal_arrows { 5 | # Disable ||| 6 | ignore sub bar bar' bar equal; 7 | ignore sub bar bar' equal; 8 | ignore sub [equal_start.seq equal_middle.seq] bar' bar bar; 9 | 10 | # Equal middle & end 11 | sub [less_equal_start.seq less_less_equal_start.seq less_equal_middle.seq less_less_equal_middle.seq greater_equal_start.seq greater_greater_equal_start.seq greater_equal_middle.seq greater_greater_equal_middle.seq bar_equal_start.seq bar_bar_equal_start.seq bar_equal_middle.seq bar_bar_equal_middle.seq slash_equal_start.seq slash_slash_equal_start.seq slash_equal_middle.seq slash_slash_equal_middle.seq colon_equal_middle.seq exclam_equal_middle.seq equal_start.seq equal_middle.seq] equal' [equal less greater bar slash colon exclam] by equal_middle.seq; 12 | 13 | sub [less_equal_start.seq less_less_equal_start.seq less_equal_middle.seq less_less_equal_middle.seq greater_equal_start.seq greater_greater_equal_start.seq greater_equal_middle.seq greater_greater_equal_middle.seq bar_equal_start.seq bar_bar_equal_start.seq bar_equal_middle.seq bar_bar_equal_middle.seq slash_equal_start.seq slash_slash_equal_start.seq slash_equal_middle.seq slash_slash_equal_middle.seq colon_equal_middle.seq exclam_equal_middle.seq equal_start.seq equal_middle.seq] equal' by equal_end.seq; 14 | 15 | # Double middles 16 | sub [equal_start.seq equal_middle.seq] less.spacer less' equal by less_less_equal_middle.seq; 17 | sub [equal_start.seq equal_middle.seq] less' less equal by less.spacer; 18 | sub [equal_start.seq equal_middle.seq] greater.spacer greater' equal by greater_greater_equal_middle.seq; 19 | sub [equal_start.seq equal_middle.seq] greater' greater equal by greater.spacer; 20 | sub [equal_start.seq equal_middle.seq] bar.spacer bar' equal by bar_bar_equal_middle.seq; 21 | sub [equal_start.seq equal_middle.seq] bar' bar equal by bar.spacer; 22 | sub [equal_start.seq equal_middle.seq] slash.spacer slash' equal by slash_slash_equal_middle.seq; 23 | sub [equal_start.seq equal_middle.seq] slash' slash equal by slash.spacer; 24 | 25 | # Single middles 26 | sub [equal_start.seq equal_middle.seq] less' equal by less_equal_middle.seq; 27 | sub [equal_start.seq equal_middle.seq] greater' equal by greater_equal_middle.seq; 28 | sub [equal_start.seq equal_middle.seq] bar' equal by bar_equal_middle.seq; 29 | sub [equal_start.seq equal_middle.seq] slash' equal by slash_equal_middle.seq; 30 | sub [equal_start.seq equal_middle.seq] colon' equal by colon_equal_middle.seq; 31 | sub [equal_start.seq equal_middle.seq] exclam' equal by exclam_equal_middle.seq; 32 | 33 | # Double ends 34 | sub [equal_start.seq equal_middle.seq] less.spacer less' by less_less_equal_end.seq; 35 | sub [equal_start.seq equal_middle.seq] less' less by less.spacer; 36 | sub [equal_start.seq equal_middle.seq] greater.spacer greater' by greater_greater_equal_end.seq; 37 | sub [equal_start.seq equal_middle.seq] greater' greater by greater.spacer; 38 | sub [equal_start.seq equal_middle.seq] bar.spacer bar' by bar_bar_equal_end.seq; 39 | sub [equal_start.seq equal_middle.seq] bar' bar by bar.spacer; 40 | sub [equal_start.seq equal_middle.seq] slash.spacer slash' by slash_slash_equal_end.seq; 41 | sub [equal_start.seq equal_middle.seq] slash' slash by slash.spacer; 42 | 43 | # Single ends 44 | sub [equal_start.seq equal_middle.seq] less' by less_equal_end.seq; 45 | sub [equal_start.seq equal_middle.seq] greater' by greater_equal_end.seq; 46 | sub [equal_start.seq equal_middle.seq] bar' by bar_equal_end.seq; 47 | sub [equal_start.seq equal_middle.seq] slash' by slash_equal_end.seq; 48 | 49 | # Double beginnings 50 | sub less.spacer less' equal by less_less_equal_start.seq; 51 | sub less' less equal by less.spacer; 52 | sub greater.spacer greater' equal by greater_greater_equal_start.seq; 53 | sub greater' greater equal by greater.spacer; 54 | sub bar.spacer bar' equal by bar_bar_equal_start.seq; 55 | sub bar' bar equal by bar.spacer; 56 | sub slash.spacer slash' equal by slash_slash_equal_start.seq; 57 | sub slash' slash equal by slash.spacer; 58 | 59 | # Disable >=< 60 | sub greater' equal less [equal less] by greater_equal_start.seq; 61 | ignore sub greater' equal less; 62 | 63 | # Disable =< 64 | sub equal' less [equal less] by equal_start.seq; 65 | 66 | # Disable =/ 67 | sub equal' slash [equal slash] by equal_start.seq; 68 | 69 | # Single beginnings 70 | sub less' equal by less_equal_start.seq; 71 | sub greater' equal by greater_equal_start.seq; 72 | sub bar' equal [equal less greater bar colon exclam slash] by bar_equal_start.seq; # disable |= 73 | sub slash' equal by slash_equal_start.seq; 74 | sub equal' [equal greater bar colon exclam] by equal_start.seq; 75 | 76 | } equal_arrows; 77 | -------------------------------------------------------------------------------- /scripts/normalize_names.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """A script to normalize the names of glyphs. 3 | Uses names from the Glyphs database and replaces names like "uni04B5" 4 | with human-readable names like "tetsecyrillic" 5 | """ 6 | 7 | from __future__ import annotations 8 | 9 | import sys 10 | import xml.etree.ElementTree as ET 11 | from pathlib import Path 12 | 13 | import requests 14 | from glyphsLib import GSComponent, GSFont, GSGlyph 15 | 16 | GITHUB_REPO = "schriftgestalt/GlyphsInfo" 17 | GITHUB_REPO_BRANCH = "Glyphs3" 18 | GLYPHS_DATA_FILE_NAME = "GlyphData.xml" 19 | GLYPHS_DATA_URL = ( 20 | f"https://raw.githubusercontent.com/{GITHUB_REPO}" 21 | f"/refs/heads/{GITHUB_REPO_BRANCH}/{GLYPHS_DATA_FILE_NAME}" 22 | ) 23 | GLYPHS_DATA_PATH = Path("/tmp") / GLYPHS_DATA_FILE_NAME 24 | 25 | Rename = tuple[str, str] 26 | 27 | 28 | def _get_glyph_data() -> bytes: 29 | """Get glyph data from the Glyphs database. 30 | Downloads and caches the data if it's not already present. 31 | Returns bytes of the XML file. 32 | """ 33 | if GLYPHS_DATA_PATH.exists(): 34 | return GLYPHS_DATA_PATH.read_bytes() 35 | 36 | print(f"Downloading {GLYPHS_DATA_FILE_NAME}...") 37 | response = requests.get(GLYPHS_DATA_URL, timeout=10) 38 | if response.status_code != 200: 39 | print(f"Failed to download {GLYPHS_DATA_FILE_NAME} from {GLYPHS_DATA_URL}") 40 | print("Status code:", response.status_code) 41 | print("Text:", response.text) 42 | exit(1) 43 | 44 | content = response.content 45 | GLYPHS_DATA_PATH.write_bytes(content) 46 | return content 47 | 48 | 49 | def _get_glyph_names(glyph_data: bytes) -> dict[str, str]: 50 | """Get a dictionary of unicode values to names from the Glyphs database.""" 51 | tree = ET.fromstring(glyph_data) 52 | names = {} 53 | for elem in tree.iter(): 54 | if elem.tag == "glyph" and "unicode" in elem.attrib: 55 | names[elem.attrib["unicode"]] = elem.attrib["name"] 56 | return names 57 | 58 | 59 | def _deep_rename(font: GSFont, renames: list[Rename]) -> None: 60 | """Renames the glyph""" 61 | for original, target in renames: 62 | for glyph in font.glyphs: 63 | if glyph.name == original: 64 | glyph.name = target 65 | for layer in glyph.layers: 66 | for shape in layer.shapes: 67 | if isinstance(shape, GSComponent) and shape.name == original: 68 | shape.name = target 69 | 70 | 71 | def _find_renames( 72 | font: GSFont, known_glyphs: dict[str, str] 73 | ) -> tuple[list[Rename], list[GSGlyph]]: 74 | """Renames uni-prefixed glyphs""" 75 | renames = [] 76 | unknown = [] 77 | for glyph in font.glyphs: 78 | if glyph.name is None or glyph.unicode is None: 79 | continue 80 | code = glyph.unicode.zfill(4).upper() 81 | if code not in known_glyphs: 82 | unknown.append(glyph) 83 | continue 84 | original = glyph.name 85 | target = known_glyphs[code] 86 | if original != target: 87 | renames.append((original, target)) 88 | 89 | changed_originals = [rename[0] for rename in renames] 90 | additional_renames = [] 91 | for original, target in renames: 92 | prefix = original + "." 93 | for glyph in font.glyphs: 94 | if glyph.name.startswith(prefix) and glyph.name not in changed_originals: 95 | child_rename = glyph.name.replace(original, target) 96 | additional_renames.append((glyph.name, child_rename)) 97 | renames += additional_renames 98 | return sorted(renames, key=lambda rename: rename[0]), unknown 99 | 100 | 101 | def normalize_names(input_path: str, output_path: str): 102 | """Normalize the names of glyphs in the input file""" 103 | print("Getting glyph data...") 104 | glyph_data = _get_glyph_data() 105 | known_glyphs = _get_glyph_names(glyph_data) 106 | 107 | print("Finding wrong names...") 108 | font = GSFont(input_path) 109 | renames, unknown = _find_renames(font, known_glyphs) 110 | 111 | if len(unknown) > 0: 112 | print("Unknown glyphs:") 113 | for glyph in unknown: 114 | print(f" {glyph.name} ({glyph.unicode})") 115 | 116 | if len(renames) == 0: 117 | print("No renames found") 118 | return 119 | 120 | print("Renaming...") 121 | _deep_rename(font, renames) 122 | 123 | for name, new_name in renames: 124 | print(f" {name} -> {new_name}") 125 | 126 | font.save(output_path) 127 | print(f"Saved to {output_path}") 128 | 129 | 130 | def main(): 131 | """Main entrypoint""" 132 | if len(sys.argv) < 2: 133 | print(f"Usage: {sys.argv[0]} []") 134 | sys.exit(1) 135 | input_path = sys.argv[1] 136 | output_path = sys.argv[2] if len(sys.argv) > 2 else input_path 137 | normalize_names(input_path, output_path) 138 | 139 | 140 | if __name__ == "__main__": 141 | main() 142 | -------------------------------------------------------------------------------- /sources/opentype_features/mark.fea: -------------------------------------------------------------------------------- 1 | # Name: Mark positioning 2 | @mGC_bottom_0_0 = [cedillacomb commaaccentcomb dotbelowcomb]; 3 | @mGC_top_0_516 = [acutecomb breveacute brevecomb brevegrave brevehook brevetilde caroncomb circumflexacute circumflexbreve circumflexcomb circumflexgrave circumflexhook circumflextilde commaturnedabovecomb dieresisacute dieresiscaron dieresiscomb dieresisgrave dieresismacron dotaccentcomb gravecomb hookabovecomb hungarumlautcomb macroncomb ringcomb tildecomb]; 4 | @mGC_top_0_698 = [breveacute.case brevegrave.case brevehook.case brevetilde.case circumflexacute.case circumflexbreve.case circumflexgrave.case circumflexhook.case circumflextilde.case dieresisacute.case dieresiscaron.case dieresisgrave.case dieresismacron.case hookabovecomb.case]; 5 | 6 | markClass @mGC_bottom_0_0 @MC_bottom; 7 | markClass @mGC_top_0_516 @MC_top; 8 | markClass @mGC_top_0_698 @MC_top; 9 | markClass commaaboverightcomb @MC_topright; 10 | markClass ogonekcomb @MC_bottomright; 11 | 12 | lookup MARK_BASE_bottom { 13 | @bGC_D_bottom = [D J]; 14 | @bGC_F_bottom = [F P]; 15 | @bGC_c_bottom = [c h n]; 16 | @bGC_f_bottom = [f C R]; 17 | @bGC_i_bottom = [i idotless]; 18 | @bGC_l_bottom = [l m o v w x z A G H I M N O T U V W X Y Z ohorn Ohorn Uhorn]; 19 | @bGC_u_bottom = [u S uhorn]; 20 | pos base @bGC_D_bottom mark @MC_bottom; 21 | pos base @bGC_F_bottom mark @MC_bottom; 22 | pos base @bGC_c_bottom mark @MC_bottom; 23 | pos base @bGC_f_bottom mark @MC_bottom; 24 | pos base @bGC_i_bottom mark @MC_bottom; 25 | pos base @bGC_l_bottom mark @MC_bottom; 26 | pos base @bGC_u_bottom mark @MC_bottom; 27 | pos base B mark @MC_bottom; 28 | pos base E mark @MC_bottom; 29 | pos base K mark @MC_bottom; 30 | pos base L mark @MC_bottom; 31 | pos base a mark @MC_bottom; 32 | pos base a.alt01 mark @MC_bottom; 33 | pos base b mark @MC_bottom; 34 | pos base d mark @MC_bottom; 35 | pos base e mark @MC_bottom; 36 | pos base k mark @MC_bottom; 37 | pos base r mark @MC_bottom; 38 | pos base s mark @MC_bottom; 39 | pos base t mark @MC_bottom; 40 | pos base y mark @MC_bottom; 41 | } MARK_BASE_bottom; 42 | 43 | 44 | lookup MARK_BASE_bottomright { 45 | @bGC_i_bottomright = [i idotless]; 46 | pos base @bGC_i_bottomright mark @MC_bottomright; 47 | pos base A mark @MC_bottomright; 48 | pos base E mark @MC_bottomright; 49 | pos base I mark @MC_bottomright; 50 | pos base U mark @MC_bottomright; 51 | pos base a mark @MC_bottomright; 52 | pos base a.alt01 mark @MC_bottomright; 53 | pos base e mark @MC_bottomright; 54 | pos base u mark @MC_bottomright; 55 | } MARK_BASE_bottomright; 56 | 57 | 58 | lookup MARK_BASE_top { 59 | @bGC_J_top = [J K]; 60 | @bGC_a.alt01_top = [a.alt01 g.alt01]; 61 | @bGC_g.alt02_top = [g.alt02 m o v w x y z ae oslash ohorn]; 62 | @bGC_g_top = [g u uhorn]; 63 | @bGC_l_top = [l A H I M N O P Q T U V W X Y Z Oslash Ohorn Uhorn]; 64 | pos base @bGC_J_top mark @MC_top; 65 | pos base @bGC_a.alt01_top mark @MC_top; 66 | pos base @bGC_g.alt02_top mark @MC_top; 67 | pos base @bGC_g_top mark @MC_top; 68 | pos base @bGC_l_top mark @MC_top; 69 | pos base AE mark @MC_top; 70 | pos base B mark @MC_top; 71 | pos base C mark @MC_top; 72 | pos base D mark @MC_top; 73 | pos base E mark @MC_top; 74 | pos base F mark @MC_top; 75 | pos base G mark @MC_top; 76 | pos base L mark @MC_top; 77 | pos base R mark @MC_top; 78 | pos base S mark @MC_top; 79 | pos base a mark @MC_top; 80 | pos base c mark @MC_top; 81 | pos base idotless mark @MC_top; 82 | pos base jdotless mark @MC_top; 83 | pos base e mark @MC_top; 84 | pos base h mark @MC_top; 85 | pos base n mark @MC_top; 86 | pos base p mark @MC_top; 87 | pos base q mark @MC_top; 88 | pos base r mark @MC_top; 89 | pos base s mark @MC_top; 90 | } MARK_BASE_top; 91 | 92 | 93 | lookup MARK_BASE_topright { 94 | pos base L mark @MC_topright; 95 | pos base d mark @MC_topright; 96 | pos base l mark @MC_topright; 97 | pos base t mark @MC_topright; 98 | } MARK_BASE_topright; 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Lilex. The font for developers.
6 | 7 | Quality Assurance 8 | 9 | 10 | Version 11 | 12 |

13 |


14 | 15 | Lilex is an extended font on top of [IBM Plex Mono](https://github.com/IBM/plex) designed for developers. It contains ligatures, special characters (e.g. PowerLine), Greek and exists in a variable format. 16 | 17 | Ligatures is just a font rendering feature: underlying code remains ASCII-compatible. This makes it easier to read and understand the code. In some cases, the ligatures connect closely related characters (`==`, `---`), while in others they optically align the glyphs (`..`, `??`). 18 | 19 | Compiled versions are available under [releases](https://github.com/mishamyrt/Lilex/releases). Bleeding edge builds can be downloaded in the [build](https://github.com/mishamyrt/Lilex/actions/workflows/build.yaml) workflow artifacts. 20 | 21 | ## Installation 22 | 23 | 1. [Download font](https://github.com/mishamyrt/Lilex/releases/latest). 24 | 2. Unzip the archive. 25 | 3. Install the font: 26 | - Mac: Select all font files in the `variable` folder and double-click them. Click the `Install Font` button. 27 | - Windows: Select all font files in the `variable` folder, right-click any of them, then click `Install` from the menu. 28 | 29 | ### Visual Studio Code 30 | 31 | 1. From the `Code` menu (`File` on Windows) go to `Preferences` → `Settings`, or use keyboard shortcut +, (Ctrl+, on Windows). 32 | 2. In the `Editor: Font Family` input box type `Lilex`. 33 | 3. To enable ligatures, go to `Editor: Font Ligatures`, click `Edit in settings.json`, and copy `"editor.fontLigatures": true` into file. 34 | 35 | If you want to enable stylistic sets, list them instead of `true`. Like: 36 | 37 | ```json 38 | "editor.fontLigatures": "'calt', 'ss02', 'ss04'" 39 | ``` 40 | 41 | ### Cursor 42 | 43 | Same as Visual Studio Code, but menu `Settings` → `VS Code Settings` 44 | 45 | ### iTerm2 46 | 47 | 1. From the `iTerm2` menu go to `Settings`. Under `Profiles`, find the `Text` tab. 48 | 2. If you have more than one profile, select the one you want to change. Or change the default one (with an asterisk). 49 | 3. Click on the font name under the 'Font' heading, find `Lilex` and select it. 50 | 51 | ### Ghostty 52 | 53 | 1. From the `Ghostty` menu go to `Settings…`, or use keyboard shortcut +,. 54 | 2. Add `font-family = Lilex` to config file. 55 | 3. Restart Ghostty (or reload configuration with +Shift+,) 56 | 57 | ## Weight 58 | 59 | There are 5 font weights available in Lilex, ranging from Thin to Bold. In addition, a variable font is available. 60 | 61 | 62 | 63 | ## Italics 64 | 65 | Lilex comes with a full set of italics: all weights, ligatures, PowerLine. Lilex Italic can do everything that Lilex does. 66 | 67 | 68 | 69 | ## Character Set 70 | 71 | The font has support for Latin, Cyrillic and Greek. It also includes ligatures and powerline symbols. 72 | 73 | 74 | 75 | A full glyph table can be found on the [preview page](https://mishamyrt.github.io/Lilex/). 76 | 77 | ## Features 78 | 79 | The font has additional styles for some characters, so it can be configured to better fit your needs. Instructions on how to activate OpenType features in your IDE can be found on the internet, or [build your own variation](#forced-feature-activation) of the font with forced features 80 | 81 | 82 | 83 | Some ligatures also have additional options. For example, certain arrows are initially switched off to avoid conflicts with logical operations. 84 | 85 | 86 | 87 | ### Arrows 88 | 89 | Lilex uses generated ligatures for arrows, so they can be infinite. Combine that to assemble your unique arrows. 90 | 91 | There is also a full set of single-character arrows (`↑`, `↓`, etc.) in the font. 92 | 93 | 94 | 95 | ## Development 96 | 97 | If you want to make improvements to the project, see [CONTRIBUTING.md](CONTRIBUTING.md). 98 | 99 | ## License 100 | 101 | Lilex typeface is available under the [OFL-1.1 License](https://github.com/mishamyrt/Lilex/blob/master/OFL.txt) and can be used free of charge, for both commercial and non-commercial purposes. 102 | 103 | The source code is available under [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0). 104 | 105 | ## Credits 106 | 107 | - Author: Mikhael Khrustik 108 | - Based on: [IBM Plex Mono](https://github.com/IBM/plex) 109 | - Inspired by: [Fira Code](https://github.com/tonsky/FiraCode) 110 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Project directories 2 | BUILD_DIR := build 3 | RELEASE_DIR := fonts 4 | REPORTS_DIR := reports 5 | SCRIPTS_DIR := scripts 6 | SOURCE_DIR := sources 7 | WEBSITE_DIR := website 8 | 9 | # Internal build variables 10 | PYTHON_VERSION := 3.13 11 | OS := $(shell uname) 12 | VENV_DIR = ./.venv 13 | VENV = . $(VENV_DIR)/bin/activate; 14 | 15 | # Font build 16 | 17 | .PHONY: configure 18 | configure: ## setup font build environment 19 | @rm -rf "$(VENV_DIR)" 20 | @uv venv --python $(PYTHON_VERSION) 21 | @uv sync 22 | @uv run youseedee A > /dev/null 23 | uv tool run lefthook install 24 | 25 | .PHONY: generate 26 | generate: ## regenerate the font sources 27 | @$(VENV) python $(SCRIPTS_DIR)/generate.py \ 28 | --config "$(SOURCE_DIR)/lilexgen_config.yaml" \ 29 | generate 30 | 31 | .PHONY: build 32 | build: ## build the font 33 | @make build-mono 34 | @make build-duo 35 | 36 | .PHONY: build-mono 37 | build-mono: ## build Lilex monospaced font 38 | @$(call build-font,Lilex) 39 | 40 | .PHONY: build-duo 41 | build-duo: ## build Lilex Duo font 42 | @$(call build-font,LilexDuo) 43 | 44 | .PHONY: release 45 | release: build ## release the font 46 | @rm -rf $(RELEASE_DIR) 47 | @cp -r $(BUILD_DIR) $(RELEASE_DIR) 48 | 49 | define build-font 50 | @rm -rf $(BUILD_DIR)/$(1) 51 | @$(VENV) cd $(SOURCE_DIR)/$(1); gftools builder config.yaml 52 | endef 53 | 54 | # Font quality check 55 | 56 | .PHONY: check 57 | check: ## check Lilex font quality 58 | @make check-mono 59 | @make check-duo 60 | 61 | .PHONY: check-mono 62 | check-mono: ## check Lilex font quality 63 | $(call fontbakery-check,parallel,Lilex) 64 | 65 | .PHONY: check-duo 66 | check-duo: ## check Lilex Duo font quality 67 | $(call fontbakery-check,parallel,LilexDuo) 68 | 69 | .PHONY: check-sequential 70 | check-sequential: ## check Lilex font quality sequentially (for CI) 71 | $(call fontbakery-check,sequential,Lilex) 72 | $(call fontbakery-check,sequential,LilexDuo) 73 | 74 | define fontbakery-check 75 | $(call fontbakery-check-format,$(1),$(2),variable) 76 | $(call fontbakery-check-format,$(1),$(2),ttf) 77 | endef 78 | 79 | # usage: $(call fontbakery-check,[parallel|sequential],font_name,font_format) 80 | define fontbakery-check-format 81 | @mkdir -p "$(REPORTS_DIR)" 82 | @$(VENV) fontbakery check-googlefonts \ 83 | $(if $(filter-out parallel,$(1)),,--auto-jobs) \ 84 | $(if $(filter-out variable,$(3)),-x opentype/STAT/ital_axis) \ 85 | -x fontdata_namecheck \ 86 | --html "$(REPORTS_DIR)/$(2)_$(3).html" \ 87 | "$(BUILD_DIR)/$(2)/$(3)/"* 88 | endef 89 | 90 | # Scripts and scripts management 91 | 92 | .PHONY: scripts-print-updates 93 | scripts-print-updates: ## print list of outdated packages 94 | @uv tree --depth 1 --outdated 95 | 96 | .PHONY: scripts-lint 97 | scripts-lint: ## lint scripts 98 | @uv tool run ruff check $(SCRIPTS_DIR)/ 99 | 100 | .PHONY: scripts-lint-fix 101 | scripts-lint-fix: ## lint scripts and autofix errors 102 | @uv tool run ruff check --fix $(SCRIPTS_DIR)/ 103 | 104 | .PHONY: scripts-format 105 | scripts-format: ## format scripts 106 | @uv tool run ruff format $(SCRIPTS_DIR)/ 107 | 108 | # Website 109 | 110 | .PHONY: website-configure 111 | website-configure: ## setup website environment 112 | @cd $(WEBSITE_DIR); pnpm install 113 | 114 | .PHONY: website-run 115 | website-serve: _website-env ## run the website 116 | @cd $(WEBSITE_DIR); pnpm run dev 117 | 118 | .PHONY: website-build 119 | website-build: _website-env ## build the website 120 | @cd $(WEBSITE_DIR); pnpm run build 121 | 122 | .PHONY: print-updates 123 | website-print-updates: ## print list of outdated packages 124 | @cd $(WEBSITE_DIR); pnpm outdated 125 | 126 | .PHONY: website-lint 127 | website-lint: ## check preview website code quality 128 | @cd $(WEBSITE_DIR); pnpm lint 129 | 130 | .PHONY: website-format 131 | website-format: ## format preview website code 132 | @cd $(WEBSITE_DIR); pnpm format 133 | 134 | .PHONY: _website-env 135 | _website-env: 136 | uv run $(SCRIPTS_DIR)/website_env.py \ 137 | generate \ 138 | $(BUILD_DIR)/Lilex/ttf/Lilex-Regular.ttf \ 139 | $(WEBSITE_DIR)/.env 140 | 141 | # Install 142 | 143 | .PHONY: install 144 | install: ## install font to system (macOS and Linux only) 145 | @make install-$(OS) 146 | 147 | .PHONY: install-Darwin 148 | install-Darwin: 149 | @rm -rf ~/Library/Fonts/Lilex ~/Library/Fonts/LilexDuo 150 | @mkdir -p ~/Library/Fonts/Lilex ~/Library/Fonts/LilexDuo 151 | @cp -r $(BUILD_DIR)/Lilex/variable ~/Library/Fonts/Lilex 152 | @cp -r $(BUILD_DIR)/LilexDuo/variable ~/Library/Fonts/LilexDuo 153 | 154 | 155 | install-Linux: 156 | @rm -rf ~/.fonts/Lilex ~/.fonts/LilexDuo 157 | @mkdir -p ~/.fonts/Lilex ~/.fonts/LilexDuo 158 | @cp -r $(BUILD_DIR)/Lilex/ttf ~/.fonts/Lilex 159 | @cp -r $(BUILD_DIR)/LilexDuo/ttf ~/.fonts/LilexDuo 160 | 161 | # Utilities 162 | 163 | .PHONY: help 164 | help: ## print this message 165 | @awk \ 166 | 'BEGIN {FS = ":.*?## "} \ 167 | /^[a-zA-Z_-]+:.*?## / \ 168 | {printf "\033[33m%-30s\033[0m %s\n", $$1, $$2}' \ 169 | $(MAKEFILE_LIST) 170 | 171 | .PHONY: clean 172 | clean: ## clean up artifacts 173 | @rm -rf $(BUILD_DIR) 174 | @rm -rf $(REPORTS_DIR) 175 | @rm -rf $(VENV_DIR) 176 | -------------------------------------------------------------------------------- /website/src/components/Powerline.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const RIGHT_BLACK_ARROW_CHAR = "\uE0B0" 3 | const RIGHT_ARROW_CHAR = "\uE0B1" 4 | const LEFT_BLACK_ARROW_CHAR = "\uE0B2" 5 | const LEFT_ARROW_CHAR = "\uE0B3" 6 | 7 | export type Part = { 8 | text: string; 9 | color?: 'grey_10' | 'grey_30' | 'grey_60' | 'yellow_60' | 'green' | 'red'; 10 | isPath?: boolean; 11 | icon?: string; 12 | } 13 | 14 | export type Props = { 15 | class?: string; 16 | parts: Part[]; 17 | direction?: 'left' | 'right'; 18 | } 19 | 20 | const { class: className, parts, direction = 'right', ...props } = Astro.props; 21 | 22 | const pathDelimiter = direction === 'right' ? RIGHT_ARROW_CHAR : LEFT_ARROW_CHAR 23 | 24 | const renderedParts = parts.map((part) => { 25 | const textSegments = part.isPath 26 | ? part.text.split('/').map(segment => segment.trim()) 27 | : [part.text]; 28 | return { 29 | textSegments, 30 | color: part.color, 31 | isPath: part.isPath, 32 | icon: part.icon 33 | } 34 | }) 35 | --- 36 | 37 |
38 | {renderedParts.map((part, i) => ( 39 |
40 | {direction === 'left' && ( 41 | {LEFT_BLACK_ARROW_CHAR} 45 | )} 46 | 47 | 48 | {part.icon} 49 | 50 | 51 | {part.isPath 52 | ? part.textSegments.map((segment, j) => ( 53 | <> 54 | {segment} 55 | {j < part.textSegments.length - 1 && ( 56 | 57 | )} 58 | 59 | )) 60 | : part.textSegments[0] 61 | } 62 | 63 | 64 | {direction === 'right' && ( 65 | {RIGHT_BLACK_ARROW_CHAR} 69 | )} 70 |
71 | ))} 72 |
73 | 74 | 180 | -------------------------------------------------------------------------------- /website/src/blocks/Superpowers/Superpowers.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import LayoutBlock from "../../components/LayoutBlock.astro"; 3 | import Stack from "../../components/Stack.astro"; 4 | import Typography from "../../components/Typography.astro"; 5 | import FeatureRow from "./FeatureRow.astro"; 6 | import PreviewLigatures from "./PreviewLigatures.astro"; 7 | import PreviewAligns from "./PreviewAligns.astro"; 8 | import PreviewWeight from "./PreviewWeight.astro"; 9 | import PreviewPowerline from "./PreviewPowerline.astro"; 10 | import Link from "../../components/Link.astro"; 11 | import { 12 | FloatingPanel, 13 | FloatingToggle, 14 | } from "../../components/FloatingActions"; 15 | import { $isSuperpowersEnabled } from "../../stores/superpowers"; 16 | import Toggle from "../../components/Toggle.astro"; 17 | 18 | const isSuperpowersEnabled = $isSuperpowersEnabled.get(); 19 | --- 20 | 21 | 22 |
23 |
24 | 25 | 26 | Lilex is built on top of the IBM Plex Mono. It includes support for 27 | variable weight and developer specific features. 28 | 29 | 36 | Superpowers 38 | 43 | 44 | 45 |
46 | 47 | 48 | 49 | The font replaces programming character sequences with special glyphs. 50 | This makes it easier to read and understand the code. 51 | 52 | 53 | In addition to static ligatures, there are special ligatures that are 54 | generated during the writing process. These ligatures cover the needs 55 | of all developers. 56 | 57 | 58 | 59 | 60 | 61 | Lilex is optmized for reading, for this purpose the font automatically 62 | aligns some characters depending on the context. 63 | 64 | 65 | 66 | 67 | 68 | In addition to the fixed weight, Lilex provides a variant option that 69 | all possible weights from 100 to 700, for both italic and roman. 70 | 71 | 72 | 73 | 74 | 75 | Lilex was created by a developer for developers, so it takes into 76 | account the needs of professional terminal users. 77 | 78 | 79 | 80 | The font contains the necessary character set for correct Powerlinedisplay. 84 | 85 | 86 | 87 | 88 | 89 | 90 | 95 | 96 |
97 |
98 | 99 | 124 | 125 | 197 | -------------------------------------------------------------------------------- /scripts/lilexgen/opentype_features/const.py: -------------------------------------------------------------------------------- 1 | """Generator constants""" 2 | 3 | SKIP_IGNORES = [ 4 | # <<*>> <<+>> <<$>> 5 | ("less", "asterisk", "greater"), 6 | ("less", "plus", "greater"), 7 | ("less", "dollar", "greater"), 8 | ] 9 | 10 | IGNORE_PREFIXES = [ 11 | ("parenleft", "question", "colon"), 12 | # Regexp lookahead/lookbehind 13 | ("parenleft", "question", "colon"), 14 | ("parenleft", "question", "equal"), 15 | ("parenleft", "question", "less", "equal"), 16 | ("parenleft", "question", "exclam"), 17 | # PHP 44 | ("less", "bar", "bar"): ["less' bar bar greater"], 45 | ("bar", "bar", "greater"): ["less bar' bar greater"], 46 | # {|} 47 | ("braceleft", "bar"): ["braceleft' bar braceright"], 48 | ("bar", "braceright"): ["braceleft bar' braceright"], 49 | # [|] 50 | ("bracketleft", "bar"): ["bracketleft' bar bracketright"], 51 | ("bar", "bracketright"): ["bracketleft bar' bracketright"], 52 | # <*>>> <+>>> <$>>> >>->> >>=>> >>= 53 | ("greater", "greater"): [ 54 | "[asterisk plus dollar] greater' greater", 55 | "[hyphen equal] greater' greater", 56 | "greater' greater hyphen", 57 | "greater' greater equal [equal less greater bar colon exclam slash]", 58 | ], 59 | # <*>>> <+>>> <$>>> 60 | ("greater", "greater", "greater"): [ 61 | "[asterisk plus dollar] greater' greater greater" 62 | ], 63 | # <<*> <<+> <<$> <<-<< <<=<< <<= 64 | ("less", "less"): [ 65 | "less' less [asterisk plus dollar]", 66 | "[hyphen equal] less' less", 67 | "less' less hyphen", 68 | "less' less equal [equal less greater bar colon exclam slash]", 69 | ], 70 | # <<<*> <<<+> <<<$> 71 | ("less", "less", "less"): ["less' less less [asterisk plus dollar]"], 72 | # =:= 73 | ("colon", "equal"): [ 74 | "equal colon' equal", 75 | ], 76 | # =!= 77 | ("exclam", "equal"): [ 78 | "equal exclam' equal", 79 | ], 80 | # =!== 81 | ("exclam", "equal", "equal"): [ 82 | "equal exclam' equal equal", 83 | ], 84 | # =<= <=< <=> <=| <=: <=! <=/ 85 | ("less", "equal"): [ 86 | "equal less' equal", 87 | "less' equal [less greater bar colon exclam slash]", 88 | ], 89 | # >=< =>= >=> >=< >=| >=: >=! >=/ 90 | ("greater", "equal"): [ 91 | "equal greater' equal", 92 | "greater' equal [less greater bar colon exclam slash]", 93 | ], 94 | # ||-|| ||=|| ||= 95 | ("bar", "bar"): [ 96 | "[hyphen equal] bar' bar", 97 | "bar' bar hyphen", 98 | "bar' bar equal [equal less greater bar colon exclam slash]", 99 | ], 100 | # //= 101 | ("slash", "slash"): [ 102 | "equal slash' slash", 103 | "slash' slash equal", 104 | ], 105 | # <--> >--< |--| 106 | ("hyphen", "hyphen"): [ 107 | "[less greater bar] hyphen' hyphen", 108 | "hyphen' hyphen [less greater bar]", 109 | ], 110 | # <==> >==< |==| /==/ =:== =!== ==:= ==!= [==[ ]==] [== ==] 111 | ("equal", "equal"): [ 112 | "bracketleft equal' equal", 113 | "equal' equal bracketright", 114 | "equal [colon exclam] equal' equal", 115 | "[less greater bar slash] equal' equal", 116 | "equal' equal [less greater bar slash]", 117 | "equal' equal [colon exclam] equal", 118 | ], 119 | # <===> >===< |===| /===/ =:=== =!=== ===:= ===!= [===[ ]===] [=== ===] 120 | ("equal", "equal", "equal"): [ 121 | "bracketleft equal' equal equal", 122 | "equal' equal equal bracketright", 123 | "equal [colon exclam] equal' equal equal", 124 | "[less greater bar slash] equal' equal equal", 125 | "equal' equal equal [less greater bar slash]", 126 | "equal' equal equal [colon exclam] equal", 127 | ], 128 | } 129 | 130 | # Replacement ignore templates map 131 | # ignore sub 132 | IGNORE_TPL = { 133 | 2: ["1 1' 2", "1' 2 2"], 134 | 3: ["1 1' 2 3", "1' 2 3 3"], 135 | 4: ["1 1' 2 3 4", "1' 2 3 4 4"], 136 | 5: ["1 1' 2 3 4", "1' 2 3 4 4"], 137 | } 138 | 139 | 140 | PRIORITIES = { 141 | # <|> 142 | ("less", "bar", "greater"): 0, 143 | # |||> ||> |> <| <|| <||| 144 | ("bar", "bar", "bar", "greater"): 1, 145 | ("bar", "bar", "greater"): 1, 146 | ("bar", "greater"): 1, 147 | ("less", "bar", "bar", "bar"): 1, 148 | ("less", "bar", "bar"): 1, 149 | ("less", "bar"): 1, 150 | # << <<< >> >>> || ||| before -- --- == === 151 | ("less", "less"): 2, 152 | ("less", "less", "less"): 2, 153 | ("greater", "greater"): 2, 154 | ("greater", "greater", "greater"): 2, 155 | ("bar", "bar"): 2, 156 | ("bar", "bar", "bar"): 2, 157 | } 158 | 159 | # Replacement templates map 160 | # sub 161 | REPLACE_TPL = { 162 | 2: ["1.spacer 2' by 1_2.liga", "1' 2 by 1.spacer"], 163 | 3: [ 164 | "1.spacer 2.spacer 3' by 1_2_3.liga", 165 | "1.spacer 2' 3 by 2.spacer", 166 | "1' 2 3 by 1.spacer", 167 | ], 168 | 4: [ 169 | "1.spacer 2.spacer 3.spacer 4' by 1_2_3_4.liga", 170 | "1.spacer 2.spacer 3' 4 by 3.spacer", 171 | "1.spacer 2' 3 4 by 2.spacer", 172 | "1' 2 3 4 by 1.spacer", 173 | ], 174 | 5: [ 175 | "1.spacer 2.spacer 3.spacer 4.spacer 5' by 1_2_3_4_5.liga", 176 | "1.spacer 2.spacer 3.spacer 4' 5 by 4.spacer", 177 | "1.spacer 2.spacer 3' 4 5 by 3.spacer", 178 | "1.spacer 2' 3 4 5 by 2.spacer", 179 | "1' 2 3 4 5 by 1.spacer", 180 | ], 181 | } 182 | 183 | NAME_TPL = { 184 | "ss": ('featureNames {\n name 3 1 0x0409 "$NAME";\n};\n'), 185 | "cv": ( 186 | 'cvParameters {\n FeatUILabelNameID{\n name 3 1 0x0409 "$NAME";\n };\n};\n' 187 | ), 188 | } 189 | --------------------------------------------------------------------------------