#!/usr/bin/env python3 """ Build a monochrome Noto Emoji subset for the chat/message UI. Dear ImGui rasterizes fonts with stb_truetype, which handles only monochrome (outline `glyf`) fonts — NOT color emoji (CBDT/sbix/COLR). So we use Google's *monochrome* Noto Emoji (github.com/google/fonts, ofl/notoemoji, OFL-licensed) and merge it into the text fonts (see Typography::loadFont, the block after the CJK merge). ImGui renders one glyph per codepoint with no shaping, so ZWJ sequences / regional-indicator flags won't compose — single-codepoint emoji (😀 🎉 ❤ 🔥 👍 …) render fine, which covers the overwhelming majority of use. Source is the variable font pinned to wght=400 → static, then subset to the emoji planes plus the higher symbol/star ranges the base UI font doesn't cover. The base Ubuntu font already owns U+2600–26FF etc.; ImGui's MergeMode gives the first-loaded glyph precedence, so those stay text-styled and only the codepoints the base lacks fall through to this font. Get the source once (OFL, redistributable): curl -fsSL -o /tmp/NotoEmoji-VF.ttf \ 'https://github.com/google/fonts/raw/main/ofl/notoemoji/NotoEmoji%5Bwght%5D.ttf' Then: python3 scripts/build_emoji_subset.py Output: res/fonts/NotoEmoji-Subset.ttf (committed; embedded via INCBIN) """ import os from fontTools import ttLib, subset from fontTools.varLib.instancer import instantiateVariableFont ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SOURCE_VF = '/tmp/NotoEmoji-VF.ttf' STATIC = '/tmp/NotoEmoji-Static.ttf' OUTPUT = os.path.join(ROOT, 'res', 'fonts', 'NotoEmoji-Subset.ttf') # Emoji codepoints to keep. Ranges are inclusive. RANGES = [ (0x1F000, 0x1FAFF), # all the main emoji planes (emoticons, pictographs, transport, supplement, extended) (0x2600, 0x27BF), # Miscellaneous Symbols + Dingbats (0x2B00, 0x2BFF), # stars (⭐ 2B50) and misc arrows (0xFE00, 0xFE0F), # variation selectors (VS16 emoji-style) (0x2194, 0x21AA), # arrows used as emoji (0x231A, 0x231B), # ⌚ ⌛ (0x23E9, 0x23FA), # media-control emoji (0x25AA, 0x25FE), # small squares ] SINGLES = [0x200D, 0x2934, 0x2935, 0x3030, 0x303D, 0x3297, 0x3299, 0x00A9, 0x00AE, 0x2122, 0x2139, 0x24C2] def main(): if not os.path.exists(SOURCE_VF): raise SystemExit(f"missing source font {SOURCE_VF} — see the header for the curl command") # 1. Pin the weight axis so stb_truetype rasterizes a clean static instance. f = ttLib.TTFont(SOURCE_VF) if 'fvar' in f: instantiateVariableFont(f, {'wght': 400}, inplace=True) f.save(STATIC) unicodes = list(SINGLES) for lo, hi in RANGES: unicodes.extend(range(lo, hi + 1)) opts = subset.Options() opts.layout_features = [] # ImGui does no shaping — drop GSUB/GPOS opts.name_IDs = [] opts.notdef_outline = True opts.glyph_names = False opts.drop_tables = ['GSUB', 'GPOS', 'GDEF', 'morx', 'kern'] font = subset.load_font(STATIC, opts) ss = subset.Subsetter(options=opts) ss.populate(unicodes=unicodes) ss.subset(font) subset.save_font(font, OUTPUT, opts) out = ttLib.TTFont(OUTPUT) cmap = out.getBestCmap() color = any(t in out.reader.keys() for t in ('CBDT', 'sbix', 'COLR')) print(f"Output: {OUTPUT}") print(f"Size: {os.path.getsize(OUTPUT)//1024} KB | glyphs: {len(cmap)} | color tables: {color}") if color: raise SystemExit("ERROR: subset has color tables — stb_truetype cannot render it") if __name__ == '__main__': main()