I need to output the result of a fabric canvas drawing to an SVG file.
I'm using fabric.js version 1.7.6 and when I have a path drawn to a canvas with an rgba fill like rgba(255,0,0,.15)
the resulting SVG has a fill of rgb(0,0,0)
. Is there some setting I need to enable to make it output the alpha chanel?
In my sample code the purple circle converts to SVG properly, but the rectangle just shows up as black.
Sample HTML:
<html>
<head>
<script src="fabric.js"></script>
</head>
<body>
<div id="canvasHolder" style="border: 3px solid black;">
<canvas id="canvasElement" width="400" height="400" />
</div>
<div id="svgHolder" style="border: 3px solid blue;">
</div>
</body>
<script>
var canvas = new fabric.Canvas('canvasElement');
var rect = new fabric.Path('M,0,0,h,100,v,100,h,-100,z',{
top:100,
left:100,
stroke: 'green',
fill: 'rgba(255,0,0,.15)'
});
canvas.add(rect);
var circ = new fabric.Circle({
radius: 30,
top:30,
left:30,
stroke: 'blue',
fill: 'purple'
});
canvas.add(circ);
canvas.renderAll();
// Make an SVG object out of the fabric canvas
var SVG = canvas.toSVG();
document.getElementById('svgHolder').innerHTML = SVG;
</script>
</html>
Output SVG:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="400" height="400" viewBox="0 0 400 400" xml:space="preserve">
<desc>Created with Fabric.js 1.7.6</desc>
<defs>
</defs>
<path d="M 0 0 h 100 v 100 h -100 z" style="stroke: rgb(0,128,0); stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform="translate(150.5 150.5) translate(-50, -50) " stroke-linecap="round"></path>
</svg>
As I said in comment, this looks like a bug, that you should report on the project's issue tracker.
Colors are all converted to rgb() (rgba, hsl, hsla, hex, keywords) and thus don't support alpha channel...
For the time being, here is an heavy workaround :
toSVG
accepts an reviver function, which will receive all the svg nodes markups. From there, you can reapply the correct styles, but not so easily.
The only parameter I could find allowing us to identify which object corresponds to the svg markup we get, is the id
one.
id
atribute, and then change its style.fill
and style.stroke
properties, before returning the serialization of this modified node.var canvas = new fabric.Canvas('canvasElement');
var colors_dict = {};
// an helper function to generate and store our colors objects
function getColorId(fill, stroke) {
var id = 'c_' + Math.random() * 10e16;
return colors_dict[id] = {
id: id,
fill: fill || 'black',
stroke: stroke || 'black' // weirdly fabric doesn't support 'none'
}
}
// first ask for the color object of the rectangle
var rect_color = getColorId('hsla(120, 50%, 50%, .5)', 'rgba(0,0,255, .25)');
var rect = new fabric.Path('M,0,0,h,100,v,100,h,-100,z', {
top: 60,
left: 60,
stroke: rect_color.stroke, // set the required stroke
fill: rect_color.fill, // fill
id: rect_color.id // and most importantly, the id
});
canvas.add(rect);
var circ_color = getColorId('rgba(200, 0,200, .7)');
var circ = new fabric.Circle({
radius: 30,
top: 30,
left: 30,
stroke: circ_color.stroke,
fill: circ_color.fill,
id: circ_color.id
});
canvas.add(circ);
canvas.renderAll();
var parser = new DOMParser();
var serializer = new XMLSerializer();
function reviveColors(svg){
// first we parse the markup we get, and extract the node we're interested in
var svg_doc = parser.parseFromString('<svg xmlns="http://www.w3.org/2000/svg">' + svg + '</svg>', 'image/svg+xml');
var svg_node = svg_doc.documentElement.firstElementChild;
var id = svg_node.getAttribute('id');
if (id && id in colors_dict) { // is this one of the colored nodes
var col = colors_dict[id]; // get back our color object
svg_node.style.fill = col.fill; // reapply the correct styles
svg_node.style.stroke = col.stroke;
// return the new markup
return serializer.serializeToString(svg_node).replace('xmlns="http://www.w3.org/2000/svg', '');
}
return svg;
}
// Make an SVG object out of the fabric canvas
var SVG = canvas.toSVG(null, reviveColors);
document.getElementById('svgHolder').innerHTML = SVG;
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.12/fabric.js"></script>
<div id="canvasHolder" style="border: 3px solid black;">
<!-- beware canvas tag can't be self-closing -->
<canvas id="canvasElement" width="400" height="200"></canvas>
</div>
<div id="svgHolder" style="border: 3px solid blue;">