code wiki / _hdl_build / nx_cdmap_export_gate.nx
nx_cdmap_export_gate.nx source
↩ module page · 269 lines · 20725 B
1// nx_cdmap_export_gate.nx -- referee for the position map's EXPORT (datavis DV6, nx_cdmap_raster_lib, 2026-09-15).
2// A planted page carries a :root token block and a small cdmap svg with one of every construct the emitter writes
3// (frame rect with stroke, dashed gridline, momentum polyline with the arrow marker, filled polygon, a bubble with a
4// translucent fill and a momentum ring, a bold haloed label, anchored axis titles, a -90 rotated title, a cdnote group,
5// an uppercase muted quadrant word, a rotation the subset does not draw, and an element outside the subset). The gate
6// composes the library IN-PROCESS and holds the render to the pixels: the token colours land where the elements are,
7// the translucent bubble blends by the declared opacity, the dash is ON where the phase says on and OFF where it says
8// off, the arrow tip is ink, the labels leave dark ink in their boxes, the rotated title leaves ink in a vertical band,
9// and every count on the stats record equals the fixture's census. Both files are then written through rs_export and
10// read back (PNG signature and IHDR dimensions; SVG namespace present and every colour variable resolved). Negative
11// controls drive the named refusals (no figure, no tokens, no font, a scale out of range) and a determinism tooth
12// renders twice. When the live search page is on disk it is exported to /tmp and announced through gv_kv, never
13// gv_need. license_tier: ORIGINAL
14import "nx_cdmap_raster_lib.nx"
15import "nx_syscalls.nx"
16import "nx_gate_verdict.nx"
17
18const G_DIR: *u8 = "/tmp/nx_cdmap_export_gate/"
19const G_PAGE: *u8 = "/tmp/nx_cdmap_export_gate/page.html"
20const G_NOSVG: *u8 = "/tmp/nx_cdmap_export_gate/nosvg.html"
21const G_NOROOT: *u8 = "/tmp/nx_cdmap_export_gate/noroot.html"
22const G_SVG: *u8 = "/tmp/nx_cdmap_export_gate/cdmap.svg"
23const G_PNG: *u8 = "/tmp/nx_cdmap_export_gate/cdmap.png"
24const G_LIVE_SVG: *u8 = "/tmp/nx_cdmap_export_gate/live.svg"
25const G_LIVE_PNG: *u8 = "/tmp/nx_cdmap_export_gate/live.png"
26const G_LIVE_PAGE: *u8 = "sites/nishifamily/compare/search/index.html"
27const G_NOFONT: *u8 = "/tmp/nx_cdmap_export_gate/absent.ttf"
28const G_FONT_NAS: *u8 = "knowledge/fonts/LiberationSans-Regular.ttf"
29const G_FONT_NAS_BOLD: *u8 = "knowledge/fonts/LiberationSans-Bold.ttf"
30const G_FONT_UP: *u8 = "../knowledge/fonts/LiberationSans-Regular.ttf"
31const G_FONT_UP_BOLD: *u8 = "../knowledge/fonts/LiberationSans-Bold.ttf"
32const G_FONT_LAPTOP: *u8 = "web_assets/fonts/LiberationSans-Regular.ttf"
33const G_FONT_LAPTOP_BOLD: *u8 = "web_assets/fonts/LiberationSans-Bold.ttf"
34const G_MODE_644: i64 = 420
35const G_DIR_MODE: i64 = 493
36const G_I64: i64 = 8
37const G_SCALE: i64 = 2
38const G_VB_W: i64 = 200
39const G_VB_H: i64 = 120
40const G_CH_HASH: i64 = 35
41const G_TOL: i64 = 2 // channel tolerance for a blended pixel (integer rounding)
42const G_DARK_MAX_R: i64 = 120 // a label pixel darker than this red channel is ink
43const G_PNG_SIG0: i64 = 137
44const G_PNG_SIG1: i64 = 80
45const G_PNG_W_OFF: i64 = 16
46const G_PNG_H_OFF: i64 = 20
47const G_BYTE: i64 = 256
48// the fixture's declared tokens
49const G_BG_R: i64 = 243
50const G_BG_G: i64 = 241
51const G_BG_B: i64 = 236
52const G_PANEL_R: i64 = 249
53const G_PANEL_G: i64 = 247
54const G_PANEL_B: i64 = 243
55const G_MUT_R: i64 = 92
56const G_MUT_G: i64 = 96
57const G_MUT_B: i64 = 104
58const G_BUB_R: i64 = 86
59const G_BUB_G: i64 = 180
60const G_BUB_B: i64 = 233
61const G_BUB_A: i64 = 820
62const G_RING_R: i64 = 40
63const G_RING_G: i64 = 160
64const G_RING_B: i64 = 90
65const G_POLY_R: i64 = 230
66const G_POLY_G: i64 = 159
67const G_POLY_B: i64 = 0
68const G_PERMIL: i64 = 1000
69
70const G_HEAD: *u8 = "<html><head><style>\n:root{\n --nx-color-bg: rgb(243,241,236);\n --nx-color-fg: rgb(26,26,28);\n --nx-color-panel: rgb(249,247,243);\n --nx-color-line: rgb(219,216,208);\n --nx-color-mut: rgb(92,96,104);\n --nx-color-accent: rgb(88,64,180);\n --nx-font-mono: ui-monospace;\n}\n@media (prefers-color-scheme: dark){:root{--nx-color-bg: rgb(0,0,0);}}\n</style></head><body><p>before</p>\n<figure class='cdfig'><svg class='cdmap' viewBox='0 0 200 120' width='100%' font-family='system-ui'>\n<title id='cdmap-title'>fixture</title><desc id='cdmap-desc'>a planted map</desc>\n<defs><marker id='cdarrow' viewBox='0 0 8 8' refX='7' refY='4'><path d='M0,0 L8,4 L0,8 z' fill='var(--nx-color-mut)'/></marker></defs>\n<rect x='20' y='10' width='160' height='90' rx='6' fill='var(--nx-color-panel)' stroke='var(--nx-color-line)'/>\n<line x1='20' y1='55' x2='180' y2='55' stroke='var(--nx-color-mut)' stroke-dasharray='6 4'/>\n<polygon points='150,70 170,70 170,90 150,90' fill='rgb(230,159,0)' fill-opacity='1'/>\n<polyline data-p='1' points='60,80 90,50' fill='none' stroke='rgb(40,160,90)' stroke-dasharray='4 3' marker-end='url("
71const G_TAIL: *u8 = "cdarrow)'/>\n<circle cx='100' cy='40' r='10' data-p='1' fill='rgb(86,180,233)' fill-opacity='0.82' stroke='rgb(40,160,90)' stroke-width='2'><title>Nishi 700 300</title></circle>\n<text x='112' y='44' data-p='1' font-size='13' fill='var(--nx-color-fg)' style='paint-order:stroke;stroke:var(--nx-color-bg);stroke-width:3px;stroke-linejoin:round' font-weight='700'>Nishi</text>\n<text x='100' y='114' fill='var(--nx-color-mut)' font-size='12' text-anchor='middle'>Centrality 700–1000 · up →</text>\n<text x='8' y='55' fill='var(--nx-color-mut)' font-size='12' text-anchor='middle' transform='rotate(-90 8 55)'>Distinct</text>\n<g class='cdnote'><title>note n1</title><rect x='120' y='12' width='50' height='20' rx='4' fill='var(--nx-color-panel)' stroke='var(--nx-color-mut)' stroke-dasharray='2 2'/>\n<text x='125' y='26' fill='var(--nx-color-fg)' font-size='11'>bright & loud</text>\n</g>\n<text x='30' y='30' fill='var(--nx-color-mut)' font-size='11' text-anchor='middle' opacity='0.75' style='letter-spacing:.12em;text-transform:uppercase'>tail</text>\n<text x='5' y='112' fill='var(--nx-color-fg)' font-size='12' transform='rotate(45 5 112)'>skew</text>\n<ellipse cx='5' cy='5' rx='2' ry='2' fill='var(--nx-color-fg)'/>\n</svg><figcaption>cap</figcaption></figure><p>after</p></body></html>\n"
72const G_NOSVG_BODY: *u8 = "<html><head><style>:root{--nx-color-bg: rgb(1,2,3);}</style></head><body><svg viewBox='0 0 10 10'><rect x='0' y='0' width='1' height='1'/></svg></body></html>\n"
73const G_NOROOT_BODY: *u8 = "<html><body><svg class='cdmap' viewBox='0 0 10 10'><rect x='0' y='0' width='1' height='1' fill='rgb(1,2,3)'/></svg></body></html>\n"
74
75func g_write(path: *u8, a: *u8, b: *u8, hash: i64) -> i64 {
76 let fd: i64 = sys_openat_wr(path, G_MODE_644)
77 if fd < 0 { return 0 - 1 }
78 sys_write(fd, a, rs_slen(a))
79 if hash == 1 { let t: *u8 = sys_mmap(2); t[0] = G_CH_HASH as u8; sys_write(fd, t, 1) }
80 if (b as i64) != 0 { sys_write(fd, b, rs_slen(b)) }
81 sys_close(fd)
82 return 0
83}
84func g_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 }
85func g_px(fb: *i64, W: i64, x: i64, y: i64, rgb: *i64) -> i64 { return rs_fb_get(fb, W, x, y, rgb) }
86func g_near(v: i64, want: i64) -> i64 { if rs_abs(v - want) <= G_TOL { return 1 } return 0 }
87func g_is(fb: *i64, W: i64, x: i64, y: i64, r: i64, g: i64, b: i64) -> i64 {
88 let rgb: *i64 = rs_i64(3)
89 g_px(fb, W, x, y, rgb)
90 let ok: i64 = g_near(rgb[0], r) * g_near(rgb[1], g) * g_near(rgb[2], b)
91 sys_munmap(rgb as *u8, 3 * G_I64)
92 return ok
93}
94// dark pixels (red channel under the ink bar) inside a canvas box, in scaled px
95func g_dark_in(fb: *i64, W: i64, H: i64, x0: i64, y0: i64, x1: i64, y1: i64) -> i64 {
96 let rgb: *i64 = rs_i64(3)
97 var c: i64 = 0
98 var y: i64 = y0
99 while y < y1 { var x: i64 = x0; while x < x1 { if x >= 0 { if y >= 0 { if x < W { if y < H { g_px(fb, W, x, y, rgb); if rgb[0] < G_DARK_MAX_R { c = c + 1 } } } } } x = x + 1 } y = y + 1 }
100 sys_munmap(rgb as *u8, 3 * G_I64)
101 return c
102}
103func g_fb_sum(fb: *i64, n: i64) -> i64 { var s: i64 = 0; var i: i64 = 0; while i < n { s = s + fb[i] % 1000003; i = i + 1 } return s }
104func g_has(buf: *u8, n: i64, needle: *u8) -> i64 { if cr_find(buf, 0, n, needle) != CR_ABSENT { return 1 } return 0 }
105func g_be32(b: *u8, o: i64) -> i64 { return (b[o] as i64) * G_BYTE * G_BYTE * G_BYTE + (b[o + 1] as i64) * G_BYTE * G_BYTE + (b[o + 2] as i64) * G_BYTE + (b[o + 3] as i64) }
106
107func main() -> i64 {
108 let ctr: *i64 = gv_ctr()
109 gv_head("=== NX-CDMAP EXPORT GATE -- the position map's SVG and PNG twins: token colours on the pixels, opacity blends, dash phase, the arrow, label and rotated ink, the census, both files read back, the named refusals ===" as *u8)
110 sys_mkdir(G_DIR, G_DIR_MODE)
111 gv_check("T0-fixture-page-written" as *u8, (g_write(G_PAGE, G_HEAD, G_TAIL, 1) == 0) as i64, ctr)
112 g_write(G_NOSVG, G_NOSVG_BODY, 0 as *u8, 0)
113 g_write(G_NOROOT, G_NOROOT_BODY, 0 as *u8, 0)
114 var freg: *u8 = G_FONT_NAS
115 var fbold: *u8 = G_FONT_NAS_BOLD
116 if g_exists(freg) == 0 { freg = G_FONT_UP; fbold = G_FONT_UP_BOLD }
117 if g_exists(freg) == 0 { freg = G_FONT_LAPTOP; fbold = G_FONT_LAPTOP_BOLD }
118 gv_check("T1-a-liberation-face-opens-at-one-of-its-three-homes" as *u8, g_exists(freg), ctr)
119 // ---- in-process render of the fixture ----
120 let nb: *i64 = rs_i64(2)
121 nb[0] = 0
122 let h: *u8 = sys_read_file(G_PAGE, nb)
123 let n: i64 = nb[0]
124 let s0: i64 = cr_find(h, 0, n, RS_SVG_OPEN)
125 let s1: i64 = cr_find(h, s0, n, RS_SVG_CLOSE)
126 gv_check("T2-fixture-carries-the-cdmap-figure" as *u8, (s0 != CR_ABSENT) as i64 * (s1 != CR_ABSENT) as i64, ctr)
127 let names: *u8 = sys_mmap(RS_TOK_MAX * RS_TOK_NAME_W)
128 let recs: *i64 = rs_i64(RS_TOK_MAX * RS_TOK_REC)
129 let ntok: i64 = rs_theme_load(h, n, names, recs)
130 gv_check_eq("T3-six-colour-tokens-read-from-the-first-root-block" as *u8, ntok, 6, ctr)
131 let rgb: *i64 = rs_i64(3)
132 gv_check("T3a-panel-token-resolves-to-its-rgb" as *u8, rs_tok_rgb(names, recs, ntok, "panel" as *u8, rgb) * g_near(rgb[0], G_PANEL_R) * g_near(rgb[2], G_PANEL_B), ctr)
133 gv_check("T3b-NEG-dark-theme-bg-does-not-override-the-first-block" as *u8, rs_tok_rgb(names, recs, ntok, "bg" as *u8, rgb) * (rgb[0] == G_BG_R) as i64, ctr)
134 let wh: *i64 = rs_i64(2)
135 gv_check_eq("T4-viewbox-read" as *u8, rs_viewbox(h, s0, s1, wh) * 1000 + wh[0] + wh[1] * 0, 1000 + G_VB_W, ctr)
136 let fhr: *i64 = tf_load(freg)
137 var fhb: *i64 = tf_load(fbold)
138 if (fhb as i64) == 0 { fhb = fhr }
139 gv_check("T5-faces-loaded" as *u8, ((fhr as i64) != 0) as i64, ctr)
140 let W: i64 = G_VB_W * G_SCALE
141 let H: i64 = G_VB_H * G_SCALE
142 let fb: *i64 = rs_i64(W * H)
143 let stats: *i64 = rs_i64(RS_S_SLOTS)
144 rs_render(h, s0, s1, names, recs, ntok, fhr, fhb, G_SCALE, fb, W, H, stats)
145 gv_check_eq("T6-census-rect" as *u8, stats[RS_S_RECT], 2, ctr)
146 gv_check_eq("T6a-census-circle" as *u8, stats[RS_S_CIRCLE], 1, ctr)
147 gv_check_eq("T6b-census-line" as *u8, stats[RS_S_LINE], 1, ctr)
148 gv_check_eq("T6c-census-polyline" as *u8, stats[RS_S_POLYLINE], 1, ctr)
149 gv_check_eq("T6d-census-polygon" as *u8, stats[RS_S_POLYGON], 1, ctr)
150 gv_check_eq("T6e-census-text" as *u8, stats[RS_S_TEXT], 6, ctr)
151 gv_check_eq("T6f-census-notes" as *u8, stats[RS_S_NOTES], 1, ctr)
152 gv_check_eq("T6g-census-arrows" as *u8, stats[RS_S_ARROWS], 1, ctr)
153 gv_check_eq("T6h-census-rotated-drawn" as *u8, stats[RS_S_ROTATED], 1, ctr)
154 gv_check_eq("T6i-census-rotated-skipped-named" as *u8, stats[RS_S_ROTSKIP], 1, ctr)
155 gv_check_eq("T6j-census-skipped-is-the-ellipse-alone" as *u8, stats[RS_S_SKIPPED], 1, ctr)
156 gv_check_eq("T6k-NEG-no-unresolved-colours-on-a-page-that-declares-every-token" as *u8, stats[RS_S_UNRESOLVED], 0, ctr)
157 gv_check("T6l-glyphs-and-ink-are-scale-fields-above-zero" as *u8, (stats[RS_S_GLYPHS] > 0) as i64 * (stats[RS_S_INK] > 0) as i64, ctr)
158 // ---- pixels ----
159 gv_check("T7-canvas-corner-is-the-bg-token" as *u8, g_is(fb, W, 2, 2, G_BG_R, G_BG_G, G_BG_B), ctr)
160 gv_check("T7a-inside-the-frame-is-the-panel-token" as *u8, g_is(fb, W, 60 * G_SCALE, 20 * G_SCALE, G_PANEL_R, G_PANEL_G, G_PANEL_B), ctr)
161 let br: i64 = (G_PANEL_R * (G_PERMIL - G_BUB_A) + G_BUB_R * G_BUB_A) / G_PERMIL
162 let bg2: i64 = (G_PANEL_G * (G_PERMIL - G_BUB_A) + G_BUB_G * G_BUB_A) / G_PERMIL
163 let bb: i64 = (G_PANEL_B * (G_PERMIL - G_BUB_A) + G_BUB_B * G_BUB_A) / G_PERMIL
164 gv_check("T8-bubble-centre-blends-by-its-fill-opacity-over-the-panel" as *u8, g_is(fb, W, 100 * G_SCALE, 40 * G_SCALE, br, bg2, bb), ctr)
165 gv_check("T8a-bubble-ring-is-the-momentum-stroke" as *u8, g_is(fb, W, 110 * G_SCALE, 40 * G_SCALE, G_RING_R, G_RING_G, G_RING_B), ctr)
166 // the dashed gridline: from x=20 with on 6 off 4 (canvas px) -> len 4 is ON, len 14 is OFF
167 gv_check("T9-dash-phase-on-is-the-muted-stroke" as *u8, g_is(fb, W, 24 * G_SCALE, 55 * G_SCALE, G_MUT_R, G_MUT_G, G_MUT_B), ctr)
168 // the pattern is 6 on 4 off in CANVAS px: canvas x=27 is 7 px along, inside the first off run (6..10) with a 1 px stamp margin either side
169 gv_check("T9a-NEG-dash-phase-off-shows-the-panel-under-it" as *u8, g_is(fb, W, 27 * G_SCALE, 55 * G_SCALE, G_PANEL_R, G_PANEL_G, G_PANEL_B), ctr)
170 gv_check("T10-polygon-interior-is-its-literal-rgb" as *u8, g_is(fb, W, 160 * G_SCALE, 80 * G_SCALE, G_POLY_R, G_POLY_G, G_POLY_B), ctr)
171 gv_check("T11-arrow-tip-is-ink-of-the-path-colour" as *u8, g_is(fb, W, 90 * G_SCALE - 1, 50 * G_SCALE + 1, G_RING_R, G_RING_G, G_RING_B), ctr)
172 let lab_ink: i64 = g_dark_in(fb, W, H, 112 * G_SCALE, 33 * G_SCALE, 150 * G_SCALE, 46 * G_SCALE)
173 gv_check("T12-bold-label-leaves-dark-ink-in-its-box" as *u8, (lab_ink > 0) as i64, ctr)
174 let rot_ink: i64 = g_dark_in(fb, W, H, 1 * G_SCALE, 25 * G_SCALE, 15 * G_SCALE, 85 * G_SCALE)
175 gv_check("T13-rotated-title-leaves-ink-in-a-vertical-band" as *u8, (rot_ink > 0) as i64, ctr)
176 // the band sits above the dashed gridline at y=55 so only a wrongly flat title could leave ink in it
177 let rot_none: i64 = g_dark_in(fb, W, H, 16 * G_SCALE, 44 * G_SCALE, 60 * G_SCALE, 50 * G_SCALE)
178 gv_check("T13a-NEG-rotated-title-leaves-no-horizontal-run-right-of-its-anchor" as *u8, (rot_none == 0) as i64, ctr)
179 let note_ink: i64 = g_dark_in(fb, W, H, 122 * G_SCALE, 14 * G_SCALE, 168 * G_SCALE, 31 * G_SCALE)
180 gv_check("T14-note-line-leaves-ink-inside-its-box" as *u8, (note_ink > 0) as i64, ctr)
181 // the bottom-left corner: left of the centred x-axis title, below the rotated y-axis title's band
182 let skew_ink: i64 = g_dark_in(fb, W, H, 3 * G_SCALE, 100 * G_SCALE, 30 * G_SCALE, 118 * G_SCALE)
183 gv_check("T14a-NEG-a-45-degree-text-is-skipped-not-drawn-flat" as *u8, (skew_ink == 0) as i64, ctr)
184 // ---- unit teeth on the text helpers ----
185 let t: *u8 = sys_mmap(32)
186 let src: *u8 = "bright & loud <x>" as *u8
187 rs_unescape(src, 0, rs_slen(src), t, 32)
188 gv_check("T15-entities-unescaped" as *u8, cr_streq(t, "bright & loud <x>" as *u8), ctr)
189 let u: *u8 = sys_mmap(8)
190 u[0] = 116 as u8; u[1] = 97 as u8; u[2] = 105 as u8; u[3] = 108 as u8; u[4] = 0 as u8
191 rs_upper(u)
192 gv_check("T15a-uppercase-transform-applied" as *u8, cr_streq(u, "TAIL" as *u8), ctr)
193 // ---- determinism ----
194 let fb2: *i64 = rs_i64(W * H)
195 let stats2: *i64 = rs_i64(RS_S_SLOTS)
196 rs_render(h, s0, s1, names, recs, ntok, fhr, fhb, G_SCALE, fb2, W, H, stats2)
197 gv_check_eq("T16-two-renders-agree-pixel-for-pixel" as *u8, g_fb_sum(fb2, W * H), g_fb_sum(fb, W * H), ctr)
198 // ---- the one-call export and the files read back ----
199 let out: *i64 = rs_i64(2)
200 let st3: *i64 = rs_i64(RS_S_SLOTS)
201 let rc: i64 = rs_export(G_PAGE, G_SVG, G_PNG, freg, fbold, G_SCALE, st3, out)
202 gv_check_eq("T17-export-returns-OK" as *u8, rc, RS_OK, ctr)
203 gv_check("T17a-both-files-report-bytes" as *u8, (out[0] > 0) as i64 * (out[1] > 0) as i64, ctr)
204 let pb: *i64 = rs_i64(2)
205 pb[0] = 0
206 let png: *u8 = sys_read_file(G_PNG, pb)
207 gv_check_eq("T18-png-bytes-on-disk-equal-the-receipt" as *u8, pb[0], out[1], ctr)
208 gv_check("T18a-png-signature" as *u8, ((png[0] as i64) == G_PNG_SIG0) as i64 * ((png[1] as i64) == G_PNG_SIG1) as i64, ctr)
209 gv_check_eq("T18b-png-width-is-the-viewbox-times-the-scale" as *u8, g_be32(png, G_PNG_W_OFF), W, ctr)
210 gv_check_eq("T18c-png-height-is-the-viewbox-times-the-scale" as *u8, g_be32(png, G_PNG_H_OFF), H, ctr)
211 let sb: *i64 = rs_i64(2)
212 sb[0] = 0
213 let svg: *u8 = sys_read_file(G_SVG, sb)
214 let sn: i64 = sb[0]
215 gv_check_eq("T19-svg-bytes-on-disk-equal-the-receipt" as *u8, sn, out[0], ctr)
216 gv_check("T19a-svg-starts-with-the-xml-declaration" as *u8, (cr_find(svg, 0, sn, "<?xml" as *u8) == 0) as i64, ctr)
217 gv_check("T19b-svg-declares-its-namespace" as *u8, g_has(svg, sn, RS_SVG_NS), ctr)
218 gv_check("T19c-NEG-no-colour-variable-survives-in-the-standalone-svg" as *u8, (g_has(svg, sn, RS_VAR_OPEN) == 0) as i64, ctr)
219 gv_check("T19d-panel-token-resolved-to-its-rgb-in-the-svg" as *u8, g_has(svg, sn, "rgb(249,247,243)" as *u8), ctr)
220 gv_check("T19e-svg-ends-with-the-closer" as *u8, (cr_rfind(svg, 0, sn, RS_SVG_CLOSE) == sn - rs_slen(RS_SVG_CLOSE) - 1) as i64, ctr)
221 gv_check("T19f-svg-keeps-the-marker-definition" as *u8, g_has(svg, sn, "id='cdarrow'" as *u8), ctr)
222 // the twin is XML: a named HTML entity is a parse error there, so every one the emitter uses is rewritten numerically
223 gv_check("T19g-NEG-no-named-html-entity-survives-in-the-svg" as *u8, ((g_has(svg, sn, "–" as *u8) + g_has(svg, sn, "·" as *u8) + g_has(svg, sn, "→" as *u8)) == 0) as i64, ctr)
224 gv_check("T19h-entities-rewritten-as-numeric-references" as *u8, g_has(svg, sn, "–" as *u8) * g_has(svg, sn, "·" as *u8) * g_has(svg, sn, "→" as *u8), ctr)
225 gv_check("T19i-the-xml-builtin-amp-is-kept-as-itself" as *u8, g_has(svg, sn, "&" as *u8), ctr)
226 let t2: *u8 = sys_mmap(48)
227 let src2: *u8 = "a–b—c·d→e f" as *u8
228 rs_unescape(src2, 0, rs_slen(src2), t2, 48)
229 gv_check("T15b-typographic-entities-drawn-as-their-nearest-ascii" as *u8, cr_streq(t2, "a-b-c.d->e f" as *u8), ctr)
230 // ---- the named refusals ----
231 gv_check_eq("T20-NEG-a-page-without-the-cdmap-figure-is-refused-by-name" as *u8, rs_export(G_NOSVG, G_SVG, G_PNG, freg, fbold, G_SCALE, st3, out), RS_E_NOSVG, ctr)
232 gv_check_eq("T20a-NEG-a-page-without-root-tokens-is-refused-by-name" as *u8, rs_export(G_NOROOT, G_SVG, G_PNG, freg, fbold, G_SCALE, st3, out), RS_E_NOTOKENS, ctr)
233 gv_check_eq("T20b-NEG-an-absent-face-is-refused-by-name" as *u8, rs_export(G_PAGE, G_SVG, G_PNG, G_NOFONT, G_NOFONT, G_SCALE, st3, out), RS_E_NOFONT, ctr)
234 gv_check_eq("T20c-NEG-a-zero-scale-is-refused-by-name" as *u8, rs_export(G_PAGE, G_SVG, G_PNG, freg, fbold, 0, st3, out), RS_E_SCALE, ctr)
235 gv_check_eq("T20d-NEG-a-scale-past-the-bound-is-refused-by-name" as *u8, rs_export(G_PAGE, G_SVG, G_PNG, freg, fbold, RS_SCALE_MAX + 1, st3, out), RS_E_SCALE, ctr)
236 gv_check_eq("T20e-NEG-an-absent-page-is-refused-by-name" as *u8, rs_export("/tmp/nx_cdmap_export_gate/nothing.html" as *u8, G_SVG, G_PNG, freg, fbold, G_SCALE, st3, out), RS_E_NOPAGE, ctr)
237 // ---- the live page, when it is on disk: announced, never required ----
238 var live_present: i64 = 0
239 var live_rc: i64 = 0 - 99
240 var live_circles: i64 = 0 - 1
241 var live_png: i64 = 0 - 1
242 var live_unres: i64 = 0 - 1
243 if g_exists(G_LIVE_PAGE) == 1 {
244 live_present = 1
245 let st4: *i64 = rs_i64(RS_S_SLOTS)
246 let out4: *i64 = rs_i64(2)
247 live_rc = rs_export(G_LIVE_PAGE, G_LIVE_SVG, G_LIVE_PNG, freg, fbold, G_SCALE, st4, out4)
248 live_circles = st4[RS_S_CIRCLE]
249 live_png = out4[1]
250 live_unres = st4[RS_S_UNRESOLVED]
251 gv_check_eq("T21-live-search-page-exports-OK" as *u8, live_rc, RS_OK, ctr)
252 gv_check("T21a-live-page-carries-marks" as *u8, (live_circles > 0) as i64, ctr)
253 gv_check_eq("T21b-live-page-resolves-every-colour" as *u8, live_unres, 0, ctr)
254 }
255 gv_values_head()
256 gv_kv("fixture_glyphs" as *u8, stats[RS_S_GLYPHS])
257 gv_kv("fixture_ink_px" as *u8, stats[RS_S_INK])
258 gv_kv("label_ink_px" as *u8, lab_ink)
259 gv_kv("rotated_ink_px" as *u8, rot_ink)
260 gv_kv("note_ink_px" as *u8, note_ink)
261 gv_kv("svg_bytes" as *u8, out[0])
262 gv_kv("png_bytes" as *u8, out[1])
263 gv_kv("live_present" as *u8, live_present)
264 gv_kv("live_rc" as *u8, live_rc)
265 gv_kv("live_circles" as *u8, live_circles)
266 gv_kv("live_png_bytes" as *u8, live_png)
267 gv_kv("live_unresolved_colors" as *u8, live_unres)
268 return gv_verdict("nx_cdmap_export_gate" as *u8, ctr, "the position map's SVG and PNG twins: colours, blends, dash phase, arrow, label and rotated ink on the pixels, the census, both files read back, the named refusals, and the live page when on disk" as *u8)
269}