157 lines
4.7 KiB
Python
157 lines
4.7 KiB
Python
#!/usr/bin/env fontforge
|
|
"""
|
|
Build aifont.ttf:
|
|
- Base: Meslo LGS NF Regular
|
|
- Replace A-Z with arch-cut versions
|
|
- Replace 0-9 with arch-cut versions
|
|
- Replace a, i, o, s with custom ai-world designs
|
|
- Keep everything else (other lowercase, symbols, nerd font icons)
|
|
"""
|
|
import fontforge
|
|
import os
|
|
|
|
BASE_FONT = "/tmp/MesloLGS NF Regular.ttf"
|
|
UPPER_DIR = "/tmp/aifont_build/upper"
|
|
DIGIT_DIR = "/tmp/aifont_build/digit"
|
|
LOWER_DIR = "/tmp/aifont_build/lower"
|
|
OUTPUT = "/tmp/aifont.ttf"
|
|
|
|
font = fontforge.open(BASE_FONT)
|
|
|
|
# Replace uppercase A-Z
|
|
for c in range(ord('A'), ord('Z')+1):
|
|
g = chr(c)
|
|
svg = os.path.join(UPPER_DIR, g + ".svg")
|
|
if os.path.exists(svg):
|
|
glyph = font[c]
|
|
w = glyph.width
|
|
glyph.clear()
|
|
glyph.importOutlines(svg)
|
|
glyph.width = w
|
|
glyph.correctDirection()
|
|
print("Upper: " + g)
|
|
|
|
# Replace digits 0-9
|
|
for c in range(ord('0'), ord('9')+1):
|
|
g = chr(c)
|
|
svg = os.path.join(DIGIT_DIR, g + ".svg")
|
|
if os.path.exists(svg):
|
|
glyph = font[c]
|
|
w = glyph.width
|
|
glyph.clear()
|
|
glyph.importOutlines(svg)
|
|
glyph.width = w
|
|
glyph.correctDirection()
|
|
print("Digit: " + g)
|
|
|
|
# Replace lowercase a, i, o, s
|
|
# These have different coordinate systems, so center them after import
|
|
import psMat
|
|
|
|
for g in ['a', 'i']:
|
|
c = ord(g)
|
|
svg = os.path.join(LOWER_DIR, g + ".svg")
|
|
if os.path.exists(svg):
|
|
glyph = font[c]
|
|
target_w = glyph.width
|
|
glyph.clear()
|
|
glyph.importOutlines(svg)
|
|
glyph.correctDirection()
|
|
|
|
# Get bounding box and center horizontally
|
|
bb = glyph.boundingBox()
|
|
xmin, ymin, xmax, ymax = bb
|
|
glyph_w = xmax - xmin
|
|
glyph_h = ymax - ymin
|
|
|
|
# Scale per glyph
|
|
if g == 'i':
|
|
target_h = 1500
|
|
elif g == 'a':
|
|
target_h = 1400
|
|
else:
|
|
target_h = 1200
|
|
if glyph_h > 0:
|
|
scale = target_h / glyph_h
|
|
glyph.transform(psMat.scale(scale))
|
|
bb = glyph.boundingBox()
|
|
xmin, ymin, xmax, ymax = bb
|
|
glyph_w = xmax - xmin
|
|
|
|
# Center horizontally within the glyph width
|
|
x_offset = (target_w - glyph_w) / 2.0 - xmin
|
|
# Align baseline
|
|
y_offset = -ymin
|
|
glyph.transform(psMat.translate(x_offset, y_offset))
|
|
glyph.width = target_w
|
|
|
|
# Thin down a, o, s (not i) - scale horizontally to 75%
|
|
if g in ['a', 'o', 's']:
|
|
bb = glyph.boundingBox()
|
|
xmin, ymin, xmax, ymax = bb
|
|
cx = (xmin + xmax) / 2.0
|
|
# Scale X to 75%, keep Y
|
|
glyph.transform(psMat.compose(
|
|
psMat.translate(-cx, 0),
|
|
psMat.compose(
|
|
psMat.scale(0.75, 1.0),
|
|
psMat.translate(cx, 0)
|
|
)
|
|
))
|
|
# Re-center
|
|
bb = glyph.boundingBox()
|
|
xmin, ymin, xmax, ymax = bb
|
|
glyph_w = xmax - xmin
|
|
x_offset = (target_w - glyph_w) / 2.0 - xmin
|
|
glyph.transform(psMat.translate(x_offset, 0))
|
|
glyph.width = target_w
|
|
|
|
print("Lower: " + g)
|
|
|
|
# Re-import custom icons at E001-E003 (same approach as original aifont.py)
|
|
# Build icon font separately, then merge
|
|
ICON_DIR = "/tmp/aifont_build"
|
|
scale = 200 # same as original
|
|
|
|
icons_font = fontforge.font()
|
|
icons_font.em = 1024
|
|
|
|
for cp, name, has_yshift in [(0xE001, "ai.svg", False), (0xE002, "syui.svg", False), (0xE003, "bluesky.svg", True)]:
|
|
svg = os.path.join(ICON_DIR, name)
|
|
if os.path.exists(svg):
|
|
glyph = icons_font.createChar(cp, name.replace(".svg", ""))
|
|
glyph.importOutlines(svg)
|
|
if has_yshift:
|
|
bb = glyph.boundingBox()
|
|
yshift = -bb[1]
|
|
glyph.transform((1, 0, 0, 1, 0, yshift))
|
|
s = scale / 100.0
|
|
glyph.transform((s, 0, 0, s, 0, 0))
|
|
glyph.width = 1024
|
|
print("Icon: " + name)
|
|
|
|
icons_font.generate("/tmp/aifont_icons.ttf")
|
|
icons_font.close()
|
|
|
|
# Clear existing icon slots and merge
|
|
for cp in [0xE001, 0xE002, 0xE003]:
|
|
font.selection.select(cp)
|
|
font.clear()
|
|
font.selection.none()
|
|
font.mergeFonts("/tmp/aifont_icons.ttf")
|
|
|
|
# Set font metadata
|
|
font.fontname = "aifont"
|
|
font.familyname = "aifont"
|
|
font.fullname = "aifont"
|
|
font.appendSFNTName("English (US)", "Family", "aifont")
|
|
font.appendSFNTName("English (US)", "SubFamily", "Regular")
|
|
font.appendSFNTName("English (US)", "UniqueID", "aifont-regular")
|
|
font.appendSFNTName("English (US)", "Fullname", "aifont")
|
|
font.appendSFNTName("English (US)", "PostScriptName", "aifont")
|
|
font.appendSFNTName("English (US)", "Preferred Family", "aifont")
|
|
font.appendSFNTName("English (US)", "Preferred Styles", "Regular")
|
|
|
|
font.generate(OUTPUT)
|
|
print("\nDone: " + OUTPUT)
|