前段时间公司有个启用了手机端访问的系统,但负责测试的姑娘很快发现了 Emoji 的乱码问题。

存储方面,因为那个系统用的 MySQL 所以只要把数据库改成支持 4 字节的 UTF-8 编码就可以正常保存 Emoji 的数据了,但管理人员的数据导出还是一串问号。

托前人的福,很快发现了这个问题的原因在于 POI 库所依赖的 xmlbeans 库上。


喜闻乐见的 tl;dr 环节:

如果你不喜欢这种 dirty hack ,请选择 xmlbeans-2.6.2.jar 。


以下是折腾流水账。

歪果仁的热心解答

Apache 官方提供的 xmlbeans 库,目前最新(实际上也是最后一个)版本是 2.6.0 。

上面说的那个 2.6.2 版本,是好心人修改源码自行编译的。虽然也解决了这个问题,但是我还没有认真去了解这个版本的修改点,所以暂且不表。

基于 stack overflow 上的这个解决方案 我们可以知道,实际上包括 Emoji 的 0x10000 到 0x10FFFF 部分的字符被当成了 bad char 过滤掉了而已。顺便这个判断区间还有遗漏的部分,所以直接改掉判断条件重新打包一个是坠吼滴。

……可我偏偏是个懒人,看到除了下完源码之外还要配置环境和 Ant 我就懒得动了。

最小修改的结论是 4 个字节

还好是 Java ,这要是 C 系列我可能就去搭编译环境了。(我得承认我不会用那些分析工具)

根据说明,问题 Class 分别是 Saver$OptimizedForSpeedSaver.class 和 Saver$TextSaver.class 。我先从 jar 里把这两个文件揪出来。

原本的判断条件里少了 0xD800 到 0xDBFF 的区间,多了 0x10000 到 0x10FFFF 的区间。那我把这个判断数改掉不就好了?

随便找个你喜欢的十六进制编辑器,找一下第一个区间的结束,0xD7FF 。很好,只有一个。

而且接下来几个区间的数值就跟在后面。

Saver$OptimizedForSpeedSaver.class

Saver$TextSaver.class

这么容易就被我发现了的东西必然…… 是先改了看看效果。

Saver$OptimizedForSpeedSaver.class

Saver$TextSaver.class

就这么改了 4 字节。然后,把这两个 Class 再塞回原来的位置就行了。

😂😂😂

你非要说 Excel 里显示的 Emoji 跟在网页上看到的不一样那我也没什么好说的了。

同一个 Emoji 就是用 Chrome 和 Edge 分别查看都可能不一样…… 况且 Excel 用的是字体里对应编码的符号,而不是用额外渲染的。

而且字体里没对应的就是显示不出来,组合式的 Emoji 就是显示拆分开的,我能怎么办,我也很绝望啊。

总不能见一个 Emoji 插一个 PNG 吧?