Skip to content

Commit

Permalink
Create a different dataUrl (#5452)
Browse files Browse the repository at this point in the history
* changed toDataUrl

* ok not broken

* ok not broken

* fix

* missed the offscreen
  • Loading branch information
asturur authored Dec 30, 2018
1 parent 3f41626 commit a8bdd40
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 53 deletions.
82 changes: 38 additions & 44 deletions src/mixins/canvas_dataurl_exporter.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,70 +41,64 @@

var format = options.format || 'png',
quality = options.quality || 1,
multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? 1 : 1 / this.getRetinaScaling()),
cropping = {
left: options.left || 0,
top: options.top || 0,
width: options.width || 0,
height: options.height || 0,
};
return this.__toDataURLWithMultiplier(format, quality, cropping, multiplier);
multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? this.getRetinaScaling() : 1),
canvasEl = this.toCanvasElement(multiplier, options);
return this.__toDataURL(canvasEl, format, quality);
},

/**
* @private
* Create a new HTMLCanvas element painted with the current canvas content.
* No need to resize the actual one or repaint it.
* Will transfer object ownership to a new canvas, paint it, and set everything back.
* This is an intermediary step used to get to a dataUrl but also it is usefull to
* create quick image copies of a canvas without passing for the dataUrl string
* @param {Number} [multiplier] a zoom factor.
* @param {Object} [cropping] Cropping informations
* @param {Number} [cropping.left] Cropping left offset.
* @param {Number} [cropping.top] Cropping top offset.
* @param {Number} [cropping.width] Cropping width.
* @param {Number} [cropping.height] Cropping height.
*/
__toDataURLWithMultiplier: function(format, quality, cropping, multiplier) {

var origWidth = this.width,
origHeight = this.height,
scaledWidth = (cropping.width || this.width) * multiplier,
toCanvasElement: function(multiplier, cropping) {
multiplier = multiplier || 1;
cropping = cropping || { };
var scaledWidth = (cropping.width || this.width) * multiplier,
scaledHeight = (cropping.height || this.height) * multiplier,
zoom = this.getZoom(),
newZoom = zoom * multiplier,
vp = this.viewportTransform,
translateX = (vp[4] - cropping.left) * multiplier,
translateY = (vp[5] - cropping.top) * multiplier,
newVp = [newZoom, 0, 0, newZoom, translateX, translateY],
translateX = (vp[4] - (cropping.left || 0)) * multiplier,
translateY = (vp[5] - (cropping.top || 0)) * multiplier,
originalInteractive = this.interactive,
originalSkipOffScreen = this.skipOffscreen,
needsResize = origWidth !== scaledWidth || origHeight !== scaledHeight;

this.viewportTransform = newVp;
originalOffscreen = this.skipOffscreen,
originalContext = this.contextContainer,
newVp = [newZoom, 0, 0, newZoom, translateX, translateY],
canvasEl = fabric.util.createCanvasElement();
canvasEl.width = scaledWidth;
canvasEl.height = scaledHeight;
this.skipOffscreen = false;
// setting interactive to false avoid exporting controls
this.interactive = false;
if (needsResize) {
this.setDimensions({ width: scaledWidth, height: scaledHeight }, { backstoreOnly: true });
}
// call a renderAll to force sync update. This will cancel the scheduled requestRenderAll
// from setDimensions
this.viewportTransform = newVp;
this.contextContainer = canvasEl.getContext('2d');
// will be renderAllExport();
this.renderAll();
var data = this.__toDataURL(format, quality, cropping);
this.interactive = originalInteractive;
this.skipOffscreen = originalSkipOffScreen;
this.viewportTransform = vp;
//setDimensions with no option object is taking care of:
//this.width, this.height, this.requestRenderAll()
if (needsResize) {
this.setDimensions({ width: origWidth, height: origHeight }, { backstoreOnly: true });
}
this.renderAll();
return data;
this.skipOffscreen = originalOffscreen;
this.contextContainer = originalContext;
this.interactive = originalInteractive;
return canvasEl;
},

/**
* since 2.5.0 does not need to be on canvas instance anymore.
* leave it here for context;
* @private
*/
__toDataURL: function(format, quality) {

var canvasEl = this.contextContainer.canvas;
var data = supportQuality
__toDataURL: function(canvasEl, format, quality) {
return supportQuality
? canvasEl.toDataURL('image/' + format, quality)
: canvasEl.toDataURL('image/' + format);

return data;
},
}
});

})();
62 changes: 53 additions & 9 deletions test/unit/canvas_static.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
// var emptyImageCanvasData = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAH0CAYAAADL1t+KAAAH7ElEQVR4nO3VMQ0AMAzAsPInvYHoMS2yEeTLHADge/M6AADYM3QACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIuMjH4b7osLFBAAAAAElFTkSuQmCC";

