Skip to content

Commit

Permalink
Do not load undefine objects in group/canvas array and fix gradients (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
asturur authored May 19, 2019
1 parent f77e485 commit f9f4f28
Show file tree
Hide file tree
Showing 12 changed files with 246 additions and 56 deletions.
28 changes: 20 additions & 8 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ addons:
- libgif-dev
- libpng-dev
- libpango1.0-dev
- libjpeg-dev
- libjpeg8-dev
- librsvg2-dev
# libcairo2-dev is preinstalled
- libcairo2-dev
- g++-4.9

stages:
- Linting and Building
- Unit Tests
- Visual Tests
- Extra OS

cache:
directories:
Expand All @@ -31,9 +34,8 @@ matrix:
jobs:
fast_finish: true
allow_failures:
- env: CANFAIL=TRUE
- env: LAUNCHER=Node CANFAIL=TRUE
- env: LAUNCHER=Firefox CANFAIL=TRUE
- env: LAUNCHER=Chrome CANFAIL=TRUE
include:
- stage: Linting and Building
env: STEP=LINT
Expand Down Expand Up @@ -70,8 +72,8 @@ jobs:
- stage: Unit Tests
node_js: "8"
- stage: Visual Tests
env: LAUNCHER=Node
node_js: "8"
env: LAUNCHER=Node CANFAIL=TRUE
node_js: "10"
script: npm run build:fast && npm run test:visual
- stage: Visual Tests
env: LAUNCHER=Chrome
Expand All @@ -85,7 +87,17 @@ jobs:
addons:
apt:
packages: # avoid installing packages

- stage: Extra OS
env: CANFAIL=TRUE
node_js: "10"
script: npm run build:fast && npm run test:visual
before_install: brew upgrade giflib && brew install pkg-config cairo pango libpng jpeg librsvg
os: osx
- stage: Extra OS
env: CANFAIL=TRUE
node_js: "10"
script: npm run build:fast && npm run test:visual
os: windows
script: npm run build:fast && npm run test

dist: trusty
dist: xenial
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@
"testem:ci": "testem ci"
},
"optionalDependencies": {
"canvas": "^2.3.0",
"jsdom": "13.1.0"
"canvas": "^2.5.0",
"jsdom": "15.1.0"
},
"devDependencies": {
"eslint": "4.18.x",
Expand Down
10 changes: 6 additions & 4 deletions src/gradient.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,13 @@
* @param {Object} object Object to create a gradient for
* @return {String} SVG representation of an gradient (linear/radial)
*/
toSVG: function(object) {
var coords = clone(this.coords, true), i, len,
toSVG: function(object, options) {
var coords = clone(this.coords, true), i, len, options = options || {},
markup, commonAttributes, colorStops = clone(this.colorStops, true),
needsSwap = coords.r1 > coords.r2,
transform = this.gradientTransform ? this.gradientTransform.concat() : fabric.iMatrix.concat(),
offsetX = object.width / 2 - this.offsetX, offsetY = object.height / 2 - this.offsetY;
offsetX = object.width / 2 - this.offsetX, offsetY = object.height / 2 - this.offsetY,
withViewport = !!options.additionalTransform;
// colorStops must be sorted ascending
colorStops.sort(function(a, b) {
return a.offset - b.offset;
Expand All @@ -191,7 +192,8 @@

commonAttributes = 'id="SVGID_' + this.id +
'" gradientUnits="userSpaceOnUse"';
commonAttributes += ' gradientTransform="matrix(' + transform.join(' ') + ')" ';
commonAttributes += ' gradientTransform="' + (withViewport ?
options.additionalTransform + ' ' : '') + fabric.util.matrixToSVG(transform) + '" ';

if (this.type === 'linear') {
markup = [
Expand Down
12 changes: 5 additions & 7 deletions src/mixins/object.svg_export.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,8 @@
*/
getSvgTransform: function(full, additionalTransform) {
var transform = full ? this.calcTransformMatrix() : this.calcOwnMatrix(),
svgTransform = transform.map(function(value) {
return toFixed(value, fabric.Object.NUM_FRACTION_DIGITS);
}).join(' ');
return 'transform="matrix(' + svgTransform + ')' +
svgTransform = 'transform="' + fabric.util.matrixToSVG(transform);
return svgTransform +
(additionalTransform || '') + this.getSvgTransformMatrix() + '" ';
},

Expand All @@ -147,7 +145,7 @@
* @return {String}
*/
getSvgTransformMatrix: function() {
return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ')' : '';
return this.transformMatrix ? ' ' + fabric.util.matrixToSVG(this.transformMatrix) : '';
},

_setSVGBg: function(textBgRects) {
Expand Down Expand Up @@ -244,10 +242,10 @@
].join('');
objectMarkup[index] = commonPieces;
if (this.fill && this.fill.toLive) {
markup.push(this.fill.toSVG(this, false));
markup.push(this.fill.toSVG(this));
}
if (this.stroke && this.stroke.toLive) {
markup.push(this.stroke.toSVG(this, false));
markup.push(this.stroke.toSVG(this));
}
if (this.shadow) {
markup.push(this.shadow.toSVG(this));
Expand Down
78 changes: 51 additions & 27 deletions src/static_canvas.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -996,27 +996,41 @@
* @param {string} property 'background' or 'overlay'
*/
_renderBackgroundOrOverlay: function(ctx, property) {
var object = this[property + 'Color'], v;
if (object) {
ctx.fillStyle = object.toLive
? object.toLive(ctx, this)
: object;

ctx.fillRect(
object.offsetX || 0,
object.offsetY || 0,
this.width,
this.height);
var fill = this[property + 'Color'], object = this[property + 'Image'],
v = this.viewportTransform, needsVpt = this[property + 'Vpt'];
if (!fill && !object) {
return;
}
if (fill) {
ctx.save();
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(this.width, 0);
ctx.lineTo(this.width, this.height);
ctx.lineTo(0, this.height);
ctx.closePath();
ctx.fillStyle = fill.toLive
? fill.toLive(ctx, this)
: fill;
if (needsVpt) {
ctx.transform(
v[0], v[1], v[2], v[3],
v[4] + (fill.offsetX || 0),
v[5] + (fill.offsetY || 0)
);
}
var m = fill.gradientTransform || fill.patternTransform;
m && ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
ctx.fill();
ctx.restore();
}
object = this[property + 'Image'];
if (object) {
if (this[property + 'Vpt']) {
v = this.viewportTransform;
ctx.save();
ctx.save();
if (needsVpt) {
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
}
object.render(ctx);
this[property + 'Vpt'] && ctx.restore();
ctx.restore();
}
},

Expand Down Expand Up @@ -1298,13 +1312,13 @@
if (this.clipPath) {
markup.push('<g clip-path="url(#' + this.clipPath.clipPathId + ')" >\n');
}
this._setSVGBgOverlayColor(markup, 'backgroundColor');
this._setSVGBgOverlayColor(markup, 'background');
this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
this._setSVGObjects(markup, reviver);
if (this.clipPath) {
markup.push('</g>\n');
}
this._setSVGBgOverlayColor(markup, 'overlayColor');
this._setSVGBgOverlayColor(markup, 'overlay');
this._setSVGBgOverlayImage(markup, 'overlayImage', reviver);

markup.push('</svg>');
Expand Down Expand Up @@ -1388,10 +1402,18 @@
*/
createSVGRefElementsMarkup: function() {
var _this = this,
markup = ['backgroundColor', 'overlayColor'].map(function(prop) {
var fill = _this[prop];
markup = ['background', 'overlay'].map(function(prop) {
var fill = _this[prop + 'Color'];
if (fill && fill.toLive) {
return fill.toSVG(_this, false);
var shouldTransform = _this[prop + 'Vpt'], vpt = _this.viewportTransform,
object = {
width: _this.width / (shouldTransform ? vpt[0] : 1),
height: _this.height / (shouldTransform ? vpt[3] : 1)
};
return fill.toSVG(
object,
{ additionalTransform: shouldTransform ? fabric.util.matrixToSVG(vpt) : '' }
);
}
});
return markup.join('');
Expand Down Expand Up @@ -1488,16 +1510,18 @@
* @private
*/
_setSVGBgOverlayColor: function(markup, property) {
var filler = this[property], vpt = this.viewportTransform, finalWidth = this.width / vpt[0],
finalHeight = this.height / vpt[3];
var filler = this[property + 'Color'], vpt = this.viewportTransform, finalWidth = this.width,
finalHeight = this.height;
if (!filler) {
return;
}
if (filler.toLive) {
var repeat = filler.repeat;
var repeat = filler.repeat, iVpt = fabric.util.invertTransform(vpt), shouldInvert = this[property + 'Vpt'],
additionalTransform = shouldInvert ? fabric.util.matrixToSVG(iVpt) : '';
markup.push(
'<rect transform="translate(', finalWidth / 2, ',', finalHeight / 2, ')"',
' x="', filler.offsetX - finalWidth / 2, '" y="', filler.offsetY - finalHeight / 2, '" ',
'<rect transform="' + additionalTransform + ' translate(', finalWidth / 2, ',', finalHeight / 2, ')"',
' x="', filler.offsetX - finalWidth / 2,
'" y="', filler.offsetY - finalHeight / 2, '" ',
'width="',
(repeat === 'repeat-y' || repeat === 'no-repeat'
? filler.source.width
Expand All @@ -1513,7 +1537,7 @@
else {
markup.push(
'<rect x="0" y="0" width="100%" height="100%" ',
'fill="', this[property], '"',
'fill="', filler, '"',
'></rect>\n'
);
}
Expand Down
26 changes: 21 additions & 5 deletions src/util/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -410,16 +410,19 @@
enlivenObjects: function(objects, callback, namespace, reviver) {
objects = objects || [];

var enlivenedObjects = [],
numLoadedObjects = 0,
numTotalObjects = objects.length;

function onLoaded() {
if (++numLoadedObjects === numTotalObjects) {
callback && callback(enlivenedObjects);
callback && callback(enlivenedObjects.filter(function(obj) {
// filter out undefined objects (objects that gave error)
return obj;
}));
}
}

var enlivenedObjects = [],
numLoadedObjects = 0,
numTotalObjects = objects.length;

if (!numTotalObjects) {
callback && callback(enlivenedObjects);
return;
Expand Down Expand Up @@ -851,6 +854,19 @@

findScaleToCover: function(source, destination) {
return Math.max(destination.width / source.width, destination.height / source.height);
},

/**
* given an array of 6 number returns something like `"matrix(...numbers)"`
* @memberOf fabric.util
* @param {Array} trasnform an array with 6 numbers
* @return {String} transform matrix for svg
* @return {Object.y} Limited dimensions by Y
*/
matrixToSVG: function(transform) {
return 'matrix(' + transform.map(function(value) {
return fabric.util.toFixed(value, fabric.Object.NUM_FRACTION_DIGITS);
}).join(' ') + ')';
}
};
})(typeof exports !== 'undefined' ? exports : this);
27 changes: 25 additions & 2 deletions test/unit/canvas_static.js
Original file line number Diff line number Diff line change
Expand Up @@ -1826,7 +1826,30 @@
},
});
var svg = canvas2.toSVG();
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="300" height="150" viewBox="0 0 300 150" xml:space="preserve">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n<linearGradient id=\"SVGID_0\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"matrix(1 0 0 1 -150 -75)\" x1=\"0\" y1=\"0\" x2=\"300\" y2=\"0\">\n<stop offset="0%" style="stop-color:black;"/>\n<stop offset="100%" style="stop-color:white;"/>\n</linearGradient>\n</defs>\n<rect transform="translate(150,75)" x="-150" y="-75" width="300" height="150" fill="url(#SVGID_0)"></rect>\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="300" height="150" viewBox="0 0 300 150" xml:space="preserve">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n<linearGradient id=\"SVGID_0\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"matrix(1 0 0 1 0 0) matrix(1 0 0 1 -150 -75)\" x1=\"0\" y1=\"0\" x2=\"300\" y2=\"0\">\n<stop offset="0%" style="stop-color:black;"/>\n<stop offset="100%" style="stop-color:white;"/>\n</linearGradient>\n</defs>\n<rect transform="matrix(1 0 0 1 0 0) translate(150,75)" x="-150" y="-75" width="300" height="150" fill="url(#SVGID_0)"></rect>\n</svg>';
assert.equal(svg, expectedSVG, 'svg is as expected');
});

QUnit.test('toSVG with background gradient and transforms', function(assert) {
fabric.Object.__uid = 0;
var canvas2 = new fabric.StaticCanvas();
canvas2.viewportTransform = [1, 2, 3, 4, 5, 6];
canvas2.backgroundColor = new fabric.Gradient({
type: 'linear',
gradientTransform: [0.2, 0.3, 0.4, 0.5, -3, -5],
colorStops: [
{ offset: 0, color: 'black' },
{ offset: 1, color: 'white' },
],
coords: {
x1: 0,
x2: 300,
y1: 0,
y2: 0,
},
});
var svg = canvas2.toSVG();
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=\"300\" height=\"150\" viewBox=\"-5 -1.5 300 37.5\" xml:space=\"preserve\">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n<linearGradient id=\"SVGID_0\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"matrix(1 2 3 4 5 6) matrix(0.2 0.3 0.4 0.5 -153 -23.75)\" x1=\"0\" y1=\"0\" x2=\"300\" y2=\"0\">\n<stop offset=\"0%\" style=\"stop-color:black;\"/>\n<stop offset=\"100%\" style=\"stop-color:white;\"/>\n</linearGradient>\n</defs>\n<rect transform=\"matrix(-2 1 1.5 -0.5 1 -2) translate(150,75)\" x=\"-150\" y=\"-75\" width=\"300\" height=\"150\" fill=\"url(#SVGID_0)\"></rect>\n</svg>';
assert.equal(svg, expectedSVG, 'svg is as expected');
});

Expand All @@ -1838,7 +1861,7 @@
repeat: 'repeat',
});
var svg = canvas2.toSVG();
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="300" height="150" viewBox="0 0 300 150" xml:space="preserve">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n<pattern id="SVGID_0" x="0" y="0" width="0" height="0">\n<image x="0" y="0" width="0" height="0" xlink:href=""></image>\n</pattern>\n</defs>\n<rect transform="translate(150,75)" x="-150" y="-75" width="300" height="150" fill="url(#SVGID_0)"></rect>\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="300" height="150" viewBox="0 0 300 150" xml:space="preserve">\n<desc>Created with Fabric.js ' + fabric.version + '</desc>\n<defs>\n<pattern id="SVGID_0" x="0" y="0" width="0" height="0">\n<image x="0" y="0" width="0" height="0" xlink:href=""></image>\n</pattern>\n</defs>\n<rect transform="matrix(1 0 0 1 0 0) translate(150,75)" x="-150" y="-75" width="300" height="150" fill="url(#SVGID_0)"></rect>\n</svg>';
assert.equal(svg, expectedSVG, 'svg is as expected');
});

Expand Down
2 changes: 1 addition & 1 deletion test/unit/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
}

var IMG_SRC = fabric.isLikelyNode ? ('file://' + require('path').join(__dirname + '/../fixtures/test_image.gif')) : getAbsolutePath('../fixtures/test_image.gif'),
IMG_SRC_REL = fabric.isLikelyNode ? ('file://' + require('path').join(__dirname + '/../fixtures/test_image.gif')) : '/fixtures/test_image.gif',
IMG_SRC_REL = fabric.isLikelyNode ? ('file://' + require('path').join(__dirname + '/../fixtures/test_image.gif')) : '../fixtures/test_image.gif',
IMG_WIDTH = 276,
IMG_HEIGHT = 110;

Expand Down
Loading

0 comments on commit f9f4f28

Please sign in to comment.