From d8d29c901973bce08128d1e80f46c2774d260929 Mon Sep 17 00:00:00 2001 From: RubenVerg Date: Thu, 21 Aug 2025 05:33:23 +0200 Subject: [PATCH 1/2] Add feature in the script to prebake font features --- script.py | 127 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 42 deletions(-) diff --git a/script.py b/script.py index d81d8cc..4968a4c 100644 --- a/script.py +++ b/script.py @@ -1,13 +1,18 @@ import fontforge import os import sys +from itertools import chain, combinations + +def powerset(iterable): + s = list(iterable) + return chain.from_iterable(combinations(s, r) for r in range(len(s)+1)) EXTENSIONS = [ - 'ttf', - 'otf', - 'woff', - 'woff2', - 'svg', + 'ttf', + 'otf', + 'woff', + 'woff2', + 'svg', ] path = sys.argv[1] @@ -18,14 +23,14 @@ apl387.version = commit try: - os.mkdir(f'{path}/output') + os.mkdir(f'{path}/output') except: - pass + pass features = [apl387.getLookupInfo(lookup)[2][0][0] for lookup in apl387.gsub_lookups] with open(f'{path}/output/chars.html', 'w') as chars: - chars.write(''' + chars.write(''' @@ -42,17 +47,17 @@ - ''') - for gl in sorted((gl for gl in apl387.glyphs() if gl.unicode != -1), key=lambda gl: gl.unicode): - chars.write(f'\n') - chars.write(''' + ''') + for gl in sorted((gl for gl in apl387.glyphs() if gl.unicode != -1), key=lambda gl: gl.unicode): + chars.write(f'\n') + chars.write('''
APL385 Unicodenew APL387 Unicode
&#{gl.unicode};&#{gl.unicode};
&#{gl.unicode};&#{gl.unicode};
- ''') + ''') with open(f'{path}/output/compare.html', 'w', encoding='utf-8') as compare: - compare.write(''' + compare.write(''' @@ -62,7 +67,7 @@ @font-face {font-family: 'APL385';src: url('APL385.ttf');} * { font-weight: unset; - font-feature-settings: inherit; + font-feature-settings: inherit; } body { @@ -93,11 +98,11 @@ - ''') - for feature in features: - compare.write(f'') - compare.write('
compare characters individually') - same = ''' + ''') + for feature in features: + compare.write(f'') + compare.write('
compare characters individually') + same = ''' @@ -181,27 +186,27 @@
APL (named after the book A Programming Language) is a programming language developed in the 1960s by Kenneth E. Iverson. Its central datatype is the multidimensional array. It uses a large range of special graphic symbols to represent most functions and operators, leading to very concise code. It has been an important influence on the development of concept modeling, spreadsheets, functional programming, and computer math packages. It has also inspired several other programming languages.

All supported characters:

-	'''
-	for idx, gl in enumerate(sorted((gl for gl in apl387.glyphs() if gl.unicode != -1), key=lambda gl: gl.unicode)):
-		if idx != 0 and idx % 16 == 0: same += '\n'
-		same += f'&#{gl.unicode};'
-	same += '
' - compare.write(f''' + ''' + for idx, gl in enumerate(sorted((gl for gl in apl387.glyphs() if gl.unicode != -1), key=lambda gl: gl.unicode)): + if idx != 0 and idx % 16 == 0: same += '\n' + same += f'&#{gl.unicode};' + same += '' + compare.write(f'''

APL385 Unicode

- {same} + {same}

New APL387 Unicode

- {same} + {same}
- + - ''') + ''') with open(f'{path}/output/index.html', 'w', encoding='utf-8') as index: - index.write(''' + index.write(''' @@ -210,15 +215,15 @@ -
+
-
- ''') - for feature in features: - index.write(f'') - index.write(''' -
+
+ ''') + for feature in features: + index.write(f'') + index.write(''' +

APL387 Unicode download side by side with APL385 source

A redrawn and extended version of Adrian Smith's classic APL385 font with clean rounded look.

@@ -307,9 +312,47 @@

Sample text:

APL (named after the book A Programming Language) is a programming language developed in the 1960s by Kenneth E. Iverson. Its central datatype is the multidimensional array. It uses a large range of special graphic symbols to represent most functions and operators, leading to very concise code. It has been an important influence on the development of concept modeling, spreadsheets, functional programming, and computer math packages. It has also inspired several other programming languages.
- + - ''') + ''') + +def bake_feature(lookup: str): + subtable = apl387.getLookupSubtables(lookup)[0] + to_swap = [] + for glyph in apl387.glyphs(): + lookups = glyph.getPosSub(subtable) + if len(lookups) == 0: continue + elif len(lookups) == 1: + to_swap.append((glyph, apl387.createMappedChar(lookups[0][2]))) + else: + raise RuntimeError(f'Glyph {glyph.glyphname} has more than one lookup, I don\'t know what to do.') + for a, b in to_swap: + swap_glyphs(a, b) + +temp = apl387.createChar(-1, 'swapTemp') + +def swap_glyphs(a, b): + apl387.selection.select(a) + apl387.copy() + apl387.selection.select(temp) + apl387.paste() + apl387.selection.select(b) + apl387.copy() + apl387.selection.select(a) + apl387.paste() + apl387.selection.select(temp) + apl387.copy() + apl387.selection.select(b) + apl387.paste() -for ext in EXTENSIONS: - apl387.generate(f'{path}/output/APL387.{ext}') +for subset in powerset(apl387.gsub_lookups): + baked_features = [apl387.getLookupInfo(lookup)[2][0][0] for lookup in subset] + apl387.familyname = f'APL387 Unicode {" ".join(baked_features)}' if len(subset) else 'APL387 Unicode' + file_name = f'APL387-{"-".join(baked_features)}' if len(subset) else 'APL387' + for lookup in subset: + bake_feature(lookup) + for ext in EXTENSIONS: + apl387.generate(f'{path}/output/{file_name}.{ext}') + # undo swaps + for lookup in subset: + bake_feature(lookup) From 55e62a58ce89854d1240acbbb4bab4c829194f7b Mon Sep 17 00:00:00 2001 From: RubenVerg Date: Tue, 26 Aug 2025 22:22:40 +0200 Subject: [PATCH 2/2] Allow download of fonts with baked features, not showing the option if none are selected --- features.js | 7 ++++++- index.css | 6 ++++++ script.py | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/features.js b/features.js index 8efc23c..3bcfc98 100644 --- a/features.js +++ b/features.js @@ -1,7 +1,12 @@ +const withBakedDownload = document.querySelector('#wb'); + const styles = new Map([...document.querySelectorAll('[type=checkbox]')].map(e => [e.value, e])); for (const element of styles.values()) { element.addEventListener('input', () => { const settings = [...styles.entries()].flatMap(([s, e]) => e.checked ? ['"' + s + '"'] : []).join(', '); document.body.style.fontFeatureSettings = settings.trim().length ? settings : 'unset'; + withBakedDownload.href = `APL387-${settings.replaceAll(', ', '-').replaceAll('"', '')}.ttf`; + if (settings.trim().length) withBakedDownload.classList.add('s'); + else withBakedDownload.classList.remove('s'); }); -} \ No newline at end of file +} diff --git a/index.css b/index.css index 184035a..ada3c4f 100644 --- a/index.css +++ b/index.css @@ -41,4 +41,10 @@ textarea { font-size: inherit; font-family: inherit; } +#wb { + display: none; +} +#wb.s { + display: unset; +} \ No newline at end of file diff --git a/script.py b/script.py index 4968a4c..f28c576 100644 --- a/script.py +++ b/script.py @@ -224,7 +224,7 @@ def powerset(iterable): index.write(f'') index.write(''' -

APL387 Unicode download side by side with APL385 source

+

APL387 Unicode download download with baked features side by side with APL385 source

A redrawn and extended version of Adrian Smith's classic APL385 font with clean rounded look.

Class