Last active
April 29, 2025 14:07
-
-
Save arrowtype/8633d568f80cc604a404b5665b02d210 to your computer and use it in GitHub Desktop.
Starter script to set up proper code ligatures in a GlyphsApp font source
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MIT License | |
Copyright 2025 ArrowType / Stephen Nixon | |
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#MenuTitle: Generate calt feature for code ligatures | |
__doc__=""" | |
Generates a 'calt' feature for code ligatures in a font. | |
- Sets up a "filler" glyph that is used to ensure the ligatures have a consistent width. | |
- The filler glyph is based on the 'space' glyph, but has a width of the monospace unit. | |
- Generates ligature feature code, and copies it to the clipboard. | |
- The ligature feature code is based on the glyph names, which should follow a specific naming convention. | |
- The naming convention is: <first_part>_<middle_part>_<last_part>.code | |
- The generated feature code will ignore the ligature sequence in both directions, and then substitute the ligature with the filler glyph. | |
""" | |
font = Glyphs.font | |
filler = "codeligfiller" | |
feaCode = """""" | |
code_ligatures = [] | |
for glyph in font.glyphs: | |
if ".code" in glyph.name: | |
code_ligatures.append(glyph.name) | |
# sort code_ligatures by number of underscores in the name | |
code_ligatures.sort(key=lambda x: x.count("_"), reverse=True) | |
# get basic unit width for the monospace font, from the "n" glyph | |
unit_width = [layer.width for layer in font["n"].layers if layer.width > 0][0] | |
# make new glyph for the code ligature filler | |
if filler not in font.glyphs: | |
newGlyph = font.glyphs['space'].copy() | |
newGlyph.name = filler | |
font.glyphs.append(newGlyph) | |
else: | |
for layer in font.glyphs[filler].layers: | |
layer.width = unit_width | |
# make sure code ligature glyphs keep right margin, but become one unit in width | |
for glyphname in code_ligatures: | |
glyph = font.glyphs[glyphname] | |
# set the width of the code ligature glyph to the unit width, with overflow to the left | |
for layer in glyph.layers: | |
for comp in layer.components: | |
comp.automaticAlignment = False | |
right_margin = layer.RSB | |
glyph_left_overflow = layer.width - unit_width | |
layer.LSB = layer.LSB - glyph_left_overflow | |
layer.RSB = right_margin | |
glyph.widthMetricsKey = "n" | |
# generate feature code for each ligature | |
for glyphname in code_ligatures: | |
glyph = font.glyphs[glyphname] | |
parts = glyph.name.replace(".code","").split("_") | |
lookup_name = glyphname.replace('.code', '') | |
lig_sequence = lookup_name.replace("_","' ") + "'" | |
first_part = parts[0] | |
last_part = parts[-1] | |
feaCode += f"lookup {lookup_name} {{\n" | |
feaCode += f" ignore sub {first_part} {lig_sequence};\n" | |
feaCode += f" ignore sub {lig_sequence} {first_part};\n" | |
for i in range(len(parts) -1, -1, -1): | |
feaCode += " sub " + (f"{filler} " * i) + " ".join([parts[i] + "'"] + parts[i + 1:]) | |
feaCode += f" by {glyphname};\n" if i == len(parts) -1 else f" by {filler};\n" | |
feaCode += f"}} {lookup_name};\n\n" | |
# copy feaCode to clipboard | |
import AppKit | |
pb = AppKit.NSPasteboard.generalPasteboard() | |
pb.declareTypes_owner_([AppKit.NSStringPboardType], None) | |
pb.setString_forType_(feaCode, AppKit.NSStringPboardType) | |
print(feaCode) | |
print(f"Copied {len(code_ligatures)} code ligature feature definitions to clipboard.") | |
print("Paste them into the font's 'calt' feature in Font Info > Features.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment