解决TextField光标乱飘

周五的时候UX同学换了一批字体,然后无意间发现TextField的光标鬼畜定位了orz 今天终于有空跟进下这个问题 顺便弥补下太久没更博客

先放一个修复后的正确效果

简单定位了下是文字高度计算出来有问题:同样是height=22的情况下,正确字体的fontHeight=23,错误字体的fontHeight=39。具体看下计算文字高度的地方

fontHeight = ((face->size->metrics.ascender - face->size->metrics.descender)) >> 6UL;

网上找了下相关的资料How to get height of font in Freetype2

Below is the method used in cocos2d-x:

_lineHeight = static_cast((face->size->metrics.ascender - face->size->metrics.descender) >> 6);

Most fonts are ok. For the not-ok case, I calibrate the value after get GlyphBitmap’s height, and then use the larger one as the max line height.

被这句Most fonts are ok给雷到了😓 不过无意间看懂>> 6操作是因为freetype就是定义ascenderdescender单位是26.6 pixel format(i.e. 1/64th of pixels),挺有意思。

咱继续看看能不能找到别的引擎是怎么处理的,参考了下UE4的TTFontImporter里两种实现方法

float EmScale = 1024.f/-nHeight;
Font->EmScale = EmScale;
if (ImportOptions->Data.bUseDistanceFieldAlpha)
{
Font->EmScale *= ImportOptions->Data.DistanceFieldScaleFactor;
}
Font->Ascent = WinTextMetrics.tmAscent * EmScale;
Font->Descent = WinTextMetrics.tmDescent * EmScale;
Font->Leading = WinTextMetrics.tmExternalLeading * EmScale;
float EmScale = 1024.f/Height;
Font->EmScale = EmScale;
if (ImportOptions->Data.bUseDistanceFieldAlpha)
Font->EmScale *= ImportOptions->Data.DistanceFieldScaleFactor;
const int32 AscenderPixels = ( FT_MulFix( Face->ascender, Face->size->metrics.y_scale ) >> 6 );
Font->Ascent = AscenderPixels * EmScale;
Font->Descent = ( FT_MulFix( Face->descender, Face->size->metrics.y_scale ) >> 6 ) * -EmScale;
Font->Leading = ( FT_MulFix( Face->height, Face->size->metrics.y_scale ) >> 6 ) * EmScale - Font->Ascent - Font->Descent;

其中第二种方法和cocos2d-x版本比较接近,我导入了下错误显示的ttf也是有问题的。

不过这里引发了我的一个疑问:WinTextMetricsFace->size->metrics是啥区别?继续Google一发,找到了Line-spacing: getting it rightLine Metrics,描述比较清楚。这些值其实是存在hheaOS/2两个不同的地方,然后作者是这么说的

We recommend that all Ascender/Ascent values be set to be equal, Descender/Descent value be set to be equal, and LineGap be set to zero.

For example, in UFO3 format font sources (fontinfo.plist) this would mean:

openTypeHheaAscender = openTypeOS2TypoAscender = openTypeOS2WinAscent = ascender
openTypeHheaDescender = openTypeOS2TypoDescender = descender
openTypeOS2WinDescent = -descender
openTypeHheaLineGap = openTypeOS2TypoLineGap = 0

然后我掏出FontForge,回过头去检查下这块信息:对于光标位置计算正确的字体,确实满足上面的建议

对于光标位置计算出错的字体,疑点出现🤣

出于好奇,我反查了第二个字体的信息,重新去网上随便下了一份替换进来,居然是一切正常的——那也就是说svn上这份祖传字体被人动过手脚有毒💀

继续折腾下,看看能不能通过修改信息让这个字体显示正确呢:

  • 尝试在General面板里直接修改Ascent和Descent为WinAscent和WinDescent的值,结果会触发字体整体的缩放(因为EmSize=Ascent+Descent),放弃
  • 倒过来尝试WinAscent/WinDescent改成TypoAscent/TypoDescent, HHeadAscent/HHeadDescent也改成TypoAscent/TypoDescent,测试了下没什么问题

Disclamer 这个操作属于我瞎尝试,不确定有没有什么隐患,准备明天再去找UX同学问问看有没有懂字体的…

至此问题看似解决,记录下来比较有意思的还是查到最后是ttf这种一般认为不会出问题的资源有👻