Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate letter size in javascript

I need to calculate the exact size of the letter in javascript. The letter can have different font-size or font-family attributes, etc.

I tried to use div element for this, but this method gives only the size of the div, not letter.

<div style="display: inline; background-color: yellow; font-size: 53px; line-height:32px">A</div>

Does anybody know how to solve this issue?

like image 675
Max Avatar asked Dec 27 '22 06:12

Max


2 Answers

This is basically not possible for the general case. Font kerning will result in variable "widths" of letters depending on the operating system, browser, etc etc, and based on which letters are next to each other. Font substitution may happen if the os+browser don't have the font you specify.

Perhaps re-asking the question with the higher-level goal you're shooting for might result in proposed other approaches to your problem that might be more fruitful?

like image 116
Irongaze.com Avatar answered Jan 13 '23 16:01

Irongaze.com


As others have mentioned, this isn't possible to measure directly. But you can get at it in a more roundabout way: draw the letter onto a canvas and determine which pixels are filled in.

Here's a demo that does this. The meat is this function:

/**
 * Draws a letter in the given font to an off-screen canvas and returns its
 * size as a {w, h, descenderH} object.
 * Results are cached.
 */
function measureLetter(letter, fontStyle) {
  var cacheKey = letter + ' ' + fontStyle;
  var cache = measureLetter.cache;
  if (!cache) {
    measureLetter.cache = cache = {};
  }
  var v = cache[cacheKey];
  if (v) return v;

  // Create a reasonably large off-screen <canvas>
  var cnv = document.createElement('canvas');
  cnv.width = '200';
  cnv.height = '200';

  // Draw the letter
  var ctx = cnv.getContext('2d');
  ctx.fillStyle = 'black';
  ctx.font = fontStyle;
  ctx.fillText(letter, 0.5, 100.5);

  // See which pixels have been filled
  var px = ctx.getImageData(0, 0, 200, 200).data;
  var minX = 200, minY = 200, maxX = 0, maxY = 0;
  var nonZero = 0;
  for (var x = 0; x < 200; x++) {
    for (var y = 0; y < 200; y++) {
      var i = 4 * (x + 200 * y);
      var c = px[i] + px[i + 1] + px[i + 2] + px[i + 3];
      if (c === 0) continue;
      nonZero++;
      minX = Math.min(x, minX);
      minY = Math.min(y, minY);
      maxX = Math.max(x, maxX);
      maxY = Math.max(y, maxY);
    }
  }

  var o = {w: maxX - minX, h: maxY - minY, descenderH: maxY - 100};
  cache[cacheKey] = o;
  return o;
}

Note that this is sensitive to antialiasing—the results might be off by a pixel.

like image 23
danvk Avatar answered Jan 13 '23 15:01

danvk