Skip to content

Instantly share code, notes, and snippets.

@CarsonSlovoka
Last active July 19, 2021 12:23
Show Gist options
  • Save CarsonSlovoka/81195ea3c04edbaa02b35b0678aa7038 to your computer and use it in GitHub Desktop.
Save CarsonSlovoka/81195ea3c04edbaa02b35b0678aa7038 to your computer and use it in GitHub Desktop.
You can convert SVG(Online, Font-awesome, exists SVG) to PNG with the script and save it.
class SVG2IMG {
/**
* @param {HTMLCanvasElement} canvas
* @param {string} src "http://.../xxx.svg" or "data:image/svg+xml;base64,${base64}"
* */
constructor(canvas, src) {
this.canvas = canvas;
this.context = this.canvas.getContext("2d")
this.src = src
this.addTextList = []
}
/**
* @param {HTMLElement} node
* @param {string} mediaType: https://en.wikipedia.org/wiki/Media_type#Common_examples_%5B10%5D
* @see https://en.wikipedia.org/wiki/List_of_URI_schemes
* */
static Convert2URIData(node, mediaType = 'data:image/svg+xml') {
const base64 = btoa(node.outerHTML)
return `${mediaType};base64,${base64}`
}
/**
* @param {string} text
* @param {int} x
* @param {int} y
* @param {"stroke"|"fill"} mode
* @param {string} size, "30px"
* @param {string} font, example: "Arial"
* @param {string} color, example: "#3ae016" or "yellow"
* @param {int} alpha, 0.0 (fully transparent) to 1.0 (fully opaque) // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors#transparency
* */
AddText(text, x, y, {mode = "fill", size = "32px", font = "Arial", color = "black", alpha = 1.0}) {
const drawFunc = (text, x, y, mode, font) => {
return () => {
// https://www.w3schools.com/graphics/canvas_text.asp
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillText
const context = this.context
const originAlpha = context.globalAlpha
context.globalAlpha = alpha
context.font = `${size} ${font}`
switch (mode) {
case "fill":
context.fillStyle = color
context.fillText(text, x, y)
break
case "stroke":
context.strokeStyle = color
context.strokeText(text, x, y)
break
default:
throw Error(`Unknown mode:${mode}`)
}
context.globalAlpha = originAlpha
}
}
this.addTextList.push(drawFunc(text, x, y, mode, font))
}
/**
* @description When the build is finished, you can click the filename to download the PNG or mouse enters to copy PNG to the clipboard.
* */
Build(filename = "download.png") {
const img = new Image(/*this.canvas.width, this.canvas.height*/)
img.src = this.src
img.crossOrigin = "anonymous" // Fixes: Tainted canvases may not be exported
img.onload = (event) => {
this.context.drawImage(event.target, 0, 0)
for (const drawTextFunc of this.addTextList) {
drawTextFunc()
}
// create a "a" node for download
const a = document.createElement('a')
document.querySelector('body').append(a)
a.innerText = filename
a.download = filename
const quality = 1.0
// a.target = "_blank"
a.href = this.canvas.toDataURL("image/png", quality)
a.append(this.canvas)
}
this.canvas.onmouseenter = (event) => {
document.featurePolicy.allowedFeatures() // Fixes: DOMException: The Clipboard API has been blocked because of a permissions policy applied to the current document
// set background to white. Otherwise, background-color is black.
this.context.globalCompositeOperation = "destination-over" // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation // https://www.w3schools.com/tags/canvas_globalcompositeoperation.asp
this.context.fillStyle = "rgb(255,255,255)"
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height)
this.canvas.toBlob(blob => navigator.clipboard.write([new ClipboardItem({'image/png': blob})])) // copy to clipboard // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/clipboard
/* It will open a new window, and you can drag the picture to your desktop to save it.
const quality = 1.0
const iframe = `<iframe width="66%" height="50%" src=${this.canvas.toDataURL("image/png", quality)}></iframe>` // Error: Tainted canvases may not be exported // img.crossOrigin="anonymous"
document.open()
document.write(iframe)
document.close()
*/
}
}
}
class Canvas {
/**
* @description for do something like that: ``<canvas width="" height=""></>canvas>``
**/
constructor(w, h) {
const canvas = document.createElement("canvas")
document.querySelector(`body`).append(canvas)
this.canvas = canvas;
[this.canvas.width, this.canvas.height] = [w, h]
}
/**
* @description If your SVG is large, you may want to know which part is what you wanted.
* */
DrawGrid(step = 100) {
const ctx = this.canvas.getContext('2d')
const w = this.canvas.width
const h = this.canvas.height
// Draw the vertical line.
ctx.beginPath();
for (let x = 0; x <= w; x += step) {
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
}
// set the color of the line
ctx.strokeStyle = 'rgba(255,0,0, 0.5)'
ctx.lineWidth = 1
ctx.stroke();
// Draw the horizontal line.
ctx.beginPath();
for (let y = 0; y <= h; y += step) {
ctx.moveTo(0, y)
ctx.lineTo(w, y)
}
ctx.strokeStyle = 'rgba(128, 128, 128, 0.5)'
ctx.lineWidth = 5
ctx.stroke()
}
}
function ImportFontAwesome() {
const range = document.createRange()
const frag = range.createContextualFragment(`
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" integrity="sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w==" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/js/all.min.js" integrity="sha512-UwcC/iaz5ziHX7V6LjSKaXgCuRRqbTp1QHpbOJ4l1nw2/boCfZ2KlFIqBUA/uRVF0onbREnY9do8rM/uT/ilqw==" crossorigin="anonymous"/>
`)
document.querySelector("head").append(frag)
}
// 👇 Below is a Test to teach you how to use it.
(() => {
window.onload = () => {
// Test 1: SVG from Online
const canvas = new Canvas(650, 500)
// canvas.DrawGrid() // If you want to show grid, you can use it.
const svg2img = new SVG2IMG(canvas.canvas, "https://upload.wikimedia.org/wikipedia/commons/b/bd/Test.svg")
svg2img.AddText("Hello", 100, 250, {mode: "fill", color: "yellow", alpha: 0.8})
svg2img.AddText("world", 200, 250, {mode: "stroke", color: "red"})
svg2img.AddText("!", 280, 250, {color: "#f700ff", size: "72px"})
svg2img.Build("Test.png")
// Test 2: URI.data
const canvas2 = new Canvas(180, 180)
const uriData = "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc21pbGUtd2luayBmYS13LTE2IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhciIgZGF0YS1pY29uPSJzbWlsZS13aW5rIiByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDQ5NiA1MTIiIGRhdGEtZmEtaTJzdmc9IiI+PHBhdGggZmlsbD0iY3VycmVudENvbG9yIiBkPSJNMjQ4IDhDMTExIDggMCAxMTkgMCAyNTZzMTExIDI0OCAyNDggMjQ4IDI0OC0xMTEgMjQ4LTI0OFMzODUgOCAyNDggOHptMCA0NDhjLTExMC4zIDAtMjAwLTg5LjctMjAwLTIwMFMxMzcuNyA1NiAyNDggNTZzMjAwIDg5LjcgMjAwIDIwMC04OS43IDIwMC0yMDAgMjAwem0xMTcuOC0xNDYuNGMtMTAuMi04LjUtMjUuMy03LjEtMzMuOCAzLjEtMjAuOCAyNS01MS41IDM5LjQtODQgMzkuNHMtNjMuMi0xNC4zLTg0LTM5LjRjLTguNS0xMC4yLTIzLjctMTEuNS0zMy44LTMuMS0xMC4yIDguNS0xMS41IDIzLjYtMy4xIDMzLjggMzAgMzYgNzQuMSA1Ni42IDEyMC45IDU2LjZzOTAuOS0yMC42IDEyMC45LTU2LjZjOC41LTEwLjIgNy4xLTI1LjMtMy4xLTMzLjh6TTE2OCAyNDBjMTcuNyAwIDMyLTE0LjMgMzItMzJzLTE0LjMtMzItMzItMzItMzIgMTQuMy0zMiAzMiAxNC4zIDMyIDMyIDMyem0xNjAtNjBjLTI1LjcgMC01NS45IDE2LjktNTkuOSA0Mi4xLTEuNyAxMS4yIDExLjUgMTguMiAxOS44IDEwLjhsOS41LTguNWMxNC44LTEzLjIgNDYuMi0xMy4yIDYxIDBsOS41IDguNWM4LjUgNy40IDIxLjYuMyAxOS44LTEwLjgtMy44LTI1LjItMzQtNDIuMS01OS43LTQyLjF6Ij48L3BhdGg+PC9zdmc+"
const svg2img2 = new SVG2IMG(canvas2.canvas, uriData)
svg2img2.Build("SmileWink.png")
// Test 3: Exists SVG
ImportFontAwesome()
const range = document.createRange()
const fragSmile = range.createContextualFragment(`<i class="far fa-smile" style="background-color:black;color:yellow"></i>`)
document.querySelector(`body`).append(fragSmile)
// use MutationObserver wait the fontawesome convert ``<i class="far fa-smile"></i>`` to SVG. If you write the element in the HTML, then you can skip this hassle way.
const observer = new MutationObserver((mutationRecordList, observer) => {
for (const mutation of mutationRecordList) {
switch (mutation.type) {
case "childList":
const targetSVG = mutation.target.querySelector(`svg`)
if (targetSVG !== null) {
const canvas3 = new Canvas(64, 64) // 👈 Focus here. The part of the observer is not important.
const svg2img3 = new SVG2IMG(canvas3.canvas, SVG2IMG.Convert2URIData(targetSVG))
svg2img3.Build("Smile.png")
targetSVG.remove() // This SVG is created by font-awesome, and it's an extra element. I don't want to see it.
observer.disconnect()
return
}
}
}
})
observer.observe(document.querySelector(`body`), {childList: true})
}
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment