code wiki / _hdl_build / nx_browser_render_shot.nx
nx_browser_render_shot.nx source
↩ module page · 106 lines · 6005 B
1// nx_browser_render_shot.nx -- R2 proof probe for the CSS-on-native-Windows arc. Reads an HTML file,
2// runs the EXTRACTED render organ (br_layout -> CSS cascade + box layout; br_shot_png -> RGBA framebuffer
3// paint -> PNG), prints box-count + page_h. Verifies nx_browser_render.nx is correct (WSL run) and, built
4// through a keystone, that the CSS layout+paint runs as a native Windows PE. NOT shipped -- a render gate.
5import "nx_browser_render.nx"
6
7func rs_puts(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
8func rs_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p }
9// load Liberation Sans and Serif in all four styles from <dir>; returns the number of slots registered
10// (0 when even the regular sans is absent). Slot order = font_face_pick: +1 bold, +2 italic, +4 serif.
11func rs_load_family(dir: *u8) -> i64 {
12 let path: *u8 = sys_mmap(512)
13 var got: i64 = 0
14 var idx: i64 = 0
15 while idx < 8 {
16 var o: i64 = rs_cat(path, 0, dir)
17 if idx >= 4 { o = rs_cat(path, o, "LiberationSerif-\x00" as *u8) } else { o = rs_cat(path, o, "LiberationSans-\x00" as *u8) }
18 let bi: i64 = idx % 4
19 if bi == 0 { o = rs_cat(path, o, "Regular.ttf\x00" as *u8) }
20 if bi == 1 { o = rs_cat(path, o, "Bold.ttf\x00" as *u8) }
21 if bi == 2 { o = rs_cat(path, o, "Italic.ttf\x00" as *u8) }
22 if bi == 3 { o = rs_cat(path, o, "BoldItalic.ttf\x00" as *u8) }
23 if br_face_load_idx(idx, path) == 1 { got = got + 1 } else { if idx == 0 { return 0 } }
24 idx = idx + 1
25 }
26 br_face_select(0)
27 return got
28}
29func rs_pn(v: i64) -> i64 {
30 var n: i64 = v
31 if n < 0 { sys_write(1, "-\x00" as *u8, 1); n = 0 - n }
32 let b: *u8 = sys_mmap(24)
33 var k: i64 = 0
34 if n == 0 { b[0] = 48 as u8; k = 1 } else { while n > 0 { b[k] = (48 + (n%10)) as u8; n = n/10; k = k+1 } }
35 let o: *u8 = sys_mmap(24)
36 var j: i64 = 0
37 while j < k { o[j] = b[k-1-j]; j = j+1 }
38 sys_write(1, o, k)
39 return 0
40}
41
42// argv[1] (optional) = layout viewport width, default 960. The GUI lays out at 1024 and this
43// tool hardcoded 960 -- with 218 @media blocks in a real stylesheet (breakpoints 640/816/980),
44// those two widths select DIFFERENT CSS, so the same bytes legitimately produce different
45// layouts. Make the width a parameter so the viewport can be measured as a variable instead of
46// being an invisible difference between two instruments.
47func rs_atoi(s: *u8) -> i64 {
48 var v: i64 = 0
49 var i: i64 = 0
50 while s[i] != (0 as u8) {
51 let c: i64 = s[i] & 0xff
52 if c < 48 { return 0 }
53 if c > 57 { return 0 }
54 v = v * 10 + (c - 48)
55 i = i + 1
56 }
57 return v
58}
59
60func main(argc: i64, argv: *i64) -> i64 {
61 var VW: i64 = 960
62 if argc >= 2 { let w: i64 = rs_atoi(argv[1] as *u8); if w > 0 { VW = w } }
63 let lp: *i64 = sys_mmap(16) as *i64
64 let html: *u8 = sys_read_file("_offc/nxhtml.html\x00" as *u8, lp)
65 let hlen: i64 = lp[0]
66 if hlen <= 0 { sys_write(2, "render_shot: no _offc/nxhtml.html\n\x00" as *u8, 33); return 1 }
67
68 let page: *Page = sys_mmap(NX_PAGE_BYTES) as *Page
69 page.raw = html
70 page.raw_len = hlen
71 // MEASURE WITH THE FONT WE PAINT WITH (2026-07-27). br_render_html_page now defaults
72 // vec_headings=2 (the vector face the GUI ships), so layout MUST reserve vector widths
73 // too -- exactly as nishi_gui.nx:765 does. Painting vector while measuring bitmap makes
74 // every run overflow its reserved box, and the paint's own wrapping then SPLITS words
75 // mid-glyph at inline boundaries (`<span>span</span>` -> "spa" + "n"), which is invisible
76 // to a human skim and lethal to OCR. Measure and paint must be the same font, always.
77 // argv[2] == "novec" -> bitmap-measure mode, for A/B-ing the vec-measure x author-CSS
78 // interaction (seq1150: wiki_aug lays out 6,467px bitmap vs 2,799px vec; Chrome 7,582).
79 var vecm: i64 = 1
80 if argc >= 3 {
81 let a2: *u8 = argv[2] as *u8
82 if (a2[0]&0xff)==110 { if (a2[1]&0xff)==111 { if (a2[2]&0xff)==118 { vecm = 0 } } }
83 // "face" (2026-09-02, supersedes "oracle"): load the REAL Liberation Sans face -- hmtx advances AND
84 // outlines AND kern together, the pair the 07-29 A/B named as the only honest lever. Refuses loudly
85 // when asked for and absent: a silent fall-through would measure the stroke font under a face label.
86 if (a2[0]&0xff)==102 { if (a2[1]&0xff)==97 { if (a2[2]&0xff)==99 {
87 // BR27: the whole Liberation family -- slot = +1 bold, +2 italic, +4 serif (font_face_pick order)
88 var fl: i64 = rs_load_family("web_assets/fonts/\x00" as *u8)
89 if fl == 0 { fl = rs_load_family("knowledge/fonts/\x00" as *u8) } // the NAS landing path
90 if fl >= 1 { rs_puts(1, "render_shot: face family loaded, slots=\x00" as *u8); rs_pn(fl); rs_puts(1, " of 8 (Liberation Sans+Serif R/B/I/BI; hmtx+kern+outlines)\n\x00" as *u8) }
91 else { rs_puts(2, "render_shot: 'face' requested but LiberationSans-Regular.ttf loaded from neither web_assets/fonts/ nor knowledge/fonts/\n\x00" as *u8); return 3 }
92 } } }
93 if (a2[0]&0xff)==111 { if (a2[1]&0xff)==114 { rs_puts(2, "render_shot: 'oracle' is RETIRED (2026-09-02) -- use 'face': the same advances read from the file, with its outlines and kern\n\x00" as *u8); return 3 } }
94 }
95 nx_layout_set_vec_measure(vecm)
96 br_layout(page, VW)
97 if page.ok != 1 { sys_write(2, "render_shot: layout failed\n\x00" as *u8, 27); return 2 }
98
99 sys_write(1, "boxes=\x00" as *u8, 6); rs_pn(page.tree.count)
100 sys_write(1, " page_h=\x00" as *u8, 8); rs_pn(page.page_h)
101 sys_write(1, "\n\x00" as *u8, 1)
102
103 br_shot_png(page, "render\x00" as *u8, 6, "_offc/nxrender.png\x00" as *u8)
104 sys_write(1, "render_shot: wrote _offc/nxrender.png\n\x00" as *u8, 38)
105 return 0
106}