Jquery: convert SVG to PNG with new size - javascript

I´m using the following function to transform an SVG into a PNG and offer it as a download:
<svg id="chart">
...some contenthere
</svg>
function() {
var svg = $("#chart")[0],
bBox = $('#chart')[0].getBBox();
var width = bBox.width*2,
height = bBox.height*2;
var canvas = $("canvas")[0],
serializer = new XMLSerializer(),
svgString = serializer.serializeToString(svg);
canvas.width = width;
canvas.height = height;
canvg(canvas, svgString);
var dataURL = canvas.toDataURL('image/png'),
data = atob(dataURL.substring('data:image/png;base64,'.length));
asArray = new Uint8Array(data.length);
for (var i = 0; i < data.length; ++i) {
asArray[i] = data.charCodeAt(i);
}
var blob = new Blob([asArray.buffer], {type: 'image/png'});
saveAs(blob, 'climatechart.png');
}
It actually works fine, despite the fact that the output image is the same size as the SVG in the browser. How can i set a new size for the image? I also tried to get the size directly from the svg not the BBox, but it was the same.

This solution is based on a remote formatting server and not local code. It uses XSL FO server-side to render SVG to bitmap directly, allowing you to set the resolution for high quality output.
Documentation: http://www.cloudformatter.com/CSS2Pdf.APIDoc.Usage
Example: http://jsfiddle.net/g75t4oyq/
Code Implementation for JPG format # 300dpi from SVG in div:
click="return xepOnline.Formatter.Format('JSFiddle', {render:'newwin', mimeType:'image/jpg', resolution:'300', srctype:'svg'})";
jQuery('#buttons').append('<button onclick="'+ click +'">JPG #300dpi</button>');

You can easily resize a canvas as you can draw it as a canvas. The following example ignores any of the SVG rendering, which is working fine according to your question, but it does show how you can easily resize a canvas using another canvas:
var myCanvas = document.createElement('canvas');
var myCtx = myCanvas.getContext('2d');
myCanvas.width = 200;
myCanvas.height = 200;
myCtx.arc(100, 100, 100, 0, Math.PI * 2, true);
myCtx.fill();
var seCanvas = document.createElement('canvas');
var seCtx = seCanvas.getContext('2d');
seCanvas.width = 200;
seCanvas.height = 200;
seCtx.drawImage(myCanvas, 0, 0, 100, 100);
document.body.appendChild(myCanvas);
document.body.appendChild(seCanvas);
After that, you can simply continue the process using this new canvas.

SVG documents can be resized via height and width attributes if the svg element defines a viewBox attribute depending on its size; e.g.
<svg width="500" height="200" viewBox="0 0 50 20" >
Once the viewBox has been set, the scaling of svg in canvas works as you coded.

Ok, thanks for the suggestions, i finally found a solution that perfectly fits (according to Cédric Hartlands hint). The viewbox attribute was the part that was completely missing in my code. The viewbox is defined as the following:
viewbox = "x y width height"
So to scale up the <svg> and ALL the elements in it, it is necessary to make sure that width and height of the viewbox exactly match the new width and height of the <svg> element:
//original version
<svg> width="100" height="100" viewbox="0 0 100 100" preserveAspectRatio="xMinYMin meet"</svg>
//scaled up version
<svg> width="200" height="200" viewbox="0 0 200 200" preserveAspectRatio="xMinYMin meet"</svg>
If i convert the scaled up svg into a canvas, it fits the whole image without loosing any quality (which is the biggest benefit of vector graphics anyway). Cannot believe it took my so long to get that.

Related

Check if point is inside of shape drawn by a complex Path2D

