The spec has a context.measureText(text) function that will tell you how much width it would require to print that text, but I can't find a way to find out how tall it is. I know it's based on the font, but I don't know to convert a font string to a text height.
-
1I would love to know a better way than the top answer. If there's some algorithm to take arbitrary point font and find the max/min bounds on it, then I would be very happy to hear about it. =) – beatgammit Sep 13 '11 at 21:54
-
@tjameson - there seems to be. See answer from ellisbben (and my enhancement to it). – Daniel Earwicker Mar 23 '12 at 23:45
-
2I'm wondering if the Unicode Character 'FULL BLOCK' (U+2588) could be used as an approximation by multiplying its width by two. – Daniel F Oct 04 '12 at 04:07
-
1It's worth noting that the answer depends a little on your requirements. For example, the height required to render the character "a" is different to the height required to render the character "y", due to the descender that extends below the baseline of the font. The HTML based answers below do not account for this and will give you a general height appropriate for any text, whereas @Noitidart's answer gives a more exact height for specific text. – Dale Anderson Aug 23 '17 at 11:07
-
4Remember that you can have characters that look like this `M̶̢̹̝͖̦̖̭͕̭̣͆̃̀̅̒̊͌̿ͅ`, so this is a really tricky problem so solve for the general case. – GetFree Dec 16 '17 at 01:09
-
When working with an SVG text element `t`, you can obtain the height via `t.getBBox().height` after applying the relevant styles via `t.setAttribute(...)`. So if the height agrees with the canvas one, this may be a simpler way to compute the height. – user2191332 Apr 05 '20 at 17:42
26 Answers
Browsers are beginning to support advanced text metrics, which will make this task trivial when it's widely supported:
let metrics = ctx.measureText(text);
let fontHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
let actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
fontHeight
gets you the bounding box height that is constant regardless of the string being rendered. actualHeight
is specific to the string being rendered.
Spec: https://www.w3.org/TR/2012/CR-2dcontext-20121217/#dom-textmetrics-fontboundingboxascent and the sections just below it.
Support status (20-Aug-2017):
- Chrome has it behind a flag (https://bugs.chromium.org/p/chromium/issues/detail?id=277215).
- Firefox has it in development (https://bugzilla.mozilla.org/show_bug.cgi?id=1102584).
- Edge has no support (https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/30922861-advanced-canvas-textmetrics).
- node-canvas (node.js module), mostly supported (https://github.com/Automattic/node-canvas/wiki/Compatibility-Status).

- 13,051
- 4
- 61
- 89
-
5Everyone please vote on the bug pages to have these features implemented sooner – Jeremiah Rose Jul 20 '18 at 01:30
-
Sadly, it's 2021 and chromium has yet to support more than width on the TextMetrics object (running in Electron). – Sam Feb 23 '21 at 20:46
-
unfortunately it is not working in ie11, because metrics.fontBoundingBoxAscent is not supported – Mizok.H Jul 08 '21 at 12:00
-
5According to Mozilla this is now supported on every modern browser. https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics/actualBoundingBoxAscent – Emperor Eto Feb 23 '22 at 14:08
UPDATE - for an example of this working, I used this technique in the Carota editor.
Following on from ellisbben's answer, here is an enhanced version to get the ascent and descent from the baseline, i.e. same as tmAscent
and tmDescent
returned by Win32's GetTextMetric API. This is needed if you want to do a word-wrapped run of text with spans in different fonts/sizes.
The above image was generated on a canvas in Safari, red being the top line where the canvas was told to draw the text, green being the baseline and blue being the bottom (so red to blue is the full height).
Using jQuery for succinctness:
var getTextHeight = function(font) {
var text = $('<span>Hg</span>').css({ fontFamily: font });
var block = $('<div style="display: inline-block; width: 1px; height: 0px;"></div>');
var div = $('<div></div>');
div.append(text, block);
var body = $('body');
body.append(div);
try {
var result = {};
block.css({ verticalAlign: 'baseline' });
result.ascent = block.offset().top - text.offset().top;
block.css({ verticalAlign: 'bottom' });
result.height = block.offset().top - text.offset().top;
result.descent = result.height - result.ascent;
} finally {
div.remove();
}
return result;
};
In addition to a text element, I add a div with display: inline-block
so I can set its vertical-align
style, and then find out where the browser has put it.
So you get back an object with ascent
, descent
and height
(which is just ascent
+ descent
for convenience). To test it, it's worth having a function that draws a horizontal line:
var testLine = function(ctx, x, y, len, style) {
ctx.strokeStyle = style;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + len, y);
ctx.closePath();
ctx.stroke();
};
Then you can see how the text is positioned on the canvas relative to the top, baseline and bottom:
var font = '36pt Times';
var message = 'Big Text';
ctx.fillStyle = 'black';
ctx.textAlign = 'left';
ctx.textBaseline = 'top'; // important!
ctx.font = font;
ctx.fillText(message, x, y);
// Canvas can tell us the width
var w = ctx.measureText(message).width;
// New function gets the other info we need
var h = getTextHeight(font);
testLine(ctx, x, y, w, 'red');
testLine(ctx, x, y + h.ascent, w, 'green');
testLine(ctx, x, y + h.height, w, 'blue');

- 114,894
- 38
- 205
- 284
-
3Why not use this text to determine the height? abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 Depending on the font you may have characters that are much higher or lower than g and M – omatase May 04 '12 at 13:04
-
"much higher" is, admittedly, overstating it (it won't let me take that out) but for instance Brush Script and Harlow Solid both have characters that dip lower than g and that's just from checking my limited list of available fonts for just a few minutes. Anyway, not a big deal, just wondered if there was a good reason not to try to cover more bases (granted you aren't going to cover them all). Love this solution by the way, is working quite well for me – omatase May 04 '12 at 13:13
-
1@ellisbben it's worth noting that the results of this differ slightly from yours, although I don't know why. For instance, yours says Courier New 8pt ==> 12 pixels high, while this says: Courier New 8pt ==> 13 pixels high. I added the "g" to your method, but that wasn't the difference. One wonders, which value would be most useful (not necessarily technically correct). – Orwellophile May 23 '13 at 07:51
-
@ellisbben If it makes you feel better, Daniel's answer doesn't handle fonts with spaces in their names. :p So, not as improbably perfect as you might think. :p – Orwellophile May 23 '13 at 07:57
-
3I could only get things working correctly when I changed the first line of `getTextHeight()` to `var text = $('Hg').css({ 'font-family': fontName, 'font-size' : fontSize });`, i.e. adding the size separately. – cameron.bracken Jul 18 '13 at 21:25
-
1How to make it work for non-english text? see http://jsfiddle.net/siddjain/6vURk/ – morpheus Feb 11 '14 at 21:59
-
1
-
Hey guys, I have distilled this technique into a module that implements a variation of this tactic and attaches it to the CanvasRenderingContext2D object: https://github.com/ryansturmer/em.js – Ryan Mar 02 '15 at 05:04
You can get a very close approximation of the vertical height by checking the length of a capital M.
ctx.font = 'bold 10px Arial';
lineHeight = ctx.measureText('M').width;

- 18,344
- 20
- 107
- 140

- 983
- 1
- 7
- 14
-
13How does the width give us an approximation of the line height? – Richard Barker Apr 20 '15 at 16:11
-
22They mean that the width of a single capital 'M' at a given font size is *about* the same as the line height. (Don't know if this is true, but thats what the answer is saying) – Nathan Nov 09 '15 at 01:42
-
3
-
4To know why, see [Em (typography)](https://en.wikipedia.org/wiki/Em_(typography)#Incorrect_and_alternative_definitions). Specifically: _One em was traditionally defined as the width of the capital 'M' in the current typeface and point size, because the 'M' was commonly cast the full-width of the square blocks, [...], which are used in printing presses._ – Shiva Prasad Oct 12 '22 at 09:46
-
1
The canvas spec doesn't give us a method for measuring the height of a string. However, you can set the size of your text in pixels and you can usually figure out what the vertical bounds are relatively easily.
If you need something more precise then you could throw text onto the canvas and then get pixel data and figure out how many pixels are used vertically. This would be relatively simple, but not very efficient. You could do something like this (it works, but draws some text onto your canvas that you would want to remove):
function measureTextHeight(ctx, left, top, width, height) {
// Draw the text in the specified area
ctx.save();
ctx.translate(left, top + Math.round(height * 0.8));
ctx.mozDrawText('gM'); // This seems like tall text... Doesn't it?
ctx.restore();
// Get the pixel data from the canvas
var data = ctx.getImageData(left, top, width, height).data,
first = false,
last = false,
r = height,
c = 0;
// Find the last line with a non-white pixel
while(!last && r) {
r--;
for(c = 0; c < width; c++) {
if(data[r * width * 4 + c * 4 + 3]) {
last = r;
break;
}
}
}
// Find the first line with a non-white pixel
while(r) {
r--;
for(c = 0; c < width; c++) {
if(data[r * width * 4 + c * 4 + 3]) {
first = r;
break;
}
}
// If we've got it then return the height
if(first != r) return last - first;
}
// We screwed something up... What do you expect from free code?
return 0;
}
// Set the font
context.mozTextStyle = '32px Arial';
// Specify a context and a rect that is safe to draw in when calling measureTextHeight
var height = measureTextHeight(context, 0, 0, 50, 50);
console.log(height);
For Bespin they do fake a height by measuring the width of a lowercase 'm'... I don't know how this is used, and I would not recommend this method. Here is the relevant Bespin method:
var fixCanvas = function(ctx) {
// upgrade Firefox 3.0.x text rendering to HTML 5 standard
if (!ctx.fillText && ctx.mozDrawText) {
ctx.fillText = function(textToDraw, x, y, maxWidth) {
ctx.translate(x, y);
ctx.mozTextStyle = ctx.font;
ctx.mozDrawText(textToDraw);
ctx.translate(-x, -y);
}
}
if (!ctx.measureText && ctx.mozMeasureText) {
ctx.measureText = function(text) {
ctx.mozTextStyle = ctx.font;
var width = ctx.mozMeasureText(text);
return { width: width };
}
}
if (ctx.measureText && !ctx.html5MeasureText) {
ctx.html5MeasureText = ctx.measureText;
ctx.measureText = function(text) {
var textMetrics = ctx.html5MeasureText(text);
// fake it 'til you make it
textMetrics.ascent = ctx.html5MeasureText("m").width;
return textMetrics;
}
}
// for other browsers
if (!ctx.fillText) {
ctx.fillText = function() {}
}
if (!ctx.measureText) {
ctx.measureText = function() { return 10; }
}
};
-
33I doubt this is what the people that wrote the HTML5 spec had in mind. – Steve Hanov Jun 26 '10 at 02:29
-
22
-
1I don't get it. Where is the connection between the font ascent and the width of the letter "m"? – kayahr Apr 11 '11 at 11:17
-
Exactly. Where is the connection? That is why I couldn't recommend their (the editor formerly known as Bespin) method. I simply included it for your consideration. – Prestaul Apr 11 '11 at 15:42
-
6`em` is a relative font measurement where one em is equal to the height of the letter `M` in the default font size. – jerone Dec 24 '11 at 16:10
-
1Right, the height not the width... I'm still confused about the connection. Also, I think that ems are irrelevant given that we only care about the height in pixels. – Prestaul Jan 04 '12 at 23:34
-
1Here is a jsfiddle with a bug fix for the CSS font size: (http://jsfiddle.net/5aG5H/) – Michaelangel007 Jul 02 '13 at 23:51
-
1Rather than `Mg` as the sample text, it might be better to use something like `Og` or `Gg` since round letters (like O and G) ascend and descend very slightly more than straight letters like M and T so that they appear aligned (otherwise the round letters appear to be short). Also, what effect does anti–aliasing have on the result? – RobG Nov 18 '15 at 10:59
EDIT: Are you using canvas transforms? If so, you'll have to track the transformation matrix. The following method should measure the height of text with the initial transform.
EDIT #2: Oddly the code below does not produce correct answers when I run it on this StackOverflow page; it's entirely possible that the presence of some style rules could break this function.
The canvas uses fonts as defined by CSS, so in theory we can just add an appropriately styled chunk of text to the document and measure its height. I think this is significantly easier than rendering text and then checking pixel data and it should also respect ascenders and descenders. Check out the following:
var determineFontHeight = function(fontStyle) {
var body = document.getElementsByTagName("body")[0];
var dummy = document.createElement("div");
var dummyText = document.createTextNode("M");
dummy.appendChild(dummyText);
dummy.setAttribute("style", fontStyle);
body.appendChild(dummy);
var result = dummy.offsetHeight;
body.removeChild(dummy);
return result;
};
//A little test...
var exampleFamilies = ["Helvetica", "Verdana", "Times New Roman", "Courier New"];
var exampleSizes = [8, 10, 12, 16, 24, 36, 48, 96];
for(var i = 0; i < exampleFamilies.length; i++) {
var family = exampleFamilies[i];
for(var j = 0; j < exampleSizes.length; j++) {
var size = exampleSizes[j] + "pt";
var style = "font-family: " + family + "; font-size: " + size + ";";
var pixelHeight = determineFontHeight(style);
console.log(family + " " + size + " ==> " + pixelHeight + " pixels high.");
}
}
You'll have to make sure you get the font style correct on the DOM element that you measure the height of but that's pretty straightforward; really you should use something like
var canvas = /* ... */
var context = canvas.getContext("2d");
var canvasFont = " ... ";
var fontHeight = determineFontHeight("font: " + canvasFont + ";");
context.font = canvasFont;
/*
do your stuff with your font and its height here.
*/

- 6,352
- 26
- 43
-
1+1 Way better solution IMO. It should be possible to get the position of the baseline as well. – Daniel Earwicker Mar 23 '12 at 23:01
-
-
Does this work? I didn't even think about sticking it in a div. This probably doesn't even have to be added to the DOM, no? – beatgammit Mar 24 '12 at 01:43
-
I'm totally ignorant of which size and position fields of a node exist when it is not part of the document. I'd be super-interested to read a reference that addresses that, if you know of one. – ellisbben Mar 24 '12 at 13:58
-
Bravo, I think this should be marked as the answer. It should be noted though that this method does absorb any pre-defined styles so a reset might help to avoid unexpected results. For example, I had to reset line-height back to normal to get an accurate value for offsetHeight. – braitsch Apr 05 '12 at 00:52
-
Depending on what you are using the height for, I've had issues when measuring the text height this way due to word wrap. There are some tiny corner cases where canvas displays differently than this pseudo div. – yoshyosh Jul 08 '13 at 05:02
-
6+1 for a screenful of complicated code that would be just context.measureText(text).height in a parallel universe with a better Canvas API – rsp Jul 05 '14 at 21:51
-
I know is quite old, but is 2021: If DOM element can determine the height (no other solution, for now), the same function can be used for width, right? This way we may unify both width and height to be in the same function.... Correct me if I am wrong. – Xerix May 24 '21 at 08:04
As JJ Stiff suggests, you can add your text to a span and then measure the offsetHeight of the span.
var d = document.createElement("span");
d.font = "20px arial";
d.textContent = "Hello world!";
document.body.appendChild(d);
var emHeight = d.offsetHeight;
document.body.removeChild(d);
As shown on HTML5Rocks

- 2,668
- 2
- 27
- 28
-
3This is so nice solution, Thanks... but I don't know why if this span was not added to the page and visible before I get its offsetHeight ! it always return height as ZERO in Chrome and Firefox ! – Mustafah Jul 14 '12 at 20:42
-
1You are right, I guess it has to be added to the dom for it to take up space. Here is a JS Fiddle of this working: http://jsfiddle.net/mpalmerlee/4NfVR/4/ I've also updated the code above. – Matt Palmerlee Jul 17 '12 at 02:16
-
Using clientHeight is also a possiblitity. Altough this answer is a solution to the problem, it is an ugly workaround. +1'd it nevertheless. – Daniel F Sep 15 '12 at 22:01
-
This does not consider the actual height of the visible text and usually comes across with additional margin on top of the text… – Ain Tohvri Mar 23 '14 at 20:58
Isn't the height of the text in pixels equal to the font size (in pts) if you define the font using context.font ?

- 6,965
- 27
- 95
- 189
-
2This is what it is claimed by this source: http://www.html5canvastutorials.com/tutorials/html5-canvas-text-metrics – Rui Marques May 29 '13 at 14:31
-
for simple cases: you can always parse out the height, from the font name: parseInt(ctx.font.split(' ')[0].replace('px', '')); //parsing string: "10px Verdana" – Sean Jan 08 '14 at 11:18
-
You can use px, pt, em, and % for font size. [This is precisely why](https://jsfiddle.net/xnx4qgkg/1/) this answer is misleading. – Jacksonkr Aug 04 '17 at 02:41
-
@Jacksonkr, Yea but still you could parse them out and adjust accordingly right? Or is there an inherent limitation somewhere to this approach? – Pacerier May 13 '18 at 00:50
-
@Pacerier The limitation is that you may introduce some bugs that cause you to pull your hair out. Just remember that mixing unit types can lead to buggy / spaghetti code. That said, I'm not above the occasional hack as long as long as the risk for issues is low. – Jacksonkr May 16 '18 at 02:56
-
This gets you close to the "font height" (but not the particular string height) usually, when the font doesn't contain characters that extend out of the em box. See https://graphicdesign.stackexchange.com/questions/4035/what-does-the-size-of-the-font-translate-to-exactly. – ZachB Jan 24 '19 at 02:40
I solved this problem straitforward - using pixel manipulation.
Here is graphical answer:
Here is code:
function textHeight (text, font) {
var fontDraw = document.createElement("canvas");
var height = 100;
var width = 100;
// here we expect that font size will be less canvas geometry
fontDraw.setAttribute("height", height);
fontDraw.setAttribute("width", width);
var ctx = fontDraw.getContext('2d');
// black is default
ctx.fillRect(0, 0, width, height);
ctx.textBaseline = 'top';
ctx.fillStyle = 'white';
ctx.font = font;
ctx.fillText(text/*'Eg'*/, 0, 0);
var pixels = ctx.getImageData(0, 0, width, height).data;
// row numbers where we first find letter end where it ends
var start = -1;
var end = -1;
for (var row = 0; row < height; row++) {
for (var column = 0; column < width; column++) {
var index = (row * width + column) * 4;
// if pixel is not white (background color)
if (pixels[index] == 0) {
// we havent met white (font color) pixel
// on the row and the letters was detected
if (column == width - 1 && start != -1) {
end = row;
row = height;
break;
}
continue;
}
else {
// we find top of letter
if (start == -1) {
start = row;
}
// ..letters body
break;
}
}
}
/*
document.body.appendChild(fontDraw);
fontDraw.style.pixelLeft = 400;
fontDraw.style.pixelTop = 400;
fontDraw.style.position = "absolute";
*/
return end - start;
}

- 1,951
- 8
- 34
- 62
-
2I believe this solution does not take into account dotted letters like lowercase i and j – wusauarus Jan 24 '14 at 21:25
Just to add to Daniel's answer (which is great! and absolutely right!), version without JQuery:
function objOff(obj)
{
var currleft = currtop = 0;
if( obj.offsetParent )
{ do { currleft += obj.offsetLeft; currtop += obj.offsetTop; }
while( obj = obj.offsetParent ); }
else { currleft += obj.offsetLeft; currtop += obj.offsetTop; }
return [currleft,currtop];
}
function FontMetric(fontName,fontSize)
{
var text = document.createElement("span");
text.style.fontFamily = fontName;
text.style.fontSize = fontSize + "px";
text.innerHTML = "ABCjgq|";
// if you will use some weird fonts, like handwriting or symbols, then you need to edit this test string for chars that will have most extreme accend/descend values
var block = document.createElement("div");
block.style.display = "inline-block";
block.style.width = "1px";
block.style.height = "0px";
var div = document.createElement("div");
div.appendChild(text);
div.appendChild(block);
// this test div must be visible otherwise offsetLeft/offsetTop will return 0
// but still let's try to avoid any potential glitches in various browsers
// by making it's height 0px, and overflow hidden
div.style.height = "0px";
div.style.overflow = "hidden";
// I tried without adding it to body - won't work. So we gotta do this one.
document.body.appendChild(div);
block.style.verticalAlign = "baseline";
var bp = objOff(block);
var tp = objOff(text);
var taccent = bp[1] - tp[1];
block.style.verticalAlign = "bottom";
bp = objOff(block);
tp = objOff(text);
var theight = bp[1] - tp[1];
var tdescent = theight - taccent;
// now take it off :-)
document.body.removeChild(div);
// return text accent, descent and total height
return [taccent,theight,tdescent];
}
I've just tested the code above and works great on latest Chrome, FF and Safari on Mac.
EDIT: I have added font size as well and tested with webfont instead of system font - works awesome.

- 2,988
- 4
- 24
- 36
I'm kind of shocked that there are no correct answers here. There is no need to make an estimate or a guess. Also, the font-size is not the actual size of the bounding box of the font. The font height depends on whether you have ascenders and descenders.
To calculate it, use ctx.measureText()
and add together the actualBoundingBoxAscent
and the actualBoundingBoxDescent
. That'll give you the actual size. You can also add together the font*
versions to get the size that is used to calculate things like element height, but isn't strictly the height of the actual used space for the font.
const text = 'Hello World';
const canvas = document.querySelector('canvas');
canvas.width = 500;
canvas.height = 200;
const ctx = canvas.getContext('2d');
const fontSize = 100;
ctx.font = `${fontSize}px Arial, Helvetica, sans-serif`;
// top is critical to the fillText() calculation
// you can use other positions, but you need to adjust the calculation
ctx.textBaseline = 'top';
ctx.textAlign = 'center';
const metrics = ctx.measureText(text);
const width = metrics.width;
const actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
// fallback to using fontSize if fontBoundingBoxAscent isn't available, like in Firefox. Should be close enough that you aren't more than a pixel off in most cases.
const fontHeight = (metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent) ?? fontSize;
ctx.fillStyle = '#00F'; // blue
ctx.fillRect((canvas.width / 2) - (width / 2), (canvas.height / 2) - (fontHeight / 2), width, fontHeight);
ctx.fillStyle = '#0F0'; // green
ctx.fillRect((canvas.width / 2) - (width / 2), (canvas.height / 2) - (actualHeight / 2), width, actualHeight);
// canvas.height / 2 - actualHeight / 2 gets you to the top of
// the green box. You have to add actualBoundingBoxAscent to shift
// it just right
ctx.fillStyle = '#F00'; // red
ctx.fillText(text, canvas.width / 2, canvas.height / 2 - actualHeight / 2 + metrics.actualBoundingBoxAscent);
<canvas></canvas>

- 25,408
- 15
- 90
- 139
-
There's a bit of a problem with this solution for Firefox users regarding `fontBoundingBoxAscent` and `fontBoundingBoxDescent`: "From version 74: this feature is behind the **dom.textMetrics.fontBoundingBox.enabled** preferences (needs to be set to true). To change preferences in Firefox, visit **about:config**." Otherwise the values are `NaN`... – Rene van der Lende Oct 18 '22 at 22:31
-
That's kind of dumb and annoying. A decent fallback then would be to just use the font size you are setting for the style. You shouldn't be off by more than a pixel in that case. – samanime Oct 19 '22 at 13:35
-
@RenevanderLende Thanks for sharing that. Updated my answer to include using the fontSize you set as a fallback in those cases, which should be good enough in most cases. – samanime Oct 19 '22 at 13:37
-
This is nitpicking, I'm aware, but with colored background elements and a lighter main background (like your demo) the UA rounding of anti-aliassing values combined with partial pixel values in the calcs can cause lighter colored lines appear on top/below the text. `canvas { image-rendering: pixelated }` will give a clearer view on that ([codepen demo](https://codepen.io/renevanderlende/pen/RwymGmq)) regardless of the **dom.textMetrics...** setting. Agreed, nothing to lose sleep over, just a nice puzzle... – Rene van der Lende Oct 19 '22 at 15:06
-
BTW, had you used the 'Roboto' font I wouldn't have noticed it. So, choice of typeface will be another issue. – Rene van der Lende Oct 19 '22 at 15:08
one line answer
var height = parseInt(ctx.font) * 1.2;
CSS "line-height: normal" is between 1 and 1.2
read here for more info

- 11,481
- 4
- 37
- 49
This is what I did based on some of the other answers here:
function measureText(text, font) {
const span = document.createElement('span');
span.appendChild(document.createTextNode(text));
Object.assign(span.style, {
font: font,
margin: '0',
padding: '0',
border: '0',
whiteSpace: 'nowrap'
});
document.body.appendChild(span);
const {width, height} = span.getBoundingClientRect();
span.remove();
return {width, height};
}
var font = "italic 100px Georgia";
var text = "abc this is a test";
console.log(measureText(text, font));

- 22,983
- 19
- 69
- 116
I'm writing a terminal emulator so I needed to draw rectangles around characters.
var size = 10
var lineHeight = 1.2 // CSS "line-height: normal" is between 1 and 1.2
context.font = size+'px/'+lineHeight+'em monospace'
width = context.measureText('m').width
height = size * lineHeight
Obviously if you want the exact amount of space the character takes up, it won't help. But it'll give you a good approximation for certain uses.
Here is a simple function. No library needed.
I wrote this function to get the top and bottom bounds relative to baseline. If textBaseline
is set to alphabetic
. What it does is it creates another canvas, and then draws there, and then finds the top most and bottom most non blank pixel. And that is the top and bottom bounds. It returns it as relative, so if height is 20px, and there is nothing below the baseline, then the top bound is -20
.
You must supply characters to it. Otherwise it will give you 0 height and 0 width, obviously.
Usage:
alert(measureHeight('40px serif', 40, 'rg').height)
Here is the function:
function measureHeight(aFont, aSize, aChars, aOptions={}) {
// if you do pass aOptions.ctx, keep in mind that the ctx properties will be changed and not set back. so you should have a devoted canvas for this
// if you dont pass in a width to aOptions, it will return it to you in the return object
// the returned width is Math.ceil'ed
console.error('aChars: "' + aChars + '"');
var defaultOptions = {
width: undefined, // if you specify a width then i wont have to use measureText to get the width
canAndCtx: undefined, // set it to object {can:,ctx:} // if not provided, i will make one
range: 3
};
aOptions.range = aOptions.range || 3; // multiples the aSize by this much
if (aChars === '') {
// no characters, so obviously everything is 0
return {
relativeBot: 0,
relativeTop: 0,
height: 0,
width: 0
};
// otherwise i will get IndexSizeError: Index or size is negative or greater than the allowed amount error somewhere below
}
// validateOptionsObj(aOptions, defaultOptions); // not needed because all defaults are undefined
var can;
var ctx;
if (!aOptions.canAndCtx) {
can = document.createElement('canvas');;
can.mozOpaque = 'true'; // improved performanceo on firefox i guess
ctx = can.getContext('2d');
// can.style.position = 'absolute';
// can.style.zIndex = 10000;
// can.style.left = 0;
// can.style.top = 0;
// document.body.appendChild(can);
} else {
can = aOptions.canAndCtx.can;
ctx = aOptions.canAndCtx.ctx;
}
var w = aOptions.width;
if (!w) {
ctx.textBaseline = 'alphabetic';
ctx.textAlign = 'left';
ctx.font = aFont;
w = ctx.measureText(aChars).width;
}
w = Math.ceil(w); // needed as i use w in the calc for the loop, it needs to be a whole number
// must set width/height, as it wont paint outside of the bounds
can.width = w;
can.height = aSize * aOptions.range;
ctx.font = aFont; // need to set the .font again, because after changing width/height it makes it forget for some reason
ctx.textBaseline = 'alphabetic';
ctx.textAlign = 'left';
ctx.fillStyle = 'white';
console.log('w:', w);
var avgOfRange = (aOptions.range + 1) / 2;
var yBaseline = Math.ceil(aSize * avgOfRange);
console.log('yBaseline:', yBaseline);
ctx.fillText(aChars, 0, yBaseline);
var yEnd = aSize * aOptions.range;
var data = ctx.getImageData(0, 0, w, yEnd).data;
// console.log('data:', data)
var botBound = -1;
var topBound = -1;
// measureHeightY:
for (y=0; y<=yEnd; y++) {
for (var x = 0; x < w; x += 1) {
var n = 4 * (w * y + x);
var r = data[n];
var g = data[n + 1];
var b = data[n + 2];
// var a = data[n + 3];
if (r+g+b > 0) { // non black px found
if (topBound == -1) {
topBound = y;
}
botBound = y; // break measureHeightY; // dont break measureHeightY ever, keep going, we till yEnd. so we get proper height for strings like "`." or ":" or "!"
break;
}
}
}
return {
relativeBot: botBound - yBaseline, // relative to baseline of 0 // bottom most row having non-black
relativeTop: topBound - yBaseline, // relative to baseline of 0 // top most row having non-black
height: (botBound - topBound) + 1,
width: w// EDIT: comma has been added to fix old broken code.
};
}
relativeBot
, relativeTop
, and height
are the useful things in the return object.
Here is example usage:
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<script>
function measureHeight(aFont, aSize, aChars, aOptions={}) {
// if you do pass aOptions.ctx, keep in mind that the ctx properties will be changed and not set back. so you should have a devoted canvas for this
// if you dont pass in a width to aOptions, it will return it to you in the return object
// the returned width is Math.ceil'ed
console.error('aChars: "' + aChars + '"');
var defaultOptions = {
width: undefined, // if you specify a width then i wont have to use measureText to get the width
canAndCtx: undefined, // set it to object {can:,ctx:} // if not provided, i will make one
range: 3
};
aOptions.range = aOptions.range || 3; // multiples the aSize by this much
if (aChars === '') {
// no characters, so obviously everything is 0
return {
relativeBot: 0,
relativeTop: 0,
height: 0,
width: 0
};
// otherwise i will get IndexSizeError: Index or size is negative or greater than the allowed amount error somewhere below
}
// validateOptionsObj(aOptions, defaultOptions); // not needed because all defaults are undefined
var can;
var ctx;
if (!aOptions.canAndCtx) {
can = document.createElement('canvas');;
can.mozOpaque = 'true'; // improved performanceo on firefox i guess
ctx = can.getContext('2d');
// can.style.position = 'absolute';
// can.style.zIndex = 10000;
// can.style.left = 0;
// can.style.top = 0;
// document.body.appendChild(can);
} else {
can = aOptions.canAndCtx.can;
ctx = aOptions.canAndCtx.ctx;
}
var w = aOptions.width;
if (!w) {
ctx.textBaseline = 'alphabetic';
ctx.textAlign = 'left';
ctx.font = aFont;
w = ctx.measureText(aChars).width;
}
w = Math.ceil(w); // needed as i use w in the calc for the loop, it needs to be a whole number
// must set width/height, as it wont paint outside of the bounds
can.width = w;
can.height = aSize * aOptions.range;
ctx.font = aFont; // need to set the .font again, because after changing width/height it makes it forget for some reason
ctx.textBaseline = 'alphabetic';
ctx.textAlign = 'left';
ctx.fillStyle = 'white';
console.log('w:', w);
var avgOfRange = (aOptions.range + 1) / 2;
var yBaseline = Math.ceil(aSize * avgOfRange);
console.log('yBaseline:', yBaseline);
ctx.fillText(aChars, 0, yBaseline);
var yEnd = aSize * aOptions.range;
var data = ctx.getImageData(0, 0, w, yEnd).data;
// console.log('data:', data)
var botBound = -1;
var topBound = -1;
// measureHeightY:
for (y=0; y<=yEnd; y++) {
for (var x = 0; x < w; x += 1) {
var n = 4 * (w * y + x);
var r = data[n];
var g = data[n + 1];
var b = data[n + 2];
// var a = data[n + 3];
if (r+g+b > 0) { // non black px found
if (topBound == -1) {
topBound = y;
}
botBound = y; // break measureHeightY; // dont break measureHeightY ever, keep going, we till yEnd. so we get proper height for strings like "`." or ":" or "!"
break;
}
}
}
return {
relativeBot: botBound - yBaseline, // relative to baseline of 0 // bottom most row having non-black
relativeTop: topBound - yBaseline, // relative to baseline of 0 // top most row having non-black
height: (botBound - topBound) + 1,
width: w
};
}
</script>
</head>
<body style="background-color:steelblue;">
<input type="button" value="reuse can" onClick="alert(measureHeight('40px serif', 40, 'rg', {canAndCtx:{can:document.getElementById('can'), ctx:document.getElementById('can').getContext('2d')}}).height)">
<input type="button" value="dont reuse can" onClick="alert(measureHeight('40px serif', 40, 'rg').height)">
<canvas id="can"></canvas>
<h1>This is a Heading</h1>
<p>This is a paragraph.</p>
</body>
</html>
The relativeBot
and relativeTop
are what you see in this image here:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_text

- 1,905
- 1
- 18
- 36

- 35,443
- 37
- 154
- 323
I have implemented a nice library for measuring the exact height and width of text using HTML canvas. This should do what you want.

- 1,144
- 2
- 12
- 27
-
You probably should read [How to offer personal open-source libraries?](https://meta.stackexchange.com/q/229085) before posting your library to more answers. – Martijn Pieters Jan 19 '16 at 11:45
Funny that TextMetrics has width only and no height:
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#textmetrics
Can you use a Span as on this example?
http://mudcu.be/journal/2011/01/html5-typographic-metrics/#alignFix

- 158
- 8
-
1The reason to only have width is this: http://stackoverflow.com/a/12112978/1085483 – Rui Marques Oct 18 '13 at 19:56
First of all, you need to set the height of a font size, and then according to the value of the font height to determine the current height of your text is how much, cross-text lines, of course, the same height of the font need to accumulate, if the text does not exceed the largest text box Height, all show, otherwise, only show the text within the box text. High values need your own definition. The larger the preset height, the greater the height of the text that needs to be displayed and intercepted.
After the effect is processed(solve)
Before the effect is processed( unsolved)
AutoWrappedText.auto_wrap = function(ctx, text, maxWidth, maxHeight) {
var words = text.split("");
var lines = [];
var currentLine = words[0];
var total_height = 0;
for (var i = 1; i < words.length; i++) {
var word = words[i];
var width = ctx.measureText(currentLine + word).width;
if (width < maxWidth) {
currentLine += word;
} else {
lines.push(currentLine);
currentLine = word;
// TODO dynamically get font size
total_height += 25;
if (total_height >= maxHeight) {
break
}
}
}
if (total_height + 25 < maxHeight) {
lines.push(currentLine);
} else {
lines[lines.length - 1] += "…";
}
return lines;};

- 151
- 1
- 4
I found that JUST FOR ARIAL the simplest, fastest and accuratest way to find height of bounding box is to use the width of certain letters. If you plan to use a certain font without letting user to choose one different, you can do a little research to find the right letter that do the job for that font.
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="700" height="200" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = "100px Arial";
var txt = "Hello guys!"
var Hsup=ctx.measureText("H").width;
var Hbox=ctx.measureText("W").width;
var W=ctx.measureText(txt).width;
var W2=ctx.measureText(txt.substr(0, 9)).width;
ctx.fillText(txt, 10, 100);
ctx.rect(10,100, W, -Hsup);
ctx.rect(10,100+Hbox-Hsup, W2, -Hbox);
ctx.stroke();
</script>
<p><strong>Note:</strong> The canvas tag is not supported in Internet
Explorer 8 and earlier versions.</p>
</body>
</html>

- 57
- 5
setting the font size might not be practical though, since setting
ctx.font = ''
will use the one defined by CSS as well as any embedded font tags. If you use the CSS font you have no idea what the height is from a programmatic way, using the measureText method, which is very short sighted. On another note though, IE8 DOES return the width and height.
This works 1) for multiline text as well 2) and even in IE9!
<div class="measureText" id="measureText">
</div>
.measureText {
margin: 0;
padding: 0;
border: 0;
font-family: Arial;
position: fixed;
visibility: hidden;
height: auto;
width: auto;
white-space: pre-wrap;
line-height: 100%;
}
function getTextFieldMeasure(fontSize, value) {
const div = document.getElementById("measureText");
// returns wrong result for multiline text with last line empty
let arr = value.split('\n');
if (arr[arr.length-1].length == 0) {
value += '.';
}
div.innerText = value;
div.style['font-size']= fontSize + "px";
let rect = div.getBoundingClientRect();
return {width: rect.width, height: rect.height};
};

- 1,752
- 18
- 17
I know this is an old answered question, but for future reference I'd like to add a short, minimal, JS-only (no jquery) solution I believe people can benefit from:
var measureTextHeight = function(fontFamily, fontSize)
{
var text = document.createElement('span');
text.style.fontFamily = fontFamily;
text.style.fontSize = fontSize + "px";
text.textContent = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
document.body.appendChild(text);
var result = text.getBoundingClientRect().height;
document.body.removeChild(text);
return result;
};

- 9,923
- 4
- 33
- 50
I monkey patched CanvasRenderingContext2D.measureText() in one of my project to include actual height of the text. It's written in vanilla JS and has zero dependencies.
/*
* Monkeypatch CanvasRenderingContext2D.measureText() to include actual height of the text
*/
; (function (global) {
"use strict";
var _measureText = global.CanvasRenderingContext2D.prototype.measureText;
global.CanvasRenderingContext2D.prototype.measureText = function () {
var textMetrics = _measureText.apply(this, arguments);
var _getHeight = function (text) {
var $span = global.document.createElement("span");
var spanTextNode = global.document.createTextNode(text);
$span.appendChild(spanTextNode);
$span.setAttribute("style", `font: ${this.font}`);
var $div = global.document.createElement("div");
$div.setAttribute("style", "display: inline-block; width: 1px; height: 0; vertical-align: super;");
var $parentDiv = global.document.createElement("div");
$parentDiv.appendChild($span);
$parentDiv.appendChild($div);
var $body = global.document.getElementsByTagName("body")[0];
$body.appendChild($parentDiv);
var divRect = $div.getBoundingClientRect();
var spanRect = $span.getBoundingClientRect();
var result = {};
$div.style.verticalAlign = "baseline";
result.ascent = divRect.top - spanRect.top;
$div.style.verticalAlign = "bottom";
result.height = divRect.top - spanRect.top;
result.descent = result.height - result.ascent;
$body.removeChild($parentDiv);
return result.height - result.descent;
}.bind(this);
var height = _getHeight(arguments[0]);
global.Object.defineProperty(textMetrics, "height", { value: height });
return textMetrics;
};
})(window);
You can use it like this
ctx.font = "bold 64px Verdana, sans-serif"; // Automatically considers it as part of height calculation
var textMetrics = ctx.measureText("Foobar");
var textHeight = textMetrics.height;
parseInt(ctx.font, 10)
e.g.
let text_height = parseInt(ctx.font, 10)
e.g. returns 35

- 302
- 2
- 11
In normal situations the following should work:
var can = CanvasElement.getContext('2d'); //get context
var lineHeight = /[0-9]+(?=pt|px)/.exec(can.font); //get height from font variable

- 3,741
- 2
- 18
- 15
-
5Completely and utterly wrong. There is a HUGE difference between a point size and a pixel size. The point size results in different sized text depending upon the DPI at which the page is being rendered, where-as a pixel size does not take that into account. – Orvid King Feb 28 '15 at 03:58
-
2Are you familiar with the concept of screen pixels? You might find it enlightening. Regardless, pt and px do indeed indicate different heights. It's worth noting that the font can fill less than it's "height", or more, anyway. I'm not sure if canvas pixels are scaled, but I assume so. It is a "wrong" answer, though. It's simple and can be used in many situations. – Lodewijk Mar 01 '15 at 07:07
This is madding... The height of the text is the font size.. Didn't any of you read the documentation?
context.font = "22px arial";
this will set the height to 22px.
the only reason there is a..
context.measureText(string).width
is because that the width of the string can not be determined unless it knows the string you want the width of but for all the strings drawn with the font.. the height will be 22px.
if you use another measurement than px then the height will still be the same but with that measurement so at most all you would have to do is convert the measurement.

- 96
- 6
-
Some letters can extend above or below the limits, see http://www.whatwg.org/specs/web-apps/current-work/images/baselines.png – Octavia Togami Jun 10 '14 at 14:27
-
1
-
read all these answers but for my simple app this was the correct answer, pt === px – rob Sep 30 '14 at 21:54
-
Just completely incorrect. Try some of the solutions proposed with varying fonts and you'll find large discrepancies. – Dale Anderson Aug 23 '17 at 11:08
Approximate solution:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = "100px Arial";
var txt = "Hello guys!"
var wt = ctx.measureText(txt).width;
var height = wt / txt.length;
This will be accurate result in monospaced font.

- 1,370
- 2
- 14
- 17