Skip to content

Honour the emoji presentation selector (VS16) in font selection - #692

Open
taladar wants to merge 1 commit into
linebender:mainfrom
taladar:fix/vs16-emoji-presentation-ordering
Open

Honour the emoji presentation selector (VS16) in font selection#692
taladar wants to merge 1 commit into
linebender:mainfrom
taladar:fix/vs16-emoji-presentation-ordering

Conversation

@taladar

@taladar taladar commented Jul 16, 2026

Copy link
Copy Markdown

A cluster carrying U+FE0F (VS16) explicitly requests the emoji presentation per UTS #51, but font selection ignored the request: the emoji family is appended after the requested families, so a text font that happens to cover the base codepoint wins and the cluster renders as a monochrome text glyph.

❤️ (U+2764 U+FE0F) is the motivating case. U+2764 has a dingbat glyph in common UI text fonts (DejaVu Sans, Inter, …), so the text font matches first and the emoji family is never reached — the selector that asks for emoji presentation has no effect. Emoji with no text-font glyph (🎉, 🇯🇵) are unaffected, which is why this is easy to miss.

The emoji family cannot simply be tried first in general: is_emoji is the raw Emoji/Extended_Pictographic property, which is also true for 5, # and — codepoints whose default presentation is text and which would then wrongly render as emoji. The request has to be honoured only where it is actually made.

So track which presentation, if either, a cluster explicitly asks for, and put the emoji family first only for VS16:

  • Add Presentation (Unspecified / Text / Emoji) and record it on CharCluster while filling it. This is deliberately finer-grained than the existing is_variation_selector(), which cannot distinguish the two emoji variation selectors — they request opposite presentations.
  • In select_font, order the emoji family before the requested families when the cluster requests Presentation::Emoji. U+FE0E (VS15) requests the text presentation, so it keeps the existing ordering, which already prefers the text font whenever one covers the codepoint.

Verified against a bundled CBDT emoji font with Inter as the primary family: ❤️ now resolves to the emoji font while (no selector), 5, # and Latin text all still resolve to Inter.

A cluster carrying `U+FE0F` (VS16) explicitly requests the emoji presentation
per UTS linebender#51, but font selection ignored the request: the emoji family is
appended *after* the requested families, so a text font that happens to cover
the base codepoint wins and the cluster renders as a monochrome text glyph.

`❤️` (`U+2764 U+FE0F`) is the motivating case. `U+2764` has a dingbat glyph in
common UI text fonts (DejaVu Sans, Inter, …), so the text font matches first and
the emoji family is never reached — the selector that asks for emoji
presentation has no effect. Emoji with no text-font glyph (`🎉`, `🇯🇵`) are
unaffected, which is why this is easy to miss.

The emoji family cannot simply be tried first in general: `is_emoji` is the raw
Emoji/Extended_Pictographic property, which is also true for `5`, `#` and `▶` —
codepoints whose default presentation is text and which would then wrongly
render as emoji. The request has to be honoured only where it is actually made.

So track which presentation, if either, a cluster explicitly asks for, and put
the emoji family first only for VS16:

- Add `Presentation` (`Unspecified` / `Text` / `Emoji`) and record it on
  `CharCluster` while filling it. This is deliberately finer-grained than the
  existing `is_variation_selector()`, which cannot distinguish the two emoji
  variation selectors — they request *opposite* presentations.
- In `select_font`, order the emoji family before the requested families when
  the cluster requests `Presentation::Emoji`. `U+FE0E` (VS15) requests the text
  presentation, so it keeps the existing ordering, which already prefers the
  text font whenever one covers the codepoint.

Verified against a bundled CBDT emoji font with Inter as the primary family:
`❤️` now resolves to the emoji font while `❤` (no selector), `5`, `#` and Latin
text all still resolve to Inter.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@taladar

taladar commented Jul 16, 2026

Copy link
Copy Markdown
Author

Apparently that macOS failure does point towards a real bug with the approach that might be worth further consideration. In particular it now prioritizes an emoji font over an explicitly configured font family that happens to also have emoji versions of the character.

So what we would need to know to solve this properly is how a given font renders it so we can decide if that font is a valid candidate, that seems related to the #695 issue I opened too where the selection logic also doesn't have all the information to make an informed decision.

@taj-p

taj-p commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

I haven't taken a close look at this PR, but I suspect (?) #620 is related - is #620 a more principled approach? Or is this something different?

@nicoburns

Copy link
Copy Markdown
Collaborator

We may well also want to support font-variant-emoji as a style.

@DJMcNab

DJMcNab commented Jul 17, 2026

Copy link
Copy Markdown
Member

(#693 (comment) also applies here).

I do suspect that the approach in #620 is more complete; I'm reviewing that PR now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants