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}