1. 什么是字号与行高
什么是字号大小?字号大小就是字体的高度,例如设置字号为 50px,那么它的高度如下图所示:
什么是行距呢?如下图所示:
其中半行距 = (lineHeight – fontSize) / 2。
但是实际上,font-size 经常不等于渲染的高度,如下图所示:
对于笔者用的 ProximaNova 这个字体,设置 font-size 为 30px,实际上高度为 42px。为什么文字的高度不等于字号的高度?这得从字体设计说起。为此装了一个 FontForge 和 RoboFont 软件设计一款自己的字体。
2. 设计字体
打开 RoboFont,如下图所示:(这个软件经常闪退,需要注意保存)
双击 l 和 y 两个字母,用钢笔工具勾勒形状,如下图所示:
从上图可以看到它有一些刻度和度量线,画一个示意图如下所示:
这些度量线的位置可以自己设置:
Units Per Em 表示一个字的高度有 1000 个单位,baseline 的坐标为 0,其它线的坐标相对于 baseline,如下图所示:
然后把这个设计好的字体导出为 my-font.ttf 文件,在网页通过 @font-face 引入,如下代码所示:
@font-face {
font-family: 'my-font';
src:url('/Users/yincheng/Desktop/my-font.ttf');
font-weight: normal;
font-style: normal;
}
然后使用这个 font-family,你会发现,这个字体的 font-size 和 height 几乎完全一致,如下图所示,分别设置 font-size 为 35px、45px、55px:
为什么我们设计的字体会如此 “完美”,而其他人的字体高度总是要大一点呢?
3. 为什么字体高度大于字体大小
为此我们用 FontForge 打开 ProximaNova.ttf,因为这个软件可以查看字体的更多信息,就是界面丑了点。如下图所示:
然后点击 Element → FontInfo,切到 OS/2 的 Metric 标签,如下图所示:
把鼠标放到相应的输入框,FontForge 会提示你 Windows 系统是使用 Win Descent 和 Ascent 决定字体内容高度,而 Mac 是用的 HHead Descent 和 Ascent。上面字体在 Mac 下的 Ascent 为 1079,Ascent 为 - 336,如下图所示:
同时它的 units of em 仍然是 1000,如下图所示:
而它的内容区域 content-area 大小为 1079 - (-336) = 1415 是 font-size 1000 unit 的 1415 / 1000 ~= 1.4 倍,这就解释了一开始提出的问题:
设置 font-size 为 30px,实际上显示 42px,因为 30 * 1.4 = 42px,为进一步验证,把我们设计的字体 my-font 改一下它的 Ascent,如下图所示:
这样它的内容区域高度就变成了 1250unit,是字号大小的 1.25 倍,导出为一个新的字体,在网页上使用,如下图所示:
设置 font-size 为 50px,它的 content-area 高度为 50 * 1.25 = 62.5px。这就证明了上面的分析是对的。
那么为什么设计师们要这样搞呢,为什么不控制在 1000 个 unit 的范围内?首先因为常用的 unit per em 为有以下几个值:
如果你的 unit 选得越大,那么曲线的光滑粒度可控制得更细,如下图所示:
但是如果选 1000 的话,因为它是一个整千,比例什么的应该会比较好控制。
其次,大于 1000 可以让可控制的区域更大,一般不会让字刚好撑到底线和顶线,如下图所示:
4. 字体的宽度
可以在 RoboFont 里面设置每个字的宽度,例如 y 这个字母我要让它比 z 占的空间小一点,如下图所示:
y 为 400,z 为 500,也就是说 y 的宽度为高度的 0.4,z 的宽度为高度的 0.5,因为高度是 1000.
font-size 为 50px 的时候,4 个 yz 的宽度为 180px,如下图所示:
因为:(50 * 0.4 + 50 * 0.5) * 4 = 180px。
再讨论一个经典的问题。
5. 图片底部的空白
有以下 html:
<div style="border:1px solid #ccc"><img src="/Users/yincheng/Desktop/2.png"></div>
在浏览器下面显示为:
为什么图片不是和 div 底部贴在一起,而会有一点空白呢?
先来看一下这个空白有多大,如下图所示,设置 div 的 font-size 为 40px,line-height 为 60px:
div 的高度为 174,图片的高度为 154,因此这里空白的高度为 174 - 154 = 20px。
为了辅助说明,在 img 的后面跟上几个字母,如下代码所示:
<div class="img-container"><img src="/Users/yincheng/Desktop/2.png">lyz</div>
画上辅助线:
这段空白的距离就是基线 baseline 到 div 底边的距离。由于基线的坐标是 0,底线的坐标为 - 250,所以基线到底线的距离为:
250 / 1000 * 40 = 10px
由于行高为 60px,font-size 为 40px,所以底线到 div 的距离即半行距为:
(60 - 40)/ 2 = 10px
因此基线到 div 底边的距离就为:
10px + 10px = 20px
到这里就解释了为什么会有空白,以及空白的大小怎么计算。
那怎么去掉这段空白呢,可以设置 div 的行高为 0。并且需要注意的是在怪异模和有限怪异模式下,为了计算行内子元素的最小高度,一个块级元素的行高必须被忽略,所以即使不设置 div 的行高为 0,图片也是和 div 贴在一起的。这个我们在《从 Chrome 源码看浏览器如何构建 DOM 树》讨论过。
参考: