code wiki / _hdl_build / nx_font_space_tool.nx

nx_font_space_tool.nx source

↩ module page · 125 lines · 6222 B

1// nx_font_space_tool.nx -- AUTO-SPACING generator (the fix for "ransom-note rhythm"): per-glyph OPTICAL 2// sidebearings, computed not hand-set. Method (Tracy/FontForge-autowidth class): render each glyph through 3// the REAL engine, measure its ink extents in the OPTICAL BAND (x-height middle, 350..650em -- where the 4// eye reads letter spacing), allow diagonals/serif-tips to overhang the band extent by DIAG_ALLOW, then set 5// shift(ch) = SB_T - eff_left (moves the glyph so its optical left edge sits at SB_T) 6// adv(ch) = SB_T + eff_width + SB_T 7// so EVERY pair gap reads ~2*SB_T -> even rhythm (kerning of specific pairs = a later refinement axis). 8// EMITS the tables as NishiLang code on stdout (build generators, not artifacts) -> pasted into nx_font.nx 9// marked GENERATED-BY; rerun this tool after any glyph-geometry change. license_tier: ORIGINAL expect_exit: 0 10import "nx_font.nx" 11import "nx_aa_raster.nx" 12const SB_MAGIC_60000: i64 = 60000 13const SB_MAGIC_6000: i64 = 6000 14 15func st_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 16func st_num(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 17 18const CW: i64 = 260 // canvas px (em/5 + margins) 19const CH2: i64 = 280 20const MX: i64 = 20 // margin px 21const SPX: i64 = 200 // em -> px scale (S): 1000em = 200px, 1px = 5em 22const SB_T: i64 = 50 // target optical sidebearing (em) 23const DIAG_ALLOW: i64 = 35 // how far diagonals may overhang the optical edge (em) 24 25func main() -> i64 { 26 st_puts("=== nx_font_space_tool -- optical sidebearings, GENERATED (paste into nx_font.nx) ===\n" as *u8) 27 let xs: *i64 = sys_mmap(8*SB_MAGIC_60000) as *i64 28 let ys: *i64 = sys_mmap(8*SB_MAGIC_60000) as *i64 29 let cstart: *i64 = sys_mmap(8*SB_MAGIC_6000) as *i64 30 let clen: *i64 = sys_mmap(8*SB_MAGIC_6000) as *i64 31 let np: *i64 = sys_mmap(16) as *i64 32 let nc: *i64 = sys_mmap(16) as *i64 33 let cs: *i64 = sys_mmap(8*20) as *i64 34 let sn: *i64 = sys_mmap(8*20) as *i64 35 font_trig_init(cs, sn) 36 let cov: *u8 = sys_mmap(CW*CH2 + 16) 37 let shifts: *i64 = sys_mmap(8*140) as *i64 38 let advs: *i64 = sys_mmap(8*140) as *i64 39 font_spec_reset() 40 // ★MODE: default organic (geo regen: uncomment) font_spec_geo(1); font_spec_mod(100); font_spec_swell(100) 41 42 // the glyph set: a-z A-Z 0-9 . , - : ! ? ( ) ' / 43 let set: *i64 = sys_mmap(8*90) as *i64 44 var nset: i64 = 0 45 var c: i64 = 97 46 while c <= 122 { set[nset]=c; nset=nset+1; c=c+1 } 47 c = 65 48 while c <= 90 { set[nset]=c; nset=nset+1; c=c+1 } 49 c = 48 50 while c <= 57 { set[nset]=c; nset=nset+1; c=c+1 } 51 set[nset]=46; nset=nset+1 ; set[nset]=44; nset=nset+1 ; set[nset]=45; nset=nset+1 52 set[nset]=58; nset=nset+1 ; set[nset]=33; nset=nset+1 ; set[nset]=63; nset=nset+1 53 set[nset]=40; nset=nset+1 ; set[nset]=41; nset=nset+1 ; set[nset]=39; nset=nset+1 54 set[nset]=47; nset=nset+1 55 56 var si: i64 = 0 57 while si < nset { 58 let ch: i64 = set[si] 59 np[0]=0; nc[0]=0 60 font_emit(ch, xs, ys, cstart, clen, np, nc, cs, sn, MX, MX, SPX) 61 var z: i64 = 0 62 while z < CW*CH2 { cov[z]=0 as u8; z=z+1 } 63 aa_render(xs, ys, cstart, clen, nc[0], cov, CW, CH2, 1) 64 // full ink extents (px) 65 var fl: i64 = CW 66 var fr: i64 = 0 - 1 67 var y: i64 = 0 68 while y < CH2 { 69 var x: i64 = 0 70 while x < CW { if (cov[y*CW+x] as i64) > 127 { if x < fl { fl = x } ; if x > fr { fr = x } } x = x + 1 } 71 y = y + 1 72 } 73 // optical-band extents (x-height middle 350..650em -> px rows) 74 let by0: i64 = MX + (350*SPX)/1000 75 let by1: i64 = MX + (650*SPX)/1000 76 var bl: i64 = CW 77 var br2: i64 = 0 - 1 78 y = by0 79 while y <= by1 { 80 var x: i64 = 0 81 while x < CW { if (cov[y*CW+x] as i64) > 127 { if x < bl { bl = x } ; if x > br2 { br2 = x } } x = x + 1 } 82 y = y + 1 83 } 84 var shift: i64 = 0 85 var adv: i64 = 520 86 if fr < 0 { shift = 0; adv = 300 } // no ink (shouldn't happen in the set) 87 else { 88 if br2 < 0 { bl = fl; br2 = fr } // nothing in the band (apostrophe) -> full extents 89 // effective optical edges (em): band edge, but let full ink overhang by at most DIAG_ALLOW 90 var el: i64 = (bl - MX) * 5 91 var er: i64 = (br2 - MX) * 5 92 let fle: i64 = (fl - MX) * 5 93 let fre: i64 = (fr - MX) * 5 94 if fle + DIAG_ALLOW < el { el = fle + DIAG_ALLOW } 95 if fre - DIAG_ALLOW > er { er = fre - DIAG_ALLOW } 96 shift = SB_T - el 97 adv = SB_T + (er - el) + SB_T 98 } 99 shifts[si] = shift 100 advs[si] = adv 101 st_puts(" ch " as *u8); st_num(ch); st_puts(" shift=" as *u8); st_num(shift); st_puts(" adv=" as *u8); st_num(adv); st_puts("\n" as *u8) 102 si = si + 1 103 } 104 105 // ---- emit the generated tables (paste into nx_font.nx) ---- 106 st_puts("\n// ---- GENERATED-BY nx_font_space_tool (optical sidebearings; rerun after glyph edits) ----\n" as *u8) 107 st_puts("func font_sp_shift(ch: i64) -> i64 {\n" as *u8) 108 si = 0 109 while si < nset { 110 if shifts[si] != 0 { 111 st_puts(" if ch == " as *u8); st_num(set[si]); st_puts(" { return " as *u8); st_num(shifts[si]); st_puts(" }\n" as *u8) 112 } 113 si = si + 1 114 } 115 st_puts(" return 0\n}\n" as *u8) 116 st_puts("func font_adv_em_base(ch: i64) -> i64 {\n" as *u8) 117 st_puts(" if ch == 32 { return 300 }\n" as *u8) 118 si = 0 119 while si < nset { 120 st_puts(" if ch == " as *u8); st_num(set[si]); st_puts(" { return " as *u8); st_num(advs[si]); st_puts(" }\n" as *u8) 121 si = si + 1 122 } 123 st_puts(" return 520\n}\n" as *u8) 124 return 0 125}