在浏览器中重现文本边界框
reproduce Bounding Box of text in Browsers
在浏览器中使用 SVG 时,浏览器有一个 getBBox
功能可以为您提供各种元素的边界框。但是当涉及到文本元素时,我真的很困惑这个边界框是如何计算的。
我知道字体大小基于字体文件中指定的 em-Box。
然而,我的测试表明,其中 none 产生与 FF 或 Chrome 相同的结果(在字体大小 1000 上仅相差几个像素):
fontSize != bbox-height
(ascender-descender)/unitsPerEm * fontSize != bbox-height
(unitsPerEm-descender)/unitsPerEm * fontSize != bbox-height
...maybe adding a fixed amount to ascender for accents? Like Ć
那么浏览器中文本的 bbox 高度背后的秘密是什么?
我什至试图查看 FF 和 Chrome 的源代码,但找到计算所基于的正确位置本身就是一个挑战
// 编辑:回应评论:
我想像在浏览器中那样计算 svg 文本的 bbox(复制行为)。我需要知道正确计算 bbox 所需的字体指标和用于计算(宽度和高度足够)
的公式
经过大量研究、尝试和错误后,我找到了一个可能的解决方案,至少可以解释文本 bbox 尺寸的 chrome 行为。
BBox 高度
首先我使用 npm 包 fontkit
来加载和解析字体文件。
fontkit
给你几个字体的整体指标,包括:
- font.ascent
- font.descent
- font.lineGap
- font.unitsPerEm
所以为了计算 bbox 的高度,我计算如下:
bboxHeight = (font.ascent - font.descent + font.lineGap) / unitsPerEm * fontSize
但是,当字体大于 em 框 (font.ascent - font.descent > unitsPerEm
) 时,这会导致错误。在这种特殊情况下 bboxHeight
是 font.ascent - font.descent
.
这导致以下高度代码:
var fontHeight = font.ascent - font.descent
var lineHeight = fontHeight > font.unitsPerEm ? fontHeight : fontHeight + font.lineGap
var height = lineHeight/font.unitsPerEm * fontSize
BBox 宽度
为了计算文本的宽度,我使用了 fontkit
的 layout
功能。 layout
使您可以访问从中绘制文本的字形,还可以访问字形的度量。我们需要的指标是 advanceWidth
,它包括当前 glpyh 旁边的其他字形的边距。通过总结所有 advanceWidth
s 并相应地缩放它们,我最终得到了 bboxWidth
:
var width = font.layout(text).glyphs.reduce((last, curr) => last + curr.advanceWidth, 0)
width = width / font.unitsPerEm * fontSize
BBox y 位置
麻烦还不止于此,我们还要计算bbox的y位置。这是一个相当简单的公式:
var bboxY = y-font.ascent/font.unitsPerEm * fontSize
其中 y 是您将从 dom(y
和 dy
属性)
中提取的理论位置
BBox x 位置
这就是您从 dom(x
和 dx
)
中提取的数字
整箱:
var box = {
x:x,
y: y-font.ascent/font.unitsPerEm * fontSize,
width: width
height: height
}
希望对其他人有所帮助!
在浏览器中使用 SVG 时,浏览器有一个 getBBox
功能可以为您提供各种元素的边界框。但是当涉及到文本元素时,我真的很困惑这个边界框是如何计算的。
我知道字体大小基于字体文件中指定的 em-Box。
然而,我的测试表明,其中 none 产生与 FF 或 Chrome 相同的结果(在字体大小 1000 上仅相差几个像素):
fontSize != bbox-height
(ascender-descender)/unitsPerEm * fontSize != bbox-height
(unitsPerEm-descender)/unitsPerEm * fontSize != bbox-height
...maybe adding a fixed amount to ascender for accents? Like Ć
那么浏览器中文本的 bbox 高度背后的秘密是什么?
我什至试图查看 FF 和 Chrome 的源代码,但找到计算所基于的正确位置本身就是一个挑战
// 编辑:回应评论: 我想像在浏览器中那样计算 svg 文本的 bbox(复制行为)。我需要知道正确计算 bbox 所需的字体指标和用于计算(宽度和高度足够)
的公式经过大量研究、尝试和错误后,我找到了一个可能的解决方案,至少可以解释文本 bbox 尺寸的 chrome 行为。
BBox 高度
首先我使用 npm 包 fontkit
来加载和解析字体文件。
fontkit
给你几个字体的整体指标,包括:
- font.ascent
- font.descent
- font.lineGap
- font.unitsPerEm
所以为了计算 bbox 的高度,我计算如下:
bboxHeight = (font.ascent - font.descent + font.lineGap) / unitsPerEm * fontSize
但是,当字体大于 em 框 (font.ascent - font.descent > unitsPerEm
) 时,这会导致错误。在这种特殊情况下 bboxHeight
是 font.ascent - font.descent
.
这导致以下高度代码:
var fontHeight = font.ascent - font.descent
var lineHeight = fontHeight > font.unitsPerEm ? fontHeight : fontHeight + font.lineGap
var height = lineHeight/font.unitsPerEm * fontSize
BBox 宽度
为了计算文本的宽度,我使用了 fontkit
的 layout
功能。 layout
使您可以访问从中绘制文本的字形,还可以访问字形的度量。我们需要的指标是 advanceWidth
,它包括当前 glpyh 旁边的其他字形的边距。通过总结所有 advanceWidth
s 并相应地缩放它们,我最终得到了 bboxWidth
:
var width = font.layout(text).glyphs.reduce((last, curr) => last + curr.advanceWidth, 0)
width = width / font.unitsPerEm * fontSize
BBox y 位置
麻烦还不止于此,我们还要计算bbox的y位置。这是一个相当简单的公式:
var bboxY = y-font.ascent/font.unitsPerEm * fontSize
其中 y 是您将从 dom(y
和 dy
属性)
BBox x 位置
这就是您从 dom(x
和 dx
)
整箱:
var box = {
x:x,
y: y-font.ascent/font.unitsPerEm * fontSize,
width: width
height: height
}
希望对其他人有所帮助!