I don't really know how to formulate my problem into one question but here is a good description:
Imagine I want to draw a square on an HTML canvas, with dimension 1" x 1". I know that using pixels as a printable length is meaningless, we need to take in account the DPI. In the following example I took a dpi of 300:
<canvas id="myCanvas" width="400" height="400"></canvas>
This is my Javascript:
var dpi = 300;
var cContext = document.getElementById("myCanvas").getContext("2d");
cContext.moveTo(50, 50);
// Draw a square
cContext.lineTo(350, 50);
cContext.lineTo(350, 350);
cContext.lineTo(50, 350);
cContext.lineTo(50, 50)
cContext.stroke();
The result is a nice square with width 300px in a 300dpi setting, so it should print a one inch square when printing on paper. But the problem is that it doesn't. I checked the printer settings and used 300dpi.
Can someone tell me what I'm doing wrong, or point me in the right direction?
The printer's DPI setting is not related to the source image DPI and is commonly known as print quality.
The image DPI is dependent on its resolution (width and height in pixels) and the size it is printed at mm or inches to give printing resolution (iDPU "image Dots Per Unit" for further reference in this answer).
The image DPI is meaningless unless you also associate a fixed print size to the image.
If the printer has a DPI set differently to the iDPI of the image the printer (or driver) will either downsample or upsample the image DPI to match the required DPI. You want to avoid downsampling as that will reduce the image quality.
To get the printer DPI to match the image iDPU . This example is for an A4 page in portrait mode. You can use any size print page but you will need to know the actual physical size.
const pageSizes = {
a4 : {
portrait : {
inch : {
width : 8.27,
height : 11.69,
},
mm : {
width : 210,
height : 297,
}
},
landscape : {
inch : {
height : 8.27,
width : 11.69,
},
mm : {
width : 297,
height : 210,
}
},
}
Create a canvas with a iDPI 300 to be 2 inches by 2 inches.
const DPI = 300; // need to have a selected dots per unit in this case inches
const units = "inch";
const pageLayout = "portrait";
const printOn = "a4";
// incase you are using mm you need to convert
const sizeWidth = 2; // canvas intended print size in units = "inch"
const sizeHeight = 2;
var canvas = document.createElement("canvas");
canvas.width = DPI * sizeWidth;
canvas.height = DPI * sizeHeight;
Scale the canvas to fit the page at the correct size to match the print size. This will be the canvas print size/page print width
canvas.style.width = ((sizeWidth / pageSizes[printOn][pageLayout][units].width) * 100) + "%";
For the height you need to assume that the pixel aspect is square and the height of the page may be longer or shorter than the print page so you must use pixels.
canvas.style.height = Math.round((sizeHeight / pageSizes[printOn][pageLayout][units].width) * innerWidth) + "px";
Add the canvas to the page
document.body.appendChild(canvas);
Note: the canvas must be added to the page body or an element that is 100% of the page width. IE the width in pixels === innerWidth.
Note: the canvas should not have a border, padding, margin or inherit any CSS styles that affect its size.
If you want borders on the printed page you need to use custom borders so that you know the border size. (NOTE example is only using inches)
const border = { // in inches
top : 0.4,
bottom : 0.4,
left : 0.4,
right : 0.4,
}
Create the canvas as shown above.
Size the canvas
canvas.style.width = ((sizeWidth / (pageSizes.a4.portrait.width - border.left - border.right) * 100) + "%";
The height becomes a little more complex as it needs the pixel width of the page adjusted for the borders.
canvas.style.height = Math.round((sizeHeight / (pageSizes.a4.portrait.width - border.left - border.right)) * innerWidth * (1 - (border.left - border.right) / pageSizes.a4.portrait.width) ) + "px";