I have an external SVG file which contains some embedded image tags in pattern. Whenever I convert this SVG into PNG using toDataURL(), the generated PNG images does not contain the image I have applied as pattern to some SVG paths. Is there any way to solve this problem?
Yes there are : append the svg into your document and encode all the included images to dataURIs.
I am writing a script that does this and also some other stuff like including external style-sheets and some other fix of where toDataURL will fail (e.g external elements referenced through xlink:href attribute or <funciri>).
Here is the function I wrote for parsing the images content :
function parseImages(){
var xlinkNS = "http://www.w3.org/1999/xlink";
var total, encoded;
// convert an external bitmap image to a dataURL
var toDataURL = function (image) {
var img = new Image();
// CORS workaround, this won't work in IE<11
// If you are sure you don't need it, remove the next line and the double onerror handler
// First try with crossorigin set, it should fire an error if not needed
img.crossOrigin = 'Anonymous';
img.onload = function () {
// we should now be able to draw it without tainting the canvas
var canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
// draw the loaded image
canvas.getContext('2d').drawImage(this, 0, 0);
// set our <image>'s href attribute to the dataURL of our canvas
image.setAttributeNS(xlinkNS, 'href', canvas.toDataURL());
// that was the last one
if (++encoded === total) exportDoc();
};
// No CORS set in the response
img.onerror = function () {
// save the src
var oldSrc = this.src;
// there is an other problem
this.onerror = function () {
console.warn('failed to load an image at : ', this.src);
if (--total === encoded && encoded > 0) exportDoc();
};
// remove the crossorigin attribute
this.removeAttribute('crossorigin');
// retry
this.src = '';
this.src = oldSrc;
};
// load our external image into our img
img.src = image.getAttributeNS(xlinkNS, 'href');
};
// get an external svg doc to data String
var parseFromUrl = function(url, element){
var xhr = new XMLHttpRequest();
xhr.onload = function(){
if(this.status === 200){
var response = this.responseText || this.response;
var dataUrl = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(response);
element.setAttributeNS(xlinkNS, 'href', dataUrl);
if(++encoded === total) exportDoc();
}
// request failed with xhr, try as an <img>
else{
toDataURL(element);
}
};
xhr.onerror = function(){toDataURL(element);};
xhr.open('GET', url);
xhr.send();
};
var images = svg.querySelectorAll('image');
total = images.length;
encoded = 0;
// loop through all our <images> elements
for (var i = 0; i < images.length; i++) {
var href = images[i].getAttributeNS(xlinkNS, 'href');
// check if the image is external
if (href.indexOf('data:image') < 0){
// if it points to another svg element
if(href.indexOf('.svg') > 0){
parseFromUrl(href, images[i]);
}
else // a pixel image
toDataURL(images[i]);
}
// else increment our counter
else if (++encoded === total) exportDoc();
}
// if there were no <image> element
if (total === 0) exportDoc();
}
Here the svgDoc is called svg,
and the exportDoc() function could just be written as :
var exportDoc = function() {
// check if our svgNode has width and height properties set to absolute values
// otherwise, canvas won't be able to draw it
var bbox = svg.getBoundingClientRect();
if (svg.width.baseVal.unitType !== 1) svg.setAttribute('width', bbox.width);
if (svg.height.baseVal.unitType !== 1) svg.setAttribute('height', bbox.height);
// serialize our node
var svgData = (new XMLSerializer()).serializeToString(svg);
// remember to encode special chars
var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
var svgImg = new Image();
svgImg.onload = function () {
var canvas = document.createElement('canvas');
// IE11 doesn't set a width on svg images...
canvas.width = this.width || bbox.width;
canvas.height = this.height || bbox.height;
canvas.getContext('2d').drawImage(svgImg, 0, 0, canvas.width, canvas.height);
doSomethingWith(canvas)
};
svgImg.src = svgURL;
};
But once again, you will have to append your svg into the document first (either through xhr or into an <iframe> or an <object> element, and you will have to be sure all your resources are CORS compliant (or from same domain) in order to get these rendered.
var svg = document.querySelector('svg');
var doSomethingWith = function(canvas) {
document.body.appendChild(canvas)
};
function parseImages() {
var xlinkNS = "http://www.w3.org/1999/xlink";
var total, encoded;
// convert an external bitmap image to a dataURL
var toDataURL = function(image) {
var img = new Image();
// CORS workaround, this won't work in IE<11
// If you are sure you don't need it, remove the next line and the double onerror handler
// First try with crossorigin set, it should fire an error if not needed
img.crossOrigin = 'anonymous';
img.onload = function() {
// we should now be able to draw it without tainting the canvas
var canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
// draw the loaded image
canvas.getContext('2d').drawImage(this, 0, 0);
// set our <image>'s href attribute to the dataURL of our canvas
image.setAttributeNS(xlinkNS, 'href', canvas.toDataURL());
// that was the last one
if (++encoded === total) exportDoc();
};
// No CORS set in the response
img.onerror = function() {
// save the src
var oldSrc = this.src;
// there is an other problem
this.onerror = function() {
console.warn('failed to load an image at : ', this.src);
if (--total === encoded && encoded > 0) exportDoc();
};
// remove the crossorigin attribute
this.removeAttribute('crossorigin');
// retry
this.src = '';
this.src = oldSrc;
};
// load our external image into our img
var href = image.getAttributeNS(xlinkNS, 'href');
// really weird bug that appeared since this answer was first posted
// we need to force a no-cached request for the crossOrigin be applied
img.src = href + (href.indexOf('?') > -1 ? + '&1': '?1');
};
// get an external svg doc to data String
var parseFromUrl = function(url, element) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if (this.status === 200) {
var response = this.responseText || this.response;
var dataUrl = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(response);
element.setAttributeNS(xlinkNS, 'href', dataUrl);
if (++encoded === total) exportDoc();
}
// request failed with xhr, try as an <img>
else {
toDataURL(element);
}
};
xhr.onerror = function() {
toDataURL(element);
};
xhr.open('GET', url);
xhr.send();
};
var images = svg.querySelectorAll('image');
total = images.length;
encoded = 0;
// loop through all our <images> elements
for (var i = 0; i < images.length; i++) {
var href = images[i].getAttributeNS(xlinkNS, 'href');
// check if the image is external
if (href.indexOf('data:image') < 0) {
// if it points to another svg element
if (href.indexOf('.svg') > 0) {
parseFromUrl(href, images[i]);
} else // a pixel image
toDataURL(images[i]);
}
// else increment our counter
else if (++encoded === total) exportDoc();
}
// if there were no <image> element
if (total === 0) exportDoc();
}
var exportDoc = function() {
// check if our svgNode has width and height properties set to absolute values
// otherwise, canvas won't be able to draw it
var bbox = svg.getBoundingClientRect();
if (svg.width.baseVal.unitType !== 1) svg.setAttribute('width', bbox.width);
if (svg.height.baseVal.unitType !== 1) svg.setAttribute('height', bbox.height);
// serialize our node
var svgData = (new XMLSerializer()).serializeToString(svg);
// remember to encode special chars
var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
var svgImg = new Image();
svgImg.onload = function() {
var canvas = document.createElement('canvas');
// IE11 doesn't set a width on svg images...
canvas.width = this.width || bbox.width;
canvas.height = this.height || bbox.height;
canvas.getContext('2d').drawImage(svgImg, 0, 0, canvas.width, canvas.height);
doSomethingWith(canvas)
};
svgImg.src = svgURL;
};
window.onload = parseImages;
canvas {
border: 1px solid green !important;
}
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<pattern id="Pattern" x="0" y="0" width=".25" height=".25">
<image xlink:href="https://dl.dropboxusercontent.com/s/1alt1303g9zpemd/UFBxY.png" width="100" height="100"/>
</pattern>
</defs>
<rect fill="url(#Pattern)" x="0" y="0" width="200" height="200"/>
</svg>
Related
I am currently working on a Javascript project and I am struggling with exporting the entire SVG image on the canvas. So far I've been only able to export the visible part of the canvas, with out the "hidden" parts.
How do I capture the full canvas content?
Is there a way to do it without messing around with the original canvas size?
I am using D3.js V3
Screenshot of my project
Here's my code:
var svgString;
window.onload = function(){
setTimeout(function() {
exportSVG = document.getElementById("canvas");
document.getElementById("canvas").style.fontFamily= "lato";
document.getElementById("canvas").style.width= exportSVG.getBBox().width * 1;
document.getElementById("canvas").style.height= exportSVG.getBBox().height * 1;
svgString = getSVGString(exportSVG);
console.log(exportSVG.getBBox().width + " / " + exportSVG.getBBox().height);
svgString2Image(svgString, exportSVG.getBBox().width, exportSVG.getBBox().height, 'png', save); // passes Blob and filesize String to the callback
console.log("svg export code loaded");
// console.log(svgString.getBBox().width); document.getElementById("canvas").getBBox().width
}, 5000);
};
function save(dataBlob, filesize) {
saveAs(dataBlob, 'D3 vis exported to PNG.png'); // FileSaver.js function
}
// Below are the functions that handle actual exporting:
// getSVGString ( svgNode ) and svgString2Image( svgString, width, height, format, callback )
function getSVGString(svgNode) {
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
var cssStyleText = getCSSStyles(svgNode);
appendCSS(cssStyleText, svgNode);
var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svgNode);
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
return svgString;
function getCSSStyles(parentElement) {
var selectorTextArr = [];
// Add Parent element Id and Classes to the list
selectorTextArr.push('#' + parentElement.id);
for (var c = 0; c < parentElement.classList.length; c++)
if (!contains('.' + parentElement.classList[c], selectorTextArr))
selectorTextArr.push('.' + parentElement.classList[c]);
// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if (!contains('#' + id, selectorTextArr))
selectorTextArr.push('#' + id);
var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if (!contains('.' + classes[c], selectorTextArr))
selectorTextArr.push('.' + classes[c]);
}
// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];
try {
if (!s.cssRules) continue;
} catch (e) {
if (e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if (contains(cssRules[r].selectorText, selectorTextArr))
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
function contains(str, arr) {
return arr.indexOf(str) === -1 ? false : true;
}
}
function appendCSS(cssText, element) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type", "text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore(styleElement, refNode);
}
}
function svgString2Image(svgString, width, height, format, callback) {
var format = format ? format : 'png';
var imgsrc = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgString))); // Convert SVG string to data URL
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
var image = new Image();
image.onload = function() {
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0, width, height);
canvas.toBlob(function(blob) {
var filesize = Math.round(blob.length / 1024) + ' KB';
if (callback) callback(blob, filesize);
});
};
image.src = imgsrc;
}
Simply change your <svg> viewBox attribute before you serialize it to a string so that it displays everything:
var svg = document.querySelector('svg');
var toExport = svg.cloneNode(true); // avoids having to reset everything afterward
// grab its inner content BoundingBox
var bb = svg.getBBox();
// update its viewBox so it displays all its inner content
toExport.setAttribute('viewBox', bb.x + ' ' + bb.y + ' ' + bb.width + ' ' + bb.height);
toExport.setAttribute('width', bb.width);
toExport.setAttribute('height', bb.height);
var svgAsStr = new XMLSerializer().serializeToString(toExport);
var blob = new Blob([svgAsStr], {type: 'image/svg+xml'});
var img = new Image();
img.onload = drawToCanvas;
img.src = URL.createObjectURL(blob);
function drawToCanvas(evt) {
var canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
canvas.getContext('2d').drawImage(this, 0,0);
document.body.appendChild(canvas);
}
svg{border: 1px solid blue}
canvas{border: 1px solid green}
<svg width="50" height="50" viewBox="0 0 50 50">
<rect x="0" y="0" width="200" height="50" fill="#CCC"/>
</svg>
Can I crop an image without a canvas? And how to crop an image without resizing the image? Like this real picture to be 133px x 133px
Large Image to be Small Image
Large Image To be Small Image
And thiis my code https://jsfiddle.net/w26La1u6/2/
$(document).on('change', '#files', function(event) {
var files = event.target.files; // FileList object
var output = document.getElementById("list");
for (var i = 0, f; f = files[i]; i++) {
if (f.type.match('image.*')) {
if (this.files[0].size < 12097152) {
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var span = document.createElement('span');
span.innerHTML = ['<img class="thumbnail" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'
].join('');
output.insertBefore(span, null);
};
})(f);
$('#clear, #list').show();
reader.readAsDataURL(f);
} else {
alert("Image Size is too big. Minimum size is 2MB.");
$(this).val("");
}
}
}
})
But if there is no other way to crop without canvas, tell me how to crop with canvas
EDIT
<input id="uploadImage" type="file" accept="image/*" capture="camera" />
<img id="imgDisplay" src="http://placehold.it/300x200" alt="Not a kitten" />
var Resample = (function (canvas) {
// (C) WebReflection Mit Style License
function Resample(img, width, height, onresample) {
var load = typeof img == "string",
i = load || img;
if (load) {
i = new Image;
// with propers callbacks
i.onload = onload;
i.onerror = onerror;
}
i._onresample = onresample;
i._width = width;
i._height = height;
load ? (i.src = img) : onload.call(img);
}
function onerror() {
throw ("not found: " + this.src);
}
function onload() {
var
img = this,
width = img._width,
height = img._height,
onresample = img._onresample
;
// Altered section - crop prior to resizing
var imgRatio = img.width / img.height;
var desiredRatio = width / height;
var cropWidth, cropHeight;
if (desiredRatio < imgRatio) {
cropHeight = img.height;
cropWidth = img.height * desiredRatio;
} else {
cropWidth = img.width;
cropHeight = img.width / desiredRatio;
}
delete img._onresample;
delete img._width;
delete img._height;
canvas.width = width;
canvas.height = height;
context.drawImage(
// original image
img,
// starting x point
0,
// starting y point
0,
// crop width
cropWidth,
// crop height
cropHeight,
// destination x point
0,
// destination y point
0,
// destination width
width,
// destination height
height
);
onresample(canvas.toDataURL("image/png"));
}
var context = canvas.getContext("2d"),
round = Math.round;
return Resample;
}(
this.document.createElement("canvas"))
);
var newCropWidth = 133;
var newCropHeight = 133;
function loadImage(data) {
document.querySelector('#imgDisplay').src = data;
}
function handleFileSelect(evt) {
if (evt.target.files.length === 1) {
var picFile = evt.target.files[0];
if (picFile.type.match('image.*')) {
var fileTracker = new FileReader;
fileTracker.onload = function() {
Resample(
this.result,
newCropWidth,
newCropHeight,
loadImage
);
}
fileTracker.readAsDataURL(picFile);
}
}
}
document.querySelector('#uploadImage').addEventListener('change', handleFileSelect, false);
There are ways to do it using jQuery UI but you can just utilize a plugin that someone else already made such as Cropper
I use Highcharts to draw a graph.
I want to add "export to PNG" option that includes both Highcharts graph and outer div. When using Highcharts export, I'm unable to add the outer div containing the chart.
I saw several examples here using html2canvas. When I tried using it, the Highcharts' SVG element wansn't included in the output image.
Does anyone know a solution, better than trying to merge SVG image inside outer HTML div image?
Update -
Using the latest version of html2canvas fixed that issue, however the output is slightly different:
Original:
html2canvas output:
As you can see some elements are rendered twice in different locations.
Anyone knows how to solve it?
I think you are dealing with a few edge cases of svg to bitmap conversion.
I am not sure which method html2canvas uses, but it certainly lacks in something here.
I wrote a function that handles some of theses edge cases that you could use before calling html2canvas, since it is able to draw bitmaps without any problem.
// without converting the svg to png
html2canvas(contentDiv, {
onrendered: function(can) {
dirty.appendChild(can);
}
});
// first convert your svg to png
exportInlineSVG(svg, function(data, canvas) {
svg.parentNode.replaceChild(canvas, svg);
// then call html2canvas
html2canvas(contentDiv, {
onrendered: function(can) {
can.id = 'canvas';
clean.appendChild(can);
}
});
})
function exportInlineSVG(svg, receiver, params, quality) {
if (!svg || !svg.nodeName || svg.nodeName !== 'svg') {
console.error('Wrong arguments : should be \n exportSVG(SVGElement, function([dataURL],[canvasElement]) || IMGElement || CanvasElement [, String_toDataURL_Params, Float_Params_quality])')
return;
}
var xlinkNS = "http://www.w3.org/1999/xlink";
var clone;
// This will convert an external image to a dataURL
var toDataURL = function(image) {
var img = new Image();
// CORS workaround, this won't work in IE<11
// If you are sure you don't need it, remove the next line and the double onerror handler
// First try with crossorigin set, it should fire an error if not needed
img.crossOrigin = 'Anonymous';
img.onload = function() {
// we should now be able to draw it without tainting the canvas
var canvas = document.createElement('canvas');
var bbox = image.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
// draw the loaded image
canvas.getContext('2d').drawImage(this, 0, 0, bbox.width, bbox.height);
// set our original <image>'s href attribute to the dataURL of our canvas
image.setAttributeNS(xlinkNS, 'href', canvas.toDataURL());
// that was the last one
if (++encoded === total) exportDoc()
}
// No CORS set in the response
img.onerror = function() {
// save the src
var oldSrc = this.src;
// there is an other problem
this.onerror = function() {
console.warn('failed to load an image at : ', this.src);
if (--total === encoded && encoded > 0) exportDoc();
}
// remove the crossorigin attribute
this.removeAttribute('crossorigin');
// retry
this.src = '';
this.src = oldSrc;
}
// load our external image into our img
img.src = image.getAttributeNS(xlinkNS, 'href');
}
// The final function that will export our svgNode to our receiver
var exportDoc = function() {
// check if our svgNode has width and height properties set to absolute values
// otherwise, canvas won't be able to draw it
var bbox = svg.getBBox();
// avoid modifying the original one
clone = svg.cloneNode(true);
if (svg.width.baseVal.unitType !== 1) clone.setAttribute('width', bbox.width);
if (svg.height.baseVal.unitType !== 1) clone.setAttribute('height', bbox.height);
parseStyles();
// serialize our node
var svgData = (new XMLSerializer()).serializeToString(clone);
// remember to encode special chars
var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
var svgImg = new Image();
svgImg.onload = function() {
// if we set a canvas as receiver, then use it
// otherwise create a new one
var canvas = (receiver && receiver.nodeName === 'CANVAS') ? receiver : document.createElement('canvas');
// IE11 doesn't set a width on svg images...
canvas.width = this.width || bbox.width;
canvas.height = this.height || bbox.height;
canvas.getContext('2d').drawImage(this, 0, 0, canvas.width, canvas.height);
// try to catch IE
try {
// if we set an <img> as receiver
if (receiver.nodeName === 'IMG') {
// make the img looks like the svg
receiver.setAttribute('style', getSVGStyles(receiver));
receiver.src = canvas.toDataURL(params, quality);
} else {
// make the canvas looks like the canvas
canvas.setAttribute('style', getSVGStyles(canvas));
// a container element
if (receiver.appendChild && receiver !== canvas)
receiver.appendChild(canvas);
// if we set a function
else if (typeof receiver === 'function')
receiver(canvas.toDataURL(params, quality), canvas);
}
} catch (ie) {
console.warn("Your ~browser~ has tainted the canvas.\n The canvas is returned");
if (receiver.nodeName === 'IMG') receiver.parentNode.replaceChild(canvas, receiver);
else receiver(null, canvas);
}
}
svgImg.onerror = function(e) {
if (svg._cleanedNS) {
console.error("Couldn't export svg, please check that the svgElement passed is a valid svg document.");
return;
}
// Some non-standard NameSpaces can cause this issues
// This will remove them all
function cleanNS(el) {
var attr = el.attributes;
for (var i = 0; i < attr.length; i++) {
if (attr[i].name.indexOf(':') > -1) el.removeAttribute(attr[i].name)
}
}
cleanNS(svg);
for (var i = 0; i < svg.children.length; i++)
cleanNS(svg.children[i]);
svg._cleanedNS = true;
// retry the export
exportDoc();
}
svgImg.src = svgURL;
}
// ToDo : find a way to get only usefull rules
var parseStyles = function() {
var styleS = [],i;
// transform the live StyleSheetList to an array to avoid endless loop
for (i = 0; i < document.styleSheets.length; i++)
styleS.push(document.styleSheets[i]);
// Do we have a `<defs>` element already ?
var defs = clone.querySelector('defs') || document.createElementNS('http://www.w3.org/2000/svg', 'defs');
if (!defs.parentNode)
clone.insertBefore(defs, clone.firstElementChild);
// iterate through all document's stylesheets
for (i = 0; i < styleS.length; i++) {
var style = document.createElement('style');
var rules = styleS[i].cssRules,
l = rules.length;
for (var j = 0; j < l; j++)
style.innerHTML += rules[j].cssText + '\n';
defs.appendChild(style);
}
// small hack to avoid border and margins being applied inside the <img>
var s = clone.style;
s.border = s.padding = s.margin = 0;
s.transform = 'initial';
}
var getSVGStyles = function(node) {
var dest = node.cloneNode(true);
svg.parentNode.insertBefore(dest, svg);
var dest_comp = getComputedStyle(dest);
var svg_comp = getComputedStyle(svg);
var mods = "";
for (var i = 0; i < svg_comp.length; i++) {
if (svg_comp[svg_comp[i]] !== dest_comp[svg_comp[i]])
mods += svg_comp[i] + ':' + svg_comp[svg_comp[i]] + ';';
}
svg.parentNode.removeChild(dest);
return mods;
}
var images = svg.querySelectorAll('image'),
total = images.length,
encoded = 0;
// Loop through all our <images> elements
for (var i = 0; i < images.length; i++) {
// check if the image is external
if (images[i].getAttributeNS(xlinkNS, 'href').indexOf('data:image') < 0)
toDataURL(images[i]);
// else increment our counter
else if (++encoded === total) exportDoc()
}
// if there were no <image> element
if (total === 0) exportDoc();
}
rect {
fill: blue;
transform: translate(35px) rotate(45deg);
}
div {
width: 250px;
display: inline-block
}
#svg {
border: 1px solid green;
}
#canvas { border: 1px solid red;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<div id="contentDiv">
<p>Some html content</p>
<svg xmlns="http://www.w3.org/2000/svg" id="svg" width="200">
<defs>
<filter id="Alien" color-interpolation-filters="sRGB">
<feComponentTransfer>
<fefuncR type="table" tablevalues="1 0 1" />
</feComponentTransfer>
</filter>
</defs>
<image filter="url(#Alien)" xlink:href="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" width="100%" height="100%" />
<rect x="0" y="0" width="50" height="50" />
</svg>
</div>
<div id="clean">clean:<br></div>
<div id="dirty">dirty :<br></div>
Note :
From this question, I started to write a full exportInlineSVG function that you can find here.
I have a function in jquery that allows me to get the name of a file that was uploaded in ie7. I am able to get the name of the file, but I am also trying to get the base64 string of the file. Is there any way to alter this function below to do that?
var thumbnail_title_preview = {
update: function(value, event_target) {
var parent_upload_fields = $(event_target).parents('.upload-photos');
var photos_list = $('.upload-photos-list-inner', parent_upload_fields);
var thumbnail = document.createElement("div");
$(thumbnail).addClass('uploaded-thumbnail-title');
var file_name = value;
if(ie7) {
file_name_array = file_name.split('\\');
file_name = file_name_array[file_name_array.length - 1];
} else {
file_name = file_name.replace('C:', '');
file_name = file_name.replace('D:', '');
file_name = file_name.replace('\\fakepath\\', '');
}
if(file_name.length > 12) {
file_name = file_name.substring(0, 12) + '...';
}
$(thumbnail).append('<p>' + file_name + '</p>');
$(thumbnail).append('<a class="remove-photo" href="#"></a>');
$(photos_list).append(thumbnail);
var number_of_thumbnails = $('.uploaded-thumbnail-title', parent_upload_fields).length;
$(photos_list).css({ 'width': number_of_thumbnails * (117 + 20) });
if(number_of_thumbnails == 5) {
$('.upload-photos-add', parent_upload_fields).css({ 'display': 'none' });
$(parent_upload_fields).addClass('has-five-photos');
} else {
$(parent_upload_fields).removeClass('has-five-photos');
}
if(ie7) {
updateTinyScrollbar();
} else {
$('.upload-photos-list').perfectScrollbar('update');
var scrollbar_style = $('.ps-scrollbar-x-rail', parent_upload_fields).css('display');
if(scrollbar_style == 'block') {
$(parent_upload_fields).addClass('has-perfect-scrollbar');
} else {
$(parent_upload_fields).removeClass('has-perfect-scrollbar');
}
}
}
}
This is the html:
<div class="upload-photos clearfix" runat="server">
<div class="upload-photos-add" id="Q0011_00" runat="server">
<asp:AjaxFileUpload ID="AjaxFileUpload1" runat="server" AllowedFileTypes="jpg,jpeg,png,gif" OnClientUploadComplete="onClientUploadComplete" OnClientUploadCompleteAll="onClientUploadCompleteAll" OnClientUploadStart="onClientUploadStart" ></asp:AjaxFileUpload>
</div>
<div class="upload-photos-list">
<div class="upload-photos-list-inner clearfix" runat="server" id="divUploadListDynamic0011_00">
</div>
</div>
</div>
If image file is not crossOrigin (see canvas.toDataURL() SecurityError , Understanding the HTML5 Canvas image security rules) , should be able to retrieve data-uri representation of image file utilizing HTMLCanvasElement.toDataURL() . If the image file , or src is crossOrigin , could utilize FileReader to retrieve data-uri of DOM img element - as type text/html - instead of canvas's .toDataURL() (type image/png) , to avoid possible CORS issue with tainted canvas (see links above)
Try
var body = document.body;
var img = document.createElement("img");
// `value` : `img` path (local , _not_ `crossOrigin`)
img.src = value;
// set `display`:`none` at `img` if _not_ displaying "original" `img`
// img.style.display = "none";
body.appendChild(img);
var canvas = document.createElement("canvas");
// set `display`:`none` at `canvas` ,
// if _not_ displaying `canvas` (`thumbnail`)
var ctx = canvas.getContext("2d");
body.appendChild(canvas);
// `data-uri` of `thumbnail` image
// to retrieve `base64` encoding only , `datauri.split(",")[1]`
// `datauri.split(",")
var datauri;
img.addEventListener("load", function(e) {
// adjust `thumbnail` `width` , `height` here
canvas.width = "50";
canvas.height = "50";
ctx.drawImage(e.target, 0, 0);
// `img` `data-uri` , scaled to `canvas` `width` , `height`
datauri = canvas.toDataURL();
// do stuff with `datauri` of `img`
body.appendChild(document.createElement("br"));
// `datauri` of `thumbnail` image
body.appendChild(document.createTextNode(datauri));
return false
}, false);
var value = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAABa0lEQVR4Xu3VwQkAQAjEQO2/aA+uijzGCkLC4s7cjcsYWEEyLT6IIK0egsR6CCJIzUCMxw8RJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4ViIIDEDMRwLESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4ViIIDEDMRwLESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhPE5Hx53K44yoAAAAAElFTkSuQmCC";
var body = document.body;
var img = document.createElement("img");
// `value` : `img` path (local , _not_ `crossOrigin`)
img.src = value;
img.title = "original image";
// set `display`:`none` at `img` if _not_ displaying "original" `img` (left)
// diplayed here to view different dimensions between "original image" ,
// "thumbnail image" (right)
// img.style.display = "none";
body.appendChild(img);
// space between original image and `thumbnail` image
body.appendChild(document.createTextNode(" "));
var canvas = document.createElement("canvas");
canvas.title = "thumbnail image";
var ctx = canvas.getContext("2d");
body.appendChild(canvas);
var datauri;
img.addEventListener("load", function(e) {
// adjust `thumbnail` `width` , `height` here
canvas.width = "50";
canvas.height = "50";
ctx.drawImage(e.target, 0, 0);
// `img` `data-uri` , scaled to `canvas` `width` , `height`
datauri = canvas.toDataURL();
// do stuff with `datauri` of `img`
body.appendChild(document.createElement("br"));
body.appendChild(document.createTextNode(datauri));
return false
}, false);
I have a JPS with a form in which a user can put an image:
<div class="photo">
<div>Photo (max 240x240 and 100 kb):</div>
<input type="file" name="photo" id="photoInput" onchange="checkPhoto(this)"/>
</div>
I have written this js:
function checkPhoto(target) {
if(target.files[0].type.indexOf("image") == -1) {
document.getElementById("photoLabel").innerHTML = "File not supported";
return false;
}
if(target.files[0].size > 102400) {
document.getElementById("photoLabel").innerHTML = "Image too big (max 100kb)";
return false;
}
document.getElementById("photoLabel").innerHTML = "";
return true;
}
which works fine to check file type and size. Now I want to check image width and height but I cannot do it.
I have tried with target.files[0].width but I get undefined. With other ways I get 0.
Any suggestions?
The file is just a file, you need to create an image like so:
var _URL = window.URL || window.webkitURL;
$("#file").change(function (e) {
var file, img;
if ((file = this.files[0])) {
img = new Image();
var objectUrl = _URL.createObjectURL(file);
img.onload = function () {
alert(this.width + " " + this.height);
_URL.revokeObjectURL(objectUrl);
};
img.src = objectUrl;
}
});
Demo: http://jsfiddle.net/4N6D9/1/
I take it you realize this is only supported in a few browsers. Mostly firefox and chrome, could be opera as well by now.
P.S. The URL.createObjectURL() method has been removed from the MediaStream interface. This method has been deprecated in 2013 and superseded by assigning streams to HTMLMediaElement.srcObject. The old method was removed because it is less safe, requiring a call to URL.revokeOjbectURL() to end the stream. Other user agents have either deprecated (Firefox) or removed (Safari) this feature feature.
For more information, please refer here.
In my view the perfect answer you must required is
var reader = new FileReader();
//Read the contents of Image File.
reader.readAsDataURL(fileUpload.files[0]);
reader.onload = function (e) {
//Initiate the JavaScript Image object.
var image = new Image();
//Set the Base64 string return from FileReader as source.
image.src = e.target.result;
//Validate the File Height and Width.
image.onload = function () {
var height = this.height;
var width = this.width;
if (height > 100 || width > 100) {
alert("Height and Width must not exceed 100px.");
return false;
}
alert("Uploaded image has valid Height and Width.");
return true;
};
};
I agree. Once it is uploaded to somewhere the user's browser can access then it is pretty easy to get the size. As you need to wait for the image to load you'll want to hook into the onload event for img.
Updated example:
// async/promise function for retrieving image dimensions for a URL
function imageSize(url) {
const img = document.createElement("img");
const promise = new Promise((resolve, reject) => {
img.onload = () => {
// Natural size is the actual image size regardless of rendering.
// The 'normal' `width`/`height` are for the **rendered** size.
const width = img.naturalWidth;
const height = img.naturalHeight;
// Resolve promise with the width and height
resolve({width, height});
};
// Reject promise on error
img.onerror = reject;
});
// Setting the source makes it start downloading and eventually call `onload`
img.src = url;
return promise;
}
// How to use in an async function
(async() => {
const imageUrl = 'http://your.website.com/userUploadedImage.jpg';
const imageDimensions = await imageSize(imageUrl);
console.info(imageDimensions); // {width: 1337, height: 42}
})();
Older example:
var width, height;
var img = document.createElement("img");
img.onload = function() {
// `naturalWidth`/`naturalHeight` aren't supported on <IE9. Fallback to normal width/height
// The natural size is the actual image size regardless of rendering.
// The 'normal' width/height are for the **rendered** size.
width = img.naturalWidth || img.width;
height = img.naturalHeight || img.height;
// Do something with the width and height
}
// Setting the source makes it start downloading and eventually call `onload`
img.src = "http://your.website.com/userUploadedImage.jpg";
This is the easiest way to check the size
let img = new Image()
img.src = window.URL.createObjectURL(event.target.files[0])
img.onload = () => {
alert(img.width + " " + img.height);
}
Check for specific size. Using 100 x 100 as example
let img = new Image()
img.src = window.URL.createObjectURL(event.target.files[0])
img.onload = () => {
if(img.width === 100 && img.height === 100){
alert(`Nice, image is the right size. It can be uploaded`)
// upload logic here
} else {
alert(`Sorry, this image doesn't look like the size we wanted. It's
${img.width} x ${img.height} but we require 100 x 100 size image.`);
}
}
Attach the function to the onchange method of the input type file /onchange="validateimg(this)"/
function validateimg(ctrl) {
var fileUpload = ctrl;
var regex = new RegExp("([a-zA-Z0-9\s_\\.\-:])+(.jpg|.png|.gif)$");
if (regex.test(fileUpload.value.toLowerCase())) {
if (typeof (fileUpload.files) != "undefined") {
var reader = new FileReader();
reader.readAsDataURL(fileUpload.files[0]);
reader.onload = function (e) {
var image = new Image();
image.src = e.target.result;
image.onload = function () {
var height = this.height;
var width = this.width;
if (height < 1100 || width < 750) {
alert("At least you can upload a 1100*750 photo size.");
return false;
}else{
alert("Uploaded image has valid Height and Width.");
return true;
}
};
}
} else {
alert("This browser does not support HTML5.");
return false;
}
} else {
alert("Please select a valid Image file.");
return false;
}
}
const ValidateImg = (file) =>{
let img = new Image()
img.src = window.URL.createObjectURL(file)
img.onload = () => {
if(img.width === 100 && img.height ===100){
alert("Correct size");
return true;
}
alert("Incorrect size");
return true;
}
}
I think this may be the simplest for uploads if you want to use it other functions.
async function getImageDimensions(file) {
let img = new Image();
img.src = URL.createObjectURL(file);
await img.decode();
let width = img.width;
let height = img.height;
return {
width,
height,
}
}
Use like
const {width, height } = await getImageDimensions(file)
Suppose you were storing an image for Tiger taken in Kenya. So you could use it like to upload to cloud storage and then store photo information.
const addImage = async (file, title, location) => {
const { width, height } = await getImageDimensions(file)
const url = await uploadToCloudStorage(file) // returns storage url
await addToDatabase(url, width, height, title, location)
}
function validateimg(ctrl) {
var fileUpload = $("#txtPostImg")[0];
var regex = new RegExp("([a-zA-Z0-9\s_\\.\-:])+(.jpg|.png|.gif)$");
if (regex.test(fileUpload.value.toLowerCase())) {
if (typeof (fileUpload.files) != "undefined") {
var reader = new FileReader();
reader.readAsDataURL(fileUpload.files[0]);
reader.onload = function (e) {
var image = new Image();
image.src = e.target.result;
image.onload = function () {
var height = this.height;
var width = this.width;
console.log(this);
if ((height >= 1024 || height <= 1100) && (width >= 750 || width <= 800)) {
alert("Height and Width must not exceed 1100*800.");
return false;
}
alert("Uploaded image has valid Height and Width.");
return true;
};
}
} else {
alert("This browser does not support HTML5.");
return false;
}
} else {
alert("Please select a valid Image file.");
return false;
}
}
You can do the steps for previewing the image without showing it which is supported on all browsers. Following js code shows you how to check the width and height :
var file = e.target.files[0];
if (/\.(jpe?g|png|gif)$/i.test(file.name)) {
var reader = new FileReader();
reader.addEventListener("load", function () {
var image = new Image();
image.src = this.result as string;
image.addEventListener('load', function () {
console.log(`height: ${this.height}, width: ${this.width}`);
});
}, false);
reader.readAsDataURL(file);
}
Based on Mozilla docs:
The readAsDataURL method is used to read the contents of the specified
Blob or File. When the read operation is finished, the readyState
becomes DONE, and the loadend is triggered. At that time, the result
attribute contains the data as a data: URL representing the file's
data as a base64 encoded string.
And the browser compatibility is listed too.
In my case, I needed to also prevent the form from being submited, so here is the solution that worked for me.
The preventDefault will stop the form action, then we check the size and dimensions of the image in the onload function.
If all good, we allow the submit.
As the submit button gets disabled if a user still tries to submit the form with an invalid image, I also had to re-able the submit button once a valid image is inputted.
const validateMaxImageFileSize = (e) => {
e.preventDefault();
const el = $("input[type='file']")[0];
if (el.files && el.files[0]) {
const file = el.files[0];
const maxFileSize = 5242880; // 5 MB
const maxWidth = 1920;
const maxHeight = 1080;
const img = new Image();
img.src = window.URL.createObjectURL(file);
img.onload = () => {
if (file.type.match('image.*') && file.size > maxFileSize) {
alert('The selected image file is too big. Please choose one that is smaller than 5 MB.');
} else if (file.type.match('image.*') && (img.width > maxWidth || img.height > maxHeight)) {
alert(`The selected image is too big. Please choose one with maximum dimensions of ${maxWidth}x${maxHeight}.`);
} else {
e.target.nodeName === 'INPUT'
? (e.target.form.querySelector("input[type='submit']").disabled = false)
: e.target.submit();
}
};
}
};
$('form.validate-image-size').on('submit', validateMaxImageFileSize);
$("form.validate-image-size input[type='file']").on('change', validateMaxImageFileSize);
function uploadfile(ctrl) {
var validate = validateimg(ctrl);
if (validate) {
if (window.FormData !== undefined) {
ShowLoading();
var fileUpload = $(ctrl).get(0);
var files = fileUpload.files;
var fileData = new FormData();
for (var i = 0; i < files.length; i++) {
fileData.append(files[i].name, files[i]);
}
fileData.append('username', 'Wishes');
$.ajax({
url: 'UploadWishesFiles',
type: "POST",
contentType: false,
processData: false,
data: fileData,
success: function(result) {
var id = $(ctrl).attr('id');
$('#' + id.replace('txt', 'hdn')).val(result);
$('#imgPictureEn').attr('src', '../Data/Wishes/' + result).show();
HideLoading();
},
error: function(err) {
alert(err.statusText);
HideLoading();
}
});
} else {
alert("FormData is not supported.");
}
}