How to render a SVG string/file onto a canvas and export it to PNG or JPEG with a custom resolution (preserving quality) in JavaScript

How to render a SVG string/file onto a canvas and export it to PNG or JPEG with a custom resolution (preserving quality) in JavaScript

Manipulation of images has been always a little bit tricky, especially when you want to do it with a language like JavaScript and if it wasn't enough, directly in the browser. In the last few days, I've been working with a very special library namely dom-to-image, this library allows you to turn any arbitrary DOM node into a vector (SVG) or raster (PNG or JPEG) image with pure JavaScript. I've used this library to simply capture a DOM element in the browser to allow the user to download the image and that's it.

In theory, it's quite simple as the DOM is only a collection of buttons with emojis in the center and it looks like this:

Buttons PNG

Note that the previous image is a screenshot made with an external tool and it keeps the original size. Now, if I decide to export the DOM elements using the mentioned library in JPEG or PNG, it will lose quality drastically:

Buttons Small Exported Image

It's not the fault of the library of course as it's rasterizing the DOM elements as an image in PNG format, however, it looks that bad because the buttons are indeed small in my screen. So, the solution would be to either make the buttons bigger or work with SVG instead of the rasterized version of the DOM elements. As you may know, you can upscale and downscale SVG to any size that you want without losing quality at all. If the DOM elements are exported to SVG, you may resize it to the size you want, for example, the following code will render the selected DOM element to SVG and will display it into a new browser tab:

let domNode = document.getElementById("myDiv");

// 1. Render the selected node to SVG
domtoimage.toSvg(domNode).then(function(SVG){
    
    // 2. Load the SVG string (it looks something like this:
    // data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="1623" height="64"><foreignObject x="0" y="0" width="100%" height="100%"><div id="myDiv" ....
    let image = new Image();
    image.src = SVG;

    // 3. Launch a new tab in the browser and load the image to see the preview
    let newWindow = window.open("");
    newWindow.document.write(image.outerHTML);
});

Then, in the new tab you will see that the image looks quite sharp and you can resize the image to the size you want without losing quality (we resized it to a picture of 3500px which is quite huge in comparison to the original size of the buttons):

Upscaled SVG Dom To Image

Therefore, you may rasterize it later into a popular format like PNG or JPEG but with decent quality! In this tutorial, I will show you how to easily use JavaScript to render an SVG string or SVG file to an image and resize it to the size you want keeping its original quality.

1. Create/obtain SVG Data URL

Data URLs, URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents. In our case, our data scheme for SVG files will be:

Important: it's better if you URL Encode the plain SVG data to encode characters that shouldn't appear in URLs.

data:image/svg+xml;charset=utf-8,[The URL Encoded Plain Data of SVG should be here]

Note that unlike other formats that are encoded using Base64, we will need the plain data in the Data URL. For this example, we will use the widely known SVG Tiger from Wikipedia. The SVG string can come from anywhere, it may be downloaded from a web resource, created within the browser or to be the output of the dom-to-image library, the point of this step is that you know that it needs to be in Data URL format.

2. Test if the SVG can be rendered in Image

To test if your create SVG Data URL is valid, you can easily use it as the source of an image element in HTML. In this example, we will use the mentioned SVG Tiger from Wikipedia and we will create it manually, storing the SVG into a string variable, then prepending the Data URL scheme to the URL Encoded SVG:

<!-- An image element where the DataURL will be displayed -->
<img src="" id="test" />
<script>
    // 1. Prepare SVG String
    let SVGString = `
        <svg id="svg2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 900" version="1.1">
            <g id="g4" fill="none" transform="matrix(1.7656463,0,0,1.7656463,324.90716,255.00942)">
                <g id="g6" stroke-width="0.17200001" stroke="#000" fill="#FFF">
                <path id="path8" d="m-122.3,84.285s0.1,1.894-0.73,1.875c-0.82-0.019-17.27-48.094-37.8-45.851,0,0,17.78-7.353,38.53,43.976z"/>
            </g>

            ....

            .... (rest of the SVG String, note that we remove the following string from the SVG: <?xml version="1.0" encoding="UTF-8" standalone="no"?>)

            ....
        </svg>
    `;

    // 2. Convert to Data URL
    let dataURL = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(SVGString);

    // 3. Set the DataURL as source of the test image
    document.getElementById("test").src = dataURL;
</script>

You can see this in action in the following fiddle that should display the SVG tiger in the Image element:

Knowing now that the SVG Data URL is valid (as long as the image that you expected from the given SVG appears in the img element), you can proceed with the next step.

3. Render SVG in Canvas and export it as an image of any size

It's now time to implement what you are looking for, rendering the SVG into a canvas to resize it to any size you want. The logic to achieve is the following one. You need an empty canvas, where you should render the SVG with the custom size, then you may export it to PNG or JPEG:

<canvas id="myOutputCanvas"></canvas>

<script>
    // 1. Obtain the canvas element and access its context
    let canvas = document.getElementById('myOutputCanvas');
    let ctx = canvas.getContext("2d");

    // 2. Declare the new width of the image that you want to generate in pixels
    // e.g create an image of 1000 pixels of width from the given SVG
    // note: the height is automatically calculated to preserve aspect ratio
    let newWidth = 1000;

    // 3. Create an image element to load the SVG
    let img = new Image();

    // 4. Once the SVGDataURL is rendered in the Image element
    // proceed to render it in the empty canvas that will contain
    // the same image, but in the size that you want preserving the
    // sharpness of the SVG string
    img.onload = function() {
        // Declare initial dimensions of the image
        let originalWidth = img.width;
        let originalHeight = img.height;

        // Declare the new width of the image
        // And calculate the new height to preserve the aspect ratio
        img.width = newWidth;
        img.height = (originalHeight / originalWidth) * newWidth;

        // Set the dimensions of the canvas to the new dimensions of the image
        canvas.width = img.width;
        canvas.height = img.height;

        // Render image in Canvas 
        ctx.drawImage(img, 0, 0, img.width, img.height); 

        // That's it! If your canvas is visible, you may now see 
        // the image of the new size!
        // Export the canvas to blob
        // You may export this now as a base64 data URL or blob
        // canvas.toBlob(function(blob){
        //  do something with the blob
        //}, "image/png", 1);
    };

    // Load the DataURL of the SVG
    img.src = "data:image/svg+xml;charset=utf-8,[The URL Encoded Plain Data of SVG should be here, replace this with the data from step #1]";
</script>

You can see a live example of this implementation in the following fiddle, where you can simply provide your SVG string into a textarea, set the new width of the output image, and export it to the image format that you want:

Happy coding ❤️!

This could interest you

Become a more social person