React Native App 自定义字体在安卓系统中的行高问题解决
Photo by Jeroen den Otter on Unsplash
问题
在 React Native app 应用自定义字体时出现了在 Android 手机端行高与 iOS 端不一致的问题。iOS端渲染的行高与设计预期一致,而安卓里所有的文字却整体上移,导致了段落行距偏小及按钮文字不对齐的问题。
判断出错点
问题出现了,那么该如何修复?字体在React Native app 里的实现不如预期,问题可能源于两处:
- 字体属性
- App 的源码
在跟工程师远程同步调试字体的 styles 后发现相关行高、margin 或 padding 的应用未能直接解决行高问题,反而会破坏本已正常渲染的 iOS 端的显示。
样式代码调试之后发现源码似乎并不是问题的所在,且当 app 应用系统默认字体时,文字的行距显示是正常的。
这也就意味着字体文件本身可能存在问题。
解决方案
经过一番搜索,终于在设计师 Martin Adamko 的 Medium 上遇见了这篇文章 “Consistent font line height rendering (一致的字体行高渲染)” 。作者在使用 React Native 开发 app 时同样遇到了自定义字体在 iOS 和 Android 系统下行高不一致的问题。
于是跟着这篇文章里的解决方法使用 Font Tools for Xcode 对字体 ascender 和 descender 的值进行了调整及字体再输出后一试,安卓的行高问题竟然解决了。
方法
-
确保 Mac 里安装了 Xcode,然后下载并安装 Font Tools for Xcode (需登录开发者账号,可用个人 Apple ID 登录)
-
打开 Terminal.app,使用命令行导出目标字体的 *.hhea.xml 文件,该文件存有字体行高相关属性
$ ftxdumperfuser -t hhea -A d pathToYourFont.ttf
-
运行上述命令行后,目标字体所在文件夹会出现一个与字体同名且格式为 .xml 的文件。打开该文件即可看到包括
ascender
,descender
和lineGap
等字体行距等相关属性。接着确保lineGap = 0
,然后考虑调整ascender
和descender
的值。 -
对于 ascender 和 descender 需要设什么值合适,我先用上述第2点的命令行分别调出纯 Noto Sans 中文的字体 的hhea及纯英文字体文件的 hhea。 经过和 app 里应用的整合了两种字体的字体包比对之后,发现 app 里用的字体的 ascender/decender 是880/-120,而 Noto Sans 中文字体是1160/-180,而英文字体的是800/-220。 于是我尝试将 app 中用的整合字体的这两个值改成了中英文单独字体相关属性的中间值,即 1080/-200。
-
然后使用下属命令行将改后的属性更新至 .ttf 文件:
$ ftxdumperfuser -t hhea -A f pathToYourFont.ttf
-
这个更新后的字体文件经工程师更新调试后发现行距问题肉眼看已恢复正常。
如果第一次更改数值后发现行高问题有改善但还需要调整,可以继续按上述方法进一步调整 ascender 和 decender 的值来调试直至行高达到理想状态。
关于该自定义字体
为了达到在中文界面下的英文也能显示品牌的特定字体,App 安装的 NotoSans 中文字体中的英文字型和半角字符经 FontLab app 整合入了另一款整体规格更小的英文字体。
然而,虽然该自定义字体在 Android 的行高问题暂时解决了,但是该字体在安卓中的字体渲染效果不佳,有锯齿。目前搜索到的信息是说因为安卓的渲染机制导致自定义字体会有各种问题,希望能早日找到解决方案。
参考
关于 hhea 里字体属性的相关信息,可参考 Apple 的 ”The hhea table” 或 Microsoft 的 ”hhea — Horizontal Header Table”