I have fabricjs canvas with image, which I loaded by fabric.Image.fromURL() method.
I need export it to SVG, but I want to export image as embedded source in base64, not as a link.
How can I do it? Is it any way to load image as a source not as a link?
Example code:
Loading image:
fabric.Image.fromURL('./assets/people.jpg', function (image) {
image.set({
left: 10,
top: 30
});
canvas.add(image);
};
Exporting to SVG:
canvas.toSVG();
In exported SVG I have:
<image xlink:href="http://localhost:8383/app/assets/people.png" />
But I want it in base64 like:
<image xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAe8AAAKuCAYAAACIZZSZAAAAAXNSR0IArs4c6QAAAARnQU1BAACx(...)" />
There is no option but you can easily achieve it like this:
Image has a method that is called 'getSvgSrc';
override it like this:
fabric.Image.prototype.getSvgSrc = function() {
return this.toDataURLforSVG();
};
fabric.Image.prototype.toDataURLforSVG = function(options) {
var el = fabric.util.createCanvasElement();
el.width = this._element.naturalWidth || this._element.width;
el.height = this._element.naturalHeight || this._element.height;
el.getContext("2d").drawImage(this._element, 0, 0);
var data = el.toDataURL(options);
return data;
};
In this way you should be able to obtain an integrated image for the svg.
you can use the toSVG method :
rasterizeSVG () {
let w = window.open('')
w.document.write(this.canvas.toSVG())
return 'data:image/svg+xml;utf8,' + encodeURIComponent(this.canvas.toSVG())
}
or :
rasterize() {
if (!fabric.Canvas.supports('toDataURL')) {
alert('This browser doesn\'t provide means to serialize canvas to an image');
}
else {
var image = new Image();
image.src = this.canvas.toDataURL('png')
var w = window.open("");
w.document.write(image.outerHTML);
}
}
Related
I'm currently trying to do some Javascript work in Laserfiche forms which requires me to save the base64 data for an image in a separate text area, and feed that data back into the image to allow the canvas to be turned into an image in which I can save into the system.
The issue is I'm trying to have a background image in which the user can draw on (in this case, a vehicle that they can draw a circle on to indicate where the damage is). I am using sketch.js to allow the drawing part of the task.
From what I've read is that the background CSS cannot be saved into the canvas. That's fine but how do I pass the background image when I'm already grabbing the base64 data and passing that back into my canvas?
The saveimage class belongs to the text area and the imagefinish belong to the checkbox that they mark when the image is ready
html
<div class="demo" id="colors_demo">
<div class="tools">
Marker
Eraser
Download
</div>
<canvas id="colors_sketch" width="750" height="500" style="border:2px solid #000000 ; background: url(http://localhost/forms/img/vanImage.jpg"></canvas>
</div>
<input name="Field11" id="Field11-0" type="checkbox" value="isLocked" vo="e" data-parsley-class-handler="#Field11" data-parsley-errors-container="#Field11" data-parsley-multiple="Field11">
<textarea id="Field13" name="Field13" aria-labelledby="Field13" class="cf-medium" rows="3" vo="e" data-parsley-id="28"></textarea>
Javascript
//submitted form
if ($('[name=IsLocked]').val() == 'True') {
var myCanvas = document.getElementById('colors_sketch');
var ctx = myCanvas.getContext('2d');
var img = new Image;
img.onload = function() {
ctx.drawImage(this, 0, 0, myCanvas.width, myCanvas.height);
}
img.src = $('.saveimage .cf-field').text();
}
else {
//fill form
//$.getScript('http://localhost/Forms/js/sketch.js', function() {
$.getScript('https://rawgit.com/intridea/sketch.js/gh-pages/lib/sketch.js', function() {
//script is loaded and executed put your dependent JS here
$.each(['#f00', '#ff0', '#0f0', '#0ff', '#00f', '#f0f', '#000', '#fff'], function() {
$('#colors_demo .tools').append("<a href='#colors_sketch' data-color='" + this + "' style='width: 10px; background: " + this + ";'></a> ");
});
$.each([3, 5, 10, 15], function() {
$('#colors_demo .tools').append("<a href='#colors_sketch' data-size='" + this + "' style='background: #ccc'>" + this + "</a> ");
});
//$('#colors_sketch').sketch();
$('#colors_sketch').sketch({defaultColor: "#000"});
});
$('.imagefinish input').change(function(){
if(this.checked) {
var myCanvas = document.getElementById('colors_sketch');
$('.saveimage textarea').val(myCanvas.toDataURL());
}
});
}
I was able to add an image by adding a variable for my image path
var image = 'http://localhost/forms/img/vanImage.jpg'
and I also added the two lines to my onload for the style of "myCanvas". I have a feeling that this solution will only work because of how Laserfiches forms software works but the marked answer is also correct.
img.onload = function() {
ctx.drawImage(this, 0, 0, myCanvas.width, myCanvas.height);
myCanvas.style.backgroundRepeat = "no-repeat";
myCanvas.style.backgroundImage = 'url('+image+')'
}
img.src = $('.saveimage .cf-field').text();
}
Load the background image on page load, or an appropriate time and when the client is ready, draw the background onto the canvas behind the user content using ctx.globalCompositeOperation = "destination-over";
Create an image to hold the background.
const backgroundImage = new Image();
backgroundImage.src = "http://localhost/forms/img/vanImage.jpg";
You need to ensure that the image has loaded when the client clicks ready, incase it has not loaded you can set a callback that will call back the ready function if needed.
var backgroundLoadedCallback = null;
backgroundImage.onload = function(){
if( typeof backgroundLoadedCallback === "function"){
backgroundLoadedCallback();
}
}
Then create the canvas -> textarea function
function canvasToTextarea(){
const myCanvas = document.getElementById('colors_sketch');
const ctx = myCanvas.getContext("2d");
ctx.globalCompositeOperation = "destination-over"; // make sure the background go under the drawn pixels
// draw and fit background to the canvas
ctx.drawImage(backgroundImage,0,0,ctx.canvas.width,ctx.canvas.height);
// then convert to URL for textarea
$('.saveimage textarea').val(myCanvas.toDataURL());
}
In the checked function
$('.imagefinish input').change(function(){
if(this.checked) {
if(backgroundImage.complete) { // image has loaded so all good
canvasToTextarea(); // put canvas data URL to textarea
} else { // background has not yet loaded (or could be an error you will have to deal with that as well
// set the callback to the canvasToTextarea function
backgroundLoadedCallback = canvasToTextarea;
// when the background has loaded the canvas and background
// will be moved to the textarea as a data URL
}
}
});
Or modify sketch.js
Below is the draw function from sketch.js (and it`s very old school)
Sketch.prototype.redraw = function() {
var sketch;
this.el.width = this.canvas.width();
this.context = this.el.getContext('2d');
sketch = this;
$.each(this.actions, function() {
if (this.tool) {
return $.sketch.tools[this.tool].draw.call(sketch, this);
}
});
if (this.painting && this.action) {
return $.sketch.tools[this.action.tool].draw.call(sketch, this.action);
}
};
Just replace it with the following. You dont need to modify the sketch.js file justy overwrite the redraw Prototype
In your code load the background and set the new redraw
const backgroundImage = new Image();
backgroundImage.src = "http://localhost/forms/img/vanImage.jpg";
// replace Sketch.js redraw function
Sketch.prototype.redraw = function(){
var sketch;
// dont need the next line use clear instead
// this.el.width = this.canvas.width();
const ctx = this.context = this.el.getContext('2d');
// clear canvas
this.context.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// If backgroundimage complete draw it first
if(backgroundImage && backgroundImage.complete){
ctx.drawImage(backgroundImage,0,0,ctx.canvas.width,ctx.canvas.height);
}
// back to the original code. :P
sketch = this;
$.each(this.actions, function() {
if (this.tool) {
return $.sketch.tools[this.tool].draw.call(sketch, this);
}
});
if (this.painting && this.action) {
return $.sketch.tools[this.action.tool].draw.call(sketch, this.action);
}
}
I am using blueimp/jQuery-File-Upload plugin to upload and resize the images on client side on my website.
I need a way to get origonal uploaded image dimensions before it is resized to my provided values in imageMaxWidth and imageMaxHeight options.
Any help or guidance will be great, or i can edit the library and add this hook if it is not already there to return original file data.
Thanks in advance.
Just discovered something without adding custom hooks:
Just override add method and calculate image dimensions from file object as such:
add: function(e, data) {
var img = new Image;
var _URL = window.URL || window.webkitURL;
img.onload = function() {
sizes =
width: this.width
height: this.height
};
img.src = _URL.createObjectURL(data.files[0]);
//below code is to let the library further process file e.g resize etc
var $this = $(this);
return data.process(function() {
return $this.fileupload('process', data);
}).done(function() {
return data.submit();
});
}
And here you have it, in the sizes hash.
Cheers!
I'm using a plugin (dom-to-image) to generate a SVG content from a div.
It returns me a dataURL like this:
data image/xml, charset utf-8, <svg...
If a put this on a <img src the image is shown to normally.
The intent is to grab this dataURL, convert it to base64 so I can save it as an image.png on a mobile app.
Is it possible?
I tryied this solution https://stackoverflow.com/a/28450879/1691609
But coudn't get to work.
The console fire an error about the dataUrl
TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.
==== UPDATE :: PROBLEM EXPLANATION/HISTORY ====
I'm using Ionic Framework, so my project is an mobile app.
The dom-to-image is already working cause right now, its rendering a PNG through toPng function.
The problem is the raster PNG is a blurry.
So I thought: Maybe the SVG will have better quality.
And it IS!! Its 100% perfect, actually.
On Ionic, I'm using 2 step procedure to save the image.
After get the PNG generated by the dom-to-img(base64) dataURL, I convert it to a Blob and then save into device.
This is working, but the final result, as I said, is blurry.
Then with SVG maybe it will be more "high quality" per say.
So, in order to do "minimal" change on a process that s already working :D I just need to convert an SVG into base64 dataURL....
Or, as some of you explained to me, into something else, like canvas...
I don't know any much :/
===
Sorry for the long post, and I really, really thank your help guys!!
EDIT COUPLE OF YARS LATER
Use JS fiddle for a working example: https://jsfiddle.net/msb42ojx/
Note, if you don't own DOM content (images), and those images don't have CORS enabled for everyone (Access-Control-Allow-Origin header), canvas cant render those images
I'm not trying to find out why is your case not working, here is how I did when I had something similar to do:
get the image sourcce (dom-to-image result)
set up a canvas with that image inside (using the image source)
download the image from canvas in whatever image you like: png, jpeg whatever
by the way you can resize the image to a standard format
document.getElementById('mydownload').onclick= function(){
var wrapper = document.getElementById('wrapper');
//dom to image
domtoimage.toSvg(wrapper).then(function (svgDataUrl) {
//download function
downloadPNGFromAnyImageSrc(svgDataUrl);
});
}
function downloadPNGFromAnyImageSrc(src)
{
//recreate the image with src recieved
var img = new Image;
//when image loaded (to know width and height)
img.onload = function(){
//drow image inside a canvas
var canvas = convertImageToCanvas(img);
//get image/png from convas
var pngImage = convertCanvasToImage(canvas);
//download
var anchor = document.createElement('a');
anchor.setAttribute('href', pngImage.src);
anchor.setAttribute('download', 'image.png');
anchor.click();
};
img.src = src;
// Converts image to canvas; returns new canvas element
function convertImageToCanvas(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
return canvas;
}
// Converts canvas to an image
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
}
#wrapper{
background: red;
color: blue;
}
<script src="https://rawgit.com/tsayen/dom-to-image/master/src/dom-to-image.js"></script>
<button id='mydownload'>Download DomToImage</button>
<div id="wrapper">
<img src="http://i.imgur.com/6GvKdxY.jpg"/>
<div> DUDE IS WORKING</div>
<img src="http://i.imgur.com/6GvKdxY.jpg"/>
</div>
I translated #SilentTremor's solution into React/JS-Class:
class SVGToPNG {
static convert = function (src) {
var img = new Image();
img.onload = function () {
var canvas = SVGToPNG.#convertImageToCanvas(img);
var pngImage = SVGToPNG.#convertCanvasToImage(canvas);
var anchor = document.createElement("a");
anchor.setAttribute("href", pngImage.src);
anchor.setAttribute("download", "image.png");
anchor.click();
};
img.src = src;
};
static #convertImageToCanvas = function (image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
return canvas;
};
static #convertCanvasToImage = function (canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
};
}
export default SVGToPNG;
Usage:
let dataUrl = someCanvas.toDataURL("image/svg+xml");
SVGToPNG.convert(dataUrl);
I need to write a new HTML file from a string using file system, I'm using Cordova 2.4.0. . That HTML would have some images loaded from a local folder, so because I need just one file (a HTML without png/jpg images alongside it) I'm trying to encode this images and apply them through CSS as background images (The CSS code is embeded in the same String, future HTML file). Well, the way I make these modifications to the string is by using functions returns. For example:
padding-top: 5%; background-image: url(' + agregaImagenLocal('../img/ESTELAR.png') + '); background-color: white;
The function "agregaImagenLocal(pathToLocalImage)" :
function agregaImagenLocal(pathToLocalFile) {
var canvas = document.getElementById('canvasOculto');
var imagen = new Image(150,100);
canvas.width = imagen.width;
canvas.height = imagen.height;
var contextoCanvas = canvas.getContext('2d');
imagen.onload = function () {
contextoCanvas.drawImage(imagen, 0, 0);
urlImagenLocal = canvas.toDataURL();
}
imagen.src = pathToLocalFile;
return urlImagenLocal //???????
}
I was doing this without the "onload" event, so it returned a blank image. But with this function inside the "onload" I don't know how to return the base64 encoded image to the first function.
This is the function to convert an Image (by file path) to Base64:
function convertImgToBase64(url, callback, outputFormat){
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
var img = new Image;
img.crossOrigin = 'Anonymous';
img.onload = function(){
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img,0,0);
var dataURL = canvas.toDataURL(outputFormat || 'image/png');
callback.call(this, dataURL);
// Clean up
canvas = null;
};
img.src = url;
}
Once the conversion is made, you can use jQuery to set the background CSS using the Base64 code.
var imageUrl = ""; //Your Local Image Path HERE
convertImgToBase64(imageUrl, function(base64Img){
$('.output').css("background-image", base64Img);
}
This will set the source of an element with the class 'output' to the base64 image.
I'm trying to use Fabric js to generate some images with text on it. But for some reason the positioning of the text in the generated image isn't consistent with the one in the canvas. Here's the image as it looks like in the canvas:
But the generated image looks like this:
The size I gave to the canvas is 881x640 and its the same with the output size I gave to the image.
Here's what I'm currently doing in my code.
I'm adding the background image into the canvas from the URL. But before I do, I first resize the image to be the same size as the canvas:
fabric.Image.fromURL(image_source, function(oImg) {
oImg.width = canvas.width;
oImg.height = canvas.height;
canvas.add(oImg);
});
Then I'm sending the stringified result of the canvas.toJSON() call to the node js server to generate the image using AJAX:
$.post('http://host:1212', {'canvas': JSON.stringify(canvas.toJSON())});
In the server side I then load the objects and create the image:
var out = fs.createWriteStream(__dirname + '/uploads/' + file_name);
var body = '';
request.on('data', function(data){
body += data;
});
request.on('end', function(){
var request_params = qs.parse(body);
//set to same size as the canvas on the client side
var canvas = fabric.createCanvasForNode(881, 640);
canvas.loadFromJSON(request_params.canvas, function(){
canvas.renderAll();
var stream = canvas.createPNGStream();
stream.on('data', function(chunk){
out.write(chunk);
});
stream.on('end', function(){
//close filestream
out.end();
});
});
Any ideas where could have I gone wrong?
Maybe you should try to add canvas.calcOffset() :
fabric.Image.fromURL(image_source, function(oImg) {
oImg.width = canvas.width;
oImg.height = canvas.height;
canvas.add(oImg);
//HERE
canvas.calcOffset()
});
I was having the same issue. It turns out there is a bug within fabric.js for rendering on node. Read about it here: https://github.com/kangax/fabric.js/issues/1754.
To fix this, look at node_modules/fabric/dist/fabric.js - line 19376
_getLeftOffset: function() {
// if (fabric.isLikelyNode) {
// return 0;
// }
return -this.width / 2;
}
Just like above, comment out the if(fabcir.isLikelyNode)...
This fixed it for me.