You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Cairo(Ruby绑定)中渲染含Emoji文本,无需依赖Pango?

在Ruby Cairo中渲染含Emoji的文本解决方案

问题描述

我希望通过Ruby绑定的Cairo绘制包含Emoji的文本,现有资料均推荐使用Pango处理复杂文本,但Cairo已满足我其他所有需求,因此希望避免新增该依赖。

测试代码如下:

require 'cairo'

# Create a new image surface based on recommended OpenGraph dimensions
width = 1_200
height = 630
surface = Cairo::ImageSurface.new(width, height)

# Create a Cairo Context for the surface
cr = Cairo::Context.new(surface)

# Apply a background colour to the whole thing (almost white)
cr.set_source_rgb(0.95, 0.95, 0.95)
cr.paint

# Add a heading
heading = "Hi! 👋"
cr.set_source_rgb(0.31, 0.21, 0.27)
cr.select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD)
cr.set_font_size(100.0)
extents = cr.text_extents(heading)
centred_text_left_edge = width / 2 - extents.width / 2
cr.move_to(centred_text_left_edge, 130)
cr.show_text(heading)

# Write the image surface to a PNG file
surface.write_to_png("emoji-test-#{Time.now.strftime('%Y-%m-%dT%H:%M:%S.%L%z')}.png")

当前在macOS上Emoji会被渲染为方块(其他平台表现可能不同):
生成的图片中用方块替代了挥手Emoji

想了解是否可以通过自定义字体或其他技术让Emoji正常显示?另外,Pango方案在Ruby中是否仍适用?我在M1 Mac上无法安装Pango gem,且能找到的Ruby结合Cairo使用Pango的示例均已过时,不在当前Pango发行版中。若无法解决,只能将Emoji保存为图片后绘制,但该方案仅适用于我需要的3个特定Emoji,无法适配任意Emoji需求。


解决方案

方法1:指定Emoji专用字体拆分渲染

Cairo不支持自动字体回退,但可以单独用系统中的Emoji字体渲染Emoji字符,再用常规字体渲染普通文本,手动对齐位置。

修改后的示例代码:

require 'cairo'

width = 1_200
height = 630
surface = Cairo::ImageSurface.new(width, height)
cr = Cairo::Context.new(surface)

# 绘制背景
cr.set_source_rgb(0.95, 0.95, 0.95)
cr.paint

# 拆分普通文本与Emoji
text_part = "Hi! "
emoji_part = "👋"

# 渲染普通文本
cr.set_source_rgb(0.31, 0.21, 0.27)
cr.select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD)
cr.set_font_size(100.0)
text_extents = cr.text_extents(text_part)
# 计算整体居中的起始位置
total_width = text_extents.width + cr.text_extents(emoji_part).width
centred_start_x = width / 2 - total_width / 2
cr.move_to(centred_start_x, 130)
cr.show_text(text_part)

# 渲染Emoji(macOS用Apple Color Emoji,其他平台替换对应字体)
cr.select_font_face("Apple Color Emoji", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL)
cr.move_to(centred_start_x + text_extents.width, 130)
cr.show_text(emoji_part)

surface.write_to_png("emoji-test-#{Time.now.strftime('%Y-%m-%dT%H:%M:%S.%L%z')}.png")

不同平台的Emoji字体名称参考:

  • macOS: Apple Color Emoji
  • Windows: Segoe UI Emoji
  • Linux: Noto Color Emoji

此方法适合少量特定Emoji的场景,需要手动拆分文本。

方法2:使用内置Emoji的全字体

部分字体(如Noto Sans)本身包含Emoji字符,直接选择这类字体即可渲染整个文本,无需拆分:

cr.select_font_face("Noto Sans", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD)

需确保系统已安装对应字体。

关于Pango的可行性

Ruby的Pango绑定(ruby-pango)在M1 Mac上存在安装兼容性问题,且相关文档示例陈旧。若需支持任意Emoji且不愿手动处理字体,可尝试:

  1. 先通过Homebrew安装Pango库:brew install pango,再执行gem install ruby-pango
  2. 考虑用mini_magick结合ImageMagick等现代库替代(仍需新增依赖)

备选方案:Emoji转图片绘制

将需要的Emoji导出为PNG图片,用Cairo绘制到画布上。示例代码片段:

# 加载Emoji图片
emoji_surface = Cairo::ImageSurface.from_png("wave-emoji.png")
# 调整大小(示例缩放到100x100)
cr.scale(100.0/emoji_surface.width, 100.0/emoji_surface.height)
# 绘制到指定位置
cr.set_source_surface(emoji_surface, centred_start_x + text_extents.width, 30)
cr.paint

此方法简单但不灵活,适合仅需少量特定Emoji的场景。


内容的提问来源于stack exchange,提问作者Matthew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 20:15:22