1

I am attempting to calculate the pixel height and width of a text object on a html canvas. I've already attempted some code seen below but I've been getting wrong results. Is there a cleaner way to calculate this? The dynamic canvas needs to adjust according to match how long and large the text could be.

  1. px height and width needs to properly calculate. Top-left most pixel to bottom-right most pixel.
  2. The canvas needs to adjust size to minimize calculation time and account for text amount including "\n"s. I tried

canvas.setWidth(400+text.getBoundingRectWidth()); canvas.setHeight(400+text.getBoundingRectHeight());

but I am not sure it works.

How can I rewrite this code to have canvas size be dynamic and adjusts to text that can change (e.g. person writing an essay in a textarea)?

How can I correctly calculate the size of the text?

If someone rewrites this code to meet my specifications above properly, I can provide a reward (money, giftcard, free pizza, etc...). I've been spending many hours trying to figure this out.

Researching: Calculate bounding box of arbitrary pixel-based drawing Looks promising.

HERE IS THE JSBIN (Based off Fabric.js: Get text bounding box width and height) http://jsbin.com/zoqolik/3/edit?html,css,js,console,output

fabric.Object.prototype.objectCaching = false;
var txt = '\n'+'Adklfmask'+'\n'+'asdfsd'+'\n'+'asdfsd'+'\n'+'asdfsd'+"\n";


var canvas = new fabric.Canvas('canvas');
var text = new fabric.IText(txt, { 
    left: 200, 
    top: 200, 
    fontFamily: 'Princess Sofia',
    padding: 5,
    fontSize: 35,
    lineHeight: 2.5,
    textAlign: 'center'
});

canvas.add(text);
canvas.renderAll();

var ctx = canvas.getContext();

var hei = Math.ceil(300+text.getBoundingRectHeight());
var wid = Math.ceil(300+text.getBoundingRectWidth());

function setWidth(width) {
  var canvas = document.getElementById("canvas");  
  canvas.width = width;
}
function setHeight(height) {
  var canvas = document.getElementById("canvas");  
  canvas.height = height;
}
canvas.setWidth(400+text.getBoundingRectWidth());
canvas.setHeight(400+text.getBoundingRectHeight());

// get/draw bounding box
var bbox = getBoundingBox(ctx, 0, 0, wid, hei);
console.log(bbox.width + ' x ' + bbox.height);
drawBoundingBox(ctx, bbox);


function getBoundingBox(ctx, left, top, width, height) {
    var ret = {};

    // Get the pixel data from the canvas
    var data = ctx.getImageData(left, top, width, height).data;
    console.log(data);
    var first = false; 
    var last = false;
    var right = false;
    var left = false;
    var r = height;
    var w = 0;
    var c = 0;
    var d = 0;

    // 1. get bottom
    while(!last && r) {
        r--;
        for(c = 0; c < width; c++) {
            if(data[r * width * 4 + c * 4 + 3]) {
                console.log('last', r);
                last = r+1;
                ret.bottom = r+1;
                break;
            }
        }
    }

    // 2. get top
    r = 0;
    var checks = [];
    while(!first && r < last) {

        for(c = 0; c < width; c++) {
            if(data[r * width * 4 + c * 4 + 3]) {
                console.log('first', r);
                first = r-1;
                ret.top = r-1;
                ret.height = last - first - 1;
                break;
            }
        }
        r++;
    }

    // 3. get right
    c = width;
    while(!right && c) {
        c--;
        for(r = 0; r < height; r++) {
            if(data[r * width * 4 + c * 4 + 3]) {
                console.log('right', c);
                right = c+1;
                ret.right = c+1;
                break;
            }
        }
    }

    // 4. get left
    c = 0;
    while(!left && c < right) {

        for(r = 0; r < height; r++) {
            if(data[r * width * 4 + c * 4 + 3]) {
                console.log('left', c-1);
                left = c;
                ret.left = c;
                ret.width = right - left - 1;
                break;
            }
        }
        c++;

        // If we've got it then return the height
        if(left) {
            return ret;    
        }
    }

    // We screwed something up...  What do you expect from free code?
    return false;
}
Community
  • 1
  • 1
sabah
  • 111
  • 1
  • 11
  • You can use `ctx.getImageData` to workout the extent of some text. render the text you want on a offscreen canvas, get all the pixels then find the top, bottom, left, and right by checking for empty rows and columns (all alpha values are zero) Not very quick but if you do each character at start and just record the top and bottom you can then use measure text to get the width and lookup the characters to find the max vertical extent. – Blindman67 Mar 19 '17 at 06:15

1 Answers1

-2

Instead of

canvas.setWidth(400+text.getBoundingRectWidth());
canvas.setHeight(400+text.getBoundingRectHeight());

use

var textSize = ctx.measureText(text);
canvas.setWidth(400+textSize.width);
canvas.setHeight(400+textSize.height);
Psi
  • 6,387
  • 3
  • 16
  • 26