Download Canvas as Png in Fabric.Js Giving Network Error

Download Canvas as PNG in fabric.js giving network Error

The problem you are facing is not directly related to fabricjs, (nor canvas and not even javascript btw), but comes from limitations some browsers (including Chrome) does have on the maximum length for the src attribute of an Anchor Element (<a>) with the donwload attribute.

When this limit is reached, then the only thing you've got is this uncatchable "Network Error" in the console ; the download as failed, but you as the developer can't know about it.

As proposed in this (you-refused-to-mark-as-) duplicate, the solution is either to directly get a Blob when available (for canvas, you may call its toBlob() method, or to first convert your dataURI to a Blob, and then create an object URL from this Blob.

Fabricjs doesn't seem to have a toBlob function implemented yet, so in your exact case, you'll have to do the later.

You can find many scripts to convert dataURI to Blob, one is available in MDN's polyfill to Canvas.toBlob() method.

Then it would look like this :

// edited from https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#Polyfill
function dataURIToBlob(dataURI, callback) {
var binStr = atob(dataURI.split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len);

for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}

callback(new Blob([arr]));
}

var callback = function(blob) {
var a = document.createElement('a');
a.download = fileName;
a.innerHTML = 'download';
// the string representation of the object URL will be small enough to workaround the browser's limitations
a.href = URL.createObjectURL(blob);
// you must revoke the object URL,
// but since we can't know when the download occured, we have to attach it on the click handler..
a.onclick = function() {
// ..and to wait a frame
requestAnimationFrame(function() {
URL.revokeObjectURL(a.href);
});
a.removeAttribute('href')
};
};

dataURIToBlob(yourDataURL, callback);

Saving a fabricjs canvas as PNG is not working

Without needing to inspect the code, if you get this error:

Uncaught DOMException: Failed to execute 'toDataURL' on 

