2

In my on-the-fly editor tool I would really appreciate to get actual rendered height of the text / font - (I do not mean just getting CSS font-size, neither computed nor preset).

Is this achieveable in javascript?

If not directly, is possible something as rendering font in canvas the same way as it is rendered as regular text - and then finding out?

EDIT - my "dev" solution: Based on suggested links I've built a little pure-javascript code, that goes through pixels in canvas and analyses whether the pixel is white or not and acts accordingly, it is hardly a developer version of a code - just outputs few useful info and shows how to access computed data - http://jsfiddle.net/DV9Bw/1325/

HTML:

<canvas id="exampleSomePrettyRandomness" width="200" height="60"></canvas>
<div id="statusSomePrettyRandomness"></div>

JS:

function findPos(obj) {
var curleft = 0, curtop = 0;
if (obj.offsetParent) {
    do {
        curleft += obj.offsetLeft;
        curtop += obj.offsetTop;
    } while (obj = obj.offsetParent);
    return { x: curleft, y: curtop };  
}
return undefined;
}


var status = document.getElementById('statusSomePrettyRandomness');
var example = document.getElementById('exampleSomePrettyRandomness');
var context = example.getContext('2d');
context.fillStyle = "rgb(255,255,255)";
context.fillRect(0, 0, 200, 200);
context.fillStyle = "rgb(0,0,0)";
context.font = "30px Arial";
context.fillText("Hello World",0,30);
var pos = findPos(example);
var x = example.pageX - pos.x;
var y = example.pageY - pos.y;
var foundTop = false;
xPos = 0;
yPos = 0;
topY = -1;
bottomY = -1;
var fuse = 1000;
while( fuse-- > 0 ){
//status.innerHTML += yPos+"<br>";
if( yPos == (example.offsetHeight - 2) ){
    xPos++;
    yPos = 0;
    continue;
}
    var data = context.getImageData(xPos, yPos, 1, 1).data;
if( ! foundTop ){
    if( (data[0] != 255) && (data[1] != 255) && (data[2] != 255) ){
        topY = yPos;
        status.innerHTML += "<br>Found top: "+topY+" X:"+xPos+" Color: rgba("+data[0]+","+data[1]+","+data[2]+")"+"<br>";
        foundTop = true;
    }
} else {
    if( (data[0] == 255) && (data[1] == 255) && (data[2] == 255) ){
        bottomY = yPos;
        status.innerHTML += "<br>Found bottom: "+bottomY+" X:"+xPos+"<br>";
        break;
    }
} 
yPos++;
if( yPos > example.offsetHeight ){
    status.innerHTML += ""
        +"Y overflow ("+yPos+">"+example.offsetHeight+")"
        +" - moving X to "+xPos
        +" - reseting Y to "+yPos 
        +"<br>"
    ;
        xPos++; 
    yPos = 0;
}
}
status.innerHTML += "Fuse:"+fuse+", Top:"+topY+", Bottom: "+bottomY+"<br>";
status.innerHTML += "Font height should be: "+(bottomY-topY)+"<br>";

EDIT 2: Why this is not a duplicate: My question is about really just real rendered height of a font or a letter, "possible duplicate" is about how much space do you need to print a text, answers provided there don't answer my exact problem anyways.

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
jave.web
  • 13,880
  • 12
  • 91
  • 125
  • I'm not sure I completely got your question, and I'm on a rush rn but since you tagged your question [html5-canvas] maybe [this question](http://stackoverflow.com/q/17627893/3702797) and its answer can help you. – Kaiido Nov 18 '15 at 10:32
  • I tagged it because I guess there could be a solution in drawing the font the same way as it is rendered as text - but in canvas, as image pixels - then I guess I could analyze somehow the canvas pixels because I know what is the base color and what is the color of the drown font :) is it more clear now ? – jave.web Nov 18 '15 at 10:36
  • 1
    Maybe try this approach? http://stackoverflow.com/questions/1134586/how-can-you-find-the-height-of-text-on-an-html-canvas – Tim Sheehan Nov 18 '15 at 10:36
  • _"get actual rendered height of the text / font"_ Which unit of measurement should be returned ? – guest271314 Nov 18 '15 at 10:56
  • @jave.web Tried `window.getComputedStyle` ? – guest271314 Nov 18 '15 at 11:22
  • @guest271314 that is, as I wrote in my question, not what I am looking for - the rendered size does NOT usually comply with niether - value got by .style nor by getComputedStyle :) – jave.web Nov 18 '15 at 12:00
  • Is your question a duplicate of http://stackoverflow.com/questions/16816071/…? If yes, you might want to delete your question – markE Nov 19 '15 at 01:11
  • @markE solution is similar, but question refferers something little different - btw deleting question with different formulation is a bad idea anyways - many people search many ways - thus better is to leave both living. – jave.web Nov 19 '15 at 04:13

