Different baseline rendering positions are different to node-canvas
Closed this issue · 1 comments
When set different baseline, the rendering positions are different to node-canvas.
Code
Click to show the code
import { createCanvas } from "canvas";
import fs from "node:fs";
import { Canvas } from "skia-canvas";
// skia-canvas
let skiaCanvas = new Canvas(1000, 1000);
testDrawText(skiaCanvas, "skia-canvas");
await skiaCanvas.saveAs("skia-canvas.pdf");
// node-canvas
let nodeCanvas = new createCanvas(1000, 1000, "pdf");
nodeCanvas.getContext("2d").textDrawingMode = "glyph";
testDrawText(nodeCanvas, "node-canvas");
fs.writeFileSync("node-canvas.pdf", nodeCanvas.toBuffer());
function testDrawText(canvas, tip) {
const crossLineHalfWidth = 25;
const step = 60;
const x = 50;
let y = 60;
let fontFamily = "zcool-gdh";
let text = "The quick brown fox jumps over the lazy dog";
let ctx = canvas.getContext("2d");
ctx.strokeStyle = "purple";
ctx.fillStyle = "black";
ctx.lineWidth = 1;
ctx.font = `normal 20pt '${fontFamily}'`;
const baselines = ["top", "hanging", "middle", "alphabetic", "ideographic", "bottom"];
drawTextWithTip(tip);
baselines.forEach((baseline, index) => drawTextWithCrossLine(text, x, y + step * (index + 1), baseline));
y = 600;
fontFamily = "Times new Roman";
ctx.font = `normal 12pt '${fontFamily}'`;
text = "Abcdefg";
drawTextWithLine(text, x + 100, y);
function drawTextWithCrossLine(text, x, y, baseline) {
ctx.textBaseline = baseline;
ctx.fillText(`${text}(${baseline})`, x, y);
ctx.moveTo(x - crossLineHalfWidth, y);
ctx.lineTo(x + crossLineHalfWidth, y);
ctx.stroke();
ctx.moveTo(x, y - crossLineHalfWidth);
ctx.lineTo(x, y + crossLineHalfWidth);
ctx.stroke();
}
function drawTextWithLine(text, x, y) {
ctx.moveTo(0, y);
ctx.lineTo(x + 1000, y);
ctx.stroke();
baselines.forEach((baseline, index) => {
const newX = x + step * 2 * index;
ctx.textBaseline = "alphabetic";
ctx.fillText(baseline, newX, y - step);
ctx.textBaseline = baseline;
ctx.fillText(text, newX, y);
});
}
function drawTextWithTip(text) {
ctx.textBaseline = "alphabetic";
ctx.fillText(text, x, y);
}
}
result
file
This is is the result PDF and font file: all-file.zip
Question
- Position is different to
node-cnavas
, maybe this is a issue like as #208 - Font metrics
height
very different, I usemeasureText
api, the result is this:
skia-canvas metrics is: TextMetrics {
width: 26.670000076293945,
actualBoundingBoxLeft: 0,
actualBoundingBoxRight: 26.670000076293945,
actualBoundingBoxAscent: 26.073354721069336,
actualBoundingBoxDescent: 5.926646709442139,
fontBoundingBoxAscent: 26.073354721069336,
fontBoundingBoxDescent: 5.926646709442139,
emHeightAscent: 26.073354721069336,
emHeightDescent: 5.926646709442139,
hangingBaseline: 16.53333282470703,
alphabeticBaseline: 0,
ideographicBaseline: -5.926646709442139,
lines: [
{
x: 0,
y: -26.073354721069336,
width: 26.670000076293945,
height: 32,
baseline: 0,
startIndex: 0,
endIndex: 6
}
]
}
node-canvas metrics is: {
width: 26.666015625,
actualBoundingBoxLeft: 1.83984375,
actualBoundingBoxRight: 24.826171875,
actualBoundingBoxAscent: 17.759765625,
actualBoundingBoxDescent: -1.4404296875,
emHeightAscent: 22.90625,
emHeightDescent: 3.759765625,
alphabeticBaseline: -0.0263671875
}
And In chrome, metrics is:
web-canvas metrics is: {
actualBoundingBoxAscent: 17.75555992126465,
actualBoundingBoxDescent: -1.4396400451660156,
actualBoundingBoxLeft: -1.839539885520935,
actualBoundingBoxRight: 24.820459365844727,
alphabeticBaseline: -0,
fontBoundingBoxAscent: 23,
fontBoundingBoxDescent: 4,
hangingBaseline: 18.399999618530273,
ideographicBaseline: -4,
width: 26.659988403320312,
}
See the result, the width is basically the same, but the height is very different(skia-canvas
is 32
and node-cnavas
is 26.67
, web-canvas
is 27
). Is this caused by different calculation algorithms for font metrics? The difference in metrics between node-canvas
and web-canvas
is even smaller.
Version 2.0.2 fixes actualBoundingBox
& line-height calculations (using the font metrics to find the default leading if not specified in ctx.font
) and calculates baseline offsets to more closely match browser behavior. Note that no two browsers behave identically when it comes to type, so there's no true 'standard' behavior to emulate.