I want to be able to calculate the actual height occupied by a text (or span) element in a SVG.
For now, I achieve this by calculating the height of the bounding box of the object, but it takes the height of the font glyph, so any text I put in the element has the same height, like in the example below :
var minA = document.getElementById('min-a'),
capA = document.getElementById('cap-a'),
minAHeight = document.getElementById('min-a-height'),
capAHeight = document.getElementById('cap-a-height')
;
minAHeight.innerHTML = minA.getBBox().height;
capAHeight.innerHTML = capA.getBBox().height;
<svg xmlns="" viewBox="0 0 180 80">
<text id="min-a" x="20" y ="20">a</text>
<text id="cap-a" x="20" y ="50">Â</text>
<text id="min-a-height" x="70" y ="20"></text>
<text id="cap-a-height" x="70" y ="50"></text>
<svg>
I want to be able to calculate the actual height occupied by a text (or span) element in a SVG.
For now, I achieve this by calculating the height of the bounding box of the object, but it takes the height of the font glyph, so any text I put in the element has the same height, like in the example below :
var minA = document.getElementById('min-a'),
capA = document.getElementById('cap-a'),
minAHeight = document.getElementById('min-a-height'),
capAHeight = document.getElementById('cap-a-height')
;
minAHeight.innerHTML = minA.getBBox().height;
capAHeight.innerHTML = capA.getBBox().height;
<svg xmlns="http://www.w3/2000/svg" viewBox="0 0 180 80">
<text id="min-a" x="20" y ="20">a</text>
<text id="cap-a" x="20" y ="50">Â</text>
<text id="min-a-height" x="70" y ="20"></text>
<text id="cap-a-height" x="70" y ="50"></text>
<svg>
How can I calculate the actual height of each element ?
Share Improve this question edited Dec 22, 2018 at 8:57 Crashalot 34.6k63 gold badges284 silver badges461 bronze badges asked Feb 20, 2017 at 7:42 FitiFiti 1991 silver badge9 bronze badges 3- There's no API for that,i.e. you can't. – Robert Longson Commented Feb 20, 2017 at 7:51
- @RobertLongson you absolutely can – cyberwombat Commented Aug 11, 2017 at 15:59
- @RobertLongson posted :) – cyberwombat Commented Aug 11, 2017 at 16:16
4 Answers
Reset to default 3@Fuzzyma is on the right track. You want to use OpenType.js and load the font in the browser.
// These two match whatever you are doing in your code
const text = 'Hello'
const fontSize = 20
// Load font - I find ttf work better than woff (has extra data for other calculations if needed)
opentype.load(`/path/to/font.ttf`, (err, font) => {
// Actual dimensions of the text
const bb = font.getPath(text, 0, 0, fontSize)
})
That will give you the real bounding box which may be enough for your needs. If you actually need to use this to know the screen position on text it is possible but its quite a bit more challenging.
The gist of it is that you need to match baselines. The font contains the ascender/descender (font.tables.hhea.asscender
, font.tables.hhea.descender
) which total up to the font size once converted to em (font.tables.head.unitsPerEm * fontSize
).
Armed with this you then need to go through each glyph of the font and get the yMax
, yMin
, xMin
and xMax
. Use the ascender/descender and the Y coords to determine how far away from the "fake" bounding box (the one the DOM returns) your new bb is. The left and right are easier except for some weird fonts that have letters that can stretch beyond their neighbors (edge case).
On top of that browsers add a per browser vertical offset... This can be extracted by getting the y
attribute of the SVG text element (i.e -5) - then you take your font size, divide by half and take of this y
and you have the vertical offset that browsers use (in our case 5). Each browser is different but the y
coordinate reflects that.
Not sure if this solves your problem, but I was able to calculate the dimensions of a single letter using the canvas measureText
method:
function getLetterSize(letter) {
const ctx = document.createElement("canvas").getContext("2d");
ctx.font = "25px sans-serif";
const dim = ctx.measureText(letter);
return {
width: dim.actualBoundingBoxRight + dim.actualBoundingBoxLeft,
height: dim.actualBoundingBoxAscent + dim.actualBoundingBoxDescent
};
}
To do that you would need to load and parse a fontfile and use the glyphs stored in there to calculate the bouding box. On a server you would use opentype.js or fontkit to load the font and get the bounding box of some text.
However on the client you need to parse the fontfile yourself. Thats quite easy for svg fonts but bees harder when you deal with binary formarts (e.g. truetype).
But when you managed to do that you can draw the paths of the glypgs and use the browsers getBBox()
to calculate the bounding box you want.
Well thats the theory - but my guess is, its easier to work around that issue than writing your own font file parser.
When someone knows one, feel free to edit!
For Text in svg you can manually set the font size if you wish
<svg:text #t
[attr.x]="xoffset"
[attr.y]="yoffset"
font-family="Courier"
[attr.font-size]="fontsize">
{{bottom_text.toUpperCase()}}
</svg:text>
this will give you a static sized font within your svg element. You also have the option of using the browser dev tools to "inspect element" on the actual element. Which on selection shows you the width and height of the selected element. (see picture for example) This "hover over" height will include any padding that is associated with the element, but inspecting the attributes listed on the element it should also contain the font only height "font-size"