1 Answers1

4

I am not aware of any method that would return the height of a text such as measureText (which does currently return the width).

However, in theory you can simply draw your text in the canvas then trim the surrounding transparent pixels then measure the canvas height..

Here is an example (the height will be logged in the console):

// Create a blank canvas (by not filling a background color).
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

// Fill it with some coloured text.. (black is default)
ctx.font = "48px serif";
ctx.textBaseline = "hanging";
ctx.fillText("Hello world", 0, 0);

// Remove the surrounding transparent pixels
// result is an actual canvas element
var result = trim(canvas);

// you could query it's width, draw it, etc..
document.body.appendChild(result);

// get the height of the trimmed area
console.log(result.height);

// Trim Canvas Pixels Method
// https://gist.github.com/remy/784508
function trim(c) {

  var ctx = c.getContext('2d'),

    // create a temporary canvas in which we will draw back the trimmed text
    copy = document.createElement('canvas').getContext('2d'),

    // Use the Canvas Image Data API, in order to get all the
    // underlying pixels data of that canvas. This will basically
    // return an array (Uint8ClampedArray) containing the data in the
    // RGBA order. Every 4 items represent one pixel.
    pixels = ctx.getImageData(0, 0, c.width, c.height),

    // total pixels
    l = pixels.data.length,
    
    // main loop counter and pixels coordinates
    i, x, y,

    // an object that will store the area that isn't transparent
    bound = { top: null, left: null, right: null, bottom: null };

  // for every pixel in there
  for (i = 0; i < l; i += 4) {

    // if the alpha value isn't ZERO (transparent pixel)
    if (pixels.data[i+3] !== 0) {

      // find it's coordinates
      x = (i / 4) % c.width;
      y = ~~((i / 4) / c.width);
  
      // store/update those coordinates
      // inside our bounding box Object

      if (bound.top === null) {
        bound.top = y;
      }
      
      if (bound.left === null) {
        bound.left = x; 
      } else if (x < bound.left) {
        bound.left = x;
      }
      
      if (bound.right === null) {
        bound.right = x; 
      } else if (bound.right < x) {
        bound.right = x;
      }
      
      if (bound.bottom === null) {
        bound.bottom = y;
      } else if (bound.bottom < y) {
        bound.bottom = y;
      }
    }
  }
  
  // actual height and width of the text
  // (the zone that is actually filled with pixels)
  var trimHeight = bound.bottom - bound.top,
      trimWidth = bound.right - bound.left,

      // get the zone (trimWidth x trimHeight) as an ImageData
      // (Uint8ClampedArray of pixels) from our canvas
      trimmed = ctx.getImageData(bound.left, bound.top, trimWidth, trimHeight);
  
  // Draw back the ImageData into the canvas
  copy.canvas.width = trimWidth;
  copy.canvas.height = trimHeight;
  copy.putImageData(trimmed, 0, 0);

  // return the canvas element
  return copy.canvas;
}
<canvas id="canvas"></canvas>

Image Data API: https://developer.mozilla.org/en-US/docs/Web/API/ImageData

Pierre
  • 18,643
  • 4
  • 41
  • 62
  • I dont really need width - all I need is height :) – jave.web Nov 18 '15 at 10:39
  • @jave.web i've updated my answer to include a working example. – Pierre Nov 18 '15 at 11:20
  • Thanks, tested, manual-measured - works. But I can't really understand what the code does - would be so nice and commented it a bit please ? :) – jave.web Nov 18 '15 at 14:17
  • @jave.web I've added some comments even though I know someone out there can probably explain it way better :) – Pierre Nov 18 '15 at 14:53
  • thanks, now I get how it all works - the mystery line for me was checking the alpha channel - nice idea :) - but there is still something that remains a mystery to me and that is the calculation of pixel coords - could you explain that too please ? :) (2 lines with bitwise not(~) and modulo(%) operators) – jave.web Nov 18 '15 at 21:11