'HTMLCanvasElement': Tainted canvases may not be exported.
at n.__toDataURL (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111706)
at n.__toDataURLWithMultiplier (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111496)
at n.toDataURL (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111060)
at downloadFabric (file://portrait.js:207:21)
at HTMLButtonElement.onclick (file://index.html:62:114)

your canvas is tainted.
That means you loaded some images without specifying the crossOrigin attribute.

Fabricjs does support crossOrigin option fromUrl as a third argument after the callback ( please check docs here http://fabricjs.com/docs/fabric.Image.html#.fromURL )

in the function replaceImage you have to take care of it by yourself before setting the .src property to the image.

    // oImgObj bread and butter, kudos @grunt
function replaceImage(oImgObj, imgUrl) {
if (!isImageLoaded) return; //return if initial image not loaded
var imgElem = oImgObj._element; //reference to actual image element
imgElem.src = imgUrl; //set image source
imgElem.onload = () => canvas.renderAll(); //render on image load
}

// initialize default frame (light brown wood oval)
fabric.Image.fromURL('https://i.imgur.com/DrzSWSa.png', function(img) {
isImageLoaded = true;
oImg = img.set({
selectable: false,
evented: false,
}).scale(0.5);
canvas.add(oImg).renderAll();
canvas.sendToBack(oImg);
});

How save image with canvas in fabric.js

Sample Image
Hello,
you have to create an image object (tshirt) with a text object that holds the message.

  1. to do that , load the image with fabric.Image.fromURL() function and inside the function , also create a text object that is going to show the tshirt message.
    so, your image and text belong to a group object.
  2. every time you want to load new text , you call the loadText function and you change the text object.
  3. i also added 4 buttons in order to manupulate up/down/left/right the text .
  4. you can export the canvas + image+ text into the function saveImg(),
    but on the jsfiddle you will get a security message for Tained canvases.
    that happens because on the example i load the image from another domain and the code runs on another domain, you can run that code on your web application with no problem at all.

  5. that is the code :

     var canvas = new fabric.Canvas('c');
    var scaleFactor=0.4

    canvas.backgroundColor = 'yellow';
    canvas.renderAll();
    var myImg = 'http://izy.urweb.eu/files/tshirt.jpg';

    fabric.Image.fromURL(myImg, function(myImg) {
    var img1 = myImg.scale(scaleFactor).set({ left: 0, top: 0 });

    var text = new fabric.Text('the_text_sample\nand more', {
    fontFamily: 'Arial',
    fontSize:20,
    });

    text.set("top",myImg.height*scaleFactor-myImg.height*scaleFactor+150);
    text.set("left",myImg.width*scaleFactor/2-text.width/2);

    var group = new fabric.Group([ img1,text ], { left: 10, top: 10 });
    canvas.add(group);
    });

    $('#loadText').on('click',loadText);
    $('#saveImg').on('click',saveImg);

    function loadText(){
    console.log($('#logo').val());
    canvas._objects[0]._objects[1].text = $('#logo').val();
    canvas.renderAll();
    }

    function saveImg(){
    console.log('export image');
    if (!fabric.Canvas.supports('toDataURL')) {
    alert('This browser doesn\'t provide means to serialize canvas to an image');
    }
    else {
    window.open(canvas.toDataURL('png'));
    }
    }

    $('#left').on('click',function(){
    canvas._objects[0]._objects[1].set('left',canvas._objects[0]._objects[1].left-1);
    canvas.renderAll();
    })

    $('#right').on('click',function(){
    canvas._objects[0]._objects[1].set('left',canvas._objects[0]._objects[1].left+1);
    canvas.renderAll();
    })

    $('#top').on('click',function(){
    canvas._objects[0]._objects[1].set('top',canvas._objects[0]._objects[1].top-1);
    canvas.renderAll();
    })

    $('#bottom').on('click',function(){
    canvas._objects[0]._objects[1].set('top',canvas._objects[0]._objects[1].top+1);
    canvas.renderAll();
    })
  6. that is the jsfiddle example: http://jsfiddle.net/tornado1979/zrazuhcq/1/

hope helps, good luck.

Failed - Network Error When trying to provide download in HTML5 using 'download' attribute

Apparently this is a Chrome issue with the data-URL getting too long. I'm still working through it myself, but there apparently are some solutions involving Blob-objects.

See here: Download Canvas as PNG in fabric.js giving network Error

And here: How to export JavaScript array info to csv (on client side)?

Downloading Canvas element to an image

The one way to save is exporting as an image... You already found this solution, and it's the best one i think ;)

    var canvas = document.getElementById("mycanvas");
var img = canvas.toDataURL("image/png");
document.write('<img src="'+img+'"/>');

You can use different image types. Change the mimetype in this function:

    canvas.toDataURL("image/jpeg");

An other way to save canvas data (into a PDF) is using the wkhtmltopdf Library

Cheers. Frank

frankneff.ch / @frankneff

Draw Image on Canvas and export print quality image

You should not style canvas directly with dimensions.
Set the canvas attribute width and height, without exceeding the screen dimensions:

<canvas width="500" height="400" ></canvas>

When exporting to image use

canvas.toDataURL({multiplier: 4});

This will give you a canvas 4 times bigger the one on the screen.

Styling the canvas with the style to reduce its diemension and having a bigger image with dataURL will make the overall experience worse.

The style squeeze the canvas to your eyes with something that fabricjs is not ready to understand. So you will not get normal sized controls.

//remove those
width:400px !important;
height:600px !important;

http://jsfiddle.net/Q3TMA/1043/

FabricJS - Tainted canvases may not be exported JS Error when run toDataURL

You need to add crossOrigin: 'anonymous' to the image element. Have added in fabric.Image.fromURL, or you need to use images from same server, or where crossOrigin is defined.

DEMO

fabric.ImageContainer = fabric.util.createClass(fabric.Rect, {  type: 'image-container',  initialize: function(options) {   options || (options = { });    options.content || (options.content = { });    options.content.angle = options.content.angle || 0;        this.callSuper('initialize', options);    this.set({      objectCaching: false,      ddpPreviousCenter: this.getCenterPoint()    });        this.on('scaling', function(el){      this.set({       width: this.width * this.scaleX,        height: this.height * this.scaleY,        scaleX: 1,        scaleY: 1      });      this.setCoords();      this._drawImage();    }.bind(this));        this.on('modified', function(el){    this._updateContentCoords();    }.bind(this));      this._drawImage();          },  _updateContentCoords: function(){    const ddpPreviousCenter = {...this.ddpPreviousCenter};      const content = {...this.content};      const shiftX = this.getCenterPoint().x - ddpPreviousCenter.x;      const shiftY = this.getCenterPoint().y - ddpPreviousCenter.y;
content.left += shiftX; content.top += shiftY; this.set({ ddpPreviousCenter: this.getCenterPoint(), content }); }, _drawImage: function() { const scaleFactor = 1; const imgSrc = [ 'https://picsum.photos/', this.content.width * scaleFactor, '/', this.content.height * scaleFactor ].join('');
fabric.Image.fromURL(imgSrc, function(img) { img.set({ left: this.content.left - this.left + this.content.width / 2, top: this.content.top - this.top + this.content.height / 2, scaleX: 1, scalY: 1, angle: this.content.angle, originX: 'center', originY: 'center', }); // img.scaleToWidth(this.content.width); const patternSourceCanvas = new fabric.StaticCanvas(); patternSourceCanvas.setDimensions({ width: this.width, height: this.height });
patternSourceCanvas.setBackgroundColor(this.backgroundColor); patternSourceCanvas.add(img); patternSourceCanvas.renderAll();
const pattern = new fabric.Pattern({ source: function() { return patternSourceCanvas.getElement(); }, repeat: 'no-repeat' }); this.set({ fill: pattern });
this.canvas.renderAll(); this.canvas.fire('image:pattern:loaded'); }.bind(this),{ crossOrigin: 'anonymous' }); }, toObject: function(options) { return fabric.util.object.extend(this.callSuper('toObject'), { ddpPreviousCenter: this.get('ddpPreviousCenter'), content: this.get('content'), });
// return fabric.util.object.extend(this.callSuper('toObject'), {}); }, fromObject: function(object, callback) { return fabric.Object._fromObject('ImageContainer', object, callback); }, _render: function(ctx) { this.callSuper('_render', ctx); }});
fabric.ImageContainer.__fromObject = function(object, callback, forceAsync) { if (!forceAsync) { fabric.util.enlivenPatterns([object.fill, object.stroke], function(patterns) { console.log(patterns); object.fill = patterns[0]; object.stroke = patterns[1]; var rect = new fabric.ImageContainer(object); callback && callback(rect); }); } else { var rect = new fabric.ImageContainer(object); callback && callback(rect); return rect; } };
// =========================================================================
let store;const canvas = new fabric.Canvas('paper');
const container = new fabric.ImageContainer({ left: 10, top: 10, width: 150, height: 150, backgroundColor: 'green', content: { left: 20, top: 20, width: 130, height: 130 }});
canvas.on('image:pattern:loaded', function(){ $('#img').attr('src', this.toDataURL());});
canvas.add(container);canvas.renderAll();
#paper {  border: solid 1px red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.2.3/fabric.js"></script><img id="image" /><canvas id="paper" width="400" height="200" style="border:1px solid #ccc"></canvas><img id="img" />

How to save an HTML5 Canvas as an image on a server?

Here is an example of how to achieve what you need:

  1. Draw something (taken from canvas tutorial)

<canvas id="myCanvas" width="578" height="200"></canvas>

<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

// begin custom shape
context.beginPath();
context.moveTo(170, 80);
context.bezierCurveTo(130, 100, 130, 150, 230, 150);
context.bezierCurveTo(250, 180, 320, 180, 340, 150);
context.bezierCurveTo(420, 150, 420, 120, 390, 100);
context.bezierCurveTo(430, 40, 370, 30, 340, 50);
context.bezierCurveTo(320, 5, 250, 20, 250, 50);
context.bezierCurveTo(200, 5, 150, 20, 170, 80);

// complete custom shape
context.closePath();
context.lineWidth = 5;
context.fillStyle = '#8ED6FF';
context.fill();
context.strokeStyle = 'blue';
context.stroke();
</script>


Related Topics



Leave a reply



Submit