Skip to content

gpui: Fix emoji rendering in SVG preview#51569

Merged
smitbarmase merged 6 commits intozed-industries:mainfrom
alanpjohn:fix/svg-emoji-render
Mar 27, 2026
Merged

gpui: Fix emoji rendering in SVG preview#51569
smitbarmase merged 6 commits intozed-industries:mainfrom
alanpjohn:fix/svg-emoji-render

Conversation

@alanpjohn
Copy link
Copy Markdown
Contributor

@alanpjohn alanpjohn commented Mar 14, 2026

Closes #50483

Findings

As reported in the original issue, emojis in SVG preview were not rendering consistently with the editor.

The SVG renderer uses usvg/resvg for parsing and rendering SVG files. The first problem was that emoji fonts were not rendering at all, which was fixed by enabling the raster_images on resvg.

Beyond that it was observed that the default font fallback mechanism in usvg searches through the font database alphabetically without prioritizing emoji fonts. This caused emojis to sometimes render in non-emoji fonts that happened to contain glyph mappings for those characters.

For example, on Linux systems with the default uvsg::FontResolver::default_fallback_selector():

  • The character ✅ would fall back to FreeSerif (monochrome)
  • Instead of Noto Color Emoji (full color)

Log output showed the inconsistent behavior:

WARN  [usvg::text] Fallback from FreeSans to Noto Color Emoji.
WARN  [usvg::text] Fallback from FreeSans to FreeSerif.
WARN  [usvg::text] Fallback from FreeSans to Noto Color Emoji.
Image

This created a jarring inconsistency where the same emoji character would render differently in:

  • The editor (correct, using platform emoji fonts)
  • SVG preview (incorrect, using arbitrary fallback fonts)

Solution

If the specified font in SVG is available on the system, we should show that. If not, we should fallback to what editors show today for that emoji.

This PR implements emoji-aware font fallback that:

  1. Enabled raster_images build feature to render emojis in SVG.
  2. Detects emoji characters using Unicode emoji properties (via \p{Emoji} regex pattern), consistent with how we check for emoji in the Editor as well.
  3. Preserves user-specified fonts by only intervening when the default font resolver would use a non-emoji font for emoji characters

Font Family Selection

I avoided completely reusing/rebuilding the logic for emoji font selection used by the editor as uvsg internally does quite a bit of the job and it felt like overcomplicating the solution. Instead using hard coded platform specific font family names.
The hardcoded emoji font families are sourced from Zed's existing platform-specific text rendering systems:

These match the fonts the editor uses for emoji rendering on each platform.
To break down further into the similarity and differences in the emoji font resolution:
Similarities:

  • Both now use the regex based emoji detection logic
  • Both prioritize the same platform-specific emoji font families
  • Both support color emoji rendering
    Differences:
  • Editor: Uses platform-native text shaping (CoreText on macOS, DirectWrite on Windows, cosmic-text on Linux) which handles fallback automatically
  • SVG: Uses custom fallback selector that explicitly queries emoji fonts first, then falls back to default usvg behavior

Testing

  • Added unit tests for is_emoji_character in util crate
  • Tested emoji detection for various Unicode characters
  • Verified platform-specific font lists compile correctly (Only linux done)
  • Manual testing with SVG files containing emojis on all platforms (Only linux done)

Release Notes:

  • Fixed SVG preview to render emojis consistently with the editor by prioritizing platform-specific color emoji fonts

@cla-bot
Copy link
Copy Markdown

cla-bot bot commented Mar 14, 2026

We require contributors to sign our Contributor License Agreement, and we don't have @alanpjohn on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'.

@zed-community-bot zed-community-bot bot added the first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions label Mar 14, 2026
@alanpjohn
Copy link
Copy Markdown
Contributor Author

@cla-bot check

@cla-bot cla-bot bot added the cla-signed The user has signed the Contributor License Agreement label Mar 14, 2026
@cla-bot
Copy link
Copy Markdown

cla-bot bot commented Mar 14, 2026

The cla-bot has been summoned, and re-checked this pull request!

