Search code examples
javascriptcanvasprintingdpi

How to draw an image in real size and ensure printing will be correct


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?


Solution

  • Print quality/DPI

    The printer's DPI setting is not related to the source image DPI and is commonly known as print quality.

    You need the print size

    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.

    Page Size

    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,
              }
          },
    }
    

    Canvas resolution

    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;
    

    Canvas size

    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.

    Printing

    • Ensure the printer quality is set to 300DPI (or higher).
    • Select the page size (in this case A4 is 210 × 297 millimeters or 8.27 × 11.69 inches)
    • Select the layout to be portrait.
    • Select no borders on the print options.
    • Print

    Printing with borders.

    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";