0

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.

flixe
  • 626
  • 1
  • 11
  • 36
  • Just create a second canvas with the desired size and use `secondContext.drawImage(firstCanvas, 0, 0, outputWidth, outputHeight)`, which will scale your image down to the size of that canvas. Then just do what you are already doing. – somethinghere Dec 16 '15 at 13:45

4 Answers4

1

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>');
A J
  • 3,970
  • 14
  • 38
  • 53
Kevin Brown
  • 8,805
  • 2
  • 20
  • 38
0

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.

  • I forgot to mention that i have already tried that. It upscales the canvas correctly when it is displayed on the page. But the downloaded version is somehow "cropped", which means that it downloads only a subset (rectangle in upper left corner ) of the full canvas. Do i have to set the size somewhere else, e.g. in the blob? – flixe Dec 16 '15 at 15:08
  • True, the canvas width/height is double the size of the svg but the image itself is still the same size. – Cédric Hartland Dec 16 '15 at 15:38
  • But is there a way to also resize that image? It has the same size, as the original SVG on my page, so i´m just wondering where it got the size information from. – flixe Dec 16 '15 at 16:41
  • Since svg is inlined in your dom node, you need to scale it before rendering it to the canvas. To do so, typical solution involves using viewBox attribute of the svg element then scale using height and width attributes. – Cédric Hartland Dec 16 '15 at 20:20
0

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.

somethinghere
  • 16,311
  • 2
  • 28
  • 42
  • The caveat of this solution is that you will loose quality while up scaling the original image. – Kaiido Dec 16 '15 at 14:15
  • @Kaiido Sure, yeah. You are converting pixels here - its a canvas. – somethinghere Dec 16 '15 at 14:15
  • But `ctx.drawImage(svgImg)` will draw an svg image, the vector one, so there is a way to not loose this quality. Anyway, I am writing my own svg to bitmap script and will never fall back to canvg, given the lot of lacks it has :-) – Kaiido Dec 16 '15 at 14:19
  • Actually yeah, I never really though about it but `drawImage` _can_ write an svg to it... Hmmm... Anyhow, I posted this as a way to resize the image, nothing else. – somethinghere Dec 16 '15 at 14:20
0

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.

flixe
  • 626
  • 1
  • 11
  • 36