Bongo
Bongo

Reputation: 3153

HTML Canvas remove letter spacing on the beginning of a word

I have a canvas and I want to print words on it in horizontal and vertical orientation. The printing itself works fine but I have a somewhat annoying spacing at the beginning of the words, which becomes extremely visible on the vertical text like the EX on the example image.

enter image description here

I just created a little example which shows what I mean

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// Create a red line in position 150
ctx.strokeStyle = "red";
ctx.moveTo(150, 20);
ctx.lineTo(150, 170);
ctx.stroke();

ctx.font = "50px Arial";    

// Show the different textAlign values
ctx.textAlign = "start";      
ctx.fillText("EX", 150, 50);        
ctx.textAlign = "end";      
ctx.fillText("EX", 150, 100);                  
ctx.textAlign = "center";         
ctx.fillText("EX", 150, 150);
<canvas id="myCanvas" width="300" height="200" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

Is there a way to place the beginning of the word at the point I am drawing to or is there a way to calculate the spacing at the beginning and then just subtract it from the starting point of the text?

Upvotes: 2

Views: 592

Answers (1)

Kaiido
Kaiido

Reputation: 136698

The TextMetrics interface has actualBoundingBox[...] information that you can use to know by how much the actual bounding box is offset relatively to the textAlign and textBaseline lines.

So adding the measured actualBoundingBoxLeft to your horizontal position when writing LTR text with the "start" text-align will remove this gap.

const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
const input = document.querySelector("input");
input.oninput = draw;
draw();

function draw() {
  const txt = input.value;
  ctx.clearRect(0, 0, c.width, c.height);
  ctx.fillStyle = "black";
  // Create a red line in position 150
  ctx.strokeStyle = "red";
  ctx.moveTo(150, 20);
  ctx.lineTo(150, 180);
  ctx.stroke();

  ctx.font = "50px Arial";    
  ctx.textBaseline = "top"; // needed for the ascent and decsent measurements

  // Show the original textAlign
  ctx.textAlign = "start";      
  ctx.fillText(txt, 150, 30);        

  // fixed
  ctx.fillStyle = "green";
  const metrics = ctx.measureText(txt);
  ctx.fillText(txt, 150 + metrics.actualBoundingBoxLeft, 110);

  // Draw the box around our text
  ctx.translate( 150, 110 );
  const x = 0; // we already did offset by actualBoundingBoxLeft
  const y = metrics.actualBoundingBoxAscent * -1;
  const width = metrics.actualBoundingBoxRight + metrics.actualBoundingBoxLeft;
  const height = metrics.actualBoundingBoxDescent + metrics.actualBoundingBoxAscent;
  ctx.strokeRect(x, y, width, height);
  ctx.setTransform(1, 0, 0, 1, 0, 0);
}
<input value="Éxy"><br>
<canvas id="myCanvas" width="300" height="200" style="border:1px solid #d3d3d3;"></canvas>

Upvotes: 1

Related Questions