为什么同样的 font-size , 文字高度不一样?
每个字体在设计的时候, 都是基于一个 EM Square, 这是活字印刷中字模的高度.
在数字化字体中, em 是空间的数字化定义总量. 在 OpenType 字体中, UPM 或 em 大小通常是 1000 单位. 在 TrueType 字体中, UPM 约定是 2 的幂, 通常是 1024 或 2048.
实际情况中, 许多字体的内容高度其实是比 em box 要大的.
同是 font-size: 30px 情况下, 此处的 Noto Sans JP 的字体空间就比 Kosugi Maru 要高. 而且在字体框内, 垂直方向上还有留白.
垂直方向留白大小的计算公式, 可以由字体文件中的定义得到:
internal leading = ascent - descent - EM_size
代码片段
可以在这个 fiddle https://jsfiddle.net/hikerpig/xkt3o4sd/4/ 里看到结果.
使用 canvas 度量文字宽度
- function getMetricsByCanvas(canvas, str, font) {
- const ctx = canvas.getContext('2d')
- ctx.font = font
- console.log(str, font, ctx.measureText(str))
- }
- Windows.onload = function() {
- const canvas = document.createElement('canvas')
- document.body.appendChild(canvas)
- Windows.canvas = canvas
- getMetricsByCanvas(canvas, '字', '30px Noto Sans JP') // 字 30px Noto Sans JP TextMetrics {width: 30}
- }
不过此 API 是拿不到字符的高度的. 就有一些比较黑的方法来估算字体的内容高度, 例如使用大写字母'M' 的宽度作为内功高度的近似. 这些技巧其实都与字形设计的惯例有关, 在拉丁字母中,'M' 是字形最为饱满和方正的字符, 高度与宽度近似.
不过明显这个惯例对于以上两个日文字体并不适用.
汉字因为字形多数为饱满的方块字, 用宽度去估计内容高度其实更容易, 例如 '人' 和 '口' 就很好用.
创建临时 dom 元素用于度量高度
能拿到更全的字形盒信息
- function getMetricsBySpan(str, font) {
- var d = document.createElement("span");
- d.style.font = font;
- d.textContent = str;
- document.body.appendChild(d);
- const emHeight = d.offsetHeight;
- const emWidth = d.offsetWidth;
- console.log(str, font, { emWidth, emHeight })
- document.body.removeChild(d);
- }
- getMetricsBySpan('字', '30px Noto Sans JP') // 字 30px Noto Sans JP {emWidth: 30, emHeight: 45}
参考
SO 上的一个问题
- FreeType Glyph Metrics
- Typographic effects in canvas
- Deep dive CSS: font metrics, line-height and vertical-align
来源: https://juejin.im/entry/5bee1964f265da61757355bc