nx_font_curve.nx source
↩ module page · 278 lines · 18137 B
1// nx_font_curve.nx -- the CURVED-stroke primitive (last stroke type): real TrueType quadratic Beziers via
2// OFF-curve points (flag 0 = control, flag 1 = on-curve). Adds an ellipse() ring helper + per-point on/off flags
3// to the glyph model (previously all-on-curve straight polygons). Proven with round glyphs across scripts:
4// O(U+4F) Q(U+51) 0(U+30 digit) + Korean ㅇ(U+3147 ieung) -- each a filled ring (outer ellipse CCW + inner CW,
5// nonzero-winding leaves the counter hollow). One self-validated .ttf -> web_assets/nishi_curve.ttf + page.
6// Completes the stroke toolkit: horizontal + vertical + diagonal + CURVE. license_tier: ORIGINAL expect_exit: 0
7import "nx_syscalls_x86_64.nx"
8const MAGIC_MAGIC_65536: i64 = 65536
9const MAGIC_MAGIC_16777216: i64 = 16777216
10const MAGIC_MAGIC_4294967296: i64 = 4294967296
11const MAGIC_MAGIC_12615: i64 = 12615
12const MAGIC_MAGIC_8192: i64 = 8192
13const MAGIC_MAGIC_16384: i64 = 16384
14const MAGIC_MAGIC_65535: i64 = 65535
15const MAGIC_MAGIC_1594834165: i64 = 1594834165
16const MAGIC_MAGIC_1033: i64 = 1033
17const MAGIC_MAGIC_196608: i64 = 196608
18const MAGIC_MAGIC_131072: i64 = 131072
19
20const MAGIC_ADJ: i64 = 2981146554
21
22func sw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
23func 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 }
24
25func si16(v: i64) -> i64 { if v<0 { return v+MAGIC_MAGIC_65536 } return v }
26func w8(b: *u8, o: i64, v: i64) -> i64 { b[o]=(v%256) as u8; return o+1 }
27func 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 }
28func 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 }
29func 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) }
30func align4(o: i64) -> i64 { while o%4 != 0 { o=o+1 } return o }
31func 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 }
32func 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 }
33func 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 }
34func b64_encode(inb: *u8, n: i64, out: *u8) -> i64 {
35 var o: i64=0; var i: i64=0
36 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
37 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 }
38 let rem: i64=n-i
39 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 }
40 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 }
41 out[o]=0 as u8; return o
42}
43func isqrt(n: i64) -> i64 { if n<=0 { return 0 } var x: i64=n; var y: i64=(x+1)/2; while y<x { x=y; y=(x + n/x)/2 } return x }
44func add_rect(px: *i64, py: *i64, on: *i64, ep: *i64, pc: i64, cc: i64, x0: i64, y0: i64, x1: i64, y1: i64) -> i64 {
45 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
46 on[pc]=1; on[pc+1]=1; on[pc+2]=1; on[pc+3]=1
47 ep[cc]=pc+3; return pc+4
48}
49func add_seg(px: *i64, py: *i64, on: *i64, ep: *i64, pc: i64, cc: i64, x0: i64, y0: i64, x1: i64, y1: i64, wr: i64) -> i64 {
50 let dx: i64=x1-x0; let dy: i64=y1-y0
51 var ln: i64=isqrt(dx*dx+dy*dy); if ln==0 { ln=1 }
52 let ox: i64=wr*dy/ln; let oy: i64=wr*dx/ln
53 let ax: i64=x0+ox; let ay: i64=y0-oy; let bx: i64=x1+ox; let by: i64=y1-oy; let cx: i64=x1-ox; let cy: i64=y1+oy; let ex: i64=x0-ox; let ey: i64=y0+oy
54 let area: i64 = ax*by-bx*ay + bx*cy-cx*by + cx*ey-ex*cy + ex*ay-ax*ey
55 if area < 0 { px[pc]=ax; py[pc]=ay; px[pc+1]=ex; py[pc+1]=ey; px[pc+2]=cx; py[pc+2]=cy; px[pc+3]=bx; py[pc+3]=by }
56 else { px[pc]=ax; py[pc]=ay; px[pc+1]=bx; py[pc+1]=by; px[pc+2]=cx; py[pc+2]=cy; px[pc+3]=ex; py[pc+3]=ey }
57 on[pc]=1; on[pc+1]=1; on[pc+2]=1; on[pc+3]=1
58 ep[cc]=pc+3; return pc+4
59}
60// ellipse ring contour via quadratic Beziers: 4 on-curve cardinals + 4 off-curve corner controls. ccw=1 outer, 0 inner(hole).
61func ellipse(px: *i64, py: *i64, on: *i64, ep: *i64, pc: i64, cc: i64, cx: i64, cy: i64, rx: i64, ry: i64, ccw: i64) -> i64 {
62 px[pc]=cx+rx; py[pc]=cy; on[pc]=1
63 if ccw==1 {
64 px[pc+1]=cx+rx; py[pc+1]=cy+ry; on[pc+1]=0
65 px[pc+2]=cx; py[pc+2]=cy+ry; on[pc+2]=1
66 px[pc+3]=cx-rx; py[pc+3]=cy+ry; on[pc+3]=0
67 px[pc+4]=cx-rx; py[pc+4]=cy; on[pc+4]=1
68 px[pc+5]=cx-rx; py[pc+5]=cy-ry; on[pc+5]=0
69 px[pc+6]=cx; py[pc+6]=cy-ry; on[pc+6]=1
70 px[pc+7]=cx+rx; py[pc+7]=cy-ry; on[pc+7]=0
71 } else {
72 px[pc+1]=cx+rx; py[pc+1]=cy-ry; on[pc+1]=0
73 px[pc+2]=cx; py[pc+2]=cy-ry; on[pc+2]=1
74 px[pc+3]=cx-rx; py[pc+3]=cy-ry; on[pc+3]=0
75 px[pc+4]=cx-rx; py[pc+4]=cy; on[pc+4]=1
76 px[pc+5]=cx-rx; py[pc+5]=cy+ry; on[pc+5]=0
77 px[pc+6]=cx; py[pc+6]=cy+ry; on[pc+6]=1
78 px[pc+7]=cx+rx; py[pc+7]=cy+ry; on[pc+7]=0
79 }
80 ep[cc]=pc+7; return pc+8
81}
82
83// glyph index: 0 .notdef, 1 space, 2 '0', 3 O, 4 Q, 5 ㅇ
84func gen_curve(gg: i64, wr: i64, ep: *i64, px: *i64, py: *i64, on: *i64, meta: *i64) -> i64 {
85 if gg==0 { add_rect(px,py,on,ep,0,0, 80,0,520,700); meta[0]=1; meta[1]=4; return 0 }
86 if gg==1 { meta[0]=0; meta[1]=0; return 0 }
87 if gg==2 { var pc: i64=0 // digit 0
88 pc=ellipse(px,py,on,ep,pc,0, 300,350,230,350,1)
89 pc=ellipse(px,py,on,ep,pc,1, 300,350,230-2*wr,350-2*wr,0)
90 meta[0]=2; meta[1]=16; return 0 }
91 if gg==3 { var pc: i64=0 // O
92 pc=ellipse(px,py,on,ep,pc,0, 300,350,290,350,1)
93 pc=ellipse(px,py,on,ep,pc,1, 300,350,290-2*wr,350-2*wr,0)
94 meta[0]=2; meta[1]=16; return 0 }
95 if gg==4 { var pc: i64=0 // Q = O + tail
96 pc=ellipse(px,py,on,ep,pc,0, 300,350,290,350,1)
97 pc=ellipse(px,py,on,ep,pc,1, 300,350,290-2*wr,350-2*wr,0)
98 pc=add_seg(px,py,on,ep,pc,2, 330,200,520,0-60,wr)
99 meta[0]=3; meta[1]=20; return 0 }
100 var pc: i64=0 // ㅇ Korean ieung (ring)
101 pc=ellipse(px,py,on,ep,pc,0, 500,500,280,280,1)
102 pc=ellipse(px,py,on,ep,pc,1, 500,500,280-2*wr,280-2*wr,0)
103 meta[0]=2; meta[1]=16; return 0
104}
105
106func append_glyph(g: *u8, go: i64, ep: *i64, px: *i64, py: *i64, on: *i64, meta: *i64) -> i64 {
107 let nc: i64=meta[0]; let np: i64=meta[1]
108 if nc==0 { return go }
109 var xmin: i64=px[0]; var xmax: i64=px[0]; var ymin: i64=py[0]; var ymax: i64=py[0]
110 var i: i64=0
111 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 }
112 var o: i64=go
113 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))
114 i=0; while i<nc { o=w16(g,o,ep[i]); i=i+1 }
115 o=w16(g,o,0)
116 i=0; while i<np { if on[i]==1 { o=w8(g,o,1) } else { o=w8(g,o,0) } i=i+1 }
117 var prev: i64=0; i=0; while i<np { o=w16(g,o,si16(px[i]-prev)); prev=px[i]; i=i+1 }
118 prev=0; i=0; while i<np { o=w16(g,o,si16(py[i]-prev)); prev=py[i]; i=i+1 }
119 if o%2 != 0 { o=w8(g,o,0) }
120 return o
121}
122
123func main() -> i64 {
124 let NG: i64=6
125 let NCP: i64=5
126 let cp: *i64=sys_mmap(128) as *i64
127 let gd: *i64=sys_mmap(128) as *i64
128 cp[0]=32; gd[0]=1 // space
129 cp[1]=48; gd[1]=2 // '0'
130 cp[2]=79; gd[2]=3 // O
131 cp[3]=81; gd[3]=4 // Q
132 cp[4]=MAGIC_MAGIC_12615; gd[4]=5 // ㅇ
133
134 let ep: *i64=sys_mmap(128) as *i64
135 let px: *i64=sys_mmap(512) as *i64
136 let py: *i64=sys_mmap(512) as *i64
137 let on: *i64=sys_mmap(512) as *i64
138 let meta: *i64=sys_mmap(16) as *i64
139 let g: *u8=sys_mmap(MAGIC_MAGIC_8192)
140 let goff: *i64=sys_mmap(128) as *i64
141 let wr: i64=54
142 var go: i64=0
143 var q: i64=0
144 while q<NG { goff[q]=go; gen_curve(q,wr,ep,px,py,on,meta); go=append_glyph(g,go,ep,px,py,on,meta); q=q+1 }
145 goff[NG]=go
146 let glyf_len: i64=go
147
148 let buf: *u8=sys_mmap(MAGIC_MAGIC_16384)
149 let toff: *i64=sys_mmap(128) as *i64
150 let tlen: *i64=sys_mmap(128) as *i64
151 let tcs: *i64=sys_mmap(128) as *i64
152 let NT: i64=10
153 var o: i64=12 + 16*NT
154
155 o=align4(o); toff[0]=o
156 o=w16(buf,o,4); o=w16(buf,o,si16(650)); o=w16(buf,o,400); o=w16(buf,o,5); o=w16(buf,o,0)
157 o=w16(buf,o,si16(650));o=w16(buf,o,si16(700));o=w16(buf,o,si16(0));o=w16(buf,o,si16(140))
158 o=w16(buf,o,si16(650));o=w16(buf,o,si16(700));o=w16(buf,o,si16(0));o=w16(buf,o,si16(480))
159 o=w16(buf,o,si16(50));o=w16(buf,o,si16(260)); o=w16(buf,o,si16(0))
160 var pi: i64=0; while pi<10 { o=w8(buf,o,0); pi=pi+1 }
161 o=w32(buf,o,0);o=w32(buf,o,0);o=w32(buf,o,0);o=w32(buf,o,0)
162 o=w8(buf,o,78);o=w8(buf,o,73);o=w8(buf,o,83);o=w8(buf,o,72)
163 o=w16(buf,o,64); o=w16(buf,o,32); o=w16(buf,o,MAGIC_MAGIC_12615)
164 o=w16(buf,o,si16(800));o=w16(buf,o,si16(-200));o=w16(buf,o,si16(0))
165 o=w16(buf,o,800);o=w16(buf,o,200)
166 o=w32(buf,o,0);o=w32(buf,o,0)
167 o=w16(buf,o,si16(500));o=w16(buf,o,si16(700)); o=w16(buf,o,0);o=w16(buf,o,32);o=w16(buf,o,1)
168 tlen[0]=o-toff[0]; o=align4(o); tcs[0]=csum(buf,toff[0],o-toff[0])
169
170 o=align4(o); toff[1]=o
171 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)
172 let sub0: i64=o
173 let segCount: i64=NCP+1
174 o=w16(buf,o,4)
175 let lenpos: i64=o; o=w16(buf,o,0)
176 o=w16(buf,o,0)
177 o=w16(buf,o,segCount*2)
178 var p2: i64=1; while p2*2<=segCount { p2=p2*2 }
179 o=w16(buf,o,p2*2)
180 var es: i64=0; var tp: i64=p2; while tp>1 { tp=tp/2; es=es+1 }
181 o=w16(buf,o,es)
182 o=w16(buf,o,segCount*2 - p2*2)
183 var ci: i64=0; while ci<NCP { o=w16(buf,o,cp[ci]); ci=ci+1 }
184 o=w16(buf,o,MAGIC_MAGIC_65535)
185 o=w16(buf,o,0)
186 ci=0; while ci<NCP { o=w16(buf,o,cp[ci]); ci=ci+1 }
187 o=w16(buf,o,MAGIC_MAGIC_65535)
188 ci=0; while ci<NCP { var dd: i64=gd[ci]-cp[ci]; if dd<0 { dd=dd+MAGIC_MAGIC_65536 } o=w16(buf,o,dd); ci=ci+1 }
189 o=w16(buf,o,1)
190 ci=0; while ci<segCount { o=w16(buf,o,0); ci=ci+1 }
191 w16(buf, lenpos, o - sub0)
192 tlen[1]=o-toff[1]; o=align4(o); tcs[1]=csum(buf,toff[1],o-toff[1])
193
194 o=align4(o); toff[2]=o
195 var gj: i64=0; while gj<glyf_len { buf[o]=g[gj]; o=o+1; gj=gj+1 }
196 tlen[2]=o-toff[2]; o=align4(o); tcs[2]=csum(buf,toff[2],o-toff[2])
197
198 o=align4(o); toff[3]=o
199 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)
200 o=w16(buf,o,11); o=w16(buf,o,1000)
201 o=w32(buf,o,0);o=w32(buf,o,0); o=w32(buf,o,0);o=w32(buf,o,0)
202 o=w16(buf,o,si16(0));o=w16(buf,o,si16(-100));o=w16(buf,o,1000);o=w16(buf,o,800)
203 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)
204 tlen[3]=o-toff[3]; o=align4(o); tcs[3]=csum(buf,toff[3],o-toff[3])
205
206 o=align4(o); toff[4]=o
207 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))
208 o=w16(buf,o,1000); o=w16(buf,o,si16(0)); o=w16(buf,o,si16(0)); o=w16(buf,o,si16(1000))
209 o=w16(buf,o,si16(1)); o=w16(buf,o,si16(0)); o=w16(buf,o,si16(0))
210 o=w16(buf,o,0);o=w16(buf,o,0);o=w16(buf,o,0);o=w16(buf,o,0)
211 o=w16(buf,o,si16(0)); o=w16(buf,o,NG)
212 tlen[4]=o-toff[4]; o=align4(o); tcs[4]=csum(buf,toff[4],o-toff[4])
213
214 o=align4(o); toff[5]=o
215 o=w16(buf,o,600);o=w16(buf,o,si16(0)); o=w16(buf,o,400);o=w16(buf,o,si16(0)); o=w16(buf,o,600);o=w16(buf,o,si16(0)); o=w16(buf,o,640);o=w16(buf,o,si16(0)); o=w16(buf,o,640);o=w16(buf,o,si16(0)); o=w16(buf,o,1000);o=w16(buf,o,si16(0))
216 tlen[5]=o-toff[5]; o=align4(o); tcs[5]=csum(buf,toff[5],o-toff[5])
217
218 o=align4(o); toff[6]=o
219 var li: i64=0; while li<NG+1 { o=w16(buf,o,goff[li]/2); li=li+1 }
220 tlen[6]=o-toff[6]; o=align4(o); tcs[6]=csum(buf,toff[6],o-toff[6])
221
222 o=align4(o); toff[7]=o
223 o=w32(buf,o,MAGIC_MAGIC_65536); o=w16(buf,o,NG); o=w16(buf,o,20); o=w16(buf,o,3)
224 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)
225 tlen[7]=o-toff[7]; o=align4(o); tcs[7]=csum(buf,toff[7],o-toff[7])
226
227 o=align4(o); toff[8]=o
228 o=w16(buf,o,0); o=w16(buf,o,4); o=w16(buf,o,54)
229 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,20);o=w16(buf,o,0)
230 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,20)
231 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,20);o=w16(buf,o,0)
232 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,18);o=w16(buf,o,34)
233 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,67);o=w8(buf,o,0);o=w8(buf,o,117);o=w8(buf,o,0);o=w8(buf,o,114);o=w8(buf,o,0);o=w8(buf,o,118)
234 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)
235 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,67);o=w8(buf,o,0);o=w8(buf,o,117);o=w8(buf,o,0);o=w8(buf,o,114);o=w8(buf,o,0);o=w8(buf,o,118)
236 tlen[8]=o-toff[8]; o=align4(o); tcs[8]=csum(buf,toff[8],o-toff[8])
237
238 o=align4(o); toff[9]=o
239 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)
240 tlen[9]=o-toff[9]; o=align4(o); tcs[9]=csum(buf,toff[9],o-toff[9])
241
242 let total: i64=o
243 var hh: i64=0
244 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)
245 let tags: *u8="OS/2cmapglyfheadhheahmtxlocamaxpnamepost" as *u8
246 var d: i64=12; var ti: i64=0
247 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]
248 w32(buf,d+4,tcs[ti]); w32(buf,d+8,toff[ti]); w32(buf,d+12,tlen[ti]); d=d+16; ti=ti+1 }
249 let fsum0: i64=csum(buf,0,total)
250 var adj: i64=MAGIC_ADJ - fsum0
251 if adj<0 { adj=adj+MAGIC_MAGIC_4294967296 }
252 w32(buf, toff[3]+8, adj)
253
254 let fd: i64=sys_openat_wr("web_assets/nishi_curve.ttf" as *u8, 420)
255 if fd<0 { sw("SAVE-FAIL\n" as *u8); return 6 }
256 sys_write(fd, buf, total); sys_close(fd)
257 let fs: i64=csum(buf,0,total)
258 sw("nx_font_curve: quadratic-Bezier rings -> O Q 0 + Korean ieung -> web_assets/nishi_curve.ttf (" as *u8); sn(total); sw("B) self-check=" as *u8)
259 if fs==MAGIC_ADJ { sw("PASS\n" as *u8) } else { sw("FAIL\n" as *u8) }
260
261 let b64: *u8=sys_mmap(MAGIC_MAGIC_65536)
262 b64_encode(buf, total, b64)
263 let pg: *u8=sys_mmap(MAGIC_MAGIC_131072)
264 var po: i64=0
265 po=he_puts(pg,po,"<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"color-scheme\" content=\"dark\"><title>Nishi Curve — quadratic Beziers</title><style>@font-face{font-family:'Nishi Curve';src:url(data:font/ttf;base64," as *u8)
266 po=he_puts(pg,po,b64)
267 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%,#1d1226,#0A0A0F);color:#F5F5FA;font-family:ui-sans-serif,system-ui,sans-serif}.wrap{text-align:center;padding:32px;max-width:1000px}.tag{display:inline-block;font-size:.8rem;letter-spacing:.2em;text-transform:uppercase;color:#9EA0B8;margin-bottom:18px}.big{font-family:'Nishi Curve';font-size:clamp(3.4rem,15vw,9rem);line-height:1.15;letter-spacing:.12em;background:linear-gradient(100deg,#FF7A59,#8A7BFF);-webkit-background-clip:text;background-clip:text;color:transparent}.lbl{font-size:.72rem;letter-spacing:.16em;text-transform:uppercase;color:#6f7290;margin:2px 0 18px}.note{color:#9EA0B8;max-width:54ch;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)
268 po=he_puts(pg,po,"<span class=\"tag\">★ curved strokes — real quadratic Beziers</span>" as *u8)
269 po=he_puts(pg,po,"<div class=\"big\">O Q 0</div><div class=\"lbl\">Latin + digit — round bowls</div>" as *u8)
270 po=he_puts(pg,po,"<div class=\"big\">ㅇ</div><div class=\"lbl\">Korean — ieung (a circle)</div>" as *u8)
271 po=he_puts(pg,po,"<p class=\"note\">These rings are real TrueType <b>quadratic Beziers</b> — off-curve control points, not straight-line approximations. Each is an outer ellipse plus an inner one, so nonzero-winding leaves the counter hollow. With this the stroke toolkit is complete: <span class=\"mono\">horizontal + vertical + diagonal + curve</span> — every shape a letter needs, in one sovereign self-validated .ttf.</p>" as *u8)
272 po=he_puts(pg,po,"</div></body></html>" as *u8)
273 let pfd: i64=sys_openat_wr("web_assets/nishi_curve.html" as *u8, 420)
274 if pfd<0 { sw("PAGE-FAIL\n" as *u8); return 7 }
275 sys_write(pfd, pg, po); sys_close(pfd)
276 sw(" -> web_assets/nishi_curve.html (open to SEE curved O Q 0 + Korean circle)\n" as *u8)
277 return 0
278}