Skip to content

Instantly share code, notes, and snippets.

@huytd
Created September 11, 2025 20:47
Show Gist options
  • Save huytd/c437a47927e9ce4f741fbc665508b681 to your computer and use it in GitHub Desktop.
Save huytd/c437a47927e9ce4f741fbc665508b681 to your computer and use it in GitHub Desktop.
Condensed Font Creator

A fontforge script to resize every glyph of a font and save as a new condensed version.

Usage

fontforge -script narrow.py In.ttf Out.ttf sx blend "Family" "Style" [tracking]

Examples:

fontforge -script narrow.py "Courier Prime.ttf" "CourierPrime-Condensed.ttf" 0.85 0.5 "Courier Prime" "Condensed" 15
# narrow.py
# Usage:
# fontforge -script narrow.py In.ttf Out.ttf sx blend "Family" "Style" [tracking]
# Examples:
# fontforge -script narrow.py "Courier Prime.ttf" "CourierPrime-Condensed.ttf" 0.85 0.5 "Courier Prime" "Condensed" 15
import sys, re, fontforge, psMat
if len(sys.argv) < 7:
raise SystemExit("Usage: fontforge -script narrow.py In.ttf Out.ttf sx blend \"Family\" \"Style\" [tracking]")
in_path = sys.argv[-6]
out_path = sys.argv[-5]
sx = float(sys.argv[-4]) # horizontal scale, e.g. 0.85
t = float(sys.argv[-3]) # blend: 0 keep widths, 1 fully scale widths
family = sys.argv[-2] # new Family (SFNT Family, Preferred Family)
style = sys.argv[-1] if len(sys.argv) == 7 else sys.argv[-1] # for clarity in usage
# Optional tracking if provided as 7th arg after style
track = 0
if len(sys.argv) >= 8:
try:
track = int(sys.argv[-1])
style = sys.argv[-2]
family= sys.argv[-3]
except ValueError:
pass
# Open font
f = fontforge.open(in_path)
# Cache original metrics for spacing logic
origW = {}
origLSB = {}
for g in f.glyphs():
origW[g.glyphname] = g.width
origLSB[g.glyphname] = g.left_side_bearing
# Scale outlines horizontally (no vertical change)
f.selection.all()
f.transform(psMat.scale(sx, 1.0))
# Blend spacing toward scaled width, then re-center outlines to keep bearings balanced
for g in f.glyphs():
W0 = origW.get(g.glyphname, g.width)
if W0 <= 0:
continue
# blended target advance width
Wt = int(round(((1.0 - t) + t * sx) * W0)) + track
# keep original LSB proportion if possible (fallback to centering)
r = origLSB.get(g.glyphname, 0) / float(W0) if W0 else 0.5
r = min(max(r, 0.0), 1.0)
target_lsb = int(round(r * Wt))
# translate outline to achieve target LSB (balances bearings visually)
dx = target_lsb - g.left_side_bearing
if dx:
g.transform(psMat.translate(dx, 0))
g.width = Wt
# Build names
full = f"{family} {style}"
ps_name = re.sub(r'[^A-Za-z0-9-]', '', full.replace(' ', '-')) # ASCII, no spaces
# Set PostScript-style fields
f.familyname = family
f.fullname = full
f.fontname = ps_name
# Set key SFNT name records for better app compatibility
def setname(lang, key, val):
# appendSFNTName adds/overwrites name records by key and language
try:
f.appendSFNTName(lang, key, val)
except Exception:
pass
lang = "English (US)"
setname(lang, "Family", family) # nameID 1
setname(lang, "SubFamily", style) # nameID 2
setname(lang, "Fullname", full) # nameID 4
setname(lang, "Preferred Family", family) # nameID 16
setname(lang, "Preferred Styles", style) # nameID 17
# Optional extra name records that some apps read:
setname(lang, "WWS Family", family) # nameID 21
setname(lang, "WWS Subfamily", style) # nameID 22
setname(lang, "Compatible Full", full) # nameID 18
# Generate the output TTF
f.generate(out_path)
WIDTH=0.88
SPACE=0.94
fontforge -script narrow.py "Courier Prime.ttf" "Courier Prime Condensed.ttf" $WIDTH $SPACE "Courier Prime Condensed" "Regular"
fontforge -script narrow.py "Courier Prime Bold.ttf" "Courier Prime Condensed Bold.ttf" $WIDTH $SPACE "Courier Prime Condensed" "Bold"
fontforge -script narrow.py "Courier Prime Italic.ttf" "Courier Prime Condensed Italic.ttf" $WIDTH $SPACE "Courier Prime Condensed" "Italic"
fontforge -script narrow.py "Courier Prime Bold Italic.ttf" "Courier Prime Condensed Bold Italic.ttf" $WIDTH $SPACE "Courier Prime Condensed" "Bold Italic"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment