From b2fe3f493efc12dd300f0e2b2ede5660ee70c193 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 7 Oct 2026 11:01:27 +0200 Subject: fix(card): draw emoji in Noto Emoji instead of tofu Cairo's toy text API binds one face with no per-glyph fallback, so an emoji in a calendar event title rendered as a box in Noto Sans. text/measure/advance now split strings into runs and draw emoji runs (U+1F000-1FAFF, U+2600-27BF) in monochrome Noto Emoji, restoring the caller's face afterwards. Nerd Font PUA icons stay in FONT_MONO. Plain strings and invalid UTF-8 take the old single-call path. No shaping: ZWJ sequences and flags draw as their parts. Pango via lgi (built for Lua 5.4) is the upgrade path. Co-Authored-By: Claude Opus 5.5 --- test/test_data.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'test/test_data.lua') diff --git a/test/test_data.lua b/test/test_data.lua index ec2b6ed..549ce40 100644 --- a/test/test_data.lua +++ b/test/test_data.lua @@ -505,3 +505,19 @@ assert(gate(100.0) == true, 'gate: first call opens') assert(gate(100.5) == false, 'gate: closed inside the interval') assert(gate(101.95) == true, 'gate: opens after it') assert(gate(102.0) == false, 'gate: interval restarts from the last open') + +-- Emoji fall back to card.FONT_EMOJI: the toy API has no per-glyph fallback, +-- so without the run split "x🦷" measured a tofu box in Noto Sans. +do + local s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10) + local cr = cairo_create(s) + card.font(cr, 'Noto Emoji', 20, false) + local tooth = card.advance(cr, '🦷') + card.font(cr, card.FONT_UI, 20, false) + local x = card.advance(cr, 'x') + assert(math.abs(card.advance(cr, 'x🦷') - (x + tooth)) < 0.01, 'emoji run uses FONT_EMOJI') + assert(card.advance(cr, 'x') == x, 'caller face restored after an emoji run') + assert(pcall(card.advance, cr, '\240\159'), 'invalid UTF-8 must not raise') + cairo_destroy(cr) + cairo_surface_destroy(s) +end -- cgit v1.2.3