0

I had this snippet used from Convert SVG to image (JPEG, PNG, etc.) in the browser

It works fine in chrome but after the lastest safari update 15, the image generated is not proper.

This is the chrome image https://pastebin.com/hcLAS51W

This is the safari generated image https://pastebin.com/S7tEWQsQ

const base64Image = 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjkyLjk3NzUyODA4OTg4NzY0IDEyOSAzNzQuMDQ0OTQzODIwMjI0NyAxMTAiIGNsYXNzPSJQU1BERktpdC04czFzYmM1MTN0MzRxMzg4OTViNGJoM2p2biIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS10ZXN0aWQ9Imluay1zdmciIHN0eWxlPSJsZWZ0OiAwcHg7IHRvcDogMHB4OyB3aWR0aDogMTAwJTsgaGVpZ2h0OiAxMDAlOyBvcGFjaXR5OiAxOyBvdmVyZmxvdzogaGlkZGVuOyI+PHJlY3QgeD0iOTcuOTc3NTI4MDg5ODg3NjQiIHk9IjEzNCIgd2lkdGg9IjM2NC4wNDQ5NDM4MjAyMjQ3IiBoZWlnaHQ9IjEwMCIgY2xhc3M9IlBTUERGS2l0LTc4NzFyZmh5eGI3cGRta3MxbjN4N2F2dnRhIiBzdHlsZT0iZmlsbDogdHJhbnNwYXJlbnQ7Ii8+PGcgc3R5bGU9ImZpbGw6IHRyYW5zcGFyZW50OyI+PGc+PHBhdGggY2xhc3M9IlBTUERGS2l0LTQ3cjV6eHB0eWtoanFwenFkdWU5YWNwaHR6IFBTUERGS2l0LTQ0OG5jcjVtdWhycGM4cDh5dmFkeTUxZW1jIFBTUERGS2l0LVNtb290aC1MaW5lcyIgZD0iTSAyMTIuNzYzODUwODAyMDQ4MiwxNjYuMDQwNTk0NDE4MjY3NDYgQyAxODMuNiwxNjAuNyAxNTQuNSwxNTUuNCAxMzUuNiwxNTIuMyBDIDExNi43LDE0OS4xIDEwNy45LDE0OC4yIDEwNC4wLDE0OS45IEMgMTAwLjIsMTUxLjUgMTAxLjEsMTU1LjkgMTAzLjMsMTYwLjcgQyAxMDUuNSwxNjUuNiAxMDguOSwxNzAuOSAxMTYuNCwxNzYuMiBDIDEyMy45LDE4MS41IDEzNS42LDE4Ni44IDE0Mi45LDE4Ny4xIEMgMTUwLjIsMTg3LjMgMTUzLjEsMTgyLjUgMTU1LjUsMTc3LjYgQyAxNTcuOSwxNzIuOCAxNTkuOSwxNjguMCAxNjEuMSwxNjUuMyBDIDE2Mi4zLDE2Mi43IDE2Mi44LDE2Mi4yIDE2NS43LDE2NS44IEMgMTY4LjYsMTY5LjQgMTczLjksMTc3LjIgMTgwLjcsMTg1LjQgQyAxODcuNSwxOTMuNiAxOTUuOCwyMDIuMyAyMDIuMSwyMDcuNiBDIDIwOC40LDIxMi45IDIxMi44LDIxNC45IDIxOC4zLDIxNS42IEMgMjIzLjksMjE2LjMgMjMwLjcsMjE1LjggMjM1LjYsMjEyLjkgQyAyNDAuNCwyMTAuMCAyNDMuMywyMDQuNyAyNDYuMywxOTcuMiBDIDI0OS4yLDE4OS43IDI1Mi4xLDE4MC4xIDI1My41LDE3NS4wIEMgMjU1LjAsMTY5LjkgMjU1LjAsMTY5LjQgMjU1LjIsMTY5LjIgQyAyNTUuNSwxNjguOSAyNTYuMCwxNjguOSAyNTYuOSwxNjkuNCBDIDI1Ny45LDE2OS45IDI1OS40LDE3MC45IDI2MS41LDE3MS44IEMgMjYzLjcsMTcyLjggMjY2LjYsMTczLjggMjY5LjgsMTc0LjMgQyAyNzIuOSwxNzQuNyAyNzYuMywxNzQuNyAyNzkuMywxNzQuMCBDIDI4Mi4yLDE3My4zIDI4NC42LDE3MS44IDI4Ni41LDE2OS45IEMgMjg4LjUsMTY4LjAgMjg5LjksMTY1LjYgMjkwLjksMTYxLjIgQyAyOTEuOSwxNTYuOSAyOTIuNCwxNTAuNiAyOTAuMiwxNDYuMCBDIDI4OC4wLDE0MS40IDI4My4xLDEzOC41IDI3Ny44LDEzNy4wIEMgMjcyLjUsMTM1LjYgMjY2LjYsMTM1LjYgMjYzLjUsMTM1LjYgQyAyNjAuMywxMzUuNiAyNTkuOCwxMzUuNiAyNTkuNCwxMzYuMSBDIDI1OC45LDEzNi42IDI1OC40LDEzNy41IDI2MS4zLDE0MC45IEMgMjY0LjIsMTQ0LjMgMjcwLjUsMTUwLjEgMjgzLjksMTU2LjEgQyAyOTcuMiwxNjIuMiAzMTcuNiwxNjguNSAzMjkuNSwxNzEuNiBDIDM0MS40LDE3NC43IDM0NC44LDE3NC43IDM1MC44LDE3NC4zIEMgMzU2LjksMTczLjggMzY1LjcsMTcyLjggMzcwLjMsMTcyLjEgQyAzNzQuOSwxNzEuNCAzNzUuNCwxNzAuOSAzNzUuNiwxNzIuMyBDIDM3NS44LDE3My44IDM3NS44LDE3Ny4yIDM3NS44LDE4Mi43IEMgMzc1LjgsMTg4LjMgMzc1LjgsMTk2LjAgMzc1LjYsMjAzLjUgQyAzNzUuNCwyMTEuMCAzNzQuOSwyMTguMiAzNzQuOSwyMjMuMSBDIDM3NC45LDIyNy45IDM3NS40LDIzMC4zIDM3OS43LDIzMS41IEMgMzg0LjEsMjMyLjcgMzkyLjMsMjMyLjcgNDAwLjYsMjMyLjIgQyA0MDguOCwyMzEuOCA0MTcuMSwyMzAuOCA0MjUuNiwyMjkuNiBDIDQzNC4xLDIyOC40IDQ0Mi44LDIyNi45IDQ0OC42LDIyNi4wIEMgNDU0LjUsMjI1LjAgNDU3LjQsMjI0LjUgNDYwLjMsMjI0LjAiIGRhdGEtdGVzdGlkPSJpbmstcGF0aCIgc3R5bGU9InN0cm9rZTogcmdiKDAsIDAsIDApOyBzdHJva2Utd2lkdGg6IDRweDsgcG9pbnRlci1ldmVudHM6IG5vbmU7Ii8+PHBhdGggY2xhc3M9IlBTUERGS2l0LTQ3cjV6eHB0eWtoanFwenFkdWU5YWNwaHR6IFBTUERGS2l0LTQ0OG5jcjVtdWhycGM4cDh5dmFkeTUxZW1jIFBTUERGS2l0LVNtb290aC1MaW5lcyIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZD0iTSAyMTIuNzYzODUwODAyMDQ4MiwxNjYuMDQwNTk0NDE4MjY3NDYgQyAxODMuNiwxNjAuNyAxNTQuNSwxNTUuNCAxMzUuNiwxNTIuMyBDIDExNi43LDE0OS4xIDEwNy45LDE0OC4yIDEwNC4wLDE0OS45IEMgMTAwLjIsMTUxLjUgMTAxLjEsMTU1LjkgMTAzLjMsMTYwLjcgQyAxMDUuNSwxNjUuNiAxMDguOSwxNzAuOSAxMTYuNCwxNzYuMiBDIDEyMy45LDE4MS41IDEzNS42LDE4Ni44IDE0Mi45LDE4Ny4xIEMgMTUwLjIsMTg3LjMgMTUzLjEsMTgyLjUgMTU1LjUsMTc3LjYgQyAxNTcuOSwxNzIuOCAxNTkuOSwxNjguMCAxNjEuMSwxNjUuMyBDIDE2Mi4zLDE2Mi43IDE2Mi44LDE2Mi4yIDE2NS43LDE2NS44IEMgMTY4LjYsMTY5LjQgMTczLjksMTc3LjIgMTgwLjcsMTg1LjQgQyAxODcuNSwxOTMuNiAxOTUuOCwyMDIuMyAyMDIuMSwyMDcuNiBDIDIwOC40LDIxMi45IDIxMi44LDIxNC45IDIxOC4zLDIxNS42IEMgMjIzLjksMjE2LjMgMjMwLjcsMjE1LjggMjM1LjYsMjEyLjkgQyAyNDAuNCwyMTAuMCAyNDMuMywyMDQuNyAyNDYuMywxOTcuMiBDIDI0OS4yLDE4OS43IDI1Mi4xLDE4MC4xIDI1My41LDE3NS4wIEMgMjU1LjAsMTY5LjkgMjU1LjAsMTY5LjQgMjU1LjIsMTY5LjIgQyAyNTUuNSwxNjguOSAyNTYuMCwxNjguOSAyNTYuOSwxNjkuNCBDIDI1Ny45LDE2OS45IDI1OS40LDE3MC45IDI2MS41LDE3MS44IEMgMjYzLjcsMTcyLjggMjY2LjYsMTczLjggMjY5LjgsMTc0LjMgQyAyNzIuOSwxNzQuNyAyNzYuMywxNzQuNyAyNzkuMywxNzQuMCBDIDI4Mi4yLDE3My4zIDI4NC42LDE3MS44IDI4Ni41LDE2OS45IEMgMjg4LjUsMTY4LjAgMjg5LjksMTY1LjYgMjkwLjksMTYxLjIgQyAyOTEuOSwxNTYuOSAyOTIuNCwxNTAuNiAyOTAuMiwxNDYuMCBDIDI4OC4wLDE0MS40IDI4My4xLDEzOC41IDI3Ny44LDEzNy4wIEMgMjcyLjUsMTM1LjYgMjY2LjYsMTM1LjYgMjYzLjUsMTM1LjYgQyAyNjAuMywxMzUuNiAyNTkuOCwxMzUuNiAyNTkuNCwxMzYuMSBDIDI1OC45LDEzNi42IDI1OC40LDEzNy41IDI2MS4zLDE0MC45IEMgMjY0LjIsMTQ0LjMgMjcwLjUsMTUwLjEgMjgzLjksMTU2LjEgQyAyOTcuMiwxNjIuMiAzMTcuNiwxNjguNSAzMjkuNSwxNzEuNiBDIDM0MS40LDE3NC43IDM0NC44LDE3NC43IDM1MC44LDE3NC4zIEMgMzU2LjksMTczLjggMzY1LjcsMTcyLjggMzcwLjMsMTcyLjEgQyAzNzQuOSwxNzEuNCAzNzUuNCwxNzAuOSAzNzUuNiwxNzIuMyBDIDM3NS44LDE3My44IDM3NS44LDE3Ny4yIDM3NS44LDE4Mi43IEMgMzc1LjgsMTg4LjMgMzc1LjgsMTk2LjAgMzc1LjYsMjAzLjUgQyAzNzUuNCwyMTEuMCAzNzQuOSwyMTguMiAzNzQuOSwyMjMuMSBDIDM3NC45LDIyNy45IDM3NS40LDIzMC4zIDM3OS43LDIzMS41IEMgMzg0LjEsMjMyLjcgMzkyLjMsMjMyLjcgNDAwLjYsMjMyLjIgQyA0MDguOCwyMzEuOCA0MTcuMSwyMzAuOCA0MjUuNiwyMjkuNiBDIDQzNC4xLDIyOC40IDQ0Mi44LDIyNi45IDQ0OC42LDIyNi4wIEMgNDU0LjUsMjI1LjAgNDU3LjQsMjI0LjUgNDYwLjMsMjI0LjAiIGRhdGEtdGVzdGlkPSJjbGlja2FibGUtcGF0aCIgc3R5bGU9InN0cm9rZS13aWR0aDogNXB4OyIvPjwvZz48L2c+PC9zdmc+';