@maxdeviant maxdeviant changed the title Fix/svg emoji render gpui: Fix emoji rendering in SVG preview Mar 15, 2026
@zelenenka zelenenka added the guild Pull requests by someone in Zed Guild. NOTE: the label application is automated via github actions label Mar 16, 2026
@alanpjohn alanpjohn marked this pull request as ready for review March 17, 2026 07:15
@alanpjohn
Copy link
Copy Markdown
Contributor Author

CC: @smitbarmase

@SomeoneToIgnore SomeoneToIgnore added the area:gpui GPUI rendering framework support label Mar 17, 2026
Copy link
Copy Markdown
Member

@smitbarmase smitbarmase left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is good improvement overall.

I have two concerns:

  1. util::is_emoji_character does not seem like the right predicate for font fallback. The added unit test also fails locally for me. I think it fails on 1.

We should verify these too in addition to existing ones:

  [
      ("©", false),
      ("©️", true),
      ("♥", false),
      ("♥️", true),
      ("1", false),
      ("1️⃣", true),
      ("🇺🇸", true),
      ("👨‍👩‍👧‍👧", true),
      ("a", false),
  ]

Can you try with Emoji_Presentation for regex directly, which might be a bit conservative but will do that job in most cases? Let's keep it local to svg_renderer and not in util.

  1. One more thing I noticed: fontdb::Database::query returns the best face from the first installed family in families. It does not keep walking the rest of EMOJI_FONT_FAMILIES looking for one that also supports ch.

That means if the first installed emoji family is already in fonts, or if it exists but does not have this glyph, we fall back to the default usvg resolver without ever trying the remaining emoji families.

I think this should iterate the families manually and check both:

  • !fonts.contains(&id)
  • db.has_char(id, ch)

@alanpjohn
Copy link
Copy Markdown
Contributor Author

Is this what you had in mind

  1. util::is_emoji_character does not seem like the right predicate for font fallback. The added unit test also fails locally for me. I think it fails on 1.

Fixed that

Can you try with Emoji_Presentation for regex directly, which might be a bit conservative but will do that job in most cases? Let's keep it local to svg_renderer and not in util.

Reverted any changes I made to util and moved all logic into crates/gpui/src/svg_renderer.rs.

I think this should iterate the families manually and check both:

  • !fonts.contains(&id)
  • db.has_char(id, ch)

I added the check to see if fonts contains ID already but I couldn't see a db.has_char(id, ch) method. For reference

@alanpjohn alanpjohn requested a review from smitbarmase March 26, 2026 16:50
alanpjohn and others added 5 commits March 27, 2026 12:28
- enabled `raster_images` feature on `resvg` crate to allow resvg to
  render emoji fonts
- Add a custom fallback selection function to make the fallback fonts
  for emojis more consistent.

Signed-off-by: Alan P John <alanpjohn@outlook.com>
Accidentally removed in db622ed

Signed-off-by: Alan P John <alanpjohn@outlook.com>
fallback

- revert changes to util crate

Signed-off-by: Alan P John <alanpjohn@outlook.com>
Signed-off-by: Alan P John <alanpjohn@outlook.com>
@smitbarmase smitbarmase force-pushed the fix/svg-emoji-render branch from 4f21838 to c980730 Compare March 27, 2026 07:00
Copy link
Copy Markdown
Member

@smitbarmase smitbarmase left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I added a has_char check and test for that.

Also, I updated the emoji presentation test with an expected shortcoming for certain emojis like 1️⃣ and ♥️, which are cluster-based rather than scalar. The fix for that will be in upstream, giving us the right API to work with for fallback rather than just char.

But this is fine for now, and the current form is a huge improvement anyways. Thanks again for working on this.

@smitbarmase smitbarmase merged commit 5197cb4 into zed-industries:main Mar 27, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:gpui GPUI rendering framework support cla-signed The user has signed the Contributor License Agreement first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions guild Pull requests by someone in Zed Guild. NOTE: the label application is automated via github actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SVG Preview not rendering emojis

4 participants