Skip to content

Instantly share code, notes, and snippets.

@tatsuyasusukida
Last active April 30, 2024 06:11
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tatsuyasusukida/1261585e3422da5645a1cbb9cf8813d6 to your computer and use it in GitHub Desktop.
Save tatsuyasusukida/1261585e3422da5645a1cbb9cf8813d6 to your computer and use it in GitHub Desktop.
πŸŒ… How to convert SVG to PNG with JavaScript [video version available]

πŸŒ… How to convert SVG to PNG with JavaScript [video version available]

Video thumbnail: How to convert SVG to PNG with JavaScript

About this article

This article describes how to convert SVG to PNG with frontend JavaScript. The related resources are as follows.

Workflow

The workflow is as follows.

  1. Preparation for coding
  2. Coding
  3. Operation check

Preparation for coding

Run the following command in the terminal to prepare for coding.

mkdir javascript-svg-png
cd javascript-svg-png
touch index.html main.js

Coding

index.html

Open index.html in the editor and enter the following content.

Click to go to index.html

main.js

Open main.js in the editor and enter the following content.

Click to go to main.js

Operation check

Open index.html in your browser. For macOS, it is convenient to run the following command in the terminal.

open index.html

Make sure you see the PNG image in the Output PNG Image section of your web page.

It looks like index.html is opened in the browser. The heading of the web page is "How to convert SVG to PNG with JavaScript", and the "Input SVG image" section and the "Output PNG image" section contain the same gray circle image respectively.

Conclusion

The sample source code contains the following sentence to convert SVG text to base64.

const svgDataBase64 = btoa(unescape(encodeURIComponent(svgData)))

I was curious about how it would be converted to base64 through the process, so I looked it up. The following is the SVG text before conversion.

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" id="input">
  <circle cx="120" cy="120" r="90" fill="#888888"/>
</svg>

Next, the following is the execution result of encodeURIComponent(svgData).

%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22240%22%20height%3D%22240%22%20id%3D%22input%22%3E%0A%20%20%20%20%20%20%20%20%3Ccircle%20cx%3D%22120%22%20cy%3D%22120%22%20r%3D%2290%22%20fill%3D%22%23888888%22%2F%3E%0A%20%20%20%20%20%20%3C%2Fsvg%3E

Next, the following is the execution result of unescape(encodeURIComponent(svgData)).

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" id="input">
  <circle cx="120" cy="120" r="90" fill="#888888"/>
</svg>

Finally, the following is the execution result of btoa(unescape(encodeURIComponent(svgData))).

PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNDAiIGhlaWdodD0iMjQwIiBpZD0iaW5wdXQiPgogICAgICAgIDxjaXJjbGUgY3g9IjEyMCIgY3k9IjEyMCIgcj0iOTAiIGZpbGw9IiM4ODg4ODgiLz4KICAgICAgPC9zdmc+

btoa is a function that converts a string to Base64, but you cannot pass a string containing non-ASCII characters as an argument. Therefore, I use the encodeURIComponent function and the unescape function to convert non-ASCII characters to ASCII characters. For example, the Japanese hiragana "あ" is 0xE3 0x81 0x82 in UTF-8, so the result of encodeURIComponent ('あ') is %E3%81%82 (By the way, in the case of encode ('あ') Will raise a SyntaxError exception). On the other hand, the unescape function recognizes %E3 %81 %82 as 3 characters, so the result of unescape('%E3%81%82') is'Γ£\x81\x82'.

I understand that the JavaScript btoa(unescape(encodeURIComponent(text))) is similar to the Buffer.from(text).toString('base64') in Node.js. So is it possible to convert from base64 to text with decodeURIComponent(escape(atob(base64))) (same asBuffer.from(base64,'base64'). toString() in Node.js? Is it something like that?). When I executed the code below, it was evaluated as "A", so it seems to be correct.

decodeURIComponent(escape(atob(btoa(unescape(encodeURIComponent('あ'))))))

