SVG foreignObject contents do not display unless plain text - html

I am trying to output HTML using the foreignObject tag inside an SVG drawing. I am using d3 to generate the elements. The only time the HTML content inside the foreignObject tag shows up is when the content inside the foreignObect tag is plain text, otherwise it just shows up as empty/blank. Please see this jsfiddle for an example of my problem: http://jsfiddle.net/R9e3Y/29/
The contents inside the foreignObject tag show up on inspecting the element this this:
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<foreignObject x="40" y="40" width="100" height="100">
<div>test</div>
</foreignObject>
</svg>
but are not visible on the screen? How do I get the content to show up?

Since you're using d3 you need to tell d3 that the div is a html div and not some element in the svg namespace. Try
.append("xhtml:div")

<foreignObject> allows embedding all kinds of markup, not just HTML. This means, there must be a way of determining what language is used. That's where namespaces come into play.
To tell SVG what kind of foreignObject you have, you have to put the content in the proper namespace.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<foreignObject x="40" y="40" width="100" height="100">
<div xmlns="http://www.w3.org/1999/xhtml">test</div>
</foreignObject>
</svg>
In your example, the <div> element is in the SVG namespace, i.e. it's an SVG element, not an HTML one (albeit a non-standard one).
The <foreignObject> element also has a requiredExtensions attribute to tell the browser which extension is used, however different browsers seem to be interpreting this attribute differently, so it's probably best to not set it.

Make sure you set width and height parameters.
Adding xmlns did not fix things for me. It turned out the problem for me was even more simple... I did not add width and height parameters, so content inside the foreignObject did not show, even though it was there. Both parameters seem to default to 0.

I developed something for this. The code is below. A more advanced version uses a proxy to pull in external non-CORS resources. svg foreignObject blocks loading of any non-CORS cross-origin request. I wrote a simple proxy to run on Runkit. See at the bottom.
The limitations of this are: no external non-CORS fonts, no non-CORS images. Anyone who wants to help improve this, including adding in support for images and fonts, can contribute here: https://github.com/dosyago-coder-0/dompeg.js/blob/master/dompeg.js
web page script:
(async function (){
const width = document.scrollingElement.scrollWidth;
const height = document.scrollingElement.scrollHeight;
const doc = document.implementation.createHTMLDocument('');
doc.write(document.documentElement.outerHTML);
doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
const styles = [];
for( let i = 0; i < document.styleSheets.length; i++ ) {
const ss = document.styleSheets[i];
if ( ss.cssRules ) {
for( let j = 0; j < ss.cssRules.length; j++ ) {
styles.push( ss.cssRules[j].cssText );
}
} else {
try {
const res = await fetch(ss.href);
const cssText = await res.text();
styles.push(cssText);
} catch(e) {
/** fetch to proxy here as fallback
* uncomment if you set up your proxy server
try {
const res = await fetch(`https://${YOUR PROXY SERVER}.runkit.sh/?url=${btoa(ss.href)}`);
const cssText = await res.text();
styles.push(cssText);
} catch(e) { **/
console.warn(`Exception adding styles from ${ss.href}`, e, e.stack);
/** uncomment if you setup proxy
}
**/
}
}
}
Array.from( doc.querySelectorAll('noscript, link, script')).forEach( el => el.remove() );
stripComments(doc);
Array.from( doc.querySelectorAll('*[style]')).forEach( el => {
const styleText = el.getAttribute('style');
const uniq = (Math.random()+''+performance.now()).replace(/\./g,'x');
const className = `class${uniq}`;
const cssText = `.${className} {${ styleText }}`;
styles.push( cssText );
el.classList.add( className );
});
const styleElement = doc.createElement('style');
styleElement.innerText = styles.join('\n');
doc.documentElement.appendChild(styleElement);
const canvas = document.createElement('canvas');
Object.assign( canvas, {width,height});
const ctx = canvas.getContext('2d');
const data = `
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
<foreignObject width="100%" height="100%">
${(new XMLSerializer).serializeToString(doc).slice(15)}
</foreignObject>
</svg>`;
const DOMURL = window.URL || window.webkitURL || window;
const img = new Image();
const svg = new Blob([data], {type: 'image/svg+xml'});
Object.assign( img, {width,height});
img.crossOrigin = "Anonymous";
img.onload = function() {
ctx.fillStyle = 'white';
ctx.fillRect( 0, 0, canvas.width, canvas.height );
ctx.drawImage(img, 0, 0);
const datauri = canvas.toDataURL('image/jpeg');
const anchor = document.createElement('a');
anchor.download = 'screen.jpg';
anchor.href = datauri;
anchor.target = "_new";
anchor.innerText = 'download screen.jpg';
anchor.addEventListener('click', e => {e.stopPropagation();anchor.remove();}, { capture: true });
document.body.appendChild(anchor);
Object.assign( anchor.style, {
position: 'fixed',
background:'white',
fontSize: '18px',
fontFamily: 'monospace',
color: 'blue',
top: 0,
left: 0,
zIndex: Number.MAX_SAFE_INTEGER
});
}
img.src = buildSvgImageUrl(data);
img.style.position = "absolute";
img.style.zIndex = "10000000";
img.style.backgroundColor = "white";
//document.body.appendChild(img);
function buildSvgImageUrl(svg) {
const b64 = btoa(unescape(encodeURIComponent(svg)));
return "data:image/svg+xml;base64," + b64;
}
function stripComments(docNode){
const commentWalker = docNode.evaluate('//comment()', docNode, null, XPathResult.ANY_TYPE, null);
let comment = commentWalker.iterateNext();
const cuts = [];
while (comment) {
cuts.push(comment);
comment = commentWalker.iterateNext();
}
cuts.forEach( node => node.remove());
}
}());
runkit proxy server script:
const request = require("request");
const rp = require('request-promise');
const {URL} = require('url');
const express = require("#runkit/runkit/express-endpoint/1.0.0");
const b64 = require('base-64');
const bodyParser = require('body-parser');
const page = (url,err) => `
<form method=POST style="
position: fixed;
position: sticky;
display: table;
top: 0px;
z-index:12000000;
background: white;">
<label for=hider99>X</label><input id=hider99 type=checkbox>
<style>
#hider99:checked ~ fieldset {
display: none;
}
</style>
<fieldset><legend>Proxy</legend>
<p>
<input required type=url size=62 name=url placeholder="any url" value="${url||'https://google.com/favicon.ico'}">
<button style=background:lime>Load</button>
${ !! err ? `<p><span style=color:red>${err}</span>` : '' }
</fieldset>
</form>`;
const app = express(module.exports);
app.use(bodyParser.urlencoded({ extended: false }));
app.get("/", async (req,res,next) => {
console.log(req.query.url);
let url;
res.type('html');
res.set('access-control-allow-origin', '*');
try {
url = b64.decode(req.query.url);
new URL(url);
} catch(e) { res.end(page('',"not a url"+e)); return; }
try {
res.type(type(url));
const data = await rp(url);
res.end(data);
} catch(e) { res.end(page('',""+e)); }
});
app.get("/:anything", async (req,res,next) => {
res.type('html');
res.end('404 Not found');
});
function type(s = '') {
return s.split(/\./g).pop() || 'html';
}
void 0;

In addition to #robert-longson's answer:
If you're using a d3 selection.join(), it would look like this:
myForeignObject.selectAll('div').data(myData).join('xhtml:div')
Note that the xhtml namespace is only specified in the join(), not in the selectAll().

var a = function(a) {
var b = enter code heredocument.createElementNS("http://www.w3.org/1999/xhtml", "div");
b.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg">' + a + "</svg>";
for (var c = document.createDocumentFragment(); b.firstChild.firstChild; ){
c.appendChild(b.firstChild.firstChild);
}
return c;
}
$('#app_canvasContainer svg').append(a('<foreignObject overflow="visible" width="200" height="100" x="'+ x +'" y="'+ y +'" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><textarea class="text-entity" readonly="true" resizeable=false xmlns="http://www.w3.org/1999/xhtml" >'+ val +'</textarea>`enter code here`</foreignObject>'));

Related

Show the uploaded file on the home page [duplicate]

I want to be able to preview a file (image) before it is uploaded. The preview action should be executed all in the browser without using Ajax to upload the image.
How can I do this?
imgInp.onchange = evt => {
const [file] = imgInp.files
if (file) {
blah.src = URL.createObjectURL(file)
}
}
<form runat="server">
<input accept="image/*" type='file' id="imgInp" />
<img id="blah" src="#" alt="your image" />
</form>
There are a couple ways you can do this. The most efficient way would be to use URL.createObjectURL() on the File from your <input>. Pass this URL to img.src to tell the browser to load the provided image.
Here's an example:
<input type="file" accept="image/*" onchange="loadFile(event)">
<img id="output"/>
<script>
var loadFile = function(event) {
var output = document.getElementById('output');
output.src = URL.createObjectURL(event.target.files[0]);
output.onload = function() {
URL.revokeObjectURL(output.src) // free memory
}
};
</script>
You can also use FileReader.readAsDataURL() to parse the file from your <input>. This will create a string in memory containing a base64 representation of the image.
<input type="file" accept="image/*" onchange="loadFile(event)">
<img id="output"/>
<script>
var loadFile = function(event) {
var reader = new FileReader();
reader.onload = function(){
var output = document.getElementById('output');
output.src = reader.result;
};
reader.readAsDataURL(event.target.files[0]);
};
</script>
One-liner solution:
The following code uses object URLs, which is much more efficient than data URL for viewing large images (A data URL is a huge string containing all of the file data, whereas an object URL, is just a short string referencing the file data in-memory):
<img id="blah" alt="your image" width="100" height="100" />
<input type="file"
onchange="document.getElementById('blah').src = window.URL.createObjectURL(this.files[0])">
Generated URL will be like:
blob:http%3A//localhost/7514bc74-65d4-4cf0-a0df-3de016824345
Try This
To PREVIEW the image before uploading it to the SERVER from the Browser without using Ajax or any complicated functions.
It needs an "onChange" event to load the image.
function preview() {
frame.src=URL.createObjectURL(event.target.files[0]);
}
<form>
<input type="file" onchange="preview()">
<img id="frame" src="" width="100px" height="100px"/>
</form>
To preview multiple image click here
The answer of LeassTaTT works well in "standard" browsers like FF and Chrome.
The solution for IE exists but looks different. Here description of cross-browser solution:
In HTML we need two preview elements, img for standard browsers and div for IE
HTML:
<img id="preview"
src=""
alt=""
style="display:none; max-width: 160px; max-height: 120px; border: none;"/>
<div id="preview_ie"></div>
In CSS we specify the following IE specific thing:
CSS:
#preview_ie {
FILTER: progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)
}
In HTML we include the standard and the IE-specific Javascripts:
<script type="text/javascript">
{% include "pic_preview.js" %}
</script>
<!--[if gte IE 7]>
<script type="text/javascript">
{% include "pic_preview_ie.js" %}
</script>
The pic_preview.js is the Javascript from the LeassTaTT's answer. Replace the $('#blah') whith the $('#preview') and add the $('#preview').show()
Now the IE specific Javascript (pic_preview_ie.js):
function readURL (imgFile) {
var newPreview = document.getElementById('preview_ie');
newPreview.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = imgFile.value;
newPreview.style.width = '160px';
newPreview.style.height = '120px';
}
That's is. Works in IE7, IE8, FF and Chrome. Please test in IE9 and report.
The idea of IE preview was found here:
http://forums.asp.net/t/1320559.aspx
http://msdn.microsoft.com/en-us/library/ms532969(v=vs.85).aspx
Short two-liner
This is size improvement of cmlevy answer - try
<input type=file oninput="pic.src=window.URL.createObjectURL(this.files[0])">
<img id="pic" />
I have edited #Ivan's answer to display "No Preview Available" image, if it is not an image:
function readURL(input) {
var url = input.value;
var ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase();
if (input.files && input.files[0]&& (ext == "gif" || ext == "png" || ext == "jpeg" || ext == "jpg")) {
var reader = new FileReader();
reader.onload = function (e) {
$('.imagepreview').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}else{
$('.imagepreview').attr('src', '/assets/no_preview.png');
}
}
Here's a multiple files version, based on Ivan Baev's answer.
The HTML
<input type="file" multiple id="gallery-photo-add">
<div class="gallery"></div>
JavaScript / jQuery
$(function() {
// Multiple images preview in browser
var imagesPreview = function(input, placeToInsertImagePreview) {
if (input.files) {
var filesAmount = input.files.length;
for (i = 0; i < filesAmount; i++) {
var reader = new FileReader();
reader.onload = function(event) {
$($.parseHTML('<img>')).attr('src', event.target.result).appendTo(placeToInsertImagePreview);
}
reader.readAsDataURL(input.files[i]);
}
}
};
$('#gallery-photo-add').on('change', function() {
imagesPreview(this, 'div.gallery');
});
});
Requires jQuery 1.8 due to the usage of $.parseHTML, which should help with XSS mitigation.
This will work out of the box, and the only dependancy you need is jQuery.
Yes. It is possible.
Html
<input type="file" accept="image/*" onchange="showMyImage(this)" />
<br/>
<img id="thumbnil" style="width:20%; margin-top:10px;" src="" alt="image"/>
JS
function showMyImage(fileInput) {
var files = fileInput.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imageType = /image.*/;
if (!file.type.match(imageType)) {
continue;
}
var img=document.getElementById("thumbnil");
img.file = file;
var reader = new FileReader();
reader.onload = (function(aImg) {
return function(e) {
aImg.src = e.target.result;
};
})(img);
reader.readAsDataURL(file);
}
}
You can get Live Demo from here.
Clean and simple
JSfiddle
This will be useful when you want The event to triggered indirectly from a div or a button.
<img id="image-preview" style="height:100px; width:100px;" src="" >
<input style="display:none" id="input-image-hidden" onchange="document.getElementById('image-preview').src = window.URL.createObjectURL(this.files[0])" type="file" accept="image/jpeg, image/png">
<button onclick="HandleBrowseClick('input-image-hidden');" >UPLOAD IMAGE</button>
<script type="text/javascript">
function HandleBrowseClick(hidden_input_image)
{
var fileinputElement = document.getElementById(hidden_input_image);
fileinputElement.click();
}
</script>
TO PREVIEW MULTIPLE FILES using jquery
$(document).ready(function(){
$('#image').change(function(){
$("#frames").html('');
for (var i = 0; i < $(this)[0].files.length; i++) {
$("#frames").append('<img src="'+window.URL.createObjectURL(this.files[i])+'" width="100px" height="100px"/>');
}
});
});
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<input type="file" id="image" name="image[]" multiple /><br/>
<div id="frames"></div>
</body>
Example with multiple images using JavaScript (jQuery) and HTML5
JavaScript (jQuery)
function readURL(input) {
for(var i =0; i< input.files.length; i++){
if (input.files[i]) {
var reader = new FileReader();
reader.onload = function (e) {
var img = $('<img id="dynamic">');
img.attr('src', e.target.result);
img.appendTo('#form1');
}
reader.readAsDataURL(input.files[i]);
}
}
}
$("#imgUpload").change(function(){
readURL(this);
});
}
Markup (HTML)
<form id="form1" runat="server">
<input type="file" id="imgUpload" multiple/>
</form>
In React, if the file is in your props, you can use:
{props.value instanceof File && (
<img src={URL.createObjectURL(props.value)}/>
)}
How about creating a function that loads the file and fires a custom event. Then attach a listener to the input. This way we have more flexibility to use the file, not just for previewing images.
/**
* #param {domElement} input - The input element
* #param {string} typeData - The type of data to be return in the event object.
*/
function loadFileFromInput(input,typeData) {
var reader,
fileLoadedEvent,
files = input.files;
if (files && files[0]) {
reader = new FileReader();
reader.onload = function (e) {
fileLoadedEvent = new CustomEvent('fileLoaded',{
detail:{
data:reader.result,
file:files[0]
},
bubbles:true,
cancelable:true
});
input.dispatchEvent(fileLoadedEvent);
}
switch(typeData) {
case 'arraybuffer':
reader.readAsArrayBuffer(files[0]);
break;
case 'dataurl':
reader.readAsDataURL(files[0]);
break;
case 'binarystring':
reader.readAsBinaryString(files[0]);
break;
case 'text':
reader.readAsText(files[0]);
break;
}
}
}
function fileHandler (e) {
var data = e.detail.data,
fileInfo = e.detail.file;
img.src = data;
}
var input = document.getElementById('inputId'),
img = document.getElementById('imgId');
input.onchange = function (e) {
loadFileFromInput(e.target,'dataurl');
};
input.addEventListener('fileLoaded',fileHandler)
Probably my code isn't as good as some users but I think you will get the point of it. Here you can see an example
Following is the working code.
<input type='file' onchange="readURL(this);" />
<img id="ShowImage" src="#" />
Javascript:
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#ShowImage')
.attr('src', e.target.result)
.width(150)
.height(200);
};
reader.readAsDataURL(input.files[0]);
}
}
Try this
window.onload = function() {
if (window.File && window.FileList && window.FileReader) {
var filesInput = document.getElementById("uploadImage");
filesInput.addEventListener("change", function(event) {
var files = event.target.files;
var output = document.getElementById("result");
for (var i = 0; i < files.length; i++) {
var file = files[i];
if (!file.type.match('image'))
continue;
var picReader = new FileReader();
picReader.addEventListener("load", function(event) {
var picFile = event.target;
var div = document.createElement("div");
div.innerHTML = "<img class='thumbnail' src='" + picFile.result + "'" +
"title='" + picFile.name + "'/>";
output.insertBefore(div, null);
});
picReader.readAsDataURL(file);
}
});
}
}
<input type="file" id="uploadImage" name="termek_file" class="file_input" multiple/>
<div id="result" class="uploadPreview">
What about this solution?
Just add the data attribute "data-type=editable" to an image tag like this:
<img data-type="editable" id="companyLogo" src="http://www.coventrywebgraphicdesign.co.uk/wp-content/uploads/logo-here.jpg" height="300px" width="300px" />
And the script to your project off course...
function init() {
$("img[data-type=editable]").each(function (i, e) {
var _inputFile = $('<input/>')
.attr('type', 'file')
.attr('hidden', 'hidden')
.attr('onchange', 'readImage()')
.attr('data-image-placeholder', e.id);
$(e.parentElement).append(_inputFile);
$(e).on("click", _inputFile, triggerClick);
});
}
function triggerClick(e) {
e.data.click();
}
Element.prototype.readImage = function () {
var _inputFile = this;
if (_inputFile && _inputFile.files && _inputFile.files[0]) {
var _fileReader = new FileReader();
_fileReader.onload = function (e) {
var _imagePlaceholder = _inputFile.attributes.getNamedItem("data-image-placeholder").value;
var _img = $("#" + _imagePlaceholder);
_img.attr("src", e.target.result);
};
_fileReader.readAsDataURL(_inputFile.files[0]);
}
};
//
// IIFE - Immediately Invoked Function Expression
// https://stackoverflow.com/questions/18307078/jquery-best-practises-in-case-of-document-ready
(
function (yourcode) {
"use strict";
// The global jQuery object is passed as a parameter
yourcode(window.jQuery, window, document);
}(
function ($, window, document) {
"use strict";
// The $ is now locally scoped
$(function () {
// The DOM is ready!
init();
});
// The rest of your code goes here!
}));
See demo at JSFiddle
Preview multiple images before it is uploaded using jQuery/javascript?
This will preview multiple files as thumbnail images at a time
Html
<input id="ImageMedias" multiple="multiple" name="ImageMedias" type="file"
accept=".jfif,.jpg,.jpeg,.png,.gif" class="custom-file-input" value="">
<div id="divImageMediaPreview"></div>
Script
$("#ImageMedias").change(function () {
if (typeof (FileReader) != "undefined") {
var dvPreview = $("#divImageMediaPreview");
dvPreview.html("");
$($(this)[0].files).each(function () {
var file = $(this);
var reader = new FileReader();
reader.onload = function (e) {
var img = $("<img />");
img.attr("style", "width: 150px; height:100px; padding: 10px");
img.attr("src", e.target.result);
dvPreview.append(img);
}
reader.readAsDataURL(file[0]);
});
} else {
alert("This browser does not support HTML5 FileReader.");
}
});
Working Demo on Codepen
Working Demo on jsfiddle
I hope this will help.
<img id="blah" alt="your image" width="100" height="100" />
<input type="file" name="photo" id="fileinput" />
<script>
$('#fileinput').change(function() {
var url = window.URL.createObjectURL(this.files[0]);
$('#blah').attr('src',url);
});
</script>
To Preview MULTIPLE Files and Single file in single function with reusable approach using Plain JavaScript
function imagePreviewFunc(that, previewerId) {
let files = that.files
previewerId.innerHTML='' // reset image preview element
for (let i = 0; i < files.length; i++) {
let imager = document.createElement("img");
imager.src = URL.createObjectURL(files[i]);
previewerId.append(imager);
}
}
<input accept="image/*" type='file' id="imageInput_1"
onchange="imagePreviewFunc(this, imagePreview_1)" />
<div id="imagePreview_1">This Div for Single Image Preview</div>
<hr />
<input class="form-control" accept="image/*" type='file' id="imageInput_2" multiple="true"
onchange="imagePreviewFunc(this, imagePreview_2)" />
<div id="imagePreview_2">This Div for Multiple Image Preview</div>
I have made a plugin which can generate the preview effect in IE 7+ thanks to the internet, but has few limitations. I put it into a github page so that its easier to get it
$(function () {
$("input[name=file1]").previewimage({
div: ".preview",
imgwidth: 180,
imgheight: 120
});
$("input[name=file2]").previewimage({
div: ".preview2",
imgwidth: 90,
imgheight: 90
});
});
.preview > div {
display: inline-block;
text-align:center;
}
.preview2 > div {
display: inline-block;
text-align:center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://rawgit.com/andrewng330/PreviewImage/master/preview.image.min.js"></script>
Preview
<div class="preview"></div>
Preview2
<div class="preview2"></div>
<form action="#" method="POST" enctype="multipart/form-data">
<input type="file" name="file1">
<input type="file" name="file2">
<input type="submit">
</form>
For Multiple image upload (Modification to the #IvanBaev's Solution)
function readURL(input) {
if (input.files && input.files[0]) {
var i;
for (i = 0; i < input.files.length; ++i) {
var reader = new FileReader();
reader.onload = function (e) {
$('#form1').append('<img src="'+e.target.result+'">');
}
reader.readAsDataURL(input.files[i]);
}
}
}
http://jsfiddle.net/LvsYc/12330/
Hope this helps someone.
It's my code.Support IE[6-9]、chrome 17+、firefox、Opera 11+、Maxthon3
function previewImage(fileObj, imgPreviewId) {
var allowExtention = ".jpg,.bmp,.gif,.png"; //allowed to upload file type
document.getElementById("hfAllowPicSuffix").value;
var extention = fileObj.value.substring(fileObj.value.lastIndexOf(".") + 1).toLowerCase();
var browserVersion = window.navigator.userAgent.toUpperCase();
if (allowExtention.indexOf(extention) > -1) {
if (fileObj.files) {
if (window.FileReader) {
var reader = new FileReader();
reader.onload = function (e) {
document.getElementById(imgPreviewId).setAttribute("src", e.target.result);
};
reader.readAsDataURL(fileObj.files[0]);
} else if (browserVersion.indexOf("SAFARI") > -1) {
alert("don't support Safari6.0 below broswer");
}
} else if (browserVersion.indexOf("MSIE") > -1) {
if (browserVersion.indexOf("MSIE 6") > -1) {//ie6
document.getElementById(imgPreviewId).setAttribute("src", fileObj.value);
} else {//ie[7-9]
fileObj.select();
fileObj.blur();
var newPreview = document.getElementById(imgPreviewId);
newPreview.style.border = "solid 1px #eeeeee";
newPreview.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='scale',src='" + document.selection.createRange().text + "')";
newPreview.style.display = "block";
}
} else if (browserVersion.indexOf("FIREFOX") > -1) {//firefox
var firefoxVersion = parseFloat(browserVersion.toLowerCase().match(/firefox\/([\d.]+)/)[1]);
if (firefoxVersion < 7) {//firefox7 below
document.getElementById(imgPreviewId).setAttribute("src", fileObj.files[0].getAsDataURL());
} else {//firefox7.0+ 
document.getElementById(imgPreviewId).setAttribute("src", window.URL.createObjectURL(fileObj.files[0]));
}
} else {
document.getElementById(imgPreviewId).setAttribute("src", fileObj.value);
}
} else {
alert("only support" + allowExtention + "suffix");
fileObj.value = ""; //clear Selected file
if (browserVersion.indexOf("MSIE") > -1) {
fileObj.select();
document.selection.clear();
}
}
}
function changeFile(elem) {
//file object , preview img tag id
previewImage(elem,'imagePreview')
}
<input type="file" id="netBarBig" onchange="changeFile(this)" />
<img src="" id="imagePreview" style="width:120px;height:80px;" alt=""/>
Default Iamge
#Html.TextBoxFor(x => x.productModels.DefaultImage, new {#type = "file", #class = "form-control", onchange = "openFile(event)", #name = "DefaultImage", #id = "DefaultImage" })
#Html.ValidationMessageFor(model => model.productModels.DefaultImage, "", new { #class = "text-danger" })
<img src="~/img/ApHandler.png" style="height:125px; width:125px" id="DefaultImagePreview"/>
</div>
<script>
var openFile = function (event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function () {
var dataURL = reader.result;
var output = document.getElementById('DefaultImagePreview');
output.src = dataURL;
};
reader.readAsDataURL(input.files[0]);
};
</script>
Here's a solution if you're using React:
import * as React from 'react'
import { useDropzone } from 'react-dropzone'
function imageDropper() {
const [imageUrl, setImageUrl] = React.useState()
const [imageFile, setImageFile] = React.useState()
const onDrop = React.useCallback(
acceptedFiles => {
const file = acceptedFiles[0]
setImageFile(file)
// convert file to data: url
const reader = new FileReader()
reader.addEventListener('load', () => setImageUrl(String(reader.result)), false)
reader.readAsDataURL(file)
},
[setImageFile, setImageUrl]
)
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop })
return (
<div>
<div {...getRootProps()}>
{imageFile ? imageFile.name : ''}
{isDragActive ? <p>Drop files here...</p> : <p>Select image file...</p>}
<input {...getInputProps()} />
</div>
{imageUrl && (
<div>
Your image: <img src={imageUrl} />
</div>
)}
</div>
)
}
https://stackoverflow.com/a/59985954/8784402
ES2017 Way
// convert file to a base64 url
const readURL = file => {
return new Promise((res, rej) => {
const reader = new FileReader();
reader.onload = e => res(e.target.result);
reader.onerror = e => rej(e);
reader.readAsDataURL(file);
});
};
// for demo
const fileInput = document.createElement('input');
fileInput.type = 'file';
const img = document.createElement('img');
img.attributeStyleMap.set('max-width', '320px');
document.body.appendChild(fileInput);
document.body.appendChild(img);
const preview = async event => {
const file = event.target.files[0];
const url = await readURL(file);
img.src = url;
};
fileInput.addEventListener('change', preview);
Here is a much easy way to preview image before upload using pure javascript;
//profile_change is the id of the input field where we choose an image
document.getElementById("profile_change").addEventListener("change", function() {
//Here we select the first file among the selected files.
const file = this.files[0];
/*here i used a label for the input field which is an image and this image will
represent the photo selected and profile_label is the id of this label */
const profile_label = document.getElementById("profile_label");
//Here we check if a file is selected
if(file) {
//Here we bring in the FileReader which reads the file info.
const reader = new FileReader();
/*After reader loads we change the src attribute of the label to the url of the
new image selected*/
reader.addEventListener("load", function() {
dp_label.setAttribute("src", this.result);
})
/*Here we are reading the file as a url i.e, we try to get the location of the
file to set that as the src of the label which we did above*/
reader.readAsDataURL(file);
}else {
//Here we simply set the src as default, whatever you want if no file is selected.
dp_label.setAttribute("src", "as_you_want")
}
});
And here is the HTML;
<label for="profile_change">
<img title="Change Profile Photo" id="profile_label"
src="as_you_want" alt="DP" style="height: 150px; width: 150px;
border-radius: 50%;" >
</label>
<input style="display: none;" id="profile_change" name="DP" type="file" class="detail form-control">
for my app, with encryped GET url parameters, only this worked. I always got a TypeError: $(...) is null.
Taken from https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
function previewFile() {
var preview = document.querySelector('img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
preview.src = reader.result;
}, false);
if (file) {
reader.readAsDataURL(file);
}
}
<input type="file" onchange="previewFile()"><br>
<img src="" height="200" alt="Image preview...">
function assignFilePreviews() {
$('input[data-previewable=\"true\"]').change(function() {
var prvCnt = $(this).attr('data-preview-container');
if (prvCnt) {
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
var img = $('<img>');
img.attr('src', e.target.result);
img.error(function() {
$(prvCnt).html('');
});
$(prvCnt).html('');
img.appendTo(prvCnt);
}
reader.readAsDataURL(this.files[0]);
}
}
});
}
$(document).ready(function() {
assignFilePreviews();
});
HTML
<input type="file" data-previewable="true" data-preview-container=".prd-img-prv" />
<div class = "prd-img-prv"></div>
This also handles case when file with invalid type ( ex. pdf ) is choosen

How to display a uploaded image on a canvas using ngx-dropzone component

i have a angular 7 application and i use a ngx-dropzone component to upload files,
is there a way to display the image on a canvas after it has been uploaded and then convert it to a jpeg before posting it to the server.
i can post the images to the server i have no problem with that part only the part where you display it on a canvas i have no idea how to do this and since i'm relatively new to this ngx-dropzone component.
html code
<div class="clr-row">
<div class="clr-col-4">
<canvas #canvas width=40 height=40 ></canvas>
</div>
<div class="clr-col-4 text-center">
<ngx-dropzone #dropzone (change)="upload($event)" class="custom-dropzone" >
<ngx-dropzone-label>CLICK OR DROP FILES HERE TO UPLOAD</ngx-dropzone-label>
</ngx-dropzone>
</div>
<div class="clr-col-4">
<button class="btn btn-success" (click)="Submit()">Submit Files</button>
</div>
</div>
code that i use to post the data to the back end
Submit() {
const formData: FormData = new FormData();
this.files.forEach((el) => {
formData.append(el.name, el);
});
this.files.forEach((el) => {
console.log(formData.get(el.name));
});
console.log(formData);
this._appService.UploadFileService.SendFiles(formData);
}
upload(event) {
console.log(event);
event.addedFiles.forEach((el) => {
this.files.push(el);
});
}
thanks in advance.
I found a solution using the tiff.js library,
below is the code that i used to display uploaded tiff image and convert it to a jpeg before sending it to the server
i imported the tiff.js library in my component
import * as Tiff from 'tiff.js';
let filereader = new FileReader();
let canvasEl = document.getElementById("canvas") as HTMLCanvasElement;
let cx = canvasEl.getContext('2d')!;
let url: string;
filereader.onload = (e) => {
let tiff = new Tiff({ buffer: filereader.result });
let ff = tiff.countDirectory();
if (ff > 1) {
for (var i = 0; i < ff; i++) {
tiff.setDirectory(i);
canvasEl = tiff.toCanvas();
url = canvasEl.toDataURL('image/jpeg', 0.7);
const imagefile = this.GetBlob(url);
const newFile = new File([imagefile], i.toString() + "_" + el.name.replace('.TIF', '.jpeg').replace('.tiff', '.jpeg').replace('.tif', '.jpeg'), { type: 'image/jpeg' });
this.files.push(newFile);
let jpfile = this.files[this.files.length - 1];
let reader = new FileReader();
reader.onload = (e) => {
let c = document.getElementById(jpfile.name) as HTMLImageElement;
c.src = reader.result as string;
}
reader.readAsDataURL(jpfile)
}
} else {
canvasEl = tiff.toCanvas();
url = canvasEl.toDataURL('image/jpeg', 0.7);
const imagefile = this.GetBlob(url);
const newFile = new File([imagefile], el.name.replace('.TIF', '.jpeg').replace('.tiff', '.jpeg'), { type: 'image/jpeg' });
this.files.push(newFile);
let jpfile = this.files[this.files.length - 1];
let reader = new FileReader();
reader.onload = (e) => {
let c = document.getElementById(jpfile.name) as HTMLImageElement;
c.src = reader.result as string;
}
reader.readAsDataURL(jpfile)
}
}
filereader.readAsArrayBuffer(el);
the above code also works with tiff files which contains multiple pages.

HTML SVG download image [duplicate]

I want to convert SVG into bitmap images (like JPEG, PNG, etc.) through JavaScript.
Here is how you can do it through JavaScript:
Use the canvg JavaScript library to render the SVG image using Canvas: https://github.com/gabelerner/canvg
Capture a data URI encoded as a JPG (or PNG) from the Canvas, according to these instructions: Capture HTML Canvas as gif/jpg/png/pdf?
jbeard4 solution worked beautifully.
I'm using Raphael SketchPad to create an SVG. Link to the files in step 1.
For a Save button (id of svg is "editor", id of canvas is "canvas"):
$("#editor_save").click(function() {
// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#editor").html());
// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
// do what you want with the base64, write to screen, post to server, etc...
});
This seems to work in most browsers:
function copyStylesInline(destinationNode, sourceNode) {
var containerElements = ["svg","g"];
for (var cd = 0; cd < destinationNode.childNodes.length; cd++) {
var child = destinationNode.childNodes[cd];
if (containerElements.indexOf(child.tagName) != -1) {
copyStylesInline(child, sourceNode.childNodes[cd]);
continue;
}
var style = sourceNode.childNodes[cd].currentStyle || window.getComputedStyle(sourceNode.childNodes[cd]);
if (style == "undefined" || style == null) continue;
for (var st = 0; st < style.length; st++){
child.style.setProperty(style[st], style.getPropertyValue(style[st]));
}
}
}
function triggerDownload (imgURI, fileName) {
var evt = new MouseEvent("click", {
view: window,
bubbles: false,
cancelable: true
});
var a = document.createElement("a");
a.setAttribute("download", fileName);
a.setAttribute("href", imgURI);
a.setAttribute("target", '_blank');
a.dispatchEvent(evt);
}
function downloadSvg(svg, fileName) {
var copy = svg.cloneNode(true);
copyStylesInline(copy, svg);
var canvas = document.createElement("canvas");
var bbox = svg.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, bbox.width, bbox.height);
var data = (new XMLSerializer()).serializeToString(copy);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob)
{
var blob = canvas.msToBlob();
navigator.msSaveOrOpenBlob(blob, fileName);
}
else {
var imgURI = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
triggerDownload(imgURI, fileName);
}
document.removeChild(canvas);
};
img.src = url;
}
The solution to convert SVG to blob URL and blob URL to png image
const svg=`<svg version="1.1" baseProfile="full" width="300" height="200"
xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="red" />
<circle cx="150" cy="100" r="80" fill="green" />
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text></svg>`
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');
callback(imgData);
// document.body.removeChild(imgPreview);
};
svgImage.src = svgUrl;
}
change svg to match your element
function svg2img(){
var svg = document.querySelector('svg');
var xml = new XMLSerializer().serializeToString(svg);
var svg64 = btoa(xml); //for utf8: btoa(unescape(encodeURIComponent(xml)))
var b64start = 'data:image/svg+xml;base64,';
var image64 = b64start + svg64;
return image64;
};svg2img()
My use case was to have the svg data loaded from a network and this ES6 Class did the Job.
class SvgToPngConverter {
constructor() {
this._init = this._init.bind(this);
this._cleanUp = this._cleanUp.bind(this);
this.convertFromInput = this.convertFromInput.bind(this);
}
_init() {
this.canvas = document.createElement("canvas");
this.imgPreview = document.createElement("img");
this.imgPreview.style = "position: absolute; top: -9999px";
document.body.appendChild(this.imgPreview);
this.canvasCtx = this.canvas.getContext("2d");
}
_cleanUp() {
document.body.removeChild(this.imgPreview);
}
convertFromInput(input, callback) {
this._init();
let _this = this;
this.imgPreview.onload = function() {
const img = new Image();
_this.canvas.width = _this.imgPreview.clientWidth;
_this.canvas.height = _this.imgPreview.clientHeight;
img.crossOrigin = "anonymous";
img.src = _this.imgPreview.src;
img.onload = function() {
_this.canvasCtx.drawImage(img, 0, 0);
let imgData = _this.canvas.toDataURL("image/png");
if(typeof callback == "function"){
callback(imgData)
}
_this._cleanUp();
};
};
this.imgPreview.src = input;
}
}
Here is how you use it
let input = "https://restcountries.eu/data/afg.svg"
new SvgToPngConverter().convertFromInput(input, function(imgData){
// You now have your png data in base64 (imgData).
// Do what ever you wish with it here.
});
If you want a vanilla JavaScript version, you could head over to Babel website and transpile the code there.
Here a function that works without libraries and returns a Promise:
/**
* converts a base64 encoded data url SVG image to a PNG image
* #param originalBase64 data url of svg image
* #param width target width in pixel of PNG image
* #return {Promise<String>} resolves to png data url of the image
*/
function base64SvgToBase64Png (originalBase64, width) {
return new Promise(resolve => {
let img = document.createElement('img');
img.onload = function () {
document.body.appendChild(img);
let canvas = document.createElement("canvas");
let ratio = (img.clientWidth / img.clientHeight) || 1;
document.body.removeChild(img);
canvas.width = width;
canvas.height = width / ratio;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
try {
let data = canvas.toDataURL('image/png');
resolve(data);
} catch (e) {
resolve(null);
}
};
img.onerror = function() {
resolve(null);
};
img.src = originalBase64;
});
}
On Firefox there is an issue for SVGs without set width / height.
See this working example including a fix for the Firefox issue.
This is an old question, in 2022 we have ES6 and we don't need 3rd party libraries.
Here is a very basic way to convert svg images into other formats.
The trick is to load the svg element as an img element, then use a canvas element to convert the image into the desired format. So, four steps are needed:
Extract svg as xml data string.
Load the xml data string into a img element
Convert the img element to a dataURL using a canvas element
Load the converted dataURL into a new img element
Step 1
Extracting a svg as xml data string is simple, we don't need to convert it as a base64 string. We just serialize it as XML then we encode the string as a URI:
// Select the element:
const $svg = document.getElementById('svg-container').querySelector('svg')
// Serialize it as xml string:
const svgAsXML = (new XMLSerializer()).serializeToString($svg)
// Encode it as a data string:
const svgData = `data:image/svg+xml,${encodeURIComponent(svgAsXML)}`
Step 2
Loading the xml data string into a img element:
// This function returns a Promise whenever the $img is loaded
const loadImage = async url => {
const $img = document.createElement('img')
$img.src = url
return new Promise((resolve, reject) => {
$img.onload = () => resolve($img)
$img.onerror = reject
$img.src = url
})
}
Step 3
Converting the img element to a dataURL using a canvas element:
const $canvas = document.createElement('canvas')
$canvas.width = $svg.clientWidth
$canvas.height = $svg.clientHeight
$canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
return $canvas.toDataURL(`image/${format}`, 1.0)
Step 4
Loading the converted dataURL into a new img element:
const $img = document.createElement('img')
$img.src = dataURL
$holder.appendChild($img)
Here you have a working snippet:
const $svg = document.getElementById('svg-container').querySelector('svg')
const $holder = document.getElementById('img-container')
const $label = document.getElementById('img-format')
const destroyChildren = $element => {
while ($element.firstChild) {
const $lastChild = $element.lastChild ?? false
if ($lastChild) $element.removeChild($lastChild)
}
}
const loadImage = async url => {
const $img = document.createElement('img')
$img.src = url
return new Promise((resolve, reject) => {
$img.onload = () => resolve($img)
$img.onerror = reject
})
}
const convertSVGtoImg = async e => {
const $btn = e.target
const format = $btn.dataset.format ?? 'png'
$label.textContent = format
destroyChildren($holder)
const svgAsXML = (new XMLSerializer()).serializeToString($svg)
const svgData = `data:image/svg+xml,${encodeURIComponent(svgAsXML)}`
const img = await loadImage(svgData)
const $canvas = document.createElement('canvas')
$canvas.width = $svg.clientWidth
$canvas.height = $svg.clientHeight
$canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
const dataURL = await $canvas.toDataURL(`image/${format}`, 1.0)
console.log(dataURL)
const $img = document.createElement('img')
$img.src = dataURL
$holder.appendChild($img)
}
const buttons = [...document.querySelectorAll('[data-format]')]
for (const $btn of buttons) {
$btn.onclick = convertSVGtoImg
}
.wrapper {
display: flex;
flex-flow: row nowrap;
width: 100vw;
}
.images {
display: flex;
flex-flow: row nowrap;
width: 70%;
}
.image {
width: 50%;
display: flex;
flex-flow: row wrap;
justify-content: center;
}
.label {
width: 100%;
text-align: center;
}
<div class="wrapper">
<div class="item images">
<div class="image left">
<div class="label">svg</div>
<div id="svg-container">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="200" height="200" viewBox="0 0 248 204">
<path fill="#1d9bf0" d="M221.95 51.29c.15 2.17.15 4.34.15 6.53 0 66.73-50.8 143.69-143.69 143.69v-.04c-27.44.04-54.31-7.82-77.41-22.64 3.99.48 8 .72 12.02.73 22.74.02 44.83-7.61 62.72-21.66-21.61-.41-40.56-14.5-47.18-35.07 7.57 1.46 15.37 1.16 22.8-.87-23.56-4.76-40.51-25.46-40.51-49.5v-.64c7.02 3.91 14.88 6.08 22.92 6.32C11.58 63.31 4.74 33.79 18.14 10.71c25.64 31.55 63.47 50.73 104.08 52.76-4.07-17.54 1.49-35.92 14.61-48.25 20.34-19.12 52.33-18.14 71.45 2.19 11.31-2.23 22.15-6.38 32.07-12.26-3.77 11.69-11.66 21.62-22.2 27.93 10.01-1.18 19.79-3.86 29-7.95-6.78 10.16-15.32 19.01-25.2 26.16z"/>
</svg>
</div>
</div>
<div class="image right">
<div id="img-format" class="label"></div>
<div id="img-container"></div>
</div>
</div>
<div class="item buttons">
<button id="btn-png" data-format="png">PNG</button>
<button id="btn-jpg" data-format="jpeg">JPG</button>
<button id="btn-webp" data-format="webp">WEBP</button>
</div>
</div>
Svg to png can be converted depending on conditions:
If svg is in format SVG (string) paths:
create canvas
create new Path2D() and set svg as parameter
draw path on canvas
create image and use canvas.toDataURL() as src.
example:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let svgText = 'M10 10 h 80 v 80 h -80 Z';
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.stroke(p);
let url = canvas.toDataURL();
const img = new Image();
img.src = url;
Note that Path2D not supported in ie and partially supported in edge. Polyfill solves that:
https://github.com/nilzona/path2d-polyfill
Create svg blob and draw on canvas using .drawImage():
make canvas element
make a svgBlob object from the svg xml
make a url object from domUrl.createObjectURL(svgBlob);
create an Image object and assign url to image src
draw image into canvas
get png data string from canvas: canvas.toDataURL();
Nice description:
https://web.archive.org/web/20200125162931/http://ramblings.mcpher.com:80/Home/excelquirks/gassnips/svgtopng
Note that in ie you will get exception on stage of canvas.toDataURL(); It is because IE has too high security restriction and treats canvas as readonly after drawing image there. All other browsers restrict only if image is cross origin.
Use canvg JavaScript library. It is separate library but has useful functions.
Like:
ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
I recently discovered a couple of image tracing libraries for JavaScript that indeed are able to build an acceptable approximation to the bitmap, both size and quality. I'm developing this JavaScript library and CLI :
https://www.npmjs.com/package/svg-png-converter
Which provides unified API for all of them, supporting browser and node, non depending on DOM, and a Command line tool.
For converting logos/cartoon/like images it does excellent job. For photos / realism some tweaking is needed since the output size can grow a lot.
It has a playground although right now I'm working on a better one, easier to use, since more features has been added:
https://cancerberosgx.github.io/demos/svg-png-converter/playground/#
There are several ways to convert SVG to PNG using the Canvg library.
In my case, I needed to get the PNG blob from inline SVG.
The library documentation provides an example (see OffscreenCanvas example).
But this method does not work at the moment in Firefox. Yes, you can enable the gfx.offscreencanvas.enabled option in the settings. But will every user on the site do this? :)
However, there is another way that will work in Firefox too.
const el = document.getElementById("some-svg"); //this is our inline SVG
var canvas = document.createElement('canvas'); //create a canvas for the SVG render
canvas.width = el.clientWidth; //set canvas sizes
canvas.height = el.clientHeight;
const svg = new XMLSerializer().serializeToString(el); //convert SVG to string
//render SVG inside canvas
const ctx = canvas.getContext('2d');
const v = await Canvg.fromString(ctx, svg);
await v.render();
let canvasBlob = await new Promise(resolve => canvas.toBlob(resolve));
For the last line thanks to this answer
get data URIs from SVG:
data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgElem))}
prepare an Image
create a canvas and use toDataURL to export.
Example
<!-- test data-->
<svg width="400" height="400"><g transform="translate(23.915343915343925,-80.03971756398937)" class="glyph" stroke="#000000" fill="#a0a0a0"><path d="M74.97 108.70L74.97 108.70L100.08 110.77Q93.89 147.91 87.35 179.89L87.35 179.89L148.23 179.89L148.23 194.34Q143.76 277.91 113.84 339.81L113.84 339.81Q144.44 363.54 163.70 382.46L163.70 382.46L146.51 402.75Q128.62 384.18 101.80 361.83L101.80 361.83Q75.32 405.85 34.39 436.80L34.39 436.80L17.20 415.82Q57.43 386.93 82.20 345.66L82.20 345.66Q57.78 326.40 27.86 304.39L27.86 304.39Q44.37 257.96 56.75 203.97L56.75 203.97L19.26 203.97L19.26 179.89L61.90 179.89Q69.47 145.16 74.97 108.70ZM93.20 323.99L93.20 323.99Q118.65 272.06 123.12 203.97L123.12 203.97L82.20 203.97Q69.47 260.03 55.71 297.17L55.71 297.17Q76.01 311.61 93.20 323.99ZM160.26 285.13L160.26 260.37L239.71 260.37L239.71 216.01Q268.25 191.24 294.05 155.48L294.05 155.48L170.58 155.48L170.58 130.71L322.94 130.71L322.94 155.48Q297.49 191.93 265.50 223.92L265.50 223.92L265.50 260.37L337.38 260.37L337.38 285.13L265.50 285.13L265.50 397.59Q265.50 431.64 237.65 431.64L237.65 431.64L187.09 431.64L180.21 407.57Q202.22 407.91 227.67 407.91L227.67 407.91Q239.71 407.91 239.71 390.03L239.71 390.03L239.71 285.13L160.26 285.13Z"></path></g></svg>
<button title="download">svg2png</button>
<script>
const output = {"name": "result.png", "width": 64, "height": 64}
document.querySelector("button").onclick = () => {
const svgElem = document.querySelector("svg")
// const uriData = `data:image/svg+xml;base64,${btoa(svgElem.outerHTML)}` // it may fail.
const uriData = `data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgElem))}`
const img = new Image()
img.src = uriData
img.onload = () => {
const canvas = document.createElement("canvas");
[canvas.width, canvas.height] = [output.width, output.height]
const ctx = canvas.getContext("2d")
ctx.drawImage(img, 0, 0, output.width, output.height)
// 👇 download
const a = document.createElement("a")
const quality = 1.0 // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality
a.href = canvas.toDataURL("image/png", quality)
a.download = output.name
a.append(canvas)
a.click()
a.remove()
}
}
</script>
Here are my 2 cents. Somehow Download anchor tag is not working as expected in code snippet, however it was working fine in Chrome.
Here is working jsFiddle
const waitForImage = imgElem => new Promise(resolve => imgElem.complete ? resolve() : imgElem.onload = imgElem.onerror = resolve);
const svgToImgDownload = ext => {
if (!['png', 'jpg', 'webp'].includes(ext))
return;
const _svg = document.querySelector("#svg_container").querySelector('svg');
const xmlSerializer = new XMLSerializer();
let _svgStr = xmlSerializer.serializeToString(_svg);
const img = document.createElement('img');
img.src = 'data:image/svg+xml;base64,' + window.btoa(_svgStr);
waitForImage(img)
.then(_ => {
const canvas = document.createElement('canvas');
canvas.width = _svg.clientWidth;
canvas.height = _svg.clientHeight;
canvas.getContext('2d').drawImage(img, 0, 0, _svg.clientWidth, _svg.clientHeight);
return canvas.toDataURL('image/' + (ext == 'jpg' ? 'jpeg' : ext), 1.0);
})
.then(dataURL => {
console.log(dataURL);
document.querySelector("#img_download_btn").innerHTML = `Download`;
})
.catch(console.error);
};
document.querySelector('#map2Png').addEventListener('click', _ => svgToImgDownload('png'));
document.querySelector('#map2Jpg').addEventListener('click', _ => svgToImgDownload('jpg'));
document.querySelector('#map2Webp').addEventListener('click', _ => svgToImgDownload('webp'));
<div id="svg_container" style="float: left; width: 50%">
<svg width="200" height="200" viewBox="-100 -100 200 200">
<circle cx="0" cy="20" r="70" fill="#D1495B" />
<circle cx="0" cy="-75" r="12" fill="none" stroke="#F79257" stroke-width="2" />
<rect x="-17.5" y="-65" width="35" height="20" fill="#F79257" />
</svg>
</div>
<div>
<button id="map2Png">PNG</button>
<button id="map2Jpg">JPG</button>
<button id="map2Webp">WEBP</button>
</div>
<div id="img_download_btn"></div>

Filtering HTML content based on URL Variable

I am looking for a way that I can hide specific HTML content based on a variable in the URL.
For example, I am passing a variable: index.html?app=new
I have various images and other content that has a prod attribute and would like to show only those that have the "new" prod value.
<img prod="new" class="image" src="../images/screen.png" height="300" width="600"/>
I have used the following to harvest the url variable:
<script>
function getUrlVars() {
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,
function(m,key,value) {
vars[key] = value;
});
return vars;
}
</script>
My problem is where to go from here
I think this CSS rule will do the magic for you:
[prod]:not([prod=new]) { display:none; }
It will hide all elements having 'prod' attribute with the value other than 'new'.
This is untested code.
HTML
<img data-prod="new" class="image" src="../images/screen.png" height="300" width="600" />
JavaScript
<script>
(function(){
'use strict';
var getUrlVars = function() {
var vars = {};
window.location.href.replace(
/[?&]+([^=&]+)=([^&]*)/gi,
function(m,key,value) {
vars[key] = value;
}
);
return vars;
};
if(typeof getUrlVars.app === 'string' && getUrlVars.app === 'new') {
var c = document.querySelectorAll('[data-prod="new"]'), i;
for(i = 0; i < c.length; i+=1) {
c[i].style.display = 'none';
}
}
})();
</script>
In the general case (i.e. app=new, app=whatever), you can substitute new in the querySelectorAll parameter as the variable itself:
if(typeof getUrlVars.app === 'string') {
var c = document.querySelectorAll('[data-prod="' + getUrlVars.app + '"]'), i;
for(i = 0; i < c.length; i+=1) {
c[i].style.display = 'none';
}
}

convert SVG code into png in javascript [duplicate]

I want to convert SVG into bitmap images (like JPEG, PNG, etc.) through JavaScript.
Here is how you can do it through JavaScript:
Use the canvg JavaScript library to render the SVG image using Canvas: https://github.com/gabelerner/canvg
Capture a data URI encoded as a JPG (or PNG) from the Canvas, according to these instructions: Capture HTML Canvas as gif/jpg/png/pdf?
jbeard4 solution worked beautifully.
I'm using Raphael SketchPad to create an SVG. Link to the files in step 1.
For a Save button (id of svg is "editor", id of canvas is "canvas"):
$("#editor_save").click(function() {
// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#editor").html());
// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
// do what you want with the base64, write to screen, post to server, etc...
});
This seems to work in most browsers:
function copyStylesInline(destinationNode, sourceNode) {
var containerElements = ["svg","g"];
for (var cd = 0; cd < destinationNode.childNodes.length; cd++) {
var child = destinationNode.childNodes[cd];
if (containerElements.indexOf(child.tagName) != -1) {
copyStylesInline(child, sourceNode.childNodes[cd]);
continue;
}
var style = sourceNode.childNodes[cd].currentStyle || window.getComputedStyle(sourceNode.childNodes[cd]);
if (style == "undefined" || style == null) continue;
for (var st = 0; st < style.length; st++){
child.style.setProperty(style[st], style.getPropertyValue(style[st]));
}
}
}
function triggerDownload (imgURI, fileName) {
var evt = new MouseEvent("click", {
view: window,
bubbles: false,
cancelable: true
});
var a = document.createElement("a");
a.setAttribute("download", fileName);
a.setAttribute("href", imgURI);
a.setAttribute("target", '_blank');
a.dispatchEvent(evt);
}
function downloadSvg(svg, fileName) {
var copy = svg.cloneNode(true);
copyStylesInline(copy, svg);
var canvas = document.createElement("canvas");
var bbox = svg.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, bbox.width, bbox.height);
var data = (new XMLSerializer()).serializeToString(copy);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob)
{
var blob = canvas.msToBlob();
navigator.msSaveOrOpenBlob(blob, fileName);
}
else {
var imgURI = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
triggerDownload(imgURI, fileName);
}
document.removeChild(canvas);
};
img.src = url;
}
The solution to convert SVG to blob URL and blob URL to png image
const svg=`<svg version="1.1" baseProfile="full" width="300" height="200"
xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="red" />
<circle cx="150" cy="100" r="80" fill="green" />
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text></svg>`
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');
callback(imgData);
// document.body.removeChild(imgPreview);
};
svgImage.src = svgUrl;
}
change svg to match your element
function svg2img(){
var svg = document.querySelector('svg');
var xml = new XMLSerializer().serializeToString(svg);
var svg64 = btoa(xml); //for utf8: btoa(unescape(encodeURIComponent(xml)))
var b64start = 'data:image/svg+xml;base64,';
var image64 = b64start + svg64;
return image64;
};svg2img()
My use case was to have the svg data loaded from a network and this ES6 Class did the Job.
class SvgToPngConverter {
constructor() {
this._init = this._init.bind(this);
this._cleanUp = this._cleanUp.bind(this);
this.convertFromInput = this.convertFromInput.bind(this);
}
_init() {
this.canvas = document.createElement("canvas");
this.imgPreview = document.createElement("img");
this.imgPreview.style = "position: absolute; top: -9999px";
document.body.appendChild(this.imgPreview);
this.canvasCtx = this.canvas.getContext("2d");
}
_cleanUp() {
document.body.removeChild(this.imgPreview);
}
convertFromInput(input, callback) {
this._init();
let _this = this;
this.imgPreview.onload = function() {
const img = new Image();
_this.canvas.width = _this.imgPreview.clientWidth;
_this.canvas.height = _this.imgPreview.clientHeight;
img.crossOrigin = "anonymous";
img.src = _this.imgPreview.src;
img.onload = function() {
_this.canvasCtx.drawImage(img, 0, 0);
let imgData = _this.canvas.toDataURL("image/png");
if(typeof callback == "function"){
callback(imgData)
}
_this._cleanUp();
};
};
this.imgPreview.src = input;
}
}
Here is how you use it
let input = "https://restcountries.eu/data/afg.svg"
new SvgToPngConverter().convertFromInput(input, function(imgData){
// You now have your png data in base64 (imgData).
// Do what ever you wish with it here.
});
If you want a vanilla JavaScript version, you could head over to Babel website and transpile the code there.
Here a function that works without libraries and returns a Promise:
/**
* converts a base64 encoded data url SVG image to a PNG image
* #param originalBase64 data url of svg image
* #param width target width in pixel of PNG image
* #return {Promise<String>} resolves to png data url of the image
*/
function base64SvgToBase64Png (originalBase64, width) {
return new Promise(resolve => {
let img = document.createElement('img');
img.onload = function () {
document.body.appendChild(img);
let canvas = document.createElement("canvas");
let ratio = (img.clientWidth / img.clientHeight) || 1;
document.body.removeChild(img);
canvas.width = width;
canvas.height = width / ratio;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
try {
let data = canvas.toDataURL('image/png');
resolve(data);
} catch (e) {
resolve(null);
}
};
img.onerror = function() {
resolve(null);
};
img.src = originalBase64;
});
}
On Firefox there is an issue for SVGs without set width / height.
See this working example including a fix for the Firefox issue.
This is an old question, in 2022 we have ES6 and we don't need 3rd party libraries.
Here is a very basic way to convert svg images into other formats.
The trick is to load the svg element as an img element, then use a canvas element to convert the image into the desired format. So, four steps are needed:
Extract svg as xml data string.
Load the xml data string into a img element
Convert the img element to a dataURL using a canvas element
Load the converted dataURL into a new img element
Step 1
Extracting a svg as xml data string is simple, we don't need to convert it as a base64 string. We just serialize it as XML then we encode the string as a URI:
// Select the element:
const $svg = document.getElementById('svg-container').querySelector('svg')
// Serialize it as xml string:
const svgAsXML = (new XMLSerializer()).serializeToString($svg)
// Encode it as a data string:
const svgData = `data:image/svg+xml,${encodeURIComponent(svgAsXML)}`
Step 2
Loading the xml data string into a img element:
// This function returns a Promise whenever the $img is loaded
const loadImage = async url => {
const $img = document.createElement('img')
$img.src = url
return new Promise((resolve, reject) => {
$img.onload = () => resolve($img)
$img.onerror = reject
$img.src = url
})
}
Step 3
Converting the img element to a dataURL using a canvas element:
const $canvas = document.createElement('canvas')
$canvas.width = $svg.clientWidth
$canvas.height = $svg.clientHeight
$canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
return $canvas.toDataURL(`image/${format}`, 1.0)
Step 4
Loading the converted dataURL into a new img element:
const $img = document.createElement('img')
$img.src = dataURL
$holder.appendChild($img)
Here you have a working snippet:
const $svg = document.getElementById('svg-container').querySelector('svg')
const $holder = document.getElementById('img-container')
const $label = document.getElementById('img-format')
const destroyChildren = $element => {
while ($element.firstChild) {
const $lastChild = $element.lastChild ?? false
if ($lastChild) $element.removeChild($lastChild)
}
}
const loadImage = async url => {
const $img = document.createElement('img')
$img.src = url
return new Promise((resolve, reject) => {
$img.onload = () => resolve($img)
$img.onerror = reject
})
}
const convertSVGtoImg = async e => {
const $btn = e.target
const format = $btn.dataset.format ?? 'png'
$label.textContent = format
destroyChildren($holder)
const svgAsXML = (new XMLSerializer()).serializeToString($svg)
const svgData = `data:image/svg+xml,${encodeURIComponent(svgAsXML)}`
const img = await loadImage(svgData)
const $canvas = document.createElement('canvas')
$canvas.width = $svg.clientWidth
$canvas.height = $svg.clientHeight
$canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
const dataURL = await $canvas.toDataURL(`image/${format}`, 1.0)
console.log(dataURL)
const $img = document.createElement('img')
$img.src = dataURL
$holder.appendChild($img)
}
const buttons = [...document.querySelectorAll('[data-format]')]
for (const $btn of buttons) {
$btn.onclick = convertSVGtoImg
}
.wrapper {
display: flex;
flex-flow: row nowrap;
width: 100vw;
}
.images {
display: flex;
flex-flow: row nowrap;
width: 70%;
}
.image {
width: 50%;
display: flex;
flex-flow: row wrap;
justify-content: center;
}
.label {
width: 100%;
text-align: center;
}
<div class="wrapper">
<div class="item images">
<div class="image left">
<div class="label">svg</div>
<div id="svg-container">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="200" height="200" viewBox="0 0 248 204">
<path fill="#1d9bf0" d="M221.95 51.29c.15 2.17.15 4.34.15 6.53 0 66.73-50.8 143.69-143.69 143.69v-.04c-27.44.04-54.31-7.82-77.41-22.64 3.99.48 8 .72 12.02.73 22.74.02 44.83-7.61 62.72-21.66-21.61-.41-40.56-14.5-47.18-35.07 7.57 1.46 15.37 1.16 22.8-.87-23.56-4.76-40.51-25.46-40.51-49.5v-.64c7.02 3.91 14.88 6.08 22.92 6.32C11.58 63.31 4.74 33.79 18.14 10.71c25.64 31.55 63.47 50.73 104.08 52.76-4.07-17.54 1.49-35.92 14.61-48.25 20.34-19.12 52.33-18.14 71.45 2.19 11.31-2.23 22.15-6.38 32.07-12.26-3.77 11.69-11.66 21.62-22.2 27.93 10.01-1.18 19.79-3.86 29-7.95-6.78 10.16-15.32 19.01-25.2 26.16z"/>
</svg>
</div>
</div>
<div class="image right">
<div id="img-format" class="label"></div>
<div id="img-container"></div>
</div>
</div>
<div class="item buttons">
<button id="btn-png" data-format="png">PNG</button>
<button id="btn-jpg" data-format="jpeg">JPG</button>
<button id="btn-webp" data-format="webp">WEBP</button>
</div>
</div>
Svg to png can be converted depending on conditions:
If svg is in format SVG (string) paths:
create canvas
create new Path2D() and set svg as parameter
draw path on canvas
create image and use canvas.toDataURL() as src.
example:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let svgText = 'M10 10 h 80 v 80 h -80 Z';
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.stroke(p);
let url = canvas.toDataURL();
const img = new Image();
img.src = url;
Note that Path2D not supported in ie and partially supported in edge. Polyfill solves that:
https://github.com/nilzona/path2d-polyfill
Create svg blob and draw on canvas using .drawImage():
make canvas element
make a svgBlob object from the svg xml
make a url object from domUrl.createObjectURL(svgBlob);
create an Image object and assign url to image src
draw image into canvas
get png data string from canvas: canvas.toDataURL();
Nice description:
https://web.archive.org/web/20200125162931/http://ramblings.mcpher.com:80/Home/excelquirks/gassnips/svgtopng
Note that in ie you will get exception on stage of canvas.toDataURL(); It is because IE has too high security restriction and treats canvas as readonly after drawing image there. All other browsers restrict only if image is cross origin.
Use canvg JavaScript library. It is separate library but has useful functions.
Like:
ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
I recently discovered a couple of image tracing libraries for JavaScript that indeed are able to build an acceptable approximation to the bitmap, both size and quality. I'm developing this JavaScript library and CLI :
https://www.npmjs.com/package/svg-png-converter
Which provides unified API for all of them, supporting browser and node, non depending on DOM, and a Command line tool.
For converting logos/cartoon/like images it does excellent job. For photos / realism some tweaking is needed since the output size can grow a lot.
It has a playground although right now I'm working on a better one, easier to use, since more features has been added:
https://cancerberosgx.github.io/demos/svg-png-converter/playground/#
There are several ways to convert SVG to PNG using the Canvg library.
In my case, I needed to get the PNG blob from inline SVG.
The library documentation provides an example (see OffscreenCanvas example).
But this method does not work at the moment in Firefox. Yes, you can enable the gfx.offscreencanvas.enabled option in the settings. But will every user on the site do this? :)
However, there is another way that will work in Firefox too.
const el = document.getElementById("some-svg"); //this is our inline SVG
var canvas = document.createElement('canvas'); //create a canvas for the SVG render
canvas.width = el.clientWidth; //set canvas sizes
canvas.height = el.clientHeight;
const svg = new XMLSerializer().serializeToString(el); //convert SVG to string
//render SVG inside canvas
const ctx = canvas.getContext('2d');
const v = await Canvg.fromString(ctx, svg);
await v.render();
let canvasBlob = await new Promise(resolve => canvas.toBlob(resolve));
For the last line thanks to this answer
get data URIs from SVG:
data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgElem))}
prepare an Image
create a canvas and use toDataURL to export.
Example
<!-- test data-->
<svg width="400" height="400"><g transform="translate(23.915343915343925,-80.03971756398937)" class="glyph" stroke="#000000" fill="#a0a0a0"><path d="M74.97 108.70L74.97 108.70L100.08 110.77Q93.89 147.91 87.35 179.89L87.35 179.89L148.23 179.89L148.23 194.34Q143.76 277.91 113.84 339.81L113.84 339.81Q144.44 363.54 163.70 382.46L163.70 382.46L146.51 402.75Q128.62 384.18 101.80 361.83L101.80 361.83Q75.32 405.85 34.39 436.80L34.39 436.80L17.20 415.82Q57.43 386.93 82.20 345.66L82.20 345.66Q57.78 326.40 27.86 304.39L27.86 304.39Q44.37 257.96 56.75 203.97L56.75 203.97L19.26 203.97L19.26 179.89L61.90 179.89Q69.47 145.16 74.97 108.70ZM93.20 323.99L93.20 323.99Q118.65 272.06 123.12 203.97L123.12 203.97L82.20 203.97Q69.47 260.03 55.71 297.17L55.71 297.17Q76.01 311.61 93.20 323.99ZM160.26 285.13L160.26 260.37L239.71 260.37L239.71 216.01Q268.25 191.24 294.05 155.48L294.05 155.48L170.58 155.48L170.58 130.71L322.94 130.71L322.94 155.48Q297.49 191.93 265.50 223.92L265.50 223.92L265.50 260.37L337.38 260.37L337.38 285.13L265.50 285.13L265.50 397.59Q265.50 431.64 237.65 431.64L237.65 431.64L187.09 431.64L180.21 407.57Q202.22 407.91 227.67 407.91L227.67 407.91Q239.71 407.91 239.71 390.03L239.71 390.03L239.71 285.13L160.26 285.13Z"></path></g></svg>
<button title="download">svg2png</button>
<script>
const output = {"name": "result.png", "width": 64, "height": 64}
document.querySelector("button").onclick = () => {
const svgElem = document.querySelector("svg")
// const uriData = `data:image/svg+xml;base64,${btoa(svgElem.outerHTML)}` // it may fail.
const uriData = `data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgElem))}`
const img = new Image()
img.src = uriData
img.onload = () => {
const canvas = document.createElement("canvas");
[canvas.width, canvas.height] = [output.width, output.height]
const ctx = canvas.getContext("2d")
ctx.drawImage(img, 0, 0, output.width, output.height)
// 👇 download
const a = document.createElement("a")
const quality = 1.0 // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality
a.href = canvas.toDataURL("image/png", quality)
a.download = output.name
a.append(canvas)
a.click()
a.remove()
}
}
</script>
Here are my 2 cents. Somehow Download anchor tag is not working as expected in code snippet, however it was working fine in Chrome.
Here is working jsFiddle
const waitForImage = imgElem => new Promise(resolve => imgElem.complete ? resolve() : imgElem.onload = imgElem.onerror = resolve);
const svgToImgDownload = ext => {
if (!['png', 'jpg', 'webp'].includes(ext))
return;
const _svg = document.querySelector("#svg_container").querySelector('svg');
const xmlSerializer = new XMLSerializer();
let _svgStr = xmlSerializer.serializeToString(_svg);
const img = document.createElement('img');
img.src = 'data:image/svg+xml;base64,' + window.btoa(_svgStr);
waitForImage(img)
.then(_ => {
const canvas = document.createElement('canvas');
canvas.width = _svg.clientWidth;
canvas.height = _svg.clientHeight;
canvas.getContext('2d').drawImage(img, 0, 0, _svg.clientWidth, _svg.clientHeight);
return canvas.toDataURL('image/' + (ext == 'jpg' ? 'jpeg' : ext), 1.0);
})
.then(dataURL => {
console.log(dataURL);
document.querySelector("#img_download_btn").innerHTML = `Download`;
})
.catch(console.error);
};
document.querySelector('#map2Png').addEventListener('click', _ => svgToImgDownload('png'));
document.querySelector('#map2Jpg').addEventListener('click', _ => svgToImgDownload('jpg'));
document.querySelector('#map2Webp').addEventListener('click', _ => svgToImgDownload('webp'));
<div id="svg_container" style="float: left; width: 50%">
<svg width="200" height="200" viewBox="-100 -100 200 200">
<circle cx="0" cy="20" r="70" fill="#D1495B" />
<circle cx="0" cy="-75" r="12" fill="none" stroke="#F79257" stroke-width="2" />
<rect x="-17.5" y="-65" width="35" height="20" fill="#F79257" />
</svg>
</div>
<div>
<button id="map2Png">PNG</button>
<button id="map2Jpg">JPG</button>
<button id="map2Webp">WEBP</button>
</div>
<div id="img_download_btn"></div>