const svg = window.atob(base64Image);

svgToPng(svg,(imgData)=>{
    const pngImage = document.createElement('img');
    document.body.appendChild(pngImage);
    pngImage.src=imgData;
});
 function svgToPng(svg, callback) {
    const url = getSvgUrl(svg);
    svgUrlToPng(url, (imgData) => {
        callback(imgData);
        URL.revokeObjectURL(url);
    });
}
function getSvgUrl(svg) {
    return  URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
}
function svgUrlToPng(svgUrl, callback) {
    const svgImage = document.createElement('img');
    // imgPreview.style.position = 'absolute';
    // imgPreview.style.top = '-9999px';
    document.body.appendChild(svgImage);
    svgImage.onload = function () {
        const canvas = document.createElement('canvas');
        canvas.width = svgImage.clientWidth;
        canvas.height = svgImage.clientHeight;
        const canvasCtx = canvas.getContext('2d');
        canvasCtx.drawImage(svgImage, 0, 0);
        const imgData = canvas.toDataURL('image/png');
        console.log(imgData, 'imgData') // This logged here gives different image in chrome and safari
        callback(imgData);
        // document.body.removeChild(imgPreview);
    };
    svgImage.src = svgUrl;
 }
pritesh
  • 2,162
  • 18
  • 24
  • Have you tried displaying the canvas on screen to see if it looks correct? You have a bunch of steps going on and the end result isn't correct, but you can't be sure which step is breaking. Figuring that out would go a long way to solving your problem. – samanime Oct 25 '21 at 15:46
  • Yes the canvas is displayed correct in both browser, even when you run above snippet you cna see that – pritesh Oct 25 '21 at 15:47
  • Ha, Safari, the new Internet Explorer – Jeremy Thille Oct 25 '21 at 15:50
  • You can report webkit bugs [here](https://bugs.webkit.org/) – Robert Longson Oct 25 '21 at 16:19

1 Answers1

1

drawImage() with an SVG image that has no intrinsic width or height is an interop nightmare.
Currently the specs text does follow Chrome's behavior here, but given they are the only one to implement it, it's hard to call it a "standard".

One of the problems here is that you are setting your canvas size based on the clientWidth and clientHeight properties of your <img> element.
This <img> element embeds an SVG image that doesn't have its own intrinsic width and height. The default width is thus interpreted as 100%.
So your <img> element will occupy whatever the size of the container (<body>) is, and you will set your canvas size to that body's size.

However, this sizing should only affect the <img> element, when its image is painted over a canvas, this <img> element should be (mostly) irrelevant.

What does matter though in this particular case of an image with no intrinsic width or height is the size of the output canvas. In this case, the current specs texts ask to use the CSS default sizing algorithm to determine the image's size, which basically says to use the intrinsic width and height if defined, or if the image has a defined intrinsic aspect-ratio (e.g viewBox), multiply the available value by this ratio, and if neither are present, use a default size.
And this default size is determined by the output canvas size.

Here, the <svg> doesn't have neither a width nor an height, but it does have an intrinsic ratio (110/374.0449438202247), so the width will be set to the canvas width and the height will be set to this width x the ratio.

So the correctly calculated size of the image to be used by drawImage should be dependent on the canvas size and thus on the screen size of your user, which I personally find very disturbing. This is what Chrome does.
Safari on the other hand uses the values in the viewBox representing the width and height. This kind of makes sense, but is problematic with various practice (e.g it's quite common to use a viewBox of 0 0 1 1 for easier absolute positioning in the SVG).

Firefox simply won't draw your image in this case.

You can avoid this discrepancy by not appending your <img>, by setting your canvas width and height based on the <img> .naturalWidth and .naturalHeight, and by setting your root <svg> element's width and height to absolute values (i.e not %).
This will give you the most cross-browser experience, and the most predictable one (since with Chrome's behavior, all your user will have different sizes of images).

const svg = `<svg
  width="374" height="110" ${/* We force width absolute width and height */""}
  xmlns="http://www.w3.org/2000/svg" viewBox="92.97752808988764 129 374.0449438202247 110" class="PSPDFKit-8s1sbc513t34q38895b4bh3jvn" focusable="false" data-testid="ink-svg" style="left: 0px; top: 0px; width: 100%; height: 100%; opacity: 1; overflow: hidden;">
  <rect x="97.97752808988764" y="134" width="364.0449438202247" height="100" class="PSPDFKit-7871rfhyxb7pdmks1n3x7avvta" style="fill: transparent;"/>
  <g style="fill: transparent;">
    <g>
      <path class="PSPDFKit-47r5zxptykhjqpzqdue9acphtz PSPDFKit-448ncr5muhrpc8p8yvady51emc PSPDFKit-Smooth-Lines"
        d="M 212.7638508020482,166.04059441826746 C 183.6,160.7 154.5,155.4 135.6,152.3 C 116.7,149.1 107.9,148.2 104.0,149.9 C 100.2,151.5 101.1,155.9 103.3,160.7 C 105.5,165.6 108.9,170.9 116.4,176.2 C 123.9,181.5 135.6,186.8 142.9,187.1 C 150.2,187.3 153.1,182.5 155.5,177.6 C 157.9,172.8 159.9,168.0 161.1,165.3 C 162.3,162.7 162.8,162.2 165.7,165.8 C 168.6,169.4 173.9,177.2 180.7,185.4 C 187.5,193.6 195.8,202.3 202.1,207.6 C 208.4,212.9 212.8,214.9 218.3,215.6 C 223.9,216.3 230.7,215.8 235.6,212.9 C 240.4,210.0 243.3,204.7 246.3,197.2 C 249.2,189.7 252.1,180.1 253.5,175.0 C 255.0,169.9 255.0,169.4 255.2,169.2 C 255.5,168.9 256.0,168.9 256.9,169.4 C 257.9,169.9 259.4,170.9 261.5,171.8 C 263.7,172.8 266.6,173.8 269.8,174.3 C 272.9,174.7 276.3,174.7 279.3,174.0 C 282.2,173.3 284.6,171.8 286.5,169.9 C 288.5,168.0 289.9,165.6 290.9,161.2 C 291.9,156.9 292.4,150.6 290.2,146.0 C 288.0,141.4 283.1,138.5 277.8,137.0 C 272.5,135.6 266.6,135.6 263.5,135.6 C 260.3,135.6 259.8,135.6 259.4,136.1 C 258.9,136.6 258.4,137.5 261.3,140.9 C 264.2,144.3 270.5,150.1 283.9,156.1 C 297.2,162.2 317.6,168.5 329.5,171.6 C 341.4,174.7 344.8,174.7 350.8,174.3 C 356.9,173.8 365.7,172.8 370.3,172.1 C 374.9,171.4 375.4,170.9 375.6,172.3 C 375.8,173.8 375.8,177.2 375.8,182.7 C 375.8,188.3 375.8,196.0 375.6,203.5 C 375.4,211.0 374.9,218.2 374.9,223.1 C 374.9,227.9 375.4,230.3 379.7,231.5 C 384.1,232.7 392.3,232.7 400.6,232.2 C 408.8,231.8 417.1,230.8 425.6,229.6 C 434.1,228.4 442.8,226.9 448.6,226.0 C 454.5,225.0 457.4,224.5 460.3,224.0"
        data-testid="ink-path" style="stroke: rgb(0, 0, 0); stroke-width: 4px; pointer-events: none;"/>
      <path class="PSPDFKit-47r5zxptykhjqpzqdue9acphtz PSPDFKit-448ncr5muhrpc8p8yvady51emc PSPDFKit-Smooth-Lines" stroke="transparent"
        d="M 212.7638508020482,166.04059441826746 C 183.6,160.7 154.5,155.4 135.6,152.3 C 116.7,149.1 107.9,148.2 104.0,149.9 C 100.2,151.5 101.1,155.9 103.3,160.7 C 105.5,165.6 108.9,170.9 116.4,176.2 C 123.9,181.5 135.6,186.8 142.9,187.1 C 150.2,187.3 153.1,182.5 155.5,177.6 C 157.9,172.8 159.9,168.0 161.1,165.3 C 162.3,162.7 162.8,162.2 165.7,165.8 C 168.6,169.4 173.9,177.2 180.7,185.4 C 187.5,193.6 195.8,202.3 202.1,207.6 C 208.4,212.9 212.8,214.9 218.3,215.6 C 223.9,216.3 230.7,215.8 235.6,212.9 C 240.4,210.0 243.3,204.7 246.3,197.2 C 249.2,189.7 252.1,180.1 253.5,175.0 C 255.0,169.9 255.0,169.4 255.2,169.2 C 255.5,168.9 256.0,168.9 256.9,169.4 C 257.9,169.9 259.4,170.9 261.5,171.8 C 263.7,172.8 266.6,173.8 269.8,174.3 C 272.9,174.7 276.3,174.7 279.3,174.0 C 282.2,173.3 284.6,171.8 286.5,169.9 C 288.5,168.0 289.9,165.6 290.9,161.2 C 291.9,156.9 292.4,150.6 290.2,146.0 C 288.0,141.4 283.1,138.5 277.8,137.0 C 272.5,135.6 266.6,135.6 263.5,135.6 C 260.3,135.6 259.8,135.6 259.4,136.1 C 258.9,136.6 258.4,137.5 261.3,140.9 C 264.2,144.3 270.5,150.1 283.9,156.1 C 297.2,162.2 317.6,168.5 329.5,171.6 C 341.4,174.7 344.8,174.7 350.8,174.3 C 356.9,173.8 365.7,172.8 370.3,172.1 C 374.9,171.4 375.4,170.9 375.6,172.3 C 375.8,173.8 375.8,177.2 375.8,182.7 C 375.8,188.3 375.8,196.0 375.6,203.5 C 375.4,211.0 374.9,218.2 374.9,223.1 C 374.9,227.9 375.4,230.3 379.7,231.5 C 384.1,232.7 392.3,232.7 400.6,232.2 C 408.8,231.8 417.1,230.8 425.6,229.6 C 434.1,228.4 442.8,226.9 448.6,226.0 C 454.5,225.0 457.4,224.5 460.3,224.0"
        data-testid="clickable-path" style="stroke-width: 5px;"/>
    </g>
  </g>
</svg>`;

svgToPng(svg, (imgData) => {
  const pngImage = document.createElement('img');
  pngImage.src = imgData;
});

function svgToPng(svg, callback) {
  const url = getSvgUrl(svg);
  svgUrlToPng(url, (imgData) => {
    callback(imgData);
    URL.revokeObjectURL(url);
  });
}

function getSvgUrl(svg) {
  return URL.createObjectURL(new Blob([svg], {
    type: 'image/svg+xml'
  }));
}

function svgUrlToPng(svgUrl, callback) {
  const svgImage = document.createElement('img');
  // do not append the <img>
  svgImage.onload = function() {
    const canvas = document.createElement('canvas');
    canvas.width = svgImage.naturalWidth;
    canvas.height = svgImage.naturalHeight;
    const canvasCtx = canvas.getContext('2d');
    canvasCtx.drawImage(svgImage, 0, 0);
    // show the <canvas> to debug
    document.body.append(canvas);
    const imgData = canvas.toDataURL('image/png');
    callback(imgData);
  };
  svgImage.src = svgUrl;
}
canvas {
  background: chocolate;
}
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • thanks. that helps but the image generated are of low quality. How can we scale up the image? or the image dimensions – pritesh Oct 26 '21 at 11:59
  • by setting the width and height attributes to the values you want. – Kaiido Oct 26 '21 at 12:34
  • same problem happens, if i increase the height and width of canvas, in chrome it works, but in safari the image comes on top left corner and rest of space occupied by white space – pritesh Oct 26 '21 at 12:50
  • Please read again this answer. It's not the size of the canvas that you need to change, it's the `width` and `height` attributes of the root `` that you are drawing. – Kaiido Oct 26 '21 at 14:11