Plan 9: Emoji
![]() |
| Noto Emoji Font |
Note: Noto Color emoji needs Color Bitmap Data (CBDT) or COLR/CPAL support.
Four independent bugs, spanning truetypefs.c, hint.c, and head.c:
1. assert(gs[i] != nil) crash in compilesub (truetypefs.c)
ttffindchar returning glyph 0 for a rune inside a declared cmap segment (a genuine gap, not just the NUL special-case) skipped the retry-via-notdef path entirely. If the font's own .notdef then also failed to rasterize, the assert took the whole fileserver down. Fixed by replacing the assert with a synthesized blank placeholder as the last resort in the fallback chain.
2. No bounds checking on glyph geometry before blit() (truetypefs.c)
blit() has zero validation of its own; compilesub sized the destination strip from the font's declared ascentpx + descentpx but never checked that an individual glyph's actual rendered width/height/vertical position fit inside that buffer. A decorative font (architectsdaughter) reporting ink extents past its declared metrics caused blit to write past the buffer, corrupting the heap. Fixed with badglyph(), gating every glyph — primary TTF, bitmap fallback, and notdef — before it's ever blitted.
3. Unbounded native recursion in the hinting interpreter (hint.c)
h_call/h_loopcall implement the TrueType CALL/LOOPCALL instructions via genuine recursive C calls to run(), with an h->level counter that was tracked but never actually checked against any bound. A font with a deeply-nested fpgm/prep program (Noto Sans; architectsdaughter's trivial/absent hinting never touched this) blew the real process stack — this is what produced the sys: trap/suicide crashes and, separately, the reported "stack overflow" text. Fixed with a MAXLEVEL cap (64) in both instructions, failing cleanly via the existing herror()/longjmp path instead of faulting.
4. assert() on the interpreter's own operand-stack bounds in push()/pop() (hint.c)
Same failure class as #1, different subsystem: a bytecode-driven condition (NPUSHB/NPUSHW pushing more values than the stack's declared capacity) wired to abort() instead of the graceful herror() path already used everywhere else in the file. Fixed by converting both to bounds checks that call herror().
5. maxp.maxStackElements trusted at face value (head.c)
Once #4 stopped crashing, it surfaced a real data problem underneath: Noto Sans's own declared stack-size field is smaller than what its hinting program actually needs — a well-documented unreliability in real-world fonts' maxp tables, not a parsing bug (the field order was verified correct against the TrueType spec). Fixed by flooring u->maxStackElements to 512 right after parsing, so both the stack allocation and the runtime bound check in runpg — which both read that same field — get the padding for free from one edit.
Net effect: architectsdaughter was never actually broken by any of this — it just never had a hinting program complex enough to reach bugs #3–5, and never had a glyph malformed enough to reach #2. Noto Sans routed through all four in sequence, one crash at a time, until it renders cleanly with #5.
Color Emoji, Ligatures and Text Shaping
Color emojis are supported under three specifications - COLR+CPAL, SVG+CPAL, and CBLC+CBDT. This code implements COLR+CPAL without gradients and the demo uses Noto Color Emoji.
Also, added a simple API to view the glyph for a codepoint. Emojis can be combined with restrictions (glyph must be available in the font) using Zero Width Joiners (ZWJ) FE0F. This process is called Text Shaping. Text shaping is also used for ligatures (using GSUB) and combining in languages like Devanagari (using GPOS and FeatureList/LangSys).
Text shaping for Devanagari script using NotoSansDevanagari-Regular.ttf.
Unicode Coverage with Truetype Font
Combine multiple Truetype font files for complete Unicode coverage.
Note:
- You can manually generate the font file too - follow the Notation section. Do ensure that you avoid any range overlaps.
- You want the ASCII range (0-128) to be rendered by a text font. e.g. NotoColorEmoji.ttf has a very wide space and misaligned numbers in relation to Dejavusans.ttf.
myfont dejavusans.ttf notosansdevanagari-regular.ttf notoemoji-regular.ttf
mine dejavusans.ttf notosansdevanagari-regular.ttf notoemoji-regular.ttf
Notation
0x0000 0x00ff s.0000-00ff
0x0100 0x01ff s.0100-01ff
0x0200 0x02ff s.0200-02ff
Color Emoji with libdraw
/shapeable and /shape APIs are exposed by Truetype file server to help with text shaping. These are ONLY used for composition and/or color emojis. If the font doesn't support these features, it falls back to font(6) architecture.
![]() |
| Devanagari support |
Sample Devanagari text
मा कर्मफलहेतुर्भूर्मा ते सङ्गोऽस्त्वकर्मणि ॥
धर्म क्षीर स्त्री ड़ाल कार्तिक
कर्म क्रम
Emoji Picker
Insert emoji in the active window using a picker (uses /dev/cons). Use left click to select and right click to quit.
Font Awesome Icons
Download the Web zip (fontawesome-free-<version>-web.zip). The webfonts/ directory has fa-solid-900.ttf, fa-regular-400.ttf, and fa-brands-400.ttf, with TrueType glyf outlines. Regular is a subset of solid font.
If you're using it with Dejavu font, note that Dejavu will claim f000-f003 if it's higher in the priority.
Unicode Command
Print a unicode character for a hex value.
∑
Code
- https://gitlab.com/atamariya/plan9front [libttf, libdraw, libmemdraw, truetypefs.c, devdraw.c (needs kernel rebuild)]





Comments
Post a Comment