Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

measureText returns incorrect actualBoundingBoxLeft and actualBoundingBoxRight #1703

Comments

@Jesse97
Copy link

Jesse97 commented Nov 14, 2020

Issue

When using measureText, the returned TextMetrics object doesn't return accurate values for actualBoundingBoxLeft and actualBoundingBoxRight. In fact, they seem to fall back to 0 and the value of width respectively.

This might overlap with #331, #801, and #1553, as indeed the output differs between a browser and Node.js (also between integer or floating point numbers), but none of these refer to the bounding box.

Steps to reproduce

I'm using Open Sans in this example. In Node, I'm using fs-extra to output the image as a PNG.

Begin the Node.js version with the following.

const fs = require('fs-extra');
const path = require('path');
const {createCanvas} = require('canvas');

const canvas = createCanvas(750, 600);

Begin the browser version with the following, ensuring that a canvas with id canvas exists on the page:

const canvas = document.getElementById('canvas');
canvas.width = 750;
canvas.height = 600;

Then add the following to both:

const ctx = canvas.getContext('2d');

experimentTextMeasurements();

async function experimentTextMeasurements() {
	ctx.fillStyle = '#fff';
	ctx.fillRect(0, 0, canvas.width, canvas.height);
	ctx.fillStyle = '#000';
	ctx.translate(50, 200);

	const fontSize = 150;

	const text = 'node! iJ';

	ctx.font = `${fontSize}px 'Open Sans', serif`;
	ctx.textBaseline = 'alphabetic';
	const m = ctx.measureText(text);
	console.log(m);
	drawText(text, m);

	const letters = Array.from(text);

	ctx.translate(0, 200);

	let alt = true;
	for (const letter of letters) {
		const m2 = ctx.measureText(letter);
		console.log(letter, m2);
		drawText(letter, m2, alt);
		ctx.translate(m2.width, 0);
		alt = !alt;
	}

	// The following line is only for Node.js, remove it for browser testing.
	await fs.writeFile(path.join(__dirname, 'canvas.png'), canvas.toBuffer());
}

function drawText(text, m, alt = false) {
	ctx.fillText(text, 0, 0);

	ctx.lineWidth = 1;
	ctx.strokeStyle = 'red';
	ctx.strokeRect(-m.actualBoundingBoxLeft, -m.actualBoundingBoxAscent,
		m.actualBoundingBoxRight + m.actualBoundingBoxLeft, m.actualBoundingBoxAscent + m.actualBoundingBoxDescent);

	ctx.strokeStyle = alt ? 'blue' : 'lime';
	ctx.beginPath();
	ctx.moveTo(0, 0);
	ctx.lineTo(m.width, 0);
	ctx.stroke();
}

Browser output:
canvas

TextMetrics {
  actualBoundingBoxAscent: 115,
  actualBoundingBoxDescent: 28,
  actualBoundingBoxLeft: -12,
  actualBoundingBoxRight: 502.7080078125,
  width: 515.771484375
}
n TextMetrics {
  actualBoundingBoxAscent: 82,
  actualBoundingBoxDescent: 0,
  actualBoundingBoxLeft: -12,
  actualBoundingBoxRight: 80
  width: 92.0654296875,
}

[...]

J TextMetrics {
  actualBoundingBoxAscent: 108,
  actualBoundingBoxDescent: 28,
  actualBoundingBoxLeft: 12,
  actualBoundingBoxRight: 27,
  width: 40.0634765625
}

Node.js output:
canvas

{
  width: 516,
  actualBoundingBoxLeft: 0,
  actualBoundingBoxRight: 516,
  actualBoundingBoxAscent: 115,
  actualBoundingBoxDescent: 28,
  emHeightAscent: 161,
  emHeightDescent: 44,
  alphabeticBaseline: -0
}
n {
  width: 92,
  actualBoundingBoxLeft: 0,
  actualBoundingBoxRight: 92,
  actualBoundingBoxAscent: 82,
  actualBoundingBoxDescent: 0,
  emHeightAscent: 161,
  emHeightDescent: 44,
  alphabeticBaseline: -0
}

[...]

J {
  width: 40,
  actualBoundingBoxLeft: 0,
  actualBoundingBoxRight: 40,
  actualBoundingBoxAscent: 108,
  actualBoundingBoxDescent: 28,
  emHeightAscent: 161,
  emHeightDescent: 44,
  alphabeticBaseline: -0
}

Your environment

  • node-canvas 2.6.1
  • Node.js 12.19.0
  • Ubuntu 18.04.1
@mserege
Copy link

mserege commented Mar 9, 2021

I do have the same issue.

Looking at the code in CanvasRenderingContext2d.cc, I might have found something in the following lines:

Nan::New<Number>(x_offset - PANGO_LBEARING(logical_rect))).Check();

Nan::New<Number>(x_offset + PANGO_RBEARING(logical_rect))).Check();

Shoul ink_rect be used instead of logical_rect for actualBoundingBoxLeft / actualBoundingBoxRight like it is done for actualBoundingBoxAscent / actualBoundingBoxDescent?

@mserege
Copy link

mserege commented Mar 26, 2021

@zbjornson : thanks for your thumbs up. Should I create a pull request with these changes or can it be handle by one of the official maintainers?

@zbjornson
Copy link
Collaborator

@mserege it would be fantastic if you want to work on a pull request with those changes. I don't know for certain if what you proposed is correct, but it sounds promising.

@mserege
Copy link

mserege commented Mar 30, 2021

@zbjornson : it is done here : #1776.

It fixes the issue for me.

@zbjornson
Copy link
Collaborator

Fixed in #1776, thanks @mserege. Will be in the upcoming 2.8.0 release.

@yasarsid
Copy link

Thanks @mserege for fixing this - ran into a similar problem.

@zbjornson could you give me a timeline when we can expect 2.8.0 to be released ?

@mserege
Copy link

mserege commented May 5, 2021

@zbjornson : could you give us an estimated date for the next release that will include this fix?
I'm waiting for it for a specific project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment