0đź‘Ť
Linear scale
It is straight forward to scale to match a given dimension as long as you establish the relationship between pixels and the scale you are working in.
For example the image size and the representative physical size can be expressed as
const imageSize = {
cm: {width: 24, height: 21},
px: {width: 1200, height: 950},
cm2px: {width: 1200 / 24, height: 950 / 21},
px2cm: {width: 24 / 1200, height: 21 / 950},
};
Giving the size of the canvas and the physical, and the conversion scales between them.
With that and knowing the scale relationship between “Points” and “cm” as 1pt = 0.0353 you can scale canvas text to match the points size in the cm scale of the canvas.
To scale and render from “px” to “point” with the above info
const pt2cm = 0.0353;
const textPointSize = 38;
ctx.font = textPointSize + "px arial"; // In px (pixels)
const xScale = pt2cm * imageSize.cm2px.width;
const yScale = pt2cm * imageSize.cm2px.height;
ctx.setTransform(xScale, 0, 0, yScale, 100, 100);
ctx.fillText("38pt text", 0, 0);
Example
The example creates a canvas and draws a cm grid. Then draws the font at various pt sizes on the canvas.
The function drawText
takes the font size in points, and the text position in cm
The 38pt text is approx 13mm tall in the scale of the canvas image using a point size of 0.0352778cm
const ctx = canvas.getContext("2d");
const imageSize = {
cm: {width: 24, height: 21},
px: {width: 1200, height: 950},
cm2px: {width: 1200 / 24, height: 950 / 21},
px2cm: {width: 24 / 1200, height: 21 / 950},
};
const pt2cm = 0.0352778;
canvas.width = imageSize.px.width;
canvas.height = imageSize.px.height;
drawGrid();
for (let i = 10; i < 40; i += 4) {
drawText("Test text "+i+"pt", i, 3, 1 + (i - 10) / 2);
}
function drawText(text, sizePt, xCm, yCm) {
const x = imageSize.cm2px.width * xCm;
const y = imageSize.cm2px.height * yCm;
ctx.font = sizePt+"px Arial";
ctx.textAlign = "left";
ctx.textBaseline = "top"
ctx.fillStyle = "#000";
const xs = pt2cm * imageSize.cm2px.width;
const ys = pt2cm * imageSize.cm2px.height;
ctx.setTransform(xs, 0, 0, ys, x, y);
ctx.fillText(text, 0, 0);
ctx.setTransform(1,0,0,1,0,0);
}
// Draws a grid
function drawGrid() {
const w = imageSize.px.width;
const h = imageSize.px.height;
const sx = imageSize.cm2px.width;
const sy = imageSize.cm2px.height;
ctx.globalAlpha = 0.3;
ctx.fillStyle = "#aaa";
ctx.beginPath();
for(let i = 0; i < Math.max(imageSize.cm.height, imageSize.cm.width) * 5; i += 1) {
if (i % 5) {
ctx.rect(0, i * sy / 5 | 0, w, 1);
ctx.rect(i * sx / 5 | 0, 0, 1, h);
}
}
ctx.fill();
ctx.fillStyle = "#000";
ctx.beginPath();
for(let i = 0; i < Math.max(imageSize.cm.height, imageSize.cm.width); i += 1) {
ctx.rect(0, i * sy | 0, w, 1);
ctx.rect(i * sx | 0, 0, 1, h);
}
ctx.fill();
ctx.globalAlpha = 1;
ctx.font = "12px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "#000";
ctx.strokeStyle = "#FFF";
ctx.lineWidth = 4;
ctx.strokeText("cm", 12, 10);
ctx.fillText("cm", 12, 10);
for(let i = 1; i < Math.max(imageSize.cm.height, imageSize.cm.width); i += 1) {
ctx.strokeText(i, 10, i * sy);
ctx.strokeText(i, i * sx, 10);
ctx.fillText(i, 10, i * sy);
ctx.fillText(i, i * sx, 10);
}
}
<canvas id="canvas"></canvas>
Note That the physical and pixel sizes do not match and thus the canvas scale is stretched in the y direction.
To get a square aspect you should use only one scale for both the x and y scale or change the canvas size to match the physical aspect.