JS Cloth: Text object selection area is larger than the actual text size

We are working with Fabric JS to add text and image objects to the canvas.

But when we add a text object and select it, it shows a selection area larger than the actual text size.

Please view this link to see a screenshot.

We use the following code to add a text object.

  var text_object = new fabric.Text ('00 ', {
                     fontSize: 192,
                     fontFamily: 'Times New Roman',
                     padding: 0
                   });
 text_object.top = parseInt ((canvas.height - text_object.height) / 2);
 text_object.left = parseInt ((canvas.width - text_object.width) / 2);
 text_object.lockUniScaling = true;
 canvas.add (text_object);
 canvas.renderAll ();
 canvas.setActiveObject (text_object);

The main problem is that we are trying to get the width and height of the added text object; it gives us the width and height of the selection area of โ€‹โ€‹a text object, not the actual size of the text.

How can we make the selection area of โ€‹โ€‹a text object the same as the actual size of the text?

Or how can we get the actual size of the text without the selection area?

+5
source share
1 answer

There is no direct way to achieve what you want, it will be a bounding rectangle of vector text paths.

If you are fine with the rough solution, render the text on a blank canvas, use getImageData to check the transparency of all pixels, and calculate max and min x and y where you will find the filled dots. This is far from ideal, but you cannot get the best solution easily.

I suppose there might be server side solutions, but I don't know anything.

And in theory, you could create your own font rendering engine in JS, and so you would have perfect bounding boxes, but, with the exception of simple / toy cases, a serious text layout mechanism takes years and years, so I guess that it is completely beyond the scope.

If you need help with the approximation method, tell me and I will give an example.

Edited to provide an example:

function getVisiblePixelsBoundingBox(canvas) { const context = canvas.getContext("2d"); const imageData = context.getImageData(0, 0, canvas.width, canvas.height).data; let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; for (let y = 0; y < canvas.height; y++) { for (let x = 0; x < canvas.width; x++) { const alpha = imageData[(y * canvas.width + x) * 4 + 3]; if (alpha > 0) { minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); maxY = Math.max(maxY, y); } } } return { left: minX, top: minY, width: maxX - minX, height: maxY - minY }; } // you will need to create a new canvas var canvas = document.createElement("canvas"), context = canvas.getContext("2d"); // and set the width and height the same as the canvas you want to draw the text // here I'm just adding some values for the example: canvas.width = 600; canvas.height = 400; // now I'm going to draw some text on it context.font = "120px serif"; context.fillText("Lipsum", 100, 200); let boundingBox = getVisiblePixelsBoundingBox(canvas); // now this is not needed in your case, but want to see the canvas and // I will draw a rectangle around the bounding box to see how it works: document.body.appendChild(canvas); context.strokeStyle = "red"; context.strokeRect(boundingBox.left, boundingBox.top, boundingBox.width, boundingBox.height); 

Ok, so this seems good, but beware of the problems:

  • Accuracy is limited by pixels, therefore, although it is completely suitable for drawing a bounding box, it is not very convenient for operations that require much higher accuracy, for example, perfectly aligned shapes. In addition, due to the measurement method, if part of the form is so thin that it is not visible at all, then it will not be measured (this is probably not a big problem for you, because extremely thin figures do not occur in fonts).

  • Doing this can be slow, as we iterate through all the pixels to get the result (and JS doesn't work specifically for this). Knowing this, you may be tempted to try to reduce the size of the canvas and not use the same canvas of your target canvas, but I advise you not to, because it will introduce new problems with the next point.

  • If you measure when your text goes beyond the canvas, the bounding box will be limited to the visible area (it will adhere to the borders). You might consider increasing the size of the canvas, if that happens, to get the actual width and height, but if you don't limit the font size and the amount of text, you can put it on the canvas so large that it slows down the browser or maybe even breaks it ... Therefore, I would simply agree that the bounding box sticks to the borders. You might consider using the width provided by measureText , but unfortunately, to get the height and height, you need to scan the entire image.

+3
source

Source: https://habr.com/ru/post/1272839/


All Articles