#300: emoji take a colour face before the symbol fonts, which drew them in one colour on Windows

This commit is contained in:
bflatastic
2026-10-15 19:21:37 +07:00
parent 3d112c4b8c
commit a89031eab2
2 changed files with 24 additions and 5 deletions
+11
View File
@@ -183,6 +183,17 @@ fn colour_emoji_carry_their_own_pixels() {
}
}
#[test]
fn emoji_in_plain_text_draw_from_a_colour_face() {
// A symbol font among the fallbacks (Windows has one) draws U+1F600 in one colour: the emoji
// face comes first for it.
if emoji_face().is_none() {
return;
}
let l = layout(&TextData::new("Hi \u{1F600}"), None, None);
assert!(l.glyphs.iter().any(|g| g.color(1.0).is_some()), "the emoji has colour ink");
}
#[test]
fn glyphs_place_their_ink_at_a_scale() {
let Some(face) = emoji_face() else { return };
+13 -5
View File
@@ -329,11 +329,10 @@ pub fn layout(text: &TextData, box_width: Option<f64>, box_height: Option<f64>)
faces.push(dbw.find(&s.font_family, &s.font_style).or_else(|| dbw.default_face()));
}
// Colour emoji are their own, large faces (Apple Color Emoji is ~180 MB): they are read only
// for a text whose characters nothing above covers.
// for a text with emoji, or with characters nothing above covers.
let covered = |c: char| c == '\n' || faces.iter().flatten().any(|f| f.has_char(c)) || fallbacks.iter().any(|f| f.has_char(c));
if chars.iter().any(|c| !covered(*c)) {
fallbacks.extend(dbw.emoji_fallbacks());
}
let emoji = if chars.iter().any(|c| is_emoji(*c) || !covered(*c)) { dbw.emoji_fallbacks() } else { Vec::new() };
fallbacks.extend(emoji.iter().cloned());
drop(dbw);
// OpenType features the user toggled (global for the node).
@@ -364,7 +363,9 @@ pub fn layout(text: &TextData, box_width: Option<f64>, box_height: Option<f64>)
if c == '\n' || primary.has_char(c) {
return primary.clone();
}
fallbacks.iter().find(|f| f.has_char(c)).cloned().unwrap_or_else(|| primary.clone())
// Emoji take a colour face before the symbol fonts, which draw many of them in one colour.
let colour = if is_emoji(c) { emoji.iter().find(|f| f.has_char(c)) } else { None };
colour.or_else(|| fallbacks.iter().find(|f| f.has_char(c))).cloned().unwrap_or_else(|| primary.clone())
};
let first = chars.get(seg_start).copied().unwrap_or(' ');
let face = face_for(first);
@@ -988,6 +989,13 @@ fn sanitize_size(s: f64) -> f64 {
if s.is_finite() { s.clamp(0.5, 5000.0) } else { 12.0 }
}
/// A character drawn as a colour emoji by default: flags and the pictograph blocks.
/// shortcut: the older symbols (U+2600–U+27BF) keep their text look even before U+FE0F; read the
/// emoji presentation data if they should turn colour too.
fn is_emoji(c: char) -> bool {
matches!(c, '\u{1F1E6}'..='\u{1F1FF}' | '\u{1F300}'..='\u{1FAFF}')
}
/// One run of chars shaped with a single face and style.
struct Segment<'a> {
start: usize,