0031. Colour emoji are Patina's: the Teleprinter code, with the font a git crate
Date: 2026-11-07
Status
Accepted. The user saw, in the other Patina apps, the emoji problems Teleprinter had already solved, and asked for Patina to bundle that emoji set. The patina-desktop emoji picker is the second user (its ADR-0120).
Context
egui (epaint 0.35) draws each glyph from its outline into a coverage-only atlas tinted with the text colour, and never reads COLR, CBDT or sbix, so emoji in any egui label are monochrome shapes from egui's own emoji fonts, or boxes. Teleprinter solved it (its ADR-0013) by bundling Noto Color Emoji's CBDT font and painting emoji as images, found through the font's cmap and GSUB ligatures. The picker copied that code, and every other app would have had to.
Decision
patina::emoji, behind theemojicargo feature:glyph(text)for a sequence's glyph,textureandpaintfor its image,runs(text)to split text into plain and emoji runs, andlabel(ui, text)to show it.- The font is the app's to bring, not part of
patina-uiand not looked for by it. It is 10 MB, which put the package over crates.io's 10 MiB limit (a cargo feature doesn't help: the limit is on the uploaded package, features or not), and where a system font lives, or whether there is one, differs on every platform Patina runs on (Linux, macOS), so Patina doesn't guess.emoji::install(bytes)takes the bytes of any Noto Color Emoji in its CBDT build (PNG images, the formatFace::glyph_raster_imagereads; a font without them is refused), andemoji::install_file(path)reads a file. The app decides where the font comes from: it ships one, finds one, or lets the user choose. - Sector F's desktop bundles ours:
patina-emoji-font(emoji-font/in this repository, a workspace member withpublish = false) is the font asFONT: &[u8], and the apps that show emoji callinstall(patina_emoji_font::FONT). It is a git dependency, which crates.io refuses, sopatina-uican't depend on it (only its tests do, as a dev-dependency), and an app published there brings its own font instead. - Without a font, nothing breaks:
installand the others sayfalse,runsfinds no emoji,labelis a plain label, and emoji look as they did before. - Optional, as
markdownis, because it adds about 11 MB to a binary. - It is the Teleprinter code, not a rewrite, so an emoji looks the same in every Patina app. Teleprinter can drop its copy for this when it next changes.
Consequences
- An app gets colour emoji by enabling the feature, installing a font and using
emoji::labelwhere its text may have emoji. egui's own labels, text edits and widgets still draw monochrome ones; that needs egui to draw colour glyphs, or a widget of our own for each. The markdownEditoris one such: with both features on, it lays emoji out in transparent text and paints the image over them. - The packaged crate is about 1 MiB, and publishes to crates.io. An app published there can't
depend on the git-only
patina-emoji-font, so it ships or finds a font itself. Only the CBDT build of Noto Color Emoji is read: not macOS's Apple Color Emoji (sbix), nor Noto's newer COLR build. labellays plain text and emoji out as separate widgets in a wrapped row, so it wraps between runs and the line height can differ slightly from a plain label's.