code wiki / _hdl_build / nx_ttf_fontlib.nx
nx_ttf_fontlib.nx
buildroot/runtime/_hdl_build/nx_ttf_fontlib.nx
about
nx_ttf_fontlib.nx -- the sovereign TrueType FONT ENGINE as a LIBRARY (the browser's text renderer).
Canonical home of the TTF read path first proven in the nx_ttf_raster demo (which has a main() and so
cannot be imported): sfnt dir -> head/cmap/loca/glyf/hhea/hmtx(+kern), cmap format-4 (BOTH the delta
path and the idRangeOffset/glyphIdArray path), glyf outline decode (flags/short/same/repeat), COMPOSITE
glyphs (component offsets + F2Dot14 scale, bounded recursion), quadratic Bezier flattening, NONZERO-
WINDING scan conversion with SSxSS supersampled ANTI-ALIASED coverage, a per-(char,size) COVERAGE CACHE
(each glyph rasterizes once per size), REAL per-glyph advances from hmtx, pair KERNING from the 'kern'
table (format 0, binary search), a mild contrast curve (stem darkening at small sizes), and an
RGBA-framebuffer ALPHA-BLEND draw (nx_framebuffer px = R,G,B,A).
2026-09-02 (BR4 first half, /compare/browser rung "Real font files + shaping"): EVERY METRIC IS NOW
READ FROM THE FONT, NEVER ASSUMED. The first cut hard-coded unitsPerEm=1000 and ascender=760 -- true
of our own emitted nishi_sans.ttf and FALSE of every professional font (Liberation Sans, Arial, Noto:
2048), so a real face would have rasterized at HALF size with its advances halved, and nothing would
have failed loudly. unitsPerEm comes from head, ascender/descender/lineGap from hhea, the cell height
is DERIVED from (ascender - descender), and the browser's 1000-em advance unit is a conversion at the
API edge (tf_adv_em1000), not an assumption inside the rasterizer. A negative left side bearing
('j', '_', 'w' in Liberation) is carried as the record's left margin so no ink is clipped, and ink
wider than the advance ('f', 'k') widens the cell instead of being cut at the advance.
Fonts proven: our own emitted web_assets/nishi_sans.ttf (96 glyphs, upem 1000) and Liberation Sans
(SIL-OFL, upem 2048, 908 kern pairs, 1,076 composite glyphs). No FreeType, no GDI, no DirectWrite.
license_tier: ORIGINAL
dependencies 2 imports · 5 importers
imports: nx_syscalls.nxnx_paint_solid_rect.nx
imported by: nx_browser_render.nxnx_browser_render_gif_t139.nxnx_cdmap_referee_lib.nxnx_font_mockup.nxnx_ttf_fontlib_gate.nx
structs
| none |
consts
| 25 | const TF_MAGIC_32768: i64 = 32768 |
| 26 | const TF_MAGIC_65536: i64 = 65536 |
| 27 | const TF_MAGIC_16777216: i64 = 16777216 |
| 28 | const TF_MAGIC_2048: i64 = 2048 |
| 29 | const TF_F2DOT14_ONE: i64 = 16384 // 1.0 in the F2Dot14 fixed-point the glyf composite scale fields use |
| 32 | const TF_H_BUF: i64 = 0 |
| 33 | const TF_H_GLYF: i64 = 1 |
| 34 | const TF_H_LOCA: i64 = 2 |
| 35 | const TF_H_LOCFMT: i64 = 3 |
| 36 | const TF_H_CMAPSUB: i64 = 4 |
| 37 | const TF_H_HMTX: i64 = 5 |
| 38 | const TF_H_NUMHM: i64 = 6 |
| 39 | const TF_H_CACHE: i64 = 7 |
| 40 | const TF_H_NSIZES: i64 = 8 |
| 41 | const TF_H_SIZE0: i64 = 9 // 9..9+TF_MAXSZ-1 = the pixel heights with a cache slot |
| 42 | const TF_MAXSZ: i64 = 16 |
| 43 | const TF_H_UPEM: i64 = 25 // head.unitsPerEm -- READ, never assumed |
| 44 | const TF_H_ASC: i64 = 26 // hhea.ascender (font units, positive up) |
| 45 | const TF_H_DESC: i64 = 27 // hhea.descender (font units, negative) |
| 46 | const TF_H_LINEGAP: i64 = 28 // hhea.lineGap |
| 47 | const TF_H_KPAIRS: i64 = 29 // byte offset of the kern format-0 pair array (0 = no horizontal kern table) |
| 48 | const TF_H_KN: i64 = 30 // number of kern pairs |
| 49 | const TF_H_LEN: i64 = 31 // byte length of the font buffer (every table offset is checked against it) |
| 50 | const TF_H_NGLYPHS: i64 = 32 // maxp.numGlyphs (bounds every gid before loca is indexed) |
| 51 | const TF_HSLOTS: i64 = 40 |
| 54 | const TF_R_COV: i64 = 0 // coverage bytes, cw x chh, row-major; row 0 = the ASCENDER line |
| 55 | const TF_R_CW: i64 = 1 // cell width px |
| 56 | const TF_R_CHH: i64 = 2 // cell height px = ((asc - desc) * ph) / upem + 1 |
| 57 | const TF_R_ADV: i64 = 3 // advance px at this size (hmtx scaled) |
| 58 | const TF_R_LM: i64 = 4 // left margin px: the cell's x0 sits this far LEFT of the pen (negative lsb) |
| 59 | const TF_R_PH: i64 = 5 |
| 60 | const TF_REC: i64 = 8 // i64 slots per record |
| 62 | const TF_ASCII_LO: i64 = 32 |
| 63 | const TF_ASCII_HI: i64 = 126 |
| 64 | const TF_ASCII_N: i64 = TF_ASCII_HI - TF_ASCII_LO + 1 |
| 65 | const TF_EM1000: i64 = 1000 // the browser's advance unit (font_adv_em speaks 1000-em) |
| 66 | const TF_SS: i64 = 4 // supersample factor per axis (SS*SS samples per pixel) |
| 67 | const TF_MAXE: i64 = 500 // flattened-edge capacity per glyph |
| 68 | const TF_BEZ_STEPS: i64 = 6 // quadratic Bezier flattening steps |
| 69 | const TF_COMPOSITE_DEPTH: i64 = 4 // bounded recursion for nested composites |
| 70 | const TF_CONTRAST_DIV: i64 = 170 // stem-darkening curve: a += a*(255-a)/170 |
| 71 | const TF_KERN_SUBHDR: i64 = 14 // kern format-0 subtable header bytes before the pair array |
| 72 | const TF_KERN_PAIR: i64 = 6 // left(2) right(2) value(2) |
| 73 | const TF_CMAP_REC: i64 = 8 // encoding record: platform(2) encoding(2) offset(4) |
| 74 | const TF_MIN_ADV_PX: i64 = 2 |
functions
| 76 | func tf_rd16(b: *u8, o: i64) -> i64 { return (b[o] as i64)*256 + (b[o+1] as i64) } |
| 77 | func tf_rd16s(b: *u8, o: i64) -> i64 { let v: i64=tf_rd16(b,o); if v>=TF_MAGIC_32768 { return v-TF_MAGIC_65536 } return v } |
| 78 | func tf_rd32(b: *u8, o: i64) -> i64 { return (b[o] as i64)*TF_MAGIC_16777216 + (b[o+1] as i64)*TF_MAGIC_65536 + (b[o+2] as i64)*256 + (b[o+3] as i64) } |
| 79 | func tf_bit(f: i64, m: i64) -> i64 { if (f/m)%2 == 1 { return 1 } return 0 } called by 1: tf_glyph_edges_x |
| 81 | func tf_rd8s(b: *u8, o: i64) -> i64 { let v: i64 = b[o] as i64; if v >= 128 { return v - 256 } return v } called by 1: tf_glyph_edges_x |
| 83 | func tf_find_table(b: *u8, t0: i64, t1: i64, t2: i64, t3: i64) -> i64 |
| 95 | func tf_cmap_lookup(b: *u8, sub: i64, c: i64) -> i64 |
| 120 | func tf_cmap_pick4(b: *u8, cmap: i64, len: i64) -> i64 |
| 134 | func tf_edge_push(ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64, ne: i64, x0: i64, y0: i64, x1: i64, y1: i64) -> i64 called by 1: tf_glyph_edges_x |
| 142 | func tf_glyph_edges_x(b: *u8, glyf: i64, loca: i64, locfmt: i64, nglyphs: i64, gid: i64, called by 2: tf_glyph_edges_xtf_glyph_edges calls 8: tf_rd16tf_rd32tf_rd16stf_bittf_rd8stf_glyph_edges_x+2 |
| 269 | func tf_glyph_edges(b: *u8, glyf: i64, loca: i64, locfmt: i64, nglyphs: i64, gid: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64) -> i64 |
| 272 | func tf_inside(gx: i64, gy: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64, ne: i64) -> i64 called by 1: tf_raster |
| 296 | func tf_load_buf(b: *u8, len: i64) -> *i64 |
| 363 | func tf_load(path: *u8) -> *i64 called by 12: br_face_load_idxbr_face_loadbr_face_load_idxbr_face_loadmainmain+6 calls 3: sys_mmapsys_read_filetf_load_buf |
| 372 | func tf_upem(fh: *i64) -> i64 { return fh[TF_H_UPEM] } |
| 373 | func tf_asc(fh: *i64) -> i64 { return fh[TF_H_ASC] } called by 1: main |
| 374 | func tf_desc(fh: *i64) -> i64 { return fh[TF_H_DESC] } called by 1: main |
| 375 | func tf_linegap(fh: *i64) -> i64 { return fh[TF_H_LINEGAP] } |
| 376 | func tf_kern_count(fh: *i64) -> i64 { return fh[TF_H_KN] } called by 1: main |
| 377 | func tf_nglyphs(fh: *i64) -> i64 { return fh[TF_H_NGLYPHS] } |
| 379 | func tf_asc_px(fh: *i64, ph: i64) -> i64 { return (fh[TF_H_ASC]*ph) / fh[TF_H_UPEM] } |
| 380 | func tf_desc_px(fh: *i64, ph: i64) -> i64 { return ((0 - fh[TF_H_DESC])*ph) / fh[TF_H_UPEM] } |
| 381 | func tf_content_px(fh: *i64, ph: i64) -> i64 { return ((fh[TF_H_ASC] - fh[TF_H_DESC])*ph) / fh[TF_H_UPEM] } |
| 383 | func tf_gid(fh: *i64, ch: i64) -> i64 { return tf_cmap_lookup(fh[TF_H_BUF] as *u8, fh[TF_H_CMAPSUB], ch) } |
| 385 | func tf_adv_units_gid(fh: *i64, gid: i64) -> i64 |
| 393 | func tf_adv_units(fh: *i64, ch: i64) -> i64 { return tf_adv_units_gid(fh, tf_gid(fh, ch)) } |
| 396 | func tf_adv_em1000(fh: *i64, ch: i64) -> i64 { return (tf_adv_units(fh, ch) * TF_EM1000) / fh[TF_H_UPEM] } |
| 399 | func tf_kern_em1000(fh: *i64, a: i64, c: i64) -> i64 |
| 424 | func tf_glyph(fh: *i64, ch: i64, ph: i64) -> *i64 called by 7: bpv_cellbpv_cellcrg_mask_labelrs_texttf_draw_texttf_char_adv+1 calls 2: tf_cmap_lookuptf_raster |
| 452 | func tf_glyph_cp(fh: *i64, cp: i64, ph: i64) -> *i64 |
| 463 | func tf_glyph_notdef(fh: *i64, ph: i64) -> *i64 |
| 470 | func tf_raster(fh: *i64, gid: i64, ph: i64, rec: *i64) -> i64 called by 3: tf_glyphtf_glyph_cptf_glyph_notdef calls 4: tf_adv_units_gidsys_mmaptf_glyph_edgestf_inside |
| 537 | func tf_draw_text(fh: *i64, fb: *Framebuffer, x: i64, y: i64, s: *u8, n: i64, ph: i64, cr: i64, cg: i64, cb: i64, cellw: i64) -> i64 |
| 597 | func tf_char_adv(fh: *i64, ch: i64, ph: i64) -> i64 called by 6: crg_mask_labelrs_text_wrs_textcr_w_metricmk_para_vecmain calls 2: tf_glyphtf_adv_units_gid |