IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    终端模拟器下使用双倍宽度多色Emoji字体

    MaskRay发表于 2016-10-02 15:54:36
    love 0

    多色Emoji字体

    Cairo支持

    较新的FreeType支持多色,但cairo-1.14.6没有默认开启支持。hexchain指出修改cairo源码一行代码即可:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    --- a/src/cairo-1.14.6/src/cairo-ft-font.c 2016-03-13 09:36:42.618325503 +0800
    +++ b/src/cairo-1.14.6/src/cairo-ft-font.c 2016-03-13 09:38:24.194288159 +0800
    @@ -2258,7 +2258,7 @@
    * Moreover, none of our backends and compositors currently support
    * color glyphs. As such, this is currently disabled.
    */
    - /* load_flags |= FT_LOAD_COLOR; */
    + load_flags |= FT_LOAD_COLOR;
    #endif
    error = FT_Load_Glyph (face,

    然后编译安装cairo。

    Arch Linux普通用户

    可以安装aur/cairo-coloredemoji。

    Arch Linux infinality-bundle用户

    huiyiqun指出,infinality-bundle用户应该:

    1. 安装https://aur.archlinux.org/packages/cairo-infinality-ultimate-with-colored-emoji/
    2. 删除/etc/fonts/conf.d/82-no-embedded-bitmaps.conf 。按https://gist.github.com/huiyiqun/9f20f177655946263a48170ee662cea9配置fontconfig。

    Fontconfig配置

    安装Noto Color Emoji字体,Arch Linux可以装extra/noto-fonts-emoji。

    在~/.config/fontconfig/fonts.conf(更准确点应用$XDG_CONFIG_HOME)中添加下面两个<match>标签(hexchain提供)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <fontconfig>
    ...
    <match target="scan">
    <test name="family">
    <string>Noto Color Emoji</string>
    </test>
    <edit name="scalable" mode="assign"><bool>true</bool></edit>
    </match>
    <match target="pattern">
    <edit name="family" mode="prepend">
    <string>Noto Color Emoji</string>
    </edit>
    </match>
    </fontconfig>

    可以用fc-match -s monospace、fc-match -s sans、fc-match -s sans-serif确定字体选择顺序。我这里Noto Color Emoji只能排到第二,不明原因。

    推荐使用hexchain的配置:https://gist.github.com/hexchain/47f550472e79d0805060。

    Emoji字符宽度

    经过以上两步配置,理应可以使用Noto Color Emoji字体了。但对于Monospace字体,emoji字符显示宽度为1,会与其后的字符重叠。

    vte中设置emoji字符宽度

    在终端模拟器中,字符是按列显示的,因此有宽度的概念。vte把emoji字符当作1,因此显示时会与之后的字符重叠。

    以终端模拟器termite使用的community/vte3-ng为例。修改源码src/vte.cc:_vte_unichar_width,让该函数对于某些Noto Color Emoji提供的Unicode block返回2。此处我硬编码了几个用到的Unicode block,不全:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    --- a/src/vte.cc 2016-03-12 23:44:04.157720973 +0800
    +++ b/src/vte.cc 2016-03-13 00:20:45.592623311 +0800
    @@ -206,6 +206,10 @@
    return 0;
    if (G_UNLIKELY (g_unichar_iswide (c)))
    return 2;
    + if (G_UNLIKELY(0x25a0 <= c && c < 0x27c0 || // Geometric Shapes, Miscellaneous Symbols, Dingbats
    + 0x2b00 <= c && c < 0x2c00 || // Miscellaneous Symbols and Arrows
    + 0x1f300 <= c && c < 0x1f700 || // Miscellaneous Symbols and Pictographs ... Geometric Shapes Extended
    + 0))
    + return 2;
    if (G_LIKELY (utf8_ambiguous_width == 1))
    return 1;
    if (G_UNLIKELY (g_unichar_iswide_cjk (c)))

    Arch Linux可以装aur/vte3-ng-fullwidth-emoji。该patch另外修复了canonical mode下退格宽字符,终端模拟器只移动一格光标的问题(其实应该是kernel的bug,不妨头痛医脚)。

    设置wcwidth宽度

    对于ncurses应用,会用wcwidth计算待擦除字符的宽度。仅经过上面的配置,wcwidth仍认为emoji字符宽度为1,擦除时宽度计算不对,可能导致一些字符残余在屏幕上。

    wcwidth对字符宽度的计算由locale决定,比如对于常用的en_US.UTF-8等,glibc提供的/usr/share/i18n/charmaps/UTF-8.gz中WIDTH、END WIDTH区块给出了字符宽度信息。但其中没有列出Emoji字符,因此宽度将用缺省值1。

    我用https://gist.github.com/MaskRay/86b71b50d30cfffbca7a重新生成一个UTF-8,gzip压缩后覆盖/usr/share/i18n/charmaps/UTF-8.gz,然后执行locale-gen。修改后,可以用https://gist.github.com/MaskRay/8042e39dc822a57c217f确定wcwidth计算出来的宽度确实变更了。

    效果

    1
    echo 🚂🚊🚉🚞🚆🚄🚅🚈🚇🚝🚋🚃🚟

    为了这张笑脸真是一把辛酸泪……

    感想

    Monospace实现很困难。要画出好看的Emoji,势必要用fullwidth,这个属性如果能由字体提供是再好不过的。对于底层的glibc、glib,它们并不能知道字体,但又不得不规定字符宽度,一不小心即与字体的实际宽度产生冲突。翻了翻http://www.unicode.org/Public/UCD/latest/ucd/等,好多地方分散地提到了fullwidth,比如Halfwidth and Fullwidth Forms、East Asian Width等,不同实现对这些概念的理解会有偏差……

    在neovim里写这篇文章时又发现neovim对字符宽度也有问题,src/nvim/mbyte.c:utf_char2cells看上去是无辜的,所以谁坏掉了呢?

    参考

    • https://pixelambacht.nl/2014/multicolor-fonts/
    • Noto Emoji字体:https://github.com/googlei18n/noto-emoji
    • Emoji One Color字体:https://github.com/eosrei/emojione-color-font
    • http://stackoverflow.com/questions/23526353/how-to-get-ncurses-to-output-astral-plane-unicode-characters/


沪ICP备19023445号-2号
友情链接