Skip to content

Instantly share code, notes, and snippets.

@bennetrr
Last active May 28, 2026 09:16
Show Gist options
  • Select an option

  • Save bennetrr/c78b7799ab3fbd0a92fc26e49eea22bb to your computer and use it in GitHub Desktop.

Select an option

Save bennetrr/c78b7799ab3fbd0a92fc26e49eea22bb to your computer and use it in GitHub Desktop.
Python script to align glyphs in fonts that are to high/low

Font Aligner

Python script to align glyphs in fonts that are to high/low.

Based on a Gist by nathanielevan, which itself was based on a Reddit post by u/janKeTami.

Usage

You need to install FontForge (at least the command line tools).

./font-aligner.py [-h] [-s START_GLYPH] [-e END_GLYPH] [-o OUT_FILE] in_file

positional arguments:
  in_file               Path to font file to fix

options:
  -h, --help            show this help message and exit
  -s, --startglyph START_GLYPH
                        Start glyph
  -e, --endglyph END_GLYPH
                        End glyph
  -o, --out OUT_FILE    Path to output file. If unset, the input file is overwritten
#! /usr/bin/env fontforge
import argparse
import sys
try:
import fontforge
import psMat
except ImportError:
print('Please run this script through FontForge.')
sys.exit(1)
argparser = argparse.ArgumentParser()
argparser.add_argument('in_file', help='Path to font file to fix')
argparser.add_argument(
'-s', '--startglyph', type=int, help='Start glyph', dest='start_glyph', default=32
)
argparser.add_argument(
'-e', '--endglyph', type=int, help='End glyph', dest='end_glyph', default=127
)
argparser.add_argument(
'-o',
'--out',
help='Path to output file. If unset, the input file is overwritten',
dest='out_file',
default=None,
)
args = argparser.parse_args()
# No need to select the font file, this will work on the font you have currently open:
font = fontforge.open(args.in_file)
for i in range(args.start_glyph, args.end_glyph + 1):
if i not in font:
print(f'Glyph {i} not found, skipping')
continue
ytop = font[i].boundingBox()[-1] # counted from baseline, excluding descent
ybot = font[i].boundingBox()[1]
# Calculation method:
# Midpoint of entire space = (ascent + descent) / 2
# Current midpoint of glyph (counted from baseline) = (ytop + ybot) / 2
# Current midpoint of glyph (counted with descent) = (ytop + ybot) / 2 + descent
# Vertical translation = midpoint of entire space - current midpoint of glyph (counted with descent)
# That is: (ascent + descent) / 2 - ((ytop + ybot) / 2 + descent)
# Simplified: (ascent - descent - (ytop + ybot)) / 2
translation = (font.ascent - font.descent - (ytop + ybot)) / 2
font[i].transform(psMat.translate(0, translation))
print(f'Glyph {i} translated by {translation} units')
# This bit for flags and bitmaps is taken from Nerd Fonts' font-patcher script
# No idea if this is necessary, but just in case
if int(fontforge.version()) >= 20201107:
gen_flags = ('opentype', 'no-FFTM-table')
else:
gen_flags = ('opentype',)
bitmaps = str()
if len(font.bitmapSizes):
print(f'Preserving bitmaps {font.bitmapSizes}')
bitmaps = str('otf') # otf/ttf, both are bf_ttf
out_file = args.out_file or args.in_file
font.generate(out_file, bitmap_type=bitmaps, flags=gen_flags)
print(f'Font patched successfully and saved to {out_file}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment