code wiki / _hdl_build / nx_font_advdiff.nx
nx_font_advdiff.nx source
↩ module page · 102 lines · 4696 B
1// nx_font_advdiff.nx -- FONT-METRICS DRIFT instrument (operator 2026-07-28: "use free fonts as
2// another benchmark -- make sure the browser isn't held back by something dumb like that").
3// Reads a real TTF's cmap(format 4) + hmtx and prints, for ASCII 32..126, the font's advance in
4// 1000/em units next to OUR font_adv_em(ch), plus the drift. Wrap points come from summed advances,
5// so per-char drift vs Arial (Chrome's default sans; metrics == Liberation Sans, SIL-OFL) IS the
6// structural-exactness gap of every line break. usage: nx_font_advdiff <path.ttf>
7import "nx_syscalls.nx"
8import "nx_font.nx"
9const K_MAGIC_65536: i64 = 65536
10
11func w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
12func n2(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{w("-" as *u8);m=0-m} 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 }
13func u16(p: *u8, o: i64) -> i64 { return ((p[o]&0xff)*256) + (p[o+1]&0xff) }
14func u32(p: *u8, o: i64) -> i64 { return (u16(p,o)*K_MAGIC_65536) + u16(p,o+2) }
15func tag_is(p: *u8, o: i64, a: i64, b: i64, c: i64, d: i64) -> i64 {
16 if (p[o]&0xff)==a { if (p[o+1]&0xff)==b { if (p[o+2]&0xff)==c { if (p[o+3]&0xff)==d { return 1 } } } }
17 return 0
18}
19
20func main(argc: i64, argv: *i64) -> i64 {
21 if argc < 2 { w("usage: nx_font_advdiff <path.ttf>\n" as *u8); sys_exit(2); return 2 }
22 let lp: *i64 = sys_mmap(16) as *i64
23 let f: *u8 = sys_read_file(argv[1] as *u8, lp)
24 if (f as i64) == 0 { w("cannot read ttf\n" as *u8); sys_exit(3); return 3 }
25 let ntab: i64 = u16(f, 4)
26 var head_o: i64 = 0 - 1
27 var hhea_o: i64 = 0 - 1
28 var hmtx_o: i64 = 0 - 1
29 var cmap_o: i64 = 0 - 1
30 var ti: i64 = 0
31 while ti < ntab {
32 let ro: i64 = 12 + ti*16
33 if tag_is(f,ro,104,101,97,100)==1 { head_o = u32(f,ro+8) } // head
34 if tag_is(f,ro,104,104,101,97)==1 { hhea_o = u32(f,ro+8) } // hhea
35 if tag_is(f,ro,104,109,116,120)==1 { hmtx_o = u32(f,ro+8) } // hmtx
36 if tag_is(f,ro,99,109,97,112)==1 { cmap_o = u32(f,ro+8) } // cmap
37 ti = ti + 1
38 }
39 if head_o < 0 { w("no head\n" as *u8); sys_exit(4); return 4 }
40 if hmtx_o < 0 { w("no hmtx\n" as *u8); sys_exit(4); return 4 }
41 if cmap_o < 0 { w("no cmap\n" as *u8); sys_exit(4); return 4 }
42 let upem: i64 = u16(f, head_o + 18)
43 let numh: i64 = u16(f, hhea_o + 34)
44 // cmap: pick a format-4 subtable (prefer windows 3/1; accept any format-4)
45 let ncm: i64 = u16(f, cmap_o + 2)
46 var sub4: i64 = 0 - 1
47 var ci: i64 = 0
48 while ci < ncm {
49 let er: i64 = cmap_o + 4 + ci*8
50 let so: i64 = cmap_o + u32(f, er+4)
51 if u16(f, so) == 4 { if sub4 < 0 { sub4 = so } if u16(f,er)==3 { if u16(f,er+2)==1 { sub4 = so } } }
52 ci = ci + 1
53 }
54 if sub4 < 0 { w("no cmap4\n" as *u8); sys_exit(5); return 5 }
55 let segx2: i64 = u16(f, sub4 + 6)
56 let endo: i64 = sub4 + 14
57 let starto: i64 = endo + segx2 + 2
58 let deltao: i64 = starto + segx2
59 let rangeo: i64 = deltao + segx2
60 w("ch ttf1000 ours drift (upem=" as *u8); n2(upem); w(" numh=" as *u8); n2(numh); w(")\n" as *u8)
61 var sumabs: i64 = 0
62 var cnt: i64 = 0
63 var big: i64 = 0
64 var ch: i64 = 32
65 while ch <= 126 {
66 // find segment
67 var g: i64 = 0
68 var si: i64 = 0
69 var go: i64 = 1
70 while go == 1 {
71 if si*2 >= segx2 { go = 0 }
72 else {
73 if u16(f, endo + si*2) >= ch {
74 if u16(f, starto + si*2) <= ch {
75 let ro2: i64 = u16(f, rangeo + si*2)
76 if ro2 == 0 { g = (ch + u16(f, deltao + si*2)) % K_MAGIC_65536 }
77 else { g = u16(f, rangeo + si*2 + ro2 + (ch - u16(f, starto + si*2))*2) }
78 }
79 go = 0
80 } else { si = si + 1 }
81 }
82 }
83 var gm: i64 = g
84 if gm >= numh { gm = numh - 1 }
85 let adv: i64 = u16(f, hmtx_o + gm*4)
86 let a1000: i64 = (adv * 1000) / upem
87 let ours: i64 = font_adv_em(ch)
88 let d: i64 = a1000 - ours
89 var ad: i64 = d
90 if ad < 0 { ad = 0 - ad }
91 sumabs = sumabs + ad
92 cnt = cnt + 1
93 if ad > 50 { big = big + 1 }
94 n2(ch); w(" " as *u8); n2(a1000); w(" " as *u8); n2(ours); w(" " as *u8); n2(d); w("\n" as *u8)
95 ch = ch + 1
96 }
97 w("SUMMARY meanabs=" as *u8); n2(sumabs / cnt)
98 w(" chars_drift_gt50=" as *u8); n2(big)
99 w("/" as *u8); n2(cnt); w("\n" as *u8)
100 sys_exit(0)
101 return 0
102}