#!/usr/bin/env python # by Jan Bobrowski 2021, 2022 / GPL # http://torinak.com/font/ import fontforge import math inp = "DroidSansMono.ttf" out = "DroidSansMonoVT.ttf" font_name = "Droid Sans Mono VT" font_subfamily = "Regular" font_psname = "DroidSansMonoVT" font = fontforge.open(inp) w = font["space"].width center_x = w >> 1 half_line_width = round(12/200*w) line_width = 2*half_line_width xl = center_x - half_line_width xr = center_x + half_line_width ε = 20 t = font.os2_winascent b = -font.os2_windescent center_y = (t + b) >> 1 y = center_y yd = y - half_line_width yu = y + half_line_width s = round(16/200*w) v = round(11/200*w) x0r = center_x - s x0l = x0r - 2*v x1l = center_x + s x1r = x1l + 2*v y0u = y - s y0d = y0u - 2*v y1d = y + s y1u = y1d + 2*v print(f"width:{w}, top:{t}, bottom:{b}") def new_glyph(u): global glyph, name_from_unicode, pen if type(u) is int: glyph = font.createChar(u) glyph.glyphname = fontforge.nameFromUnicode(u) else: glyph = font.createChar(-1, u) pen = glyph.glyphPen() def end_glyph(): global glyph, pen pen = None glyph.width = w def contour(l): global last_contour p = l.pop(0) x, y = p pen.moveTo(x, y) if isinstance (l[0], tuple): if l[0] == (): r = [p] l.pop(0) while l: y = l.pop(0) r.append((x, y)) x = l.pop(0) r.append((x, y)) r.append((x, p[1])) else: r = [p] + l else: r = [p] while l: x = l.pop(0) r.append((x, y)) y = l.pop(0) r.append((x, y)) r.append((p[0], y)) for p in r: pen.lineTo(p[0], p[1]) pen.closePath() last_contour = r def rect(l, t, r, b): contour([(l,b), (l,t), (r,t), (r,b)]) # Drill a hole in a polygon (with sufficiently long edges). def drill(lw = line_width): def f(a, b): dx = b[0] - a[0] dy = b[1] - a[1] n = 1/math.sqrt(dx*dx + dy*dy) return n*dy, -n*dx l = last_contour a = l.pop(0) u = f(l[-1], a) l.append(a) r = [] for b in l: v = f(a, b) det = u[0]*v[1] - u[1]*v[0] m = lw/det dx = m*(v[1] - u[1]) dy = m*(u[0] - v[0]) r.append((a[0] + dx, a[1] + dy)) a = b u = v r.reverse() contour(r) hm = center_x vm = center_y rects = { 0x2590: (hm,t,w+ε,b), # ▐ 0x2580: (0,t,w+ε,hm), # ▀ 0x2596: (0,vm,hm,b), # ▖ 0x2597: (hm,vm,w+ε,b), # ▗ 0x2598: (0,t,hm,vm), # ▘ 0x259D: (hm,t,w+ε,vm), # ▝ 0x2500: (0,yu,w+ε,yd), # ─ 0x2502: (xl,t,xr,b), # │ 0x2574: (0,yu,xr,yd),# ╴ 0x2575: (xl,t,xr,yd),# ╵ 0x2576: (xl,yu,w+ε,yd),# ╶ 0x2577: (xl,yu,xr,b),# ╷ "line-LR": (0,y1u,w+ε,y1d), "line-RL": (0,y0u,w+ε,y0d), "line-DU": (x0l,t,x0r,b), "line-UD": (x1l,t,x1r,b), "line-l0": (0,yu,x0r,yd), "line-u0": (xl,t,xr,y1d), "line-r0": (x1l,yu,w+ε,yd), "line-d0": (xl,y0u,xr,b), } for u in range(0x2581, 0x2589): # ▁▂▃▄▅▆▇█ v = (u - 0x2580) / 8 y = round(t*v + b*(1 - v)) rects[u] = (0, y, w+ε, b) if u == 0x2587: rects[0x2594] = (0, t, w, y) # ▔ for u in range(0x2589, 0x2590): # ▉▊▋▌▍▎▏ x = round(w * (0x2590 - u) / 8) rects[u] = (0, t, x, b) if u == 0x2589: rects[0x2595] = (x, t, w+ε, b) # ▕ for u,o in rects.items(): new_glyph(u) rect(o[0], o[1], o[2], o[3]) end_glyph() contours = { 0x2552: [(xr,b), xl,y1u,w,y1d,xr,y0u,w+ε,y0d], # ╒ 0x2553: [(x0r,b), x0l,yu,w+ε,yd,x1r,b,x1l,yd], # ╓ 0x2555: [(0,y0d), (), y0u,xl,y1d,0,y1u,xr,b,xl], # ╕ 0x2556: [(0,yd), (), yu,x1r,b,x1l,yd,x0r,b,x0l], # ╖ 0x2558: [(xl,y0d), (), t,xr,y1u,w+ε,y1d,xr,y0u,w+ε], # ╘ 0x2559: [(x0l,t), x0r,yu,x1l,t,x1r,yu,w+ε,yd], # ╙ 0x255B: [(0,y0d), (), y0u,xl,y1d,0,y1u,xl,t,xr], # ╛ 0x255C: [(0,yd), (), yu,x0l,t,x0r,yu,x1l,t,x1r], # ╜ 0x255E: [(xr,b), xl,t,xr,y1u,w+ε,y1d,xr,y0u,w+ε,y0d], # ╞ 0x2561: [(xr,b), xl,y0d,0,y0u,xl,y1d,0,y1u,xl,t], # ╡ 0x2565: [(x0r,b), x0l,yd,0,yu,w+ε,yd,x1r,b,x1l,yd], # ╥ 0x2568: [(0,yd), (), yu,x0l,t,x0r,yu,x1l,t,x1r,yu,w+ε], # ╨ 0x2599: [(0,b), (), t,hm,vm,w+ε], # ▙ 0x259B: [(0,b), (), t,w+ε,vm,hm], # ▛ 0x259C: [(0,vm), (), t,w+ε,b,hm], # ▜ 0x259F: [(0,b), (), vm,hm,t,w+ε], # ▟ # 0x25E2: [(w+ε,t), (w+ε,b), (0,b)], # ◢ # 0x25E3: [(w,b), (0,b), (0,t)], # ◣ # 0x25E4: [(0,b), (0,t), (w,t)], # ◤ # 0x25E5: [(0,t), (w+ε,t), (w+ε,b)], # ◥ "line-LU": [(0,y1d), (), y1u,x0l,t,x0r], "line-UL": [(0,y0d), (), y0u,x1l,t,x1r], "line-UR": [(x1l,t), x1r,y1u,w+ε,y1d], "line-RU": [(x0l,t), x0r,y0u,w+ε,y0d], "line-RD": [(x1l,b), (), y0u,w+ε,y0d,x1r], "line-DR": [(x0l,b), (), y1u,w+ε,y1d,x0r], "line-DL": [(0,y0d), (), y0u,x0r,b,x0l], "line-LD": [(0,y1d), (), y1u,x1r,b,x1l], } for u,l in contours.items(): new_glyph(u) contour(l) end_glyph() dirs = [ fontforge.nameFromUnicode(0x2574), fontforge.nameFromUnicode(0x2575), fontforge.nameFromUnicode(0x2576), fontforge.nameFromUnicode(0x2577) ] lines = [ 0, 0, 0, 0x2518, # r=0 d=0 ┘ 0, 0, 0x2514, 0x2534, # r=1 d=0 └┴ 0, 0x2510, 0, 0x2524, # r=0 d=1 ┐ ┤ 0x250C, 0x252C, 0x251C, 0x253C # r=1 d=1 ┌┬├┼ ] for i in range(3, 16): if lines[i]: new_glyph(lines[i]) k = i if (k & (1|4)) == (1|4): pen.addComponent(fontforge.nameFromUnicode(0x2500)) k &= ~(1|4) if (k & (2|8)) == (2|8): pen.addComponent(fontforge.nameFromUnicode(0x2502)) k &= ~(2|8) for j in range(0, 4): if k & 1<> 1 for i in range(-2, 2): if i != 0: new_glyph(u) rect(0, yu + i*s, w + ε, yd + i*s) end_glyph() u += 1 font['underscore'].altuni = u, def composite(u, *a): new_glyph(u) for i in a: pen.addComponent(fontforge.nameFromUnicode(i)) end_glyph() composite(0x259A, 0x2597, 0x2598) # ▚ composite(0x259E, 0x2596, 0x259D) # ▞ x = (xl + xr)/2; y = (yd + yu)/2 s0 = w/5; s1 = w/3 new_glyph(0x2023) # ‣ contour([(x - s0, y - s1), (x - s0, y + s1), (x + s0, y)]) end_glyph() s = 875 x = (w - s) >> 1 y = 160 new_glyph(0x25A0) # ■ rect(x, y+s, x+s, y) end_glyph() new_glyph(0x25A1) # □ contour(last_contour); drill() end_glyph() m1 = 288 m2 = 111 d = round((t - b)/3) new_glyph(0x25BA) # ► contour([(m1, vm - d), (m1, vm + d), (w - m2,vm)]) end_glyph() new_glyph(0x25C4) # ◄ contour([(w - m1, vm - d), (m2, vm), (w - m1, vm + d)]) end_glyph() y1 = round(.7*t) y0 = round(.1*t) new_glyph(0x25B2) # ▲ contour([(hm, y1 + ε), (w - m2, y0), (m2, y0)]) end_glyph() new_glyph(0x25B3) # △ contour(last_contour); drill() end_glyph() new_glyph(0x25BC) # ▼ contour([(hm, y0 - ε), (m2, y1), (w - m2, y1)]) end_glyph() new_glyph(0x25BD) # ▽ contour(last_contour); drill() end_glyph() x = (xl + xr)/2; y = (yd + yu)/2 s0 = w/4; s1 = w/3 new_glyph(0x25B8) # ▸ contour([(x - s0, y - s1), (x - s0, y + s1), (x + s0, y)]) end_glyph() new_glyph(0x25C2) # ◂ contour([(x - s0, y), (x + s0, y + s1), (x + s0, y - s1)]) end_glyph() new_glyph(0x25B4) # ▴ contour([(x - s1, y - s0), (x, y + s0), (x + s1, y - s0)]) end_glyph() new_glyph(0x25BE) # ▾ contour([(x - s1, y + s0), (x + s1,y + s0), (x, y - s0)]) end_glyph() dw = w/12 dh = (t - b)/20 new_glyph(0x2591) # ░ y = b for i in range(10): x = 0 if i & 1 else 2*dw for j in range(3): rect(x, y + dh, x + dw, y) x += 4*dw y += 2*dh end_glyph() new_glyph(0x2592) # ▒ y = b for i in range(10): x = 0 if i & 1 else dw for j in range(6): rect(x, y + dh, x + dw, y) x += 2*dw y += 2*dh end_glyph() new_glyph(0x2593) # ▓ y = b pen.moveTo((0,y)) for i in range(4): pen.lineTo((0, y + 3*dh)) pen.lineTo((dw, y + 3*dh)) pen.lineTo((dw, y + 4*dh)) y += 4*dh pen.lineTo((0, y)) y += 3*dh; x = 0 for i in range(6): pen.lineTo((x, y)) x += dw pen.lineTo((x, y)) pen.lineTo((x, t)) x += dw pen.lineTo((x, t)) y += 2*dh for i in range(5): pen.lineTo((x, y - 3*dh)) pen.lineTo((x - dw, y - 3*dh)) y -= 4*dh pen.lineTo((x - dw, y)) pen.lineTo((x, y)) pen.lineTo((x, b)) pen.closePath() y = b - dh for i in range(10): x = dw if i & 1 else 2*dw for j in range(5): contour([(x,y), (x+dw,y), (x+dw,y+dh), (x,y+dh)]) x += 2*dw y += 2*dh end_glyph() new_glyph(0x2261) # ≡ xa,ya,xb,yb = font['equal'].boundingBox() y = round((ya + yb)/2) s = yb - ya - line_width for i in range(-1, 2): rect(xa, y + i*s + half_line_width, xb, y + i*s - half_line_width) end_glyph() def arrow(t, v, lw = line_width): hw = lw / 2 tx, ty = v[0]*t[0] - v[1]*t[1], v[0]*t[1] + v[1]*t[0] def D(x,y): return v[0]*x + v[1]*y, v[0]*y - v[1]*x k = w/3 d = round(math.sqrt(2)*line_width) x = tx - hw y = ty - hw - k pen.lineTo(D(x, y-d+k)) pen.lineTo(D(x-k, y-d)) pen.lineTo(D(x-k, y)) pen.lineTo(D(tx,ty)) x += lw pen.lineTo(D(x+k, y)) pen.lineTo(D(x+k, y-d)) pen.lineTo(D(x, y-d+k)) a1 = font.ascent a0 = -font.descent new_glyph(0x2191) # ↑ pen.moveTo((xr, a0)) pen.lineTo((xl, a0)) arrow(((xl + xr)>>1, a1 - 22), (1, 0)) end_glyph() new_glyph(0x2193) # ↓ pen.addComponent(fontforge.nameFromUnicode(0x2191), (1, 0, 0, -1, 0, a0 + a1)) end_glyph() new_glyph(0x2192) # → pen.moveTo((0, yd)) pen.lineTo((0, yu)) arrow((w, (yd + yu)>>1), (0, 1)) end_glyph() def flip_h(to_u, from_u, add = w + ε): new_glyph(to_u) pen.addComponent(fontforge.nameFromUnicode(from_u), (-1, 0, 0, 1, add, 0)) end_glyph() flip_h(0x2190, 0x2192) # ← new_glyph(0x21B2) # ↲ x, y = 3*w>>2, (b + 7*t)>>3 pen.moveTo((x - half_line_width, y)) pen.lineTo((x + half_line_width, y)) tx, ty = 0, (5*b + 3*t)>>3 pen.lineTo((x + half_line_width, ty - half_line_width)) arrow((tx,ty), (0,-1)) pen.lineTo((x - half_line_width, ty + half_line_width)) end_glyph() x, y = center_x, center_y hs = round((13/28)*w); vs = round(1.25*hs) new_glyph(0x25C6) # ◆ contour([(x - hs, y), (x, y + vs), (x + hs, y), (x, y - vs)]) glyph.altuni = 0x2666, # ♦ end_glyph() def curv(p0,p1,p2, ey, p3,p4,p5): pen.moveTo(p0) pen.qCurveTo(p1, p2) if ey != 0: pen.lineTo((p2[0],ey)) pen.lineTo((p3[0],ey)) pen.lineTo(p3) pen.qCurveTo(p4, p5) a0 = yd - max(xl, w - xr) a1 = yu + max(xl, w - xr) new_glyph(0x256D) # ╭ curv((w,yd), (xr,yd), (xr,a0), b, (xl,a0), (xl,yu), (w,yu)) end_glyph() new_glyph(0x256E) # ╮ curv((0,yu), (xr,yu), (xr,a0), b, (xl,a0), (xl,yd), (0,yd)) end_glyph() new_glyph(0x256F) # ╯ curv((0,yu), (xl,yu), (xl,a1), t, (xr,a1), (xr,yd), (0,yd)) end_glyph() new_glyph(0x2570) # ╰ curv((w,yd), (xl,yd), (xl,a1), t, (xr,a1), (xr,yu), (w,yu)) end_glyph() font['periodcentered'].altuni = 0x2219, 0x22C5 # ∙⋅ font['hyphen'].altuni = 0x2010, 0x2011, 0x2012 # ‐‑‒ def qcircle(p, d0, d1): x,y = p d0x,d0y = d0 d1x,d1y = d1 a = math.sqrt(2) - 1 b = 1/math.sqrt(2) pen.qCurveTo((round(x + a*d0x), round(y + a*d0y)), (round(x + b*d0x + (1 - b)*d1x), round(y + b*d0y + (1 - b)*d1y))) x += d0x + d1x y += d0y + d1y pen.qCurveTo((round(x - a*d1x), round(y - a*d1y)), (x, y)) return x,y def circle(x, y, r, reverse = 0): r = math.floor(r) p = (round(x - r), round(y)) d0 = (0, r) d1 = (r, 0) if reverse: d0 = (0, -r) pen.moveTo(p) for i in range(4): p = qcircle(p, d0, d1) d2 = (-d0[0], -d0[1]) d0,d1 = d1,d2 pen.closePath() x = w/2; y = (t + b)/2; r = 7*w >> 4 new_glyph(0x25CF) # ● circle(x, y, r) end_glyph() new_glyph(0x25CB) # ○ circle(x, y, r) circle(x, y, r - w/8, 1) end_glyph() new_glyph(0x2713) # ✓ contour([(.47*w,0), (0,.36*w), (.07*w,.46*w), (.44*w,.18*w), (.90*w,.86*w), (w,.78*w)]) end_glyph() new_glyph(0x2714) # ✔ contour([(.47*w,0), (0,.36*w), (.14*w,.56*w), (.41*w,.35*w), (.81*w,.93*w), (w,.78*w)]) end_glyph() def ex(x,y,s,e): contour([(x-s+e, y-s-e), (x-s-e, y-s+e), (x-2*e, y), (x-s-e, y+s-e), (x-s+e, y+s+e), (x, y+2*e), (x+s-e, y+s+e), (x+s+e, y+s-e), (x+2*e, y), (x+s+e, y-s+e), (x+s-e, y-s-e), (x, y-2*e)]) new_glyph(0x2715) # ✕ e = half_line_width/math.sqrt(2) s = (7/16)*w - e ex(center_x, s + e, s, e) end_glyph() new_glyph(0x2716) # ✖ e = 2*half_line_width/math.sqrt(2) s = (7/16)*w - e ex(center_x, s + e, s, e) end_glyph() def chevron(u, sc, xa, ya, xb, yb): v1 = sc*line_width v2 = sc*1.2*line_width new_glyph(u) contour([(xb - v1, ya), (xa, (ya + yb)/2), (xb - v1, yb + ε), (xb, yb), (xa + v2, (ya + yb)/2), (xb, ya + ε)]) end_glyph() xa,ya,xb,yb = font['parenleft'].boundingBox() chevron(0x27E8, 1, xa, ya, xb, yb) # ⟨ flip_h(0x27E9, 0x27E8) # ⟩ chevron(0x276C, 1.3, xa, ya, xb, yb) # ❬ flip_h(0x276D, 0x276C) # ❭ chevron(0x2770, 2.1, xa, ya, xb, yb) # ❰ flip_h(0x2771, 0x2770) # ❱ xa -= half_line_width xb += half_line_width chevron(0x276E, 1.7, xa, ya, xb, yb) # ❮ flip_h(0x276F, 0x276E) # ❯ new_glyph(0x2665) # ♥ x = round(.5*hm) y = round(.55*hm) pen.moveTo((hm, 0)) pen.lineTo((x, y)) pen.qCurveTo((0, 2*y), (0, 4*y), (hm, 4*y), (hm, 3*y)) pen.qCurveTo((hm, 4*y), (w, 4*y), (w, 2*y), (w - x, y)) end_glyph() def dot(x, y, r): pen.moveTo((x - r, y)) pen.qCurveTo((x - r, y + r), (x, y + r)) pen.qCurveTo((x + r, y + r), (x + r, y)) pen.qCurveTo((x + r, y - r), (x, y - r)) pen.qCurveTo((x - r, y - r), (x - r, y)) pen.closePath() new_glyph(0x22EE) # ⋮ dy = (t - b)/4 for i in range(1, 4): dot(center_x, int(b + i*dy), int((t - b)/16)) end_glyph() # Braile # 0 3 # 1 4 # 2 5 # 6 7 dx = int(4*w/9); x = int(dx/2) dy = int((t - b)/4); r = int(2*dy/9); y = b + r da = [ (x, y + 3*dy), (x, y + 2*dy), (x, y + 1*dy), (x + dx, y + 3*dy), (x + dx, y + 2*dy), (x + dx, y + 1*dy), (x, y), (x + dx, y) ] ma = [(0,3), (1,4), (2,5), (6,7)] # ⠉⠒⠤⣀ for b0,b1 in ma: new_glyph(0x2800 | 1< 1: new_glyph(0x2800 | n) for m in a: pen.addComponent(fontforge.nameFromUnicode(0x2800 | m)) end_glyph() font['space'].altuni = 0x2800, a = {0x2409:"HT", 0x240C:"FF", 0x240D:"CR", 0x240A:"LF", 0x2424:"NL", 0x240B:"VT"} for u,c in a.items(): new_glyph(u) # ␉␊␋␌␍ s = 9/16 pen.addComponent(c[0], (s, 0, 0, s, 0, t*(1 - s))) pen.addComponent(c[1], (s, 0, 0, s, w*(1 - s), b*(1 - s))) end_glyph() g = font['zero'] xa,ya,xb,yb = g.boundingBox() x = round((xa + xb)/2) y = round((ya + yb)/2) s = round((xb - xa)/10) new_glyph('zero-dot') rect(x-s, y-s, x+s, y+s) end_glyph() new_glyph('dotted-zero') pen.addComponent('zero') pen.addComponent('zero-dot') g = glyph end_glyph() font['zero'].glyphname = 'bare-zero' g.glyphname = 'zero' g.altuni = 0x0030, # 0 new_glyph(0x23E8) # ⏨ s = 11./16 pen.addComponent('one', (s, 0, 0, s, -1./16*w, 0)) pen.addComponent('bare-zero', (s, 0, 0, s, w*(17./16 - s), 0)) end_glyph() sup_sub = { 'zero': (0x2070,0x2080), 'one': (0x00B9,0x2081), 'two': (0x00B2,0x2082), 'three': (0x00B3,0x2083), 'four': (0x2074,0x2084), 'five': (0x2075,0x2085), 'six': (0x2076,0x2086), 'seven': (0x2077,0x2087), 'eight': (0x2078,0x2088), 'nine': (0x2079,0x2089), 'n': (0x207f,0x2099) } for s,(u0,u1) in sup_sub.items(): n0 = s + 'superior' g0 = font[n0] if g0 and g0.unicode < 0: g0.unicode = u0 new_glyph(u1) pen.addComponent(n0, (1,0,0,1, 0,.9*(b - t)/2)) end_glyph() flip_h(0x2310, 0x00AC, w) # ⌐ # Private area priv = { 0xE0B0: [(0,b), (0,t), (w,center_y)], #  0xE0B2: [(w+ε,b), (0,center_y), (w+ε,t)], #  0xE0B8: [(0,b), (0,t), (w,b)], #  0xE0BA: [(w+ε,t), (w+ε,b), (0,b)], #  0xE0BC: [(0,b), (0,t), (w,t)], #  0xE0BE: [(0,t), (w+ε,t), (w+ε,b)], #  } for u,l in priv.items(): new_glyph(u) contour(l) end_glyph() r = center_y - b new_glyph(0xE0B4) #  p = (0,t) pen.moveTo(p) p = qcircle(p, (r,0), (0,-r)) qcircle(p, (0,-r), (-r,0)) end_glyph() new_glyph(0xE0B6) #  p = (w+ε,b) pen.moveTo(p) p = qcircle(p, (-r,0), (0,r)) qcircle(p, (0,r), (r,0)) end_glyph() font.sfnt_names = [] en = "English (US)" font.appendSFNTName(en, "Fullname", font_name) font.appendSFNTName(en, "Family", font_name) font.appendSFNTName(en, "SubFamily", font_subfamily) font.appendSFNTName(en, "PostScriptName", font_psname) font.appendSFNTName(en, 12, "http://torinak.com/font/") #print(font.sfnt_names) #font.save(out.replace(".ttf", "") + ".sfd") font.generate(out)