I want to check if a given point is inside of the space drawn inside of a irregular path - inside of the brain (the path is below).
I don't seem to be able to use CanvasRenderingContext2D.isPointInPath(path, x, y) because it only returns true if the point is literally inside the path (the white outline).
I also don't think I can use the odd polygon rule, given that it is possible for a point not to be in the edge and its line still hit the shape wall an even number of times...
As you're working with a SVG, here's a workaround which doesn't involve any abstract calculations.
(1)
Make the inside of your shape, thus the area you want to detect a color completely different from the rest of the shape or any other visuals. In your case for example it would be nothing (or black) to red.
This is controlled by the svg's fill attribute, which takes a hex #ff0000 or a rgb value rgb(255,0,0). Well for reasons that will be important later we make it a rgba(255,0,0,1) value though it ignores the alpha value.
As we don't want the fill to be visible, we also need to set the fill-opacity value to 0.005. This is the lowest value possible and equals the CanvasRenderingContext2D value of 1 in a range from 0-255.
(2)
Now we need to turn the svg into an Image object that can be drawn onto a canvas. This can be done using the following lines:
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let img = new Image();
img.onload = function(e) {
ctx.drawImage(e.currentTarget, canvas.width / 2 - 50, canvas.height / 2 - 50, 100, 100);
}
img.src = 'data:image/svg+xml;charset=utf8,' + encodeURIComponent(svg);
svg is just a string representation of your svg's data.
(3)
The final step involves getting the pixel color at a specific position on the canvas. For this we utilize the .getImageData(x, y, width, height) method, which returns an object consisting of a Uint8ClampedArray which holds four values per pixel. If we set both width and height to 1 we get exactly the four color components for a single pixel - red, green, blue and alpha.
Now we simply compare the color with the red we've used in step (1) and if it's equal, we know it's inside the shape.
Here's a working example (hover your mouse over the image):
let hitColor = 'rgba(255,0,0,1)';
let svg = `<?xml version="1.0" encoding="iso-8859-1"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 511.989 511.989" style="enable-background:new 0 0 511.989 511.989;" xml:space="preserve">
<path style="fill:${hitColor};fill-opacity:0.005" d="M489.333,255.088l-21.344-17.625l8-34.671l4-18.812l-30.656-42.141l6.656-22.53l-8-38.483
l-30.655-30.077h-39.999c0,0-22.655,12.312-16,0c6.672-12.328-33.326-36.577-33.326-36.577h-17.328l-40.663,15.483l-9.602,7.828
l-31.062-23.702h-30.672l-23.999,0.391l-20.929,23.312L150.41,50.75h-22.383l-23.998,10.654L66.694,73.295l-6.664,29.358
l-5.336,42.577l-19.999,22.891v26.516l12.397,37.623l-12.397,16.547l-18.664,22.672v41.326l18.664,29.984v36.67l23.999,55.999
c0,0,35.999,18.672,41.335,21.327c5.328,2.672,14.664,0,14.664,0l29.327,32l27.999,9.328h21.336l42.663-18.656l24.397-18.672
l10.664,24l42.257,13.328h21.344l31.998-6.656l21.719-38.671l11.609,3.999l38.67-7.999l28-28.327l10.656-46.327v-31.343
l17.718-20.156l10.281-35.171L489.333,255.088z"/>
<path style="fill:#ffffff;" d="M511.973,294.009c0-24.546-12.891-46.093-32.28-58.218c7.984-11.203,12.672-24.905,12.672-39.701
c0-22.203-10.562-41.938-26.938-54.453c2.625-7.655,4.078-15.858,4.078-24.42c0-41.281-33.484-74.749-74.764-74.749
c-8.344,0-16.375,1.375-23.875,3.906C363.21,19.594,338.554,0,309.321,0c-22.266,0-41.858,11.359-53.327,28.608
C244.525,11.359,224.925,0,202.667,0c-29.233,0-53.889,19.594-61.537,46.374c-7.5-2.531-15.531-3.906-23.89-3.906
c-41.288,0-74.757,33.468-74.757,74.749c0,8.562,1.445,16.765,4.086,24.42c-16.382,12.516-26.952,32.25-26.952,54.453
c0,14.796,4.695,28.498,12.672,39.701c-19.383,12.125-32.273,33.672-32.273,58.218c0,21.327,9.727,40.374,24.984,52.952
c-2.375,8.375-3.656,17.202-3.656,26.343c0,52.53,42.194,95.185,94.537,95.966c13.405,25.406,40.069,42.719,70.787,42.719
c29.632,0,55.506-16.125,69.326-40.062c13.82,23.938,39.687,40.062,69.327,40.062c30.718,0,57.389-17.312,70.795-42.719
c52.342-0.781,94.529-43.436,94.529-95.966c0-9.141-1.281-17.968-3.656-26.343C502.238,334.383,511.973,315.336,511.973,294.009z
M186.668,490.644c-32.351,0-58.663-26.312-58.663-58.654c0-16.406,7.195-31.688,18.078-42.344c2.008-1.938,3.25-4.641,3.25-7.656
c0-5.891-4.773-10.655-10.664-10.655c-2.906,0-5.547,1.156-7.469,3.047c-15.569,14.593-24.53,34.577-24.53,57.608
c0,5.265,0.516,10.421,1.492,15.405c-16.469-2-31.702-9.391-43.616-21.312c-14.102-14.094-21.867-32.844-21.867-52.78
c0-19.952,7.766-38.702,21.867-52.796c14.102-14.109,32.851-21.875,52.796-21.875c6.351,0,12.585,0.781,18.585,2.312
c0.883,0.234,1.797,0.375,2.742,0.375c5.891,0,10.664-4.78,10.664-10.671c0-5.203-3.727-9.531-8.656-10.469
c-7.477-1.875-15.289-2.875-23.335-2.875c-35.805,0-67.03,19.608-83.529,48.655c-7.734-8.422-12.469-19.641-12.469-31.952
c0-16.406,8.32-31.405,22.265-40.14l9.789-6.125c2.898-1.906,4.812-5.188,4.812-8.922c0-2.266-0.719-4.391-1.938-6.109l-6.609-9.297
c-5.695-7.999-8.711-17.452-8.711-27.326c0-13.922,6.07-26.453,15.695-35.094c13.578,18.766,35.655,30.984,60.593,30.984
c5.89,0,10.765-4.781,10.765-10.672s-4.844-10.656-10.733-10.656c-29.453,0-53.452-23.969-53.452-53.436
c0-29.453,23.968-53.422,53.421-53.422c7.679,0,14.976,1.641,21.585,4.562c2.242,33.312,29.968,59.624,63.842,59.624
c5.891,0,10.664-4.766,10.664-10.671c0-5.891-4.773-10.657-10.664-10.657c-23.522,0-42.663-19.14-42.663-42.671
c0-23.515,19.133-42.655,42.663-42.655c23.523,0,42.663,19.141,42.663,42.655v106.67h-0.016
c-0.156,5.734-4.867,10.359-10.655,10.359c-5.891,0-10.664,4.781-10.664,10.672s4.773,10.671,10.664,10.671
c3.741,0,7.335-0.656,10.671-1.828v87.451c0,17.64-14.358,31.983-31.999,31.983c-5.891,0-10.664,4.781-10.664,10.672
s4.773,10.672,10.664,10.672c12.008,0,23.086-3.969,31.999-10.672v101.357C245.33,464.332,219.011,490.644,186.668,490.644z
M478.177,325.961c-16.5-29.047-47.718-48.655-83.529-48.655c-8.047,0-15.859,1-23.344,2.875c-4.922,0.938-8.656,5.266-8.656,10.469
c0,5.891,4.781,10.671,10.672,10.671c0.953,0,1.859-0.141,2.734-0.375c6-1.531,12.25-2.312,18.594-2.312
c19.938,0,38.687,7.766,52.795,21.875c14.109,14.094,21.859,32.844,21.859,52.796c0,19.937-7.75,38.687-21.859,52.78
c-11.922,11.921-27.14,19.312-43.607,21.312c0.969-4.984,1.484-10.141,1.484-15.405c0-23.031-8.953-43.016-24.531-57.608
c-1.922-1.891-4.562-3.047-7.469-3.047c-5.891,0-10.672,4.765-10.672,10.655c0,3.016,1.25,5.719,3.25,7.656
c10.891,10.656,18.094,25.938,18.094,42.344c0,32.342-26.312,58.654-58.67,58.654c-32.344,0-58.663-26.312-58.663-58.654V330.633
c8.914,6.703,19.991,10.672,31.991,10.672c5.906,0,10.672-4.781,10.672-10.672s-4.766-10.672-10.672-10.672
c-17.625,0-31.991-14.344-31.991-31.983v-87.451c3.336,1.172,6.93,1.828,10.663,1.828c5.891,0,10.672-4.78,10.672-10.671
c0-5.891-4.781-10.672-10.672-10.672c-5.78,0-10.491-4.625-10.647-10.359h-0.016V63.982c0-23.515,19.147-42.655,42.663-42.655
s42.671,19.141,42.671,42.655c0,23.531-19.155,42.671-42.671,42.671c-5.891,0-10.672,4.767-10.672,10.657
c0,5.905,4.781,10.671,10.672,10.671c33.874,0,61.607-26.312,63.842-59.624c6.609-2.922,13.906-4.562,21.578-4.562
c29.468,0,53.436,23.969,53.436,53.422c0,29.467-23.999,53.436-53.467,53.436c-5.891,0-10.719,4.766-10.719,10.656
c0,5.89,4.875,10.672,10.75,10.672c24.937,0,47.029-12.219,60.592-30.984c9.625,8.641,15.703,21.172,15.703,35.094
c0,9.874-3.016,19.327-8.719,27.326l-6.609,9.297c-1.219,1.719-1.938,3.844-1.938,6.109c0,3.734,1.922,7.016,4.812,8.922
l9.797,6.125c13.938,8.734,22.266,23.733,22.266,40.14C490.645,306.32,485.911,317.539,478.177,325.961z"/>
</svg>
`;
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let rect = canvas.getBoundingClientRect();
let img = new Image();
img.onload = function(e) {
ctx.drawImage(e.target, canvas.width / 2 - 50, canvas.height / 2 - 50, 100, 100);
canvas.addEventListener('mousemove', (e) => {
let data = ctx.getImageData(e.clientX - rect.left, e.clientY - rect.top, 1, 1).data;
let hit = hitColor == `rgba(${data[0]},${data[1]},${data[2]},${data[3]})`;
document.getElementById('message').innerText = `inside path: ${hit}`;
});
}
img.src = 'data:image/svg+xml;charset=utf8,' + encodeURIComponent(svg);
<canvas id="canvas" style="background:black;"></canvas><br>
<span id="message">inside path:</span>

How to export Canva Render has GIF with CSS modification

I'm using p5.js to make a GIF animation but I have an issue when I want to export it.
I did a pixelased effect on my animation by adding these css properties to the Canva (140*140) :
image-rendering: pixelated;
width:500px;
height:500px;
My problem is that I can't export this Canva with the properties I added. (I'm using CCapture)
My gif is in 140x140 without the pixelated effect.
How can I get the rendering I need?
There is a difference between the width & height you can set for a HTML element e.g. <canvas width='140' height='140'> and the CSS width & height properties.
The former defines the actual size of the canvas - 140x 140in this case.
If we now set the CSS width & height to something deviating from the HTML element's width & height e.g. <canvas width='140' height='140' style='width: 500px; height:500px;'> the actual size in pixels of the canvas does not change - it stays 140 x 140 pixels. The CSS properties just control the displayed size of the element inside the browser, meaning the 140 x 140 are simply stretched to 500 x 500.
So if you get actual pixel data of the canvas - for exporting to gif/png - the final image will have the original dimensions of the canvas - not the rendered.
The fix is quite simple though. Instead of directly using the source canvas for exporting, draw it's content on a second canvas, the size of your desired resolution.
To force the 'export' canvas to not use any filtering/smoothing, you need to set the imageSmoothingEnabled property of it's context to false.
Here's an example (you can right-click and save both images to see the difference):
var ctx = document.getElementById('canvas').getContext('2d');
var image = new Image();
image.onload = function() {
ctx.drawImage(image, 0, 0);
let sourceCanvas = document.getElementById("canvas");
let virtualWidth = parseInt(getComputedStyle(sourceCanvas).width);
let virtualHeight = parseInt(getComputedStyle(sourceCanvas).height);
var canvas = document.getElementById("exportCanvas");
canvas.width = virtualWidth;
canvas.height = virtualHeight;
canvas.getContext('2d').imageSmoothingEnabled = false;
canvas.getContext('2d').drawImage(sourceCanvas, 0, 0, virtualWidth, virtualHeight);
}
image.src = 'https://mdn.mozillademos.org/files/12640/cat.png';
canvas {
width: 500px;
height: 500px;
}
<span>Rendered canvas</span><br>
<canvas id="canvas" width="140" height="140"></canvas><br>
<span>Export canvas</span><br>
<canvas id="exportCanvas"></canvas>

phantomjs throwing DOM exception 18 on canvas.toDataURL using src images

I'm trying to convert svg to png images using phantomjs:
var page = require('webpage').create();
page.evaluate(function() {
var svg = '<svg class="tnt_tracks_svg" width="800" height="180" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/\
xlink"><rect width="10" height="10" fill="red"></rect></svg>';
var src = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svg)));
var img = new Image();
img.src = src;
img.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0);
var src = canvas.toDataURL(); // DOM Exception 18!
}
});
But I'm getting a DOM Exception 18 when calling toDataURL. I have seen in other similar questions that this may happen if the svg is hosted in other domains etc..., but in my case I'm providing the svg src!. What is wrong with the code above? Why is it firing the DOM Exception?
A similar code looks good in jsfiddle (no phantomjs, tested in chrome and FF). And this phantom example is also working using fabric.js:
page.includeJs("http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js", function () {
page.evaluate(function() {
var svg_text = '<svg width="600" height="400"><g><rect width="10" height="10" fill="red"></rect></g></svg>';
fabric.loadSVGFromString(svg_text, function (objects, options) {
var loadedObject = fabric.util.groupSVGElements(objects, options);
var canvas = new fabric.Canvas('canvas');
canvas.add(loadedObject);
canvas.calcOffset();
canvas.renderAll();
console.log (canvas.toDataURL('png'));
});
});
});
I'm trying to find the relevant code in fabric.js, but any help would be much appreciated.
Ok, answering my question after some research...
It looks like webkit is always setting the origin-clean flag to false every time a data-uri string is set as source attribute of a HTML image, which is then rendered on Canvas element. Firefox and Chrome (for example), allow this for some cases, but not webkit.
Fabric.js doesn't suffer from this problem because it renders the shapes into canvas but don't use the data uri for that, so the canvas is not tainted.
Another solution is to use canvg and SVG.toDataURL.
References here:
Issue explained: http://svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back/#security_issues
Webkit issues: Several, see for example this and this

Give height to the image object using javascript is not working

I am trying to give height to an image object using javascript, But its not working.
html code
<canvas id="canvas1" height="500px" width="500px">
</canvas>
I want to create background image using javascript and this image must be set according to canvas size.
Javascript code:
canvas = document.getElementById("canvas1");
c = canvas.getContext("2d");
var bg = new Image();
bg.src = "images/bg.png";
bg.height = "100";
bg.width = "100";
c.drawImage(bg,0,0);
I can see the image but can not give the height and width of that image at run time.
How can i give the height and width of image object at runtime.?
Thank you in advance for your help.
There are a few issues in the code:
Issue 1
The first one being the tag attributes. Doesn't really affect the result but the correct syntax is an integer value without px at the end as width and height for canvas can only use pixels:
<canvas id="canvas1" height="500" width="500"></canvas>
Issue 2
The next issue is that you try to set read-only properties on the image element:
var bg = new Image();
bg.src = "images/bg.png";
//bg.height = "100"; invalid
//bg.width = "100"; invalid
You don't really need to set the image size; you can if you absolutely want by using this syntax (then get actual size using naturalWidth / naturalHeight if you should need them later - the full image will be loaded in any case):
var bg = new Image(width, height);
But you can simply use width and height with the drawImage() method (see below).
Issue 3
The third issue is that you don't allow the image to load. Image loading is asynchronous so you need to add an onload handler:
var bg = new Image();
bg.onload = done; /// wait for image to load, then call done()
bg.src = "images/bg.png";
function done() {
c.drawImage(bg,0,0);
/// continue here
}
Optionally define the destination size like this:
function done() {
c.drawImage(bg,0,0, width, height); /// the size you want
/// continue here
}
If you want to fill the canvas use:
c.drawImage(bg,0,0, canvas.width, canvas.height);
Hope this helps!
Since it's on a canvas you need to set them in the drawimage function
canvas = document.getElementById("canvas1");
c = canvas.getContext("2d");
var bg = new Image();
bg.src = "images/bg.png";
c.drawImage(bg,0,0, 100, 100);

correct canvas height and width not being passed to a function

Canvas element not being passed with correct height and width to a function, for now I fixed the problem by again assigning the height and width of the canvas after it has been passed to the accepting function. As I am new to this so I want to know if this is a problem with canvas or not?.
$(document).ready(function(){
calling_function = function(eventObj){
//some code
ajaxOptsFtn = {
url: '/xyz_data/',
dataType: 'json',
data: form_vals,
success: function(resp){
//initialisation for function.
if(resp.var_ready === true){
//dynamically adding canvas element.
var canvas_obj = $('<canvas/>').css({width:160, height:240});
$(clicked_element).children('canvas').remove();
$(clicked_element).append(canvas_obj);
//intilise other arguments with some values
var x = 30;
var y= http://abcs.com/dds.jpg;
var z = resp.apparel_img_url;
var nl = gamma_value;
var wD = 23;
var wU = 26;
acceptingFunction(canvas_obj[0],y,z,x,n,wU,wD);
}
else{
console.log('some other message');
}
},
};
if (data.var_ready) {
$.ajax(ajaxOptsFtn);
}
else{
console.log('some message');
};
};
acceptingFunction = function(canvas_obj,y,z,x,n,wU,wD){
canvas = canvas_obj;
console.log("canvas passed height and width:"+ canvas.height +","+canvas.width);
console.log("re assigning expected values");
canvas.width = 160;
canvas.height = 240;
var context = canvas.getContext('2d');
//some code
AimageObj.onload = function () {
//some code
};
BimageObj.onload = function () {
//some code
};
};
You must set the size of the canvas using its attributes, not CSS - for example:
var canvas_obj = $('<canvas/>').attr({'width': 160, 'height': 240});
If you don't do this the canvas element will default to size 300 x 150 pixels which is only stretched by CSS (like an image).
And likewise you also read the same attributes/properties when you want to get the canvas' size.
I have written a blog post that explains this in details - it's too long for SO but here is the essential part:
If we don’t set any actual size for the canvas’ source bitmap it will
default to 300 x 150 pixels as per specification. If you now set the
CSS size of the element to lets say 900 x 450 pixels what happens is
that those 300 x 150 pixels are simply scaled to the new size as the
CSS applies to the element while the default 300 x 150 applies to the
source bitmap (ie. the image). The applied CSS rule doesn’t do
anything with the actual bitmap size.
It would be exactly the same if the canvas was an image, which works
in a similar fashion: it has the image element and then the source
bitmap – the image itself. If you choose to use a different size for
the element than what the image is, the image is simply stretched but
its original data stays the same. There are no more or less pixels in
the original image.
It's also very easy to do it from an HTML5 standpoint. As Simple as:
<canvas id="canvasName" width="160" height="240">
<p>Sorry, The Canvas element is not supported in this browser :(</p>
</canvas>
Adding the paragraph makes it so that a browser that doesn't support the Canvas (IE6 for example) makes this line pop up instead of the Canvas. But putting this into the HTML5 file makes it simple for drawing the Canvas which can be worked with with Javascript. Just make sure you load the Canvas before you load the Javascript, otherwise it will crash
You would then have to declare it in Javascript with
var canvas = document.getElementById("mCanvas");
var context = canvas.getContext("2d");
And you can Console.log it to make sure it is connected properly

Categories