作者 Moonshot 日期 2018-07-10
那一天和妹纸聊完天后,一切都 索然无味 怅然落失。
因为 我要是一整天没和你说上话,那一天都黯淡无光。 —HIMYM
然后就开始看看咨询和文章,然后发现Google提供Emoji兼容包EmojiCompat Support Library .是通过字体来实现Emoji的兼容。这时候我才发现原来系统Emoji的渲染是通过字体的实现的,原来字体里可以放非纯色的字形。
那么如果可以将国内主流的Emoji样式图片(Apple的样式)打包进字体,或者设计师自己设计的一套Emoji。只需设置字体,不需要通过常见的ImageSpan进行过多的代码上的兼容,那不是很完美吗?
可行性
使用自定义字体方案 最重要的保证除了Emoji之外其他部分能用原系统字体进行渲染,才能保证原有的文本体验。
要验证可行性, 那么首先需要有个彩色字体进行验证。然后发现这样一个谷歌的开源库noto-emoji。里面NotoColorEmoji.ttf这个彩色字体文件。
测试后发现在8.0上正常,但是在一台5.1、4.4的机子是异常的。然后网上说是用旧版本才可以。然后经测试Tag:v2017-05-18-cook-color-fix下的彩色字体在8.0、7.0、5.1、4.4上都是正常的。且普通文本(字体文件未定义的文本)和平时渲染一样、
但是在4.3上是无法生效的。原因是google在4.4的之后的版本才通过noto-font来支持彩色的Emoji表情。4.3上默认是黑白的Emoji,所以只能兼容4.4及之后的版本。
打包彩色字体文件
然后花了十分钟了解了下这个库,打包的关键在于third_party下的python脚本emoji_builder.py
基础准备
- 安装python2.7的环境
- 安装依赖的另一个库nototools 。具体步骤详见官方Github
- 了解普通字体的基本一些基本概念(其实也可以不用)
打包步骤
打包命令如下:
python emoji_builder.py source-input.ttf color-font-outout.ttf ./emoji_image/
因此我们需要一个源字体文件和需要的打包的图片资源的文件夹。在开源库中分别就是NotoEmoji-Regular.ttf]和png/128/。然后代志没有憨人想的那么简单,直接用这两个资源去执行脚本并不能成功打彩色字体文件。(提供程序员门槛,从而避免失业???)然后就需要自己的改脚本了。(见后文)
没兴趣的同学可以直接使用修改后的版本-分支android_emoji_font
准备一个普通的字体文件(可使用NotoEmoji-Regular.ttf使用相应的工具进行修改),内含需要处理的emoji的unicode编码和命名。
要适配的图片资源按规则修改并修改成相应的文件名如1f603.png 。1f603为unicode编码,对应这个步骤中的字体中的编码。
然后执行打包的脚本命令即可
踩坑改脚本
确定图片资源的命名规则
遇到的第一个问题:图片命名导致编码读取失败,脚本终止
img_files = {}
glb = "%s*.png" % img_prefix
print("Looking for images matching '%s'." % glb)
for img_file in glob.glob (glb):
codes = img_file[len (img_prefix):-4]
if "_" in codes:
pieces = codes.split ("_")
cps = [int(code, 16) for code in pieces]
uchars = "".join ([unichr(cp) for cp in cps if not is_vs(cp)])
else:
cp = int(codes, 16)
if is_vs(cp):
print("ignoring unexpected vs input %04x" % cp)
continue
uchars = unichr(cp)
这段代码主要遍历图片文件夹下的图片,获得根据文件名获取对应的unicode编码。问题在于img_file[len (img_prefix):-4]只去除了后缀.png,但是官方提供的文件是emoji_u[xxx].png的的文件所以无法直接使用。(所以直接去掉前缀即可)
修复0-/u00ff (0-255)与/uffff编码之外的打包失败
首先了解下普通字体内部的组成,一个字体文件定义了若干图形。用工具打开ttf文件可以看到类似下图的内容。
一般有三个部分:字形(一般是矢量图)、编码、命名
0-255的编码根本不能满足我们的需求,且占用了基本的字符,而一般emoji的编码范围是在/uFFFF之外,显然现状是不行的。
那么问题出在哪?主要有两个地方 1.编码的获取 2.对应的命名/字形名的获取
命名的获取
codes = img_file[len (img_prefix):-4]
if "_" in codes:
pieces = codes.split ("_")
cps = [int(code, 16) for code in pieces]
uchars = "".join ([unichr(cp) for cp in cps if not is_vs(cp)])
else:
cp = int(codes, 16)
if is_vs(cp):
print("ignoring unexpected vs input %04x" % cp)
continue
uchars = unichr(cp)
字形名的获取
当len (uchars)为1时(/u00ff/uffff 这种长度为2)通过Cmap)表获取编码对应的命名
超过时根据提供的其他函数获取命名
if len (uchars) == 1:
try:
glyph_name = unicode_cmap.cmap[ord (uchars)]
except:
print("no cmap entry for %x" % ord(uchars))
raise ValueError("%x" % ord(uchars))
else:
glyph_name = get_glyph_name_from_gsub (uchars, font, unicode_cmap.cmap)
glyph_id = font.getGlyphID (glyph_name)
glyph_imgs[glyph_id] = img_file
其中255内正常,在命名超过00ff.png时uchars = unichr(cp)会报错。这个函数是获取相应的的unicode的字符串(其实知道为什么错误)然后通过直接获取对应unicode编码的16进制数字。
uchars = int(img_file[len(img_prefix):-4], 16)
相应ord (uchars)直接替换成uchars后则直接成功
然后获取字形名直接使用: 这样则可以保证在/uffff之外的编码正常
glyph_name = unicode_cmap.cmap[uchars]
其他注意事项
- 图片命名先需要严格限定为[unicode].png 例如1f609.png
- 原始的普通字体需要只放置需要的打包的字形的信息,其他无关的内容要删除,否则会导致Texiview无法显示该字形
性能分析与对比
Android上的表情主流方案是使用ImageSpan去处理。这里直接使用Github上一个库进行测试Emojicon它使用的图片资源的规格也是45*45px.
对比时间:Activity onCreate 到TextView onDraw结束
Emoji编码数量 | EmojiCon三次耗时 ms | 彩色字体三次耗时 |
---|---|---|
40 | 390+208+222 | 119+184+128 |
200 | 441+305+306 | 216+118+121 |
800 | 588+599+457 | 208+131+124 |
由于ImageSpan的方案需要做更多的编码匹配,加载图片设置Span等操作,可以预见性能上会差于使用彩色字体的方案。当然也少些了很多代码。
在iOS使用彩色字体
先上一张图,其实各个平台对彩色字体都有支持,只是可是所扩展的规则不太一样。所以理论上只有打出合适的字体文件,在IOS也是可以使用的。就可以达到设计师设计一套独特的emoji表情在多平台上使用。
然后一不做二不休,花了21天(滑稽)学习了下iOS。
打iOS的彩色字体文件使用Emoji-Tools。这个工具可以打iOS的字体文件也可以打Android的字体文件(所以上文也不是很有必要,心酸)
在iOS使用自定义字体
UIFont *font = [UIFont fontWithName:@"AppleCustomEmoji" size:24];
然后其实iOS使用自定义字体最麻烦的就是确认这个AppleCustomEmoji这个字体名,实际上Emoji-Tools打包的出来的字体名是Apple Color Emoji和系统默认是一样的名字,所以是不能直接使用。
然后最终找到一个工具TTFEDIT,使用它修改掉字体名,然后重新添加在项目中即可使用
最终效果:
Android7.0的果冻人是真的好看(辣鸡谷歌8.0变得难看得一匹)
在Web上使用彩色字体
TODO
总结
-彩色字体来适配或者实现独立一套Emoji表情适用各个平台
-在Android上只能兼容4.4及以上的系统,但性能较好
-字体包体积实测略大于图片资源文件总大小,在接受范围内。(图片太大可走在线现在)
缺点:不支持打包gif、webp格式的图片(其实没试过,应该是不行)
参考文章
- 彩色字体生成小记 http://www.shushilvshe.com/data/color-font-build.html#data/color-font-build
- 探索在Android中使用Emoji Font的方法 http://ragnraok.github.io/android-emoji-font-method.html
- afdko的Python项目详细描述... 34631
- matlab 中图字体设置,关于m... 2958
- 易思软件中字体过大及解决y... 3675
- 命令行字体批处理工具——A... 3744
- FontLab for Mac(Mac字体编... 2964
- 彩色字体:Emoji全平台适配... 3676
- 教程没人看系列之字体属性... 5675
- Glyphs:字体设计师的福音 3791
- photoshop之PS 格式全介绍 ... 33057
- 再探微软开源字体:Cascadi... 4526