I didn't always understand the reason and just copied and pasted it, so I got smarter because I had time to investigate. I hope this article helps anyone with similar questions. Thank you for reading!

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>How to convert SVG to PNG with JavaScript</title>
</head>
<body>
<h1>How to convert SVG to PNG with JavaScript</h1>
<section>
<h2>Input SVG image</h2>
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" id="input">
<circle cx="120" cy="120" r="90" fill="#888888" />
</svg>
</section>
<section>
<h2>Output PNG image</h2>
<img alt="" id="output">
</section>
<script src="main.js"></script>
</body>
</html>
main()
function main () {
const input = document.querySelector('#input')
const output = document.querySelector('#output')
const svgData = new XMLSerializer().serializeToString(input)
const svgDataBase64 = btoa(unescape(encodeURIComponent(svgData)))
const svgDataUrl = `data:image/svg+xml;charset=utf-8;base64,${svgDataBase64}`
// console.log(svgData)
// console.log(encodeURIComponent(svgData))
// console.log(unescape(encodeURIComponent(svgData)))
// console.log(btoa(unescape(encodeURIComponent(svgData))))
const image = new Image()
image.addEventListener('load', () => {
const width = input.getAttribute('width')
const height = input.getAttribute('height')
const canvas = document.createElement('canvas')
canvas.setAttribute('width', width)
canvas.setAttribute('height', height)
const context = canvas.getContext('2d')
context.drawImage(image, 0, 0, width, height)
const dataUrl = canvas.toDataURL('image/png')
output.src = dataUrl
})
image.src = svgDataUrl
}
@simonramosb
Copy link

Thank you so much, sir!

@mdigliodo
Copy link

This function was very useful, amazing work. Thank you so much @tatsuyasusukida 🫑

@tatsuyasusukida
Copy link
Author

@simonramosb
@mdigliodo

Thank you for your comments!
I'm glad if it was helpful for you πŸ˜„

@govizlora
Copy link

govizlora commented Jul 11, 2023

Thanks Tatsuya!

I found that you could also use input.outerHTML to get the string representation of the SVG, though it's not as rigorous as new XMLSerializer().serializeToString(input) (Here is an answer I found that might help: https://stackoverflow.com/a/73383017/6946572)

Also, for this part:

const svgDataBase64 = btoa(unescape(encodeURIComponent(svgData)))
const svgDataUrl = `data:image/svg+xml;charset=utf-8;base64,${svgDataBase64}`

Below works the same for drawing to the canvas.

const blob = new Blob([svgData], { type: "image/svg+xml" })
const svgDataUrl = URL.createObjectURL(blob)

Though, manually revoking the object URL is recommended (https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static#memory_management)

@tatsuyasusukida
Copy link
Author

@govizlora

Thank you for your comment!

Thanks to your comment, I learned a lot.

In particular, I think the code below is smarter than mine.

const blob = new Blob([svgData], { type: "image/svg+xml" })
const svgDataUrl = URL.createObjectURL(blob)

I will use the code you taught me πŸ˜„

@welpher
Copy link

welpher commented Nov 10, 2023

Thanks Tatsuya!

I found that you could also use input.outerHTML to get the string representation of the SVG, though it's not as rigorous as new XMLSerializer().serializeToString(input) (Here is an answer I found that might help: https://stackoverflow.com/a/73383017/6946572)

Also, for this part:

const svgDataBase64 = btoa(unescape(encodeURIComponent(svgData)))
const svgDataUrl = `data:image/svg+xml;charset=utf-8;base64,${svgDataBase64}`

Below works the same for drawing to the canvas.

const blob = new Blob([svgData], { type: "image/svg+xml" })
const svgDataUrl = URL.createObjectURL(blob)

Though, manually revoking the object URL is recommended (https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static#memory_management)

the second way is always error in webpack dev server like this "DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported." even i add crossOrigin 'anonymous' to image

@tatsuyasusukida
Copy link
Author

@welpher
Thank you for your comment! Since your comment was interesting, I created the demo below and deployed it to Vercel.

https://github.com/tatsuyasusukida/svg-to-png
https://svg-to-png-nine.vercel.app/

As a result, I have confirmed that both methods are successful. I used Google Chrome, Firefox, and Safari on macOS.

Thank you for the motivation to check!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment