Reputation: 8474
When using SVG in the Browser the browser has a getBBox
function to give you the bounding box of various elements. But when it comes to text elements it really confused me how this bouding box is calculated.
I know that fontsize is based on the em-Box which is specified in the font file.
However my tests show that none of these produce the same results as in FF or Chrome (which differ only a few px on fontsize 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 Ć
So what is the secret behind the bbox height of text in browsers?
I even tried to look into the source code of FF and Chrome but finding the right place where the calculation is based is a challenge on its own
// EDIT: In response to the comment: I want to calculate the bbox of svg text as done in the browser (replicate the behavior). I need to know the metrics of the font which are needed to correctly calculate the bbox and the formular which is used to calculate the (width and height is sufficiant)
Upvotes: 1
Views: 1386
Reputation: 8474
After lots of reasearch and tril and error I found a possible solution to at least explain chromes behavior of text bbox dimensions.
First of all I used the npm package fontkit
to load and parse the fontfile.
fontkit
give you several metrics for the font in a whole which includes:
So to calculate the height of the bbox I figured the following:
bboxHeight = (font.ascent - font.descent + font.lineGap) / unitsPerEm * fontSize
However, that leads to errors when having a font which is bigger then the em box (font.ascent - font.descent > unitsPerEm
). In this special case the bboxHeight
is font.ascent - font.descent
.
That leads to following code for the height:
var fontHeight = font.ascent - font.descent
var lineHeight = fontHeight > font.unitsPerEm ? fontHeight : fontHeight + font.lineGap
var height = lineHeight/font.unitsPerEm * fontSize
to calculate the width of the text I made use of the layout
feature of fontkit
. layout
gives you access to the glyphs the text is drawn from and also access to the metrics of the glyph. The metric we need is the advanceWidth
which includes the margins to other glyphs next to the current glpyh. By summming up all advanceWidth
s and scaling them accordingly I ended up with the bboxWidth
:
var width = font.layout(text).glyphs.reduce((last, curr) => last + curr.advanceWidth, 0)
width = width / font.unitsPerEm * fontSize
Trouble does not stop here, we still have to calculate the y position of the bbox. Thats a rather simple formula:
var bboxY = y-font.ascent/font.unitsPerEm * fontSize
Where y is the theoretical position you would pull from the dom (y
and dy
attribute)
Thats just the figure you pull from the dom (x
and dx
)
var box = {
x:x,
y: y-font.ascent/font.unitsPerEm * fontSize,
width: width
height: height
}
Hope it helps someone else!
Upvotes: 7