Skip to content

Instantly share code, notes, and snippets.

@placek
Created May 13, 2024 21:27
Show Gist options
  • Save placek/1bb83e079c5089cb308e31d9509f4cf7 to your computer and use it in GitHub Desktop.
Save placek/1bb83e079c5089cb308e31d9509f4cf7 to your computer and use it in GitHub Desktop.
MiniGent TTF font generator
#! /usr/bin/env nix-shell
#! nix-shell -i python3 -p "(pkgs.python3.withPackages (python-pkgs: [ python-pkgs.fontforge python-pkgs.shapely ]))"
import fontforge
import os
import tempfile
from shapely import MultiPolygon, Polygon, box, unary_union
from shapely.affinity import translate
glyph_data = {
'!': [0,1,0,0,1,0,0,0,0,0,1,0],
'$': [0,1,0,1,1,1,0,1,1,1,1,1,0,1,0],
'%': [0,0,1,1,1,0,0,1,0,1,0,1],
'&': [0,1,1,0,1,1,1,0,1,0,1,1],
'(': [0,0,1,0,1,0,0,1,0,0,0,1],
')': [1,0,0,0,1,0,0,1,0,1,0,0],
'+': [0,0,0,0,1,0,1,1,1,0,1,0],
',': [0,0,0,0,0,0,0,0,0,0,1,0,0,1,0],
'-': [0,0,0,0,0,0,1,1,0,0,0,0],
'—': [0,0,0,0,0,0,1,1,1,0,0,0],
'.': [0,0,0,0,0,0,0,0,0,0,1,0],
'<': [0,0,0,0,1,0,1,0,0,0,1,0],
'>': [0,0,0,0,1,0,0,0,1,0,1,0],
'/': [0,0,1,0,1,0,0,1,0,1,0,0],
'0': [1,1,1,1,0,1,1,0,1,1,1,1],
'1': [0,1,0,1,1,0,0,1,0,1,1,1],
'2': [1,1,0,0,0,1,1,1,0,1,1,1],
'3': [1,1,1,0,0,1,0,1,1,1,1,1],
'4': [1,0,0,1,0,1,1,1,1,0,0,1],
'5': [1,1,1,1,1,0,0,0,1,1,1,0],
'6': [0,1,1,1,0,0,1,1,1,1,1,1],
'7': [1,1,1,0,0,1,0,1,0,0,1,0],
'8': [1,1,1,1,1,1,1,0,1,1,1,1],
'9': [1,1,1,1,1,1,0,0,1,1,1,0],
':': [0,0,0,0,1,0,0,0,0,0,1,0],
';': [0,0,0,0,1,0,0,0,0,0,1,0,0,1,0],
'=': [0,0,0,1,1,1,0,0,0,1,1,1],
'?': [1,1,0,0,0,1,0,0,0,0,1,0],
'@': [0,1,1,1,1,0,1,1,1,0,0,0,0,1,1,0],
'A': [0,1,0,1,0,1,1,1,1,1,0,1],
'B': [1,1,0,1,1,0,1,0,1,1,1,0],
'C': [0,1,1,1,0,0,1,0,0,0,1,1],
'D': [1,1,0,1,0,1,1,0,1,1,1,0],
'E': [0,1,1,1,0,0,1,1,0,1,1,1],
'F': [0,1,1,1,0,0,1,1,0,1,0,0],
'G': [0,1,1,1,0,0,1,0,1,0,1,1],
'H': [1,0,1,1,0,1,1,1,1,1,0,1],
'I': [1,1,1,0,1,0,0,1,0,1,1,1],
'J': [0,1,1,0,0,1,0,0,1,1,1,0],
'K': [1,0,1,1,0,1,1,1,0,1,0,1],
'L': [1,0,0,1,0,0,1,0,0,0,1,1],
'M': [1,1,1,1,1,1,1,0,1,1,0,1],
'N': [1,1,0,1,0,1,1,0,1,1,0,1],
'O': [0,1,0,1,0,1,1,0,1,0,1,0],
'P': [1,1,0,1,0,1,1,1,0,1,0,0],
'Q': [0,1,0,1,0,1,1,0,1,0,1,1],
'R': [1,1,0,1,0,1,1,1,0,1,0,1],
'S': [0,1,1,1,0,0,0,1,1,1,1,0],
'T': [1,1,1,0,1,0,0,1,0,0,1,0],
'U': [1,0,1,1,0,1,1,0,1,0,1,1],
'V': [1,0,1,1,0,1,1,0,1,0,1,0],
'W': [1,0,1,1,0,1,1,1,1,1,1,1],
'X': [1,0,1,1,0,1,0,1,0,1,0,1],
'Y': [1,0,1,1,0,1,0,1,0,0,1,0],
'Z': [1,1,1,0,0,1,0,1,0,1,1,1],
'[': [0,1,1,0,1,0,0,1,0,0,1,1],
'\"': [0,1,1,0,1,1,0,0,0,0,0,0],
'\'': [0,1,0,0,1,0,0,0,0,0,0,0],
'\\': [1,0,0,0,1,0,0,1,0,0,0,1],
']': [1,1,0,0,1,0,0,1,0,1,1,0],
'^': [0,1,0,1,0,1,0,0,0,0,0,0],
'_': [0,0,0,0,0,0,0,0,0,1,1,1],
'`': [0,1,0,0,0,1,0,0,0,0,0,0],
'a': [0,0,0,0,1,1,1,0,1,1,1,1],
'b': [1,0,0,1,1,1,1,0,1,1,1,1],
'c': [0,0,0,1,1,1,1,0,0,1,1,1],
'd': [0,0,1,1,1,1,1,0,1,1,1,1],
'e': [0,0,0,1,1,1,1,0,1,1,1,0],
'f': [0,1,0,1,0,0,1,1,0,1,0,0],
'g': [0,0,0,1,1,1,1,1,1,0,0,1,1,1,1],
'h': [1,0,0,1,1,1,1,0,1,1,0,1],
'i': [0,1,0,0,0,0,0,1,0,0,1,0],
'j': [0,1,0,0,0,0,0,1,0,0,1,0,1,1,0],
'k': [1,0,0,1,0,1,1,1,0,1,0,1],
'l': [0,1,0,0,1,0,0,1,0,0,0,1],
'm': [0,0,0,1,1,1,1,1,1,1,0,1],
'n': [0,0,0,1,1,1,1,0,1,1,0,1],
'o': [0,0,0,1,1,1,1,0,1,1,1,1],
'p': [0,0,0,1,1,1,1,0,1,1,1,1,1,0,0],
'q': [0,0,0,1,1,1,1,0,1,1,1,1,0,0,1],
'r': [0,0,0,1,1,0,1,0,0,1,0,0],
's': [0,0,0,0,1,1,0,1,0,1,1,0],
't': [0,1,0,1,1,1,0,1,0,0,1,0],
'u': [0,0,0,1,0,1,1,0,1,1,1,1],
'v': [0,0,0,1,0,1,1,0,1,0,1,0],
'w': [0,0,0,1,0,1,1,1,1,1,1,1],
'x': [0,0,0,1,0,1,0,1,0,1,0,1],
'y': [0,0,0,1,0,1,1,1,1,0,0,1,1,1,1],
'z': [0,0,0,1,1,0,0,1,0,0,1,1],
'{': [0,1,1,0,1,0,1,1,0,0,1,1],
'|': [0,1,0,0,1,0,0,1,0,0,1,0],
'}': [1,1,0,0,1,0,0,1,1,1,1,0],
'~': [0,0,0,0,0,1,0,1,1,0,1,0,0,0,0,0],
'Α': [0,1,0,1,0,1,1,1,1,1,0,1],
'Β': [1,1,0,1,1,1,1,0,1,1,1,1],
'Γ': [1,1,1,1,0,0,1,0,0,1,0,0],
'Δ': [0,1,0,1,0,1,1,0,1,1,1,1],
'Ε': [1,1,1,1,0,0,1,1,0,1,1,1],
'Ζ': [1,1,1,0,0,1,0,1,0,1,1,1],
'Η': [1,0,1,1,1,1,1,0,1,1,0,1],
'Θ': [0,1,1,1,0,1,1,1,1,1,1,1],
'Ι': [0,1,0,0,1,0,0,1,0,0,1,0],
'Κ': [1,0,1,1,1,0,1,0,1,1,0,1],
'Λ': [0,1,0,1,0,1,1,0,1,1,0,1],
'Μ': [1,1,0,1,1,1,1,0,1,1,0,1],
'Ν': [0,1,1,1,0,1,1,0,1,1,0,1],
'Ξ': [1,1,1,0,0,0,0,1,0,1,1,1],
'Ο': [0,1,1,1,0,1,1,0,1,1,1,1],
'Π': [1,1,1,1,0,1,1,0,1,1,0,1],
'Ρ': [1,1,1,1,0,1,1,1,1,1,0,0],
'Σ': [1,1,1,1,0,0,0,1,0,1,1,1],
'Τ': [1,1,1,0,1,0,0,1,0,0,1,0],
'Υ': [1,0,1,1,0,1,0,1,0,0,1,0],
'Φ': [0,1,0,1,1,1,1,0,1,1,1,1,0,1,0],
'Χ': [1,0,1,0,1,0,1,0,1,1,0,1],
'Ψ': [1,1,1,1,1,1,1,1,1,0,1,0],
'Ω': [1,1,1,1,0,1,0,1,0,1,1,1],
'α': [0,0,0,0,1,1,1,0,1,0,1,1],
'β': [1,1,0,1,1,0,1,0,1,1,1,0,1,0,0],
'γ': [0,0,0,1,0,0,1,0,1,1,1,0,0,1,0],
'δ': [0,0,1,0,1,0,1,0,1,0,1,0],
'ε': [0,0,0,0,1,1,1,1,0,0,1,1],
'ζ': [1,1,1,0,0,1,0,1,0,0,0,1,0,1,0],
'η': [0,0,0,1,1,0,1,0,1,1,0,1,0,0,1],
'θ': [0,1,0,1,0,1,1,1,1,0,1,0],
'ι': [0,0,0,0,1,0,0,1,0,0,0,1],
'κ': [0,0,0,1,0,1,1,1,0,1,0,1],
'λ': [1,0,0,0,1,0,0,1,1,1,0,1],
'μ': [0,0,0,1,0,1,1,0,1,1,1,0,1,0,0],
'ν': [0,0,0,1,0,1,0,1,1,0,1,0],
'ξ': [0,1,1,1,0,0,0,1,0,1,0,0,0,1,0],
'ο': [0,0,0,0,1,0,1,0,1,0,1,0],
'π': [0,0,0,1,1,1,1,0,1,1,0,1],
'ρ': [0,0,0,0,1,0,1,0,1,1,1,0,1,0,0],
'ς': [0,0,0,0,1,1,1,0,0,0,1,1,0,0,1],
'σ': [0,0,0,0,1,1,1,0,1,0,1,0],
'τ': [0,0,0,1,1,1,0,1,0,0,0,1],
'υ': [0,0,0,1,0,1,1,0,1,1,1,0],
'φ': [0,1,0,0,1,0,1,0,1,1,1,1,0,1,0],
'χ': [1,0,0,0,1,1,0,1,0,1,0,1],
'ψ': [0,1,0,0,1,0,1,1,1,1,1,1,0,1,0],
'ω': [0,0,0,1,0,1,1,1,1,1,1,0],
'⁰': [1,1,0,1,1,0,1,1,0],
'¹': [0,1,0,0,1,0,0,1,0],
'²': [1,0,0,0,1,0,1,1,0],
'³': [1,1,0,0,1,0,1,1,0],
'⁴': [1,0,0,1,1,0,0,1,0],
'⁵': [1,1,0,1,0,0,0,1,0],
'⁶': [1,0,0,1,1,0,1,1,0],
'⁷': [1,1,0,0,1,0,0,1,0],
'⁸': [1,1,0,0,0,0,1,1,0],
'⁹': [1,1,0,1,1,0,0,1,0],
'₀': [0,0,0,1,1,0,1,1,0,1,1,0],
'₁': [0,0,0,0,1,0,0,1,0,0,1,0],
'₂': [0,0,0,1,0,0,0,1,0,1,1,0],
'₃': [0,0,0,1,1,0,0,1,0,1,1,0],
'₄': [0,0,0,1,0,0,1,1,0,0,1,0],
'₅': [0,0,0,1,1,0,1,0,0,0,1,0],
'₆': [0,0,0,1,0,0,1,1,0,1,1,0],
'₇': [0,0,0,1,1,0,0,1,0,0,1,0],
'₈': [0,0,0,1,1,0,0,0,0,1,1,0],
'₉': [0,0,0,1,1,0,1,1,0,0,1,0],
'#': [0,1,0,1,1,1,0,1,0,1,1,1,0,1,0],
}
def array_to_polygons(bits, cols=3, size=100):
boxes = []
for idx, bit in enumerate(bits):
if bit == 1:
x = (idx % cols) * size
y = (idx // cols) * size
boxes.append(box(x, -y, x + size, -y - size))
return translate(unary_union(boxes, 0.5), xoff=0, yoff=400)
def create_glyph(font, char, bits):
glyph = font.createChar(ord(char))
draw_shape(glyph.glyphPen(), array_to_polygons(bits))
glyph.width = 400
glyph.vwidth = 400
def draw_shape(pen, shape):
contour = fontforge.contour()
if isinstance(shape, MultiPolygon):
for polygon in shape.geoms:
draw_polygon(pen, polygon)
elif isinstance(shape, Polygon):
draw_polygon(pen, shape)
contour.draw(pen)
pen = None
def draw_polygon(pen, polygon):
points = list(polygon.exterior.coords)
draw_line(pen, points)
for interior in polygon.interiors:
points = list(interior.coords)
draw_line(pen, points)
def draw_line(pen, points):
pen.moveTo(points[0])
for point in points[1:]:
pen.lineTo(point)
pen.closePath()
font = fontforge.font()
font.familyname = "MiniGent"
font.fontname = "MiniGent-Regular"
font.fullname = "MiniGent Regular"
for char, bits in glyph_data.items():
create_glyph(font, char, bits)
font.generate('MiniGent-Regular.ttf')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment