regex 总览待审核
渲染检测 · measureText / getImageData

检测 emoji 支持:字体里到底有没有这个字形

RGI 那页结尾说过:一段 emoji 合不合法(\p{RGI_Emoji} 说了算)和它渲不渲染得出来是两件事——后者取决于当前设备的字体里有没有对应字形,和系统版本、地区强绑定。 常见做法是嗅探 User-Agent 猜操作系统厂商、再假定它的 emoji 字体,但 UA 可随意伪装,也不反映字体实际装到了哪一代 Unicode。可靠的做法是直接测量渲染结果。 本页给出三种互补的检查手段,合到一个 lab 里:勾选启用哪几种,对任意 emoji 得出一个综合结论

基线:缺字形的码点都回退成同一个「豆腐块」

一个码点没有字形时会回退到末位字体的 .notdef(豆腐块 ▯),而所有缺字形的码点都回退到同一个 .notdef——渲染出来完全一样。判断一个字符有没有专属字形,就看它渲染出来是否等于豆腐块:一致 = 没字形 = 未支持。 注意用渲染像素比、而不是只比 measureText宽度:某些系统的 .notdef 步进宽度恰好等于 emoji 宽度,只比宽度会把能显示的字(如黑白的 🥷)误判成不支持。宽度留给下一节的连字塌缩用,那里比的是相对宽度,不受此坑影响。

三种检查手段:各管一类 emoji

没有单一指标能覆盖所有 emoji——单码点、ZWJ 序列、国旗各有各的「未支持」表现。三种手段分别对准一类:

A · 字形存在getImageData适用:单码点(含 VS16)。规则:把目标和豆腐块都渲染出来比像素,不一样 ⇒ 有专属字形 ⇒ 支持。比像素而非比宽度——某些系统 .notdef 宽度恰好等于 emoji,只比宽度会把能显示的字(如黑白的 🥷)误判为不支持。只要求「有独立字形」,不要求彩色
B · 连字塌缩measureText适用:ZWJ / 肤色 / 国旗等序列(纯 Tag 序列除外)。规则:序列宽度 < 拆开写法宽度 ⇒ 字体把它塌成一个字形 ⇒ 支持:国旗不支持时会退成一个「字母方块」,宽度变窄,B 会误判为支持——需要 C 纠正。
C · 彩色像素getImageData适用:国旗 / Tag 子地区旗。真旗渲染成彩色;不支持时退成灰度字母方块(TW→方块里两个字母)或基底黑旗——都几乎无彩色像素。规则:彩色像素占比 > 阈值 ⇒ 真的画出了旗 ⇒ 支持。这一步要读像素。
为什么国旗要单独用 C?TW 为例:在中国大陆地区的系统上,🇹🇼 不渲染成旗子,而是退成一个方块、里面是两个字母。这个方块也是一个字形、宽度也塌窄了,所以 B(宽度塌缩)会把它误判为「支持」。 真旗和字母方块的区别在颜色:真旗有大片彩色,字母方块是灰度——所以国旗要用 C(彩色像素)来判。下面的 lab 里把 C 的勾去掉,就能复现这个误判。

合到一个 lab:勾选检查手段,得出综合结论

综合规则用「不支持优先」:在勾选启用适用于当前输入的检查里,只要有一个判「不支持」,综合结论就是不支持;都判支持才是支持;若勾选的检查都不适用,则无法判定。下面就是本页实际运行的检测:

本页实际运行的检测measureText + getImageData

  

体检:对照组(任何设备可复现)+ 版本阶梯(反映本机)

两组都跑三种检查全开的综合判定。对照组不依赖设备新旧——未分配码点(豆腐块)、无效地区码(字母块)在任何机器上都判不支持,总能演示三种检查的区别。版本阶梯是这台设备的实时探测:越靠下版本越新,某个版本起开始出现 ✗ 就是这台设备 emoji 字体的更新截止线

对照组 · 不依赖设备新旧,总能看出三种检查的区别

版本阶梯 · 反映这台设备 emoji 字体的新旧(越靠下越新)

这条线和 \p{RGI_Emoji}正交的两问:属性那页\p{RGI_Emoji} 只判「这串码点在 Unicode 里是不是合法 emoji」(在哪台机器上都一样);本页判「当前字体渲不渲染得出来」(每台机器、每个系统版本、每个地区都可能不同)。 上线一个 emoji 前,合法性用正则查、渲染支持度用本页的三种检查探——两者都过,才是真的「能用」。 B 只用 measureText,不读像素、跨机稳定、不受反指纹噪声影响;A / C 需要 getImageData 读像素(A 比「渲染是否 = 豆腐块」,C 数彩色像素占比)。因此在开启了 canvas 指纹保护的浏览器里,A / C 的像素读数可能被掺入噪声、结论不稳——这时 A 可退回只比宽度(measureText),但要接受 .notdef 宽度与 emoji 撞车导致的误判。