views:

1478

answers:

4

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.

+2  A: 

I don't know for sure, but I remember Dion Almaer said something about this in a Google IO presentation about Mozilla Bespin. It was something about flipping the M letter and then measuring its width. But I may be wrong.

Ionuț G. Stan
Interesting. My current approximation is to take width of the letter 'e' and double it and was looking if there was a better way.
swampsjohn
Since you provide a context to the measureText method... I wonder if it gives you the width after applying rotation. If so, this is brilliant.
Allain Lalonde
+2  A: 

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; }
    }
};
Prestaul
I doubt this is what the people that wrote the HTML5 spec had in mind.
Steve Hanov
This is a terrible terrible hack that I absolutely love. +1
Allain Lalonde
+1  A: 

What about using the font-size ?

For example, with

ctx.font = "32px Arial";

The text height would be 32px.

Fabien Ménager
A: 

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.

Rahly