Skip to content

Instantly share code, notes, and snippets.

@arrowtype
Last active April 29, 2025 14:07
Show Gist options
  • Save arrowtype/8633d568f80cc604a404b5665b02d210 to your computer and use it in GitHub Desktop.
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
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.
#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