Higher DPI graphics with HTML5 canvas

后端 未结 4 984
梦如初夏
梦如初夏 2020-12-08 05:56

Is there a way to set a custom DPI/PPI when creating an image using the HTML5 canvas? I know how can I draw on the canvas and export it as an image, but how can I make sure

相关标签:
4条回答
  • 2020-12-08 06:23

    Use the library changedpi:

    npm install changedpi --save
    

    Also see

    • https://github.com/shutterstock/changeDPI
    • https://github.com/hongru/canvas2image

    Example code that also allows to adapt the px size and resolution for png or jpg export:

    Canvas2Image.saveAsImage('fileName.png', canvas, 2000, 3000, 300, 'png');
    

    -

    import Url from './url';
    import * as ChangeDpi from 'changeDPI';
    
    export default class Canvas2Image {
      static saveAsImage(fileName, canvas, width, height, dpi, type) {
        type = this._fixType(type);
        canvas = this._scaleCanvas(canvas, width, height);
        let dataUrl = canvas.toDataURL(type);
        let dataUrlWithDpi = ChangeDpi.changeDpiDataUrl(dataUrl, dpi)
        dataUrlWithDpi = dataUrlWithDpi.replace(type, 'image/octet-stream');
        Url.download(fileName, dataUrlWithDpi);
      }
    
      static _fixType(type) {
        type = type.toLowerCase().replace(/jpg/i, 'jpeg');
        const r = type.match(/png|jpeg|bmp|gif/)[0];
        return `image/${r}`;
      }
    
      static _scaleCanvas(canvas, width, height) {
        const w = canvas.width;
        const h = canvas.height;
        if (width === undefined) {
          width = w;
        }
        if (height === undefined) {
          height = h;
        }
    
        const retCanvas = document.createElement('canvas');
        const retCtx = retCanvas.getContext('2d');
        retCanvas.width = width;
        retCanvas.height = height;
        retCtx.drawImage(canvas, 0, 0, w, h, 0, 0, width, height);
        return retCanvas;
      }
    }
    

    -

    export default class Url {
      static download(fileName, url) {
        const element = document.createElement('a');
        element.setAttribute('href', url);
        element.setAttribute('download', fileName);
        element.style.display = 'none';
        document.body.appendChild(element);
        element.click();
        document.body.removeChild(element);
      }
    
      static createUrlForBlob(blob) {
        return this._URL.createObjectURL(blob);
      }
    
      static clearBlobUrl(blobUrl) {
        this._URL.revokeObjectURL(blobUrl);
      }
    
      static get _URL() {
        return window.URL || window.webkitURL || window;
      }
    }
    
    0 讨论(0)
  • 2020-12-08 06:24

    Canvases have two different 'sizes': their DOM width/height and their CSS width/height. You can increase a canvas' resolution by increasing the DOM size while keeping the CSS size fixed, and then using the .scale() method to scale all of your future draws to the new bigger size. Here's an example:

    function changeResolution(canvas, scaleFactor) {
        // Set up CSS size.
        canvas.style.width = canvas.style.width || canvas.width + 'px';
        canvas.style.height = canvas.style.height || canvas.height + 'px';
    
        // Resize canvas and scale future draws.
        canvas.width = Math.ceil(canvas.width * scaleFactor);
        canvas.height = Math.ceil(canvas.height * scaleFactor);
        var ctx = canvas.getContext('2d');
        ctx.scale(scaleFactor, scaleFactor);
    }
    

    The canvas default resolution is 96dpi (CSS inches, not based on the actual screen). So a scaleFactor of 2 gives 192dpi, 3 is 288dpi, etc. In fact, here's a version that should give your desired DPI:

    function setDPI(canvas, dpi) {
        // Set up CSS size.
        canvas.style.width = canvas.style.width || canvas.width + 'px';
        canvas.style.height = canvas.style.height || canvas.height + 'px';
    
        // Resize canvas and scale future draws.
        var scaleFactor = dpi / 96;
        canvas.width = Math.ceil(canvas.width * scaleFactor);
        canvas.height = Math.ceil(canvas.height * scaleFactor);
        var ctx = canvas.getContext('2d');
        ctx.scale(scaleFactor, scaleFactor);
    }
    

    Have fun! Note that both these code samples can only be used once per canvas, they assume the current DOM size is the original (they could be tweaked to change that). Also the rescaling needs to happen before you do any drawing on the canvas. Thanks to this post for the method and information!

    Edit: Here is a more robust function that will scale future draws and maintain existing canvas contents. This can be called to rescale multiple times.

    function setDPI(canvas, dpi) {
        // Set up CSS size.
        canvas.style.width = canvas.style.width || canvas.width + 'px';
        canvas.style.height = canvas.style.height || canvas.height + 'px';
    
        // Get size information.
        var scaleFactor = dpi / 96;
        var width = parseFloat(canvas.style.width);
        var height = parseFloat(canvas.style.height);
    
        // Backup the canvas contents.
        var oldScale = canvas.width / width;
        var backupScale = scaleFactor / oldScale;
        var backup = canvas.cloneNode(false);
        backup.getContext('2d').drawImage(canvas, 0, 0);
    
        // Resize the canvas.
        var ctx = canvas.getContext('2d');
        canvas.width = Math.ceil(width * scaleFactor);
        canvas.height = Math.ceil(height * scaleFactor);
    
        // Redraw the canvas image and scale future draws.
        ctx.setTransform(backupScale, 0, 0, backupScale, 0, 0);
        ctx.drawImage(backup, 0, 0);
        ctx.setTransform(scaleFactor, 0, 0, scaleFactor, 0, 0);
    }
    
    0 讨论(0)
  • 2020-12-08 06:27

    You cannot (ugh) access the DPI of a display of the current web page in any browser:

    Detecting the system DPI/PPI from JS/CSS?

    For printing: You most likely cannot set the DPI of exported <canvas> image (PNG, JPEG) using browser standard functions. However, if you use a pure Javascript encoder image encoder you are free to create any sort of binary file you wish and manually adjust the DPI value embedded int he binary.

    https://gist.github.com/1245476

    0 讨论(0)
  • 2020-12-08 06:31

    If you just want to set the dpi of the PNG (ie not increase the number of pixels) then this library lets you set the pHYs chunk (amongst other things):

    https://github.com/imaya/CanvasTool.PngEncoder

    Minimal example to export an HTML5 canvas to base64-encoded PNG:

            // convert dots per inch into dots per metre
            var pixelsPerM = dpi * 100 / 2.54;
    
            var param = {
                bitDepth : 8,
                colourType : 2,
                filterType : 0,
                height : canvas.height,
                interlaceMethod : 0,
                phys : {
                    unit : 1,
                    x : pixelsPerM,
                    y : pixelsPerM
                },
                width : canvas.width
            };
    
            var array = canvas.getContext('2d').getImageData(0, 0, canvas.width,
                    canvas.height).data;
    
            var png = new window.CanvasTool.PngEncoder(array, param).convert();
    
            var base64 = 'data:image/png;base64,' + btoa(png);
    
    0 讨论(0)
提交回复
热议问题