var CANVAS_SVG = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="600" height="600" viewBox="0 0 600 600" xml:space="preserve">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n</defs>\n</svg>';
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="200" height="200" viewBox="0 0 200 200" xml:space="preserve">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n</defs>\n</svg>';

var CANVAS_SVG_VIEWBOX = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="600" height="600" viewBox="100 100 300 300" xml:space="preserve">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n</defs>\n</svg>';
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="200" height="200" viewBox="100 100 300 300" xml:space="preserve">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n</defs>\n</svg>';

var PATH_JSON = '{"version":"' + fabric.version + '","objects": [{"type": "path", "version":"' + fabric.version + '", "originX": "left", "originY": "top", "left": 268, "top": 266, "width": 51, "height": 49,' +
' "fill": "rgb(0,0,0)", "stroke": null, "strokeWidth": 1, "scaleX": 1, "scaleY": 1, ' +
Expand Down Expand Up @@ -143,8 +143,8 @@

// force creation of static canvas
// TODO: fix this
var canvas = this.canvas = new fabric.StaticCanvas(null, {enableRetinaScaling: false, width: 600, height: 600});
var canvas2 = this.canvas2 = new fabric.StaticCanvas(null, {enableRetinaScaling: false, width: 600, height: 600});
var canvas = this.canvas = new fabric.StaticCanvas(null, {enableRetinaScaling: false, width: 200, height: 200});
var canvas2 = this.canvas2 = new fabric.StaticCanvas(null, {enableRetinaScaling: false, width: 200, height: 200});


var lowerCanvasEl = canvas.lowerCanvasEl;
Expand All @@ -158,6 +158,8 @@
beforeEach: function() {
fabric.Object.__uid = 0;
canvas.clear();
canvas.setDimensions({ width: 200, heigth: 200 });
canvas2.setDimensions({ width: 200, heigth: 200 });
canvas.backgroundColor = fabric.StaticCanvas.prototype.backgroundColor;
canvas.backgroundImage = fabric.StaticCanvas.prototype.backgroundImage;
canvas.overlayColor = fabric.StaticCanvas.prototype.overlayColor;
Expand Down Expand Up @@ -454,6 +456,30 @@
assert.equal(canvas, canvas.renderAll());
});

// QUnit.test('setDimensions', function(assert) {
// assert.ok(typeof canvas.setDimensions === 'function');;
// canvas.setDimensions({ width: 4, height: 5 });
// assert.equal(canvas.getWidth(), 4);
// assert.equal(canvas.getHeight(), 5);
// assert.equal(canvas.lowerCanvasEl.style.width, '5px');
// assert.equal(canvas.lowerCanvasEl.style.height, '4px');
// });

QUnit.test('toCanvasElement', function(assert) {
assert.ok(typeof canvas.toCanvasElement === 'function');;
var canvasEl = canvas.toCanvasElement();
assert.equal(canvasEl.width, canvas.getWidth(), 'get a canvas of same width');
assert.equal(canvasEl.height, canvas.getHeight(), 'get a canvas of same height');
});

QUnit.test('toCanvasElement with multiplier', function(assert) {
assert.ok(typeof canvas.toCanvasElement === 'function');
var multiplier = 2;
var canvasEl = canvas.toCanvasElement(multiplier);
assert.equal(canvasEl.width, canvas.getWidth() * multiplier, 'get a canvas of multiplied width');
assert.equal(canvasEl.height, canvas.getHeight() * multiplier, 'get a canvas of multiplied height');
});

