nx_font_cjk.nx source
↩ module page · 303 lines · 18704 B
1// nx_font_cjk.nx -- FIRST CJK / ASIAN rung: prove the stroke generator extends to Han (Chinese/Japanese/Korean).
2// Han characters ARE stroke-based (horizontal/vertical/etc.), so our stroke->rectangle generator suits them
3// naturally. Generates 8 REAL, common Han characters composed of horizontal+vertical strokes and maps them to
4// their TRUE Unicode codepoints via a GENERIC cmap-format-4 (scattered BMP codepoints) -> web_assets/nishi_cjk.ttf,
5// self-validated. Chars: 一(U+4E00 one) 三(4E09 three) 二(4E8C two) 十(5341 ten) 口(53E3 mouth) 日(65E5 sun/day)
6// 田(7530 field) 王(738B king). HONEST: 8 chars is a PROOF the generator handles CJK -- NOT coverage (full CJK =
7// 101,000+ Han, the scale rung). license_tier: ORIGINAL expect_exit: 0
8import "nx_syscalls_x86_64.nx"
9const MAGIC_MAGIC_65536: i64 = 65536
10const MAGIC_MAGIC_16777216: i64 = 16777216
11const MAGIC_MAGIC_4294967296: i64 = 4294967296
12const MAGIC_MAGIC_20096: i64 = 20096
13const MAGIC_MAGIC_20105: i64 = 20105
14const MAGIC_MAGIC_20108: i64 = 20108
15const MAGIC_MAGIC_21313: i64 = 21313
16const MAGIC_MAGIC_5341: i64 = 5341
17const MAGIC_MAGIC_21475: i64 = 21475
18const MAGIC_MAGIC_26085: i64 = 26085
19const MAGIC_MAGIC_30000: i64 = 30000
20const MAGIC_MAGIC_7530: i64 = 7530
21const MAGIC_MAGIC_29579: i64 = 29579
22const MAGIC_MAGIC_8192: i64 = 8192
23const MAGIC_MAGIC_16384: i64 = 16384
24const MAGIC_MAGIC_65535: i64 = 65535
25const MAGIC_MAGIC_1594834165: i64 = 1594834165
26const MAGIC_MAGIC_1033: i64 = 1033
27const MAGIC_MAGIC_196608: i64 = 196608
28const MAGIC_MAGIC_131072: i64 = 131072
29
30const MAGIC_ADJ: i64 = 2981146554
31
32func sw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
33func sn(v: i64) -> i64 { let bb: *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{bb[i]=t[k-1-i];i=i+1} sys_write(1,bb,k); return 0 }
34
35func si16(v: i64) -> i64 { if v<0 { return v+MAGIC_MAGIC_65536 } return v }
36func w8(b: *u8, o: i64, v: i64) -> i64 { b[o]=(v%256) as u8; return o+1 }
37func w16(b: *u8, o: i64, v: i64) -> i64 { b[o]=((v/256)%256) as u8; b[o+1]=(v%256) as u8; return o+2 }
38func w32(b: *u8, o: i64, v: i64) -> i64 { b[o]=((v/MAGIC_MAGIC_16777216)%256) as u8; b[o+1]=((v/MAGIC_MAGIC_65536)%256) as u8; b[o+2]=((v/256)%256) as u8; b[o+3]=(v%256) as u8; return o+4 }
39func r32(b: *u8, o: i64) -> i64 { return (b[o] as i64)*MAGIC_MAGIC_16777216 + (b[o+1] as i64)*MAGIC_MAGIC_65536 + (b[o+2] as i64)*256 + (b[o+3] as i64) }
40func align4(o: i64) -> i64 { while o%4 != 0 { o=o+1 } return o }
41func csum(b: *u8, off: i64, len: i64) -> i64 { var s: i64=0; var i: i64=off; while i < off+len { s = (s + r32(b,i)) % MAGIC_MAGIC_4294967296; i=i+4 } return s }
42func he_puts(buf: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=0 { buf[o]=s[i]; o=o+1; i=i+1 } return o }
43func b64c(v: i64) -> i64 { if v<26 { return 65+v } if v<52 { return 97+(v-26) } if v<62 { return 48+(v-52) } if v==62 { return 43 } return 47 }
44func b64_encode(inb: *u8, n: i64, out: *u8) -> i64 {
45 var o: i64=0; var i: i64=0
46 while i+3<=n { let a: i64=inb[i] as i64; let b: i64=inb[i+1] as i64; let c: i64=inb[i+2] as i64
47 out[o]=b64c(a/4) as u8; o=o+1; out[o]=b64c((a%4)*16 + b/16) as u8; o=o+1; out[o]=b64c((b%16)*4 + c/64) as u8; o=o+1; out[o]=b64c(c%64) as u8; o=o+1; i=i+3 }
48 let rem: i64=n-i
49 if rem==1 { let a: i64=inb[i] as i64; out[o]=b64c(a/4) as u8; o=o+1; out[o]=b64c((a%4)*16) as u8; o=o+1; out[o]=61 as u8; o=o+1; out[o]=61 as u8; o=o+1 }
50 if rem==2 { let a: i64=inb[i] as i64; let b: i64=inb[i+1] as i64; out[o]=b64c(a/4) as u8; o=o+1; out[o]=b64c((a%4)*16 + b/16) as u8; o=o+1; out[o]=b64c((b%16)*4) as u8; o=o+1; out[o]=61 as u8; o=o+1 }
51 out[o]=0 as u8; return o
52}
53func add_rect(px: *i64, py: *i64, ep: *i64, pc: i64, cc: i64, x0: i64, y0: i64, x1: i64, y1: i64) -> i64 {
54 px[pc]=x0; py[pc]=y0; px[pc+1]=x1; py[pc+1]=y0; px[pc+2]=x1; py[pc+2]=y1; px[pc+3]=x0; py[pc+3]=y1
55 ep[cc]=pc+3; return pc+4
56}
57
58// gen a Han glyph from horizontal/vertical strokes. wr = stroke half-thickness. gid: 0 .notdef,
59// 1 一, 2 三, 3 二, 4 十, 5 口, 6 日, 7 田, 8 王.
60func gen_han(gid: i64, wr: i64, ep: *i64, px: *i64, py: *i64, meta: *i64) -> i64 {
61 if gid==0 { add_rect(px,py,ep,0,0, 120,0,880,780); meta[0]=1; meta[1]=4; return 0 } // .notdef box (tofu)
62 if gid==1 { add_rect(px,py,ep,0,0, 120,490-wr,880,490+wr); meta[0]=1; meta[1]=4; return 0 } // 一 one
63 if gid==2 { // 三 three
64 var pc: i64=0
65 pc=add_rect(px,py,ep,pc,0, 200,720-wr,800,720+wr)
66 pc=add_rect(px,py,ep,pc,1, 260,490-wr,740,490+wr)
67 pc=add_rect(px,py,ep,pc,2, 140,210-wr,860,210+wr)
68 meta[0]=3; meta[1]=12; return 0
69 }
70 if gid==3 { // 二 two
71 var pc: i64=0
72 pc=add_rect(px,py,ep,pc,0, 220,610-wr,780,610+wr)
73 pc=add_rect(px,py,ep,pc,1, 140,290-wr,860,290+wr)
74 meta[0]=2; meta[1]=8; return 0
75 }
76 if gid==4 { // 十 ten (cross)
77 var pc: i64=0
78 pc=add_rect(px,py,ep,pc,0, 120,490-wr,880,490+wr)
79 pc=add_rect(px,py,ep,pc,1, 500-wr,140,500+wr,850)
80 meta[0]=2; meta[1]=8; return 0
81 }
82 if gid==5 { // 口 mouth (box)
83 var pc: i64=0
84 pc=add_rect(px,py,ep,pc,0, 220,220,220+2*wr,780)
85 pc=add_rect(px,py,ep,pc,1, 780-2*wr,220,780,780)
86 pc=add_rect(px,py,ep,pc,2, 220,780-2*wr,780,780)
87 pc=add_rect(px,py,ep,pc,3, 220,220,780,220+2*wr)
88 meta[0]=4; meta[1]=16; return 0
89 }
90 if gid==6 { // 日 sun/day (box + mid bar)
91 var pc: i64=0
92 pc=add_rect(px,py,ep,pc,0, 260,220,260+2*wr,780)
93 pc=add_rect(px,py,ep,pc,1, 740-2*wr,220,740,780)
94 pc=add_rect(px,py,ep,pc,2, 260,780-2*wr,740,780)
95 pc=add_rect(px,py,ep,pc,3, 260,220,740,220+2*wr)
96 pc=add_rect(px,py,ep,pc,4, 260,500-wr,740,500+wr)
97 meta[0]=5; meta[1]=20; return 0
98 }
99 if gid==7 { // 田 field (box + cross)
100 var pc: i64=0
101 pc=add_rect(px,py,ep,pc,0, 220,220,220+2*wr,780)
102 pc=add_rect(px,py,ep,pc,1, 780-2*wr,220,780,780)
103 pc=add_rect(px,py,ep,pc,2, 220,780-2*wr,780,780)
104 pc=add_rect(px,py,ep,pc,3, 220,220,780,220+2*wr)
105 pc=add_rect(px,py,ep,pc,4, 220,500-wr,780,500+wr)
106 pc=add_rect(px,py,ep,pc,5, 500-wr,220,500+wr,780)
107 meta[0]=6; meta[1]=24; return 0
108 }
109 var pc: i64=0 // 王 king (3 horiz + vert)
110 pc=add_rect(px,py,ep,pc,0, 220,755-wr,780,755+wr)
111 pc=add_rect(px,py,ep,pc,1, 300,490-wr,700,490+wr)
112 pc=add_rect(px,py,ep,pc,2, 180,225-wr,820,225+wr)
113 pc=add_rect(px,py,ep,pc,3, 500-wr,225,500+wr,755)
114 meta[0]=4; meta[1]=16; return 0
115}
116
117func append_glyph(g: *u8, go: i64, ep: *i64, px: *i64, py: *i64, meta: *i64) -> i64 {
118 let nc: i64=meta[0]; let np: i64=meta[1]
119 if nc==0 { return go }
120 var xmin: i64=px[0]; var xmax: i64=px[0]; var ymin: i64=py[0]; var ymax: i64=py[0]
121 var i: i64=0
122 while i<np { if px[i]<xmin{xmin=px[i]} if px[i]>xmax{xmax=px[i]} if py[i]<ymin{ymin=py[i]} if py[i]>ymax{ymax=py[i]} i=i+1 }
123 var o: i64=go
124 o=w16(g,o,si16(nc)); o=w16(g,o,si16(xmin)); o=w16(g,o,si16(ymin)); o=w16(g,o,si16(xmax)); o=w16(g,o,si16(ymax))
125 i=0; while i<nc { o=w16(g,o,ep[i]); i=i+1 }
126 o=w16(g,o,0)
127 i=0; while i<np { o=w8(g,o,1); i=i+1 }
128 var prev: i64=0; i=0; while i<np { o=w16(g,o,si16(px[i]-prev)); prev=px[i]; i=i+1 }
129 prev=0; i=0; while i<np { o=w16(g,o,si16(py[i]-prev)); prev=py[i]; i=i+1 }
130 if o%2 != 0 { o=w8(g,o,0) }
131 return o
132}
133
134func main() -> i64 {
135 let NG: i64=9 // .notdef + 8 Han
136 let NCP: i64=8
137 let cp: *i64=sys_mmap(80) as *i64
138 let gid: *i64=sys_mmap(80) as *i64
139 cp[0]=MAGIC_MAGIC_20096; gid[0]=1 // 4E00 一
140 cp[1]=MAGIC_MAGIC_20105; gid[1]=2 // 4E09 三
141 cp[2]=MAGIC_MAGIC_20108; gid[2]=3 // 4E8C 二
142 cp[3]=MAGIC_MAGIC_21313; gid[3]=4 // MAGIC_MAGIC_5341 十
143 cp[4]=MAGIC_MAGIC_21475; gid[4]=5 // 53E3 口
144 cp[5]=MAGIC_MAGIC_26085; gid[5]=6 // 65E5 日
145 cp[6]=MAGIC_MAGIC_30000; gid[6]=7 // MAGIC_MAGIC_7530 田
146 cp[7]=MAGIC_MAGIC_29579; gid[7]=8 // 738B 王
147
148 let ep: *i64=sys_mmap(128) as *i64
149 let px: *i64=sys_mmap(512) as *i64
150 let py: *i64=sys_mmap(512) as *i64
151 let meta: *i64=sys_mmap(16) as *i64
152 let g: *u8=sys_mmap(MAGIC_MAGIC_8192)
153 let goff: *i64=sys_mmap(128) as *i64
154 let wr: i64=58
155 var go: i64=0
156 var q: i64=0
157 while q<NG { goff[q]=go; gen_han(q,wr,ep,px,py,meta); go=append_glyph(g,go,ep,px,py,meta); q=q+1 }
158 goff[NG]=go
159 let glyf_len: i64=go
160
161 let buf: *u8=sys_mmap(MAGIC_MAGIC_16384)
162 let toff: *i64=sys_mmap(128) as *i64
163 let tlen: *i64=sys_mmap(128) as *i64
164 let tcs: *i64=sys_mmap(128) as *i64
165 let NT: i64=10
166 var o: i64=12 + 16*NT
167
168 // OS/2
169 o=align4(o); toff[0]=o
170 o=w16(buf,o,4); o=w16(buf,o,si16(1000)); o=w16(buf,o,400); o=w16(buf,o,5); o=w16(buf,o,0)
171 o=w16(buf,o,si16(650));o=w16(buf,o,si16(700));o=w16(buf,o,si16(0));o=w16(buf,o,si16(140))
172 o=w16(buf,o,si16(650));o=w16(buf,o,si16(700));o=w16(buf,o,si16(0));o=w16(buf,o,si16(480))
173 o=w16(buf,o,si16(50));o=w16(buf,o,si16(260)); o=w16(buf,o,si16(0))
174 var pi: i64=0; while pi<10 { o=w8(buf,o,0); pi=pi+1 }
175 o=w32(buf,o,0);o=w32(buf,o,0);o=w32(buf,o,0);o=w32(buf,o,0)
176 o=w8(buf,o,78);o=w8(buf,o,73);o=w8(buf,o,83);o=w8(buf,o,72)
177 o=w16(buf,o,64); o=w16(buf,o,MAGIC_MAGIC_20096); o=w16(buf,o,MAGIC_MAGIC_29579)
178 o=w16(buf,o,si16(800));o=w16(buf,o,si16(-200));o=w16(buf,o,si16(0))
179 o=w16(buf,o,880);o=w16(buf,o,120)
180 o=w32(buf,o,0);o=w32(buf,o,0)
181 o=w16(buf,o,si16(500));o=w16(buf,o,si16(700)); o=w16(buf,o,0);o=w16(buf,o,MAGIC_MAGIC_20096);o=w16(buf,o,1)
182 tlen[0]=o-toff[0]; o=align4(o); tcs[0]=csum(buf,toff[0],o-toff[0])
183
184 // cmap: generic format-4 from cp[]/gid[] (scattered BMP)
185 o=align4(o); toff[1]=o
186 o=w16(buf,o,0); o=w16(buf,o,1); o=w16(buf,o,3); o=w16(buf,o,1); o=w32(buf,o,12)
187 let sub0: i64=o
188 let segCount: i64=NCP+1
189 o=w16(buf,o,4)
190 let lenpos: i64=o; o=w16(buf,o,0)
191 o=w16(buf,o,0)
192 o=w16(buf,o,segCount*2)
193 var p2: i64=1; while p2*2<=segCount { p2=p2*2 }
194 o=w16(buf,o,p2*2)
195 var es: i64=0; var tp: i64=p2; while tp>1 { tp=tp/2; es=es+1 }
196 o=w16(buf,o,es)
197 o=w16(buf,o,segCount*2 - p2*2)
198 var ci: i64=0; while ci<NCP { o=w16(buf,o,cp[ci]); ci=ci+1 }
199 o=w16(buf,o,MAGIC_MAGIC_65535)
200 o=w16(buf,o,0)
201 ci=0; while ci<NCP { o=w16(buf,o,cp[ci]); ci=ci+1 }
202 o=w16(buf,o,MAGIC_MAGIC_65535)
203 ci=0; while ci<NCP { var dd: i64=gid[ci]-cp[ci]; if dd<0 { dd=dd+MAGIC_MAGIC_65536 } o=w16(buf,o,dd); ci=ci+1 }
204 o=w16(buf,o,1)
205 ci=0; while ci<segCount { o=w16(buf,o,0); ci=ci+1 }
206 w16(buf, lenpos, o - sub0)
207 tlen[1]=o-toff[1]; o=align4(o); tcs[1]=csum(buf,toff[1],o-toff[1])
208
209 // glyf
210 o=align4(o); toff[2]=o
211 var gj: i64=0; while gj<glyf_len { buf[o]=g[gj]; o=o+1; gj=gj+1 }
212 tlen[2]=o-toff[2]; o=align4(o); tcs[2]=csum(buf,toff[2],o-toff[2])
213
214 // head
215 o=align4(o); toff[3]=o
216 o=w32(buf,o,MAGIC_MAGIC_65536); o=w32(buf,o,MAGIC_MAGIC_65536); o=w32(buf,o,0); o=w32(buf,o,MAGIC_MAGIC_1594834165)
217 o=w16(buf,o,11); o=w16(buf,o,1000)
218 o=w32(buf,o,0);o=w32(buf,o,0); o=w32(buf,o,0);o=w32(buf,o,0)
219 o=w16(buf,o,si16(0));o=w16(buf,o,si16(0));o=w16(buf,o,1000);o=w16(buf,o,850)
220 o=w16(buf,o,0); o=w16(buf,o,8); o=w16(buf,o,2); o=w16(buf,o,0); o=w16(buf,o,0)
221 tlen[3]=o-toff[3]; o=align4(o); tcs[3]=csum(buf,toff[3],o-toff[3])
222
223 // hhea
224 o=align4(o); toff[4]=o
225 o=w32(buf,o,MAGIC_MAGIC_65536); o=w16(buf,o,si16(850)); o=w16(buf,o,si16(-150)); o=w16(buf,o,si16(0))
226 o=w16(buf,o,1000); o=w16(buf,o,si16(0)); o=w16(buf,o,si16(0)); o=w16(buf,o,si16(1000))
227 o=w16(buf,o,si16(1)); o=w16(buf,o,si16(0)); o=w16(buf,o,si16(0))
228 o=w16(buf,o,0);o=w16(buf,o,0);o=w16(buf,o,0);o=w16(buf,o,0)
229 o=w16(buf,o,si16(0)); o=w16(buf,o,NG)
230 tlen[4]=o-toff[4]; o=align4(o); tcs[4]=csum(buf,toff[4],o-toff[4])
231
232 // hmtx: NG metrics (.notdef 600, Han full-width 1000)
233 o=align4(o); toff[5]=o
234 o=w16(buf,o,600); o=w16(buf,o,si16(0))
235 var hi: i64=1; while hi<NG { o=w16(buf,o,1000); o=w16(buf,o,si16(0)); hi=hi+1 }
236 tlen[5]=o-toff[5]; o=align4(o); tcs[5]=csum(buf,toff[5],o-toff[5])
237
238 // loca (short): NG+1 entries
239 o=align4(o); toff[6]=o
240 var li: i64=0; while li<NG+1 { o=w16(buf,o,goff[li]/2); li=li+1 }
241 tlen[6]=o-toff[6]; o=align4(o); tcs[6]=csum(buf,toff[6],o-toff[6])
242
243 // maxp
244 o=align4(o); toff[7]=o
245 o=w32(buf,o,MAGIC_MAGIC_65536); o=w16(buf,o,NG); o=w16(buf,o,24); o=w16(buf,o,6)
246 o=w16(buf,o,0);o=w16(buf,o,0); o=w16(buf,o,2); o=w16(buf,o,0);o=w16(buf,o,0);o=w16(buf,o,0);o=w16(buf,o,0);o=w16(buf,o,0);o=w16(buf,o,0);o=w16(buf,o,0);o=w16(buf,o,0)
247 tlen[7]=o-toff[7]; o=align4(o); tcs[7]=csum(buf,toff[7],o-toff[7])
248
249 // name
250 o=align4(o); toff[8]=o
251 o=w16(buf,o,0); o=w16(buf,o,4); o=w16(buf,o,54)
252 o=w16(buf,o,3);o=w16(buf,o,1);o=w16(buf,o,MAGIC_MAGIC_1033);o=w16(buf,o,1);o=w16(buf,o,18);o=w16(buf,o,0)
253 o=w16(buf,o,3);o=w16(buf,o,1);o=w16(buf,o,MAGIC_MAGIC_1033);o=w16(buf,o,2);o=w16(buf,o,14);o=w16(buf,o,18)
254 o=w16(buf,o,3);o=w16(buf,o,1);o=w16(buf,o,MAGIC_MAGIC_1033);o=w16(buf,o,4);o=w16(buf,o,18);o=w16(buf,o,0)
255 o=w16(buf,o,3);o=w16(buf,o,1);o=w16(buf,o,MAGIC_MAGIC_1033);o=w16(buf,o,6);o=w16(buf,o,16);o=w16(buf,o,32)
256 o=w8(buf,o,0);o=w8(buf,o,78);o=w8(buf,o,0);o=w8(buf,o,105);o=w8(buf,o,0);o=w8(buf,o,115);o=w8(buf,o,0);o=w8(buf,o,104);o=w8(buf,o,0);o=w8(buf,o,105);o=w8(buf,o,0);o=w8(buf,o,32);o=w8(buf,o,0);o=w8(buf,o,72);o=w8(buf,o,0);o=w8(buf,o,97);o=w8(buf,o,0);o=w8(buf,o,110)
257 o=w8(buf,o,0);o=w8(buf,o,82);o=w8(buf,o,0);o=w8(buf,o,101);o=w8(buf,o,0);o=w8(buf,o,103);o=w8(buf,o,0);o=w8(buf,o,117);o=w8(buf,o,0);o=w8(buf,o,108);o=w8(buf,o,0);o=w8(buf,o,97);o=w8(buf,o,0);o=w8(buf,o,114)
258 o=w8(buf,o,0);o=w8(buf,o,78);o=w8(buf,o,0);o=w8(buf,o,105);o=w8(buf,o,0);o=w8(buf,o,115);o=w8(buf,o,0);o=w8(buf,o,104);o=w8(buf,o,0);o=w8(buf,o,105);o=w8(buf,o,0);o=w8(buf,o,72);o=w8(buf,o,0);o=w8(buf,o,97);o=w8(buf,o,0);o=w8(buf,o,110)
259 tlen[8]=o-toff[8]; o=align4(o); tcs[8]=csum(buf,toff[8],o-toff[8])
260
261 // post
262 o=align4(o); toff[9]=o
263 o=w32(buf,o,MAGIC_MAGIC_196608); o=w32(buf,o,0); o=w16(buf,o,si16(-100)); o=w16(buf,o,50); o=w32(buf,o,0);o=w32(buf,o,0);o=w32(buf,o,0);o=w32(buf,o,0);o=w32(buf,o,0)
264 tlen[9]=o-toff[9]; o=align4(o); tcs[9]=csum(buf,toff[9],o-toff[9])
265
266 let total: i64=o
267 var hh: i64=0
268 hh=w32(buf,hh,MAGIC_MAGIC_65536); hh=w16(buf,hh,NT); hh=w16(buf,hh,128); hh=w16(buf,hh,3); hh=w16(buf,hh,32)
269 let tags: *u8="OS/2cmapglyfheadhheahmtxlocamaxpnamepost" as *u8
270 var d: i64=12; var ti: i64=0
271 while ti<NT { buf[d]=tags[ti*4]; buf[d+1]=tags[ti*4+1]; buf[d+2]=tags[ti*4+2]; buf[d+3]=tags[ti*4+3]
272 w32(buf,d+4,tcs[ti]); w32(buf,d+8,toff[ti]); w32(buf,d+12,tlen[ti]); d=d+16; ti=ti+1 }
273 let fsum0: i64=csum(buf,0,total)
274 var adj: i64=MAGIC_ADJ - fsum0
275 if adj<0 { adj=adj+MAGIC_MAGIC_4294967296 }
276 w32(buf, toff[3]+8, adj)
277
278 let fd: i64=sys_openat_wr("web_assets/nishi_cjk.ttf" as *u8, 420)
279 if fd<0 { sw("SAVE-FAIL\n" as *u8); return 6 }
280 sys_write(fd, buf, total); sys_close(fd)
281 let fs: i64=csum(buf,0,total)
282 sw("nx_font_cjk: 8 REAL Han chars generated -> web_assets/nishi_cjk.ttf (" as *u8); sn(total); sw("B) self-check=" as *u8)
283 if fs==MAGIC_ADJ { sw("PASS\n" as *u8) } else { sw("FAIL\n" as *u8) }
284
285 // test page: @font-face + the Han chars via numeric refs (ASCII source)
286 let b64: *u8=sys_mmap(MAGIC_MAGIC_65536)
287 b64_encode(buf, total, b64)
288 let pg: *u8=sys_mmap(MAGIC_MAGIC_131072)
289 var po: i64=0
290 po=he_puts(pg,po,"<!DOCTYPE html><html lang=\"zh\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"color-scheme\" content=\"dark\"><title>Nishi Han — sovereign CJK from strokes</title><style>@font-face{font-family:'Nishi Han';src:url(data:font/ttf;base64," as *u8)
291 po=he_puts(pg,po,b64)
292 po=he_puts(pg,po,") format('truetype')}*{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;background:radial-gradient(1100px 560px at 50% -10%,#241016,#0A0A0F);color:#F5F5FA;font-family:ui-sans-serif,system-ui,sans-serif}.wrap{text-align:center;padding:32px;max-width:900px}.tag{display:inline-block;font-size:.8rem;letter-spacing:.2em;text-transform:uppercase;color:#9EA0B8;margin-bottom:18px}.han{font-family:'Nishi Han';font-size:clamp(3rem,13vw,8rem);line-height:1.15;letter-spacing:.08em;background:linear-gradient(100deg,#FF7A59,#FFC24B);-webkit-background-clip:text;background-clip:text;color:transparent}.note{color:#9EA0B8;max-width:52ch;margin:30px auto 0;font-size:1.05rem;line-height:1.65}.note b{color:#F5F5FA}.mono{font-family:ui-monospace,Consolas,monospace;color:#FF7A59}</style></head><body><div class=\"wrap\">" as *u8)
293 po=he_puts(pg,po,"<span class=\"tag\">★ real Chinese characters, generated from strokes</span>" as *u8)
294 po=he_puts(pg,po,"<div class=\"han\">一二三十</div>" as *u8)
295 po=he_puts(pg,po,"<div class=\"han\">口日田王</div>" as *u8)
296 po=he_puts(pg,po,"<p class=\"note\">These are 8 real Han characters — 一(one) 二(two) 三(three) 十(ten) 口(mouth) 日(sun) 田(field) 王(king) — each generated by <span class=\"mono\">nx_font_cjk</span> from horizontal/vertical STROKES (Han is stroke-based, so our stroke generator fits it naturally), mapped to their true Unicode codepoints (U+4E00…) via a sovereign cmap. A proof the generator reaches CJK — <b>not</b> coverage: full CJK is 101,000+ Han characters, the scale rung ahead.</p>" as *u8)
297 po=he_puts(pg,po,"</div></body></html>" as *u8)
298 let pfd: i64=sys_openat_wr("web_assets/nishi_cjk.html" as *u8, 420)
299 if pfd<0 { sw("PAGE-FAIL\n" as *u8); return 7 }
300 sys_write(pfd, pg, po); sys_close(pfd)
301 sw(" -> web_assets/nishi_cjk.html (open to SEE our own Chinese characters)\n" as *u8)
302 return 0
303}