QUnit.test('toDataURL', function(assert) {
assert.ok(typeof canvas.toDataURL === 'function');
if (!fabric.Canvas.supports('toDataURL')) {
Expand Down Expand Up @@ -885,12 +911,12 @@
canvas.renderOnAddRemove = false;
canvas.backgroundImage = imageBG;
canvas.overlayImage = imageOL;
var expectedSVG = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" width=\"600\" height=\"600\" viewBox=\"0 0 600 600\" xml:space=\"preserve\">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n</defs>\n<g transform=\"matrix(1 0 0 1 0 0)\" >\n\t<image style=\"stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;\" xlink:href=\"\" x=\"0\" y=\"0\" width=\"0\" height=\"0\"></image>\n</g>\n<g transform=\"matrix(1 0 0 1 0 0)\" >\n\t<image style=\"stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;\" xlink:href=\"\" x=\"0\" y=\"0\" width=\"0\" height=\"0\"></image>\n</g>\n</svg>';
var expectedSVG = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" width=\"200\" height=\"200\" viewBox=\"0 0 200 200\" xml:space=\"preserve\">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n</defs>\n<g transform=\"matrix(1 0 0 1 0 0)\" >\n\t<image style=\"stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;\" xlink:href=\"\" x=\"0\" y=\"0\" width=\"0\" height=\"0\"></image>\n</g>\n<g transform=\"matrix(1 0 0 1 0 0)\" >\n\t<image style=\"stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;\" xlink:href=\"\" x=\"0\" y=\"0\" width=\"0\" height=\"0\"></image>\n</g>\n</svg>';
var svg1 = canvas.toSVG();
assert.equal(svg1, expectedSVG, 'svg with bg and overlay do not match');
imageBG.excludeFromExport = true;
imageOL.excludeFromExport = true;
var expectedSVG2 = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="600" height="600" viewBox="0 0 600 600" xml:space="preserve">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n</defs>\n</svg>';
var expectedSVG2 = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="200" height="200" viewBox="0 0 200 200" xml:space="preserve">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n</defs>\n</svg>';
var svg2 = canvas.toSVG();
assert.equal(svg2, expectedSVG2, 'svg without bg and overlay do not match');
canvas.backgroundImage = null;
Expand Down Expand Up @@ -1470,15 +1496,15 @@

QUnit.test('getSetWidth', function(assert) {
assert.ok(typeof canvas.getWidth === 'function');
assert.equal(canvas.getWidth(), 600);
assert.equal(canvas.getWidth(), 200);
assert.equal(canvas.setWidth(444), canvas, 'should be chainable');
assert.equal(canvas.getWidth(), 444);
assert.equal(canvas.lowerCanvasEl.style.width, 444 + 'px');
});

QUnit.test('getSetHeight', function(assert) {
assert.ok(typeof canvas.getHeight === 'function');
assert.equal(canvas.getHeight(), 600);
assert.equal(canvas.getHeight(), 200);
assert.equal(canvas.setHeight(765), canvas, 'should be chainable');
assert.equal(canvas.getHeight(), 765);
assert.equal(canvas.lowerCanvasEl.style.height, 765 + 'px');
Expand All @@ -1497,7 +1523,16 @@
canvas.setHeight('100%', { cssOnly: true });

assert.equal(canvas.lowerCanvasEl.style.height, '100%', 'Should be as the css only value');
assert.equal(canvas.getWidth(), 123, 'Should be as the none css only value');
assert.equal(canvas.getHeight(), 123, 'Should be as the none css only value');
});

QUnit.test('setDimensions css only', function(assert) {
canvas.setDimensions({ width: 200, height: 200 });
canvas.setDimensions({ width: '250px', height: '350px' }, { cssOnly: true });
assert.equal(canvas.lowerCanvasEl.style.width, '250px', 'Should be as none backstore only value + "px"');
assert.equal(canvas.lowerCanvasEl.style.height, '350px', 'Should be as none backstore only value + "px"');
assert.equal(canvas.getWidth(), 200, 'Should be as the backstore only value');
assert.equal(canvas.getHeight(), 200, 'Should be as the backstore only value');
});

QUnit.test('setWidth backstore only', function(assert) {
Expand All @@ -1516,6 +1551,15 @@
assert.equal(canvas.getHeight(), 500, 'Should be as the backstore only value');
});

QUnit.test('setDimensions backstore only', function(assert) {
canvas.setDimensions({ width: 200, height: 200 });
canvas.setDimensions({ width: 250, height: 350 }, { backstoreOnly: true });
assert.equal(canvas.lowerCanvasEl.style.width, 200 + 'px', 'Should be as none backstore only value + "px"');
assert.equal(canvas.lowerCanvasEl.style.height, 200 + 'px', 'Should be as none backstore only value + "px"');
assert.equal(canvas.getWidth(), 250, 'Should be as the backstore only value');
assert.equal(canvas.getHeight(), 350, 'Should be as the backstore only value');
});

QUnit.test('fxRemove', function(assert) {
var done = assert.async();
assert.ok(typeof canvas.fxRemove === 'function');
Expand Down

0 comments on commit a8bdd40

Please sign in to comment.