diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7068d957488..a14a5d85d4d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## [2.6.0]
+- Fix: avoid ie11 to throw on weird draw images [#5428](https://github.com/fabricjs/fabric.js/pull/5428)
+- Fix: a rare case of invisible clipPath [#5477](https://github.com/fabricjs/fabric.js/pull/5477)
+- Fix: testability of code under node when webgl is involved [#5478](https://github.com/fabricjs/fabric.js/pull/5478)
+- Add: Grapeheme text wrapping for Textbox (Textbox.splitByGrapheme) [#5479](https://github.com/fabricjs/fabric.js/pull/5479)
+- Add: fabric.Object.toCanvasElement [#5481](https://github.com/fabricjs/fabric.js/pull/5481)
+
## [2.5.0]
- Fix: textbox transform report newScaleX and newScaleY values [#5464](https://github.com/fabricjs/fabric.js/pull/5464)
- Fix: export of svg and gradient with transforms [#5456](https://github.com/fabricjs/fabric.js/pull/5456)
diff --git a/HEADER.js b/HEADER.js
index 181ae26ec56..d447dd87cdf 100644
--- a/HEADER.js
+++ b/HEADER.js
@@ -1,6 +1,6 @@
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
-var fabric = fabric || { version: '2.5.0' };
+var fabric = fabric || { version: '2.6.0' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
diff --git a/dist/fabric.js b/dist/fabric.js
index a63c005e447..85fc9bd879c 100644
--- a/dist/fabric.js
+++ b/dist/fabric.js
@@ -1,7 +1,7 @@
/* build: `node build.js modules=ALL exclude=gestures,accessors requirejs minifier=uglifyjs` */
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
-var fabric = fabric || { version: '2.5.0' };
+var fabric = fabric || { version: '2.6.0' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
@@ -1174,6 +1174,7 @@ fabric.CommonMethods = {
/**
* Creates a canvas element that is a copy of another and is also painted
+ * @param {CanvasElement} canvas to copy size and content of
* @static
* @memberOf fabric.util
* @return {CanvasElement} initialized canvas element
@@ -1186,6 +1187,19 @@ fabric.CommonMethods = {
return newCanvas;
},
+ /**
+ * since 2.6.0 moved from canvas instance to utility.
+ * @param {CanvasElement} canvasEl to copy size and content of
+ * @param {String} format 'jpeg' or 'png', in some browsers 'webp' is ok too
+ * @param {Number} quality <= 1 and > 0
+ * @static
+ * @memberOf fabric.util
+ * @return {String} data url
+ */
+ toDataURL: function(canvasEl, format, quality) {
+ return canvasEl.toDataURL('image/' + format, quality);
+ },
+
/**
* Creates image element (works on client and node)
* @static
@@ -8328,7 +8342,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
* (either those of HTMLCanvasElement itself, or rendering context)
*
* @param {String} methodName Method to check support for;
- * Could be one of "getImageData", "toDataURL", "toDataURLWithQuality" or "setLineDash"
+ * Could be one of "setLineDash"
* @return {Boolean | null} `true` if method is supported (or at least exists),
* `null` if canvas element or context can not be initialized
*/
@@ -8346,23 +8360,9 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
switch (methodName) {
- case 'getImageData':
- return typeof ctx.getImageData !== 'undefined';
-
case 'setLineDash':
return typeof ctx.setLineDash !== 'undefined';
- case 'toDataURL':
- return typeof el.toDataURL !== 'undefined';
-
- case 'toDataURLWithQuality':
- try {
- el.toDataURL('image/jpeg', 0);
- return true;
- }
- catch (e) { }
- return false;
-
default:
return null;
}
@@ -12074,9 +12074,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
(function () {
-
- var supportQuality = fabric.StaticCanvas.supports('toDataURLWithQuality');
-
fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
/**
@@ -12118,7 +12115,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
quality = options.quality || 1,
multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? this.getRetinaScaling() : 1),
canvasEl = this.toCanvasElement(multiplier, options);
- return this.__toDataURL(canvasEl, format, quality);
+ return fabric.util.toDataURL(canvasEl, format, quality);
},
/**
@@ -12168,17 +12165,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
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(canvasEl, format, quality) {
- return supportQuality
- ? canvasEl.toDataURL('image/' + format, quality)
- : canvasEl.toDataURL('image/' + format);
- }
});
})();
@@ -13586,8 +13572,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
drawObject: function(ctx, forClipping) {
-
+ var originalFill = this.fill, originalStroke = this.stroke;
if (forClipping) {
+ this.fill = 'black';
+ this.stroke = '';
this._setClippingProperties(ctx);
}
else {
@@ -13597,6 +13585,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
}
this._render(ctx);
this._drawClipPath(ctx);
+ this.fill = originalFill;
+ this.stroke = originalStroke;
},
_drawClipPath: function(ctx) {
@@ -13951,6 +13941,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Creates an instance of fabric.Image out of an object
+ * could make use of both toDataUrl or toCanvasElement.
* @param {Function} callback callback, invoked with an instance as a first argument
* @param {Object} [options] for clone as image, passed to toDataURL
* @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
@@ -13966,20 +13957,16 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @return {fabric.Object} thisArg
*/
cloneAsImage: function(callback, options) {
- var dataUrl = this.toDataURL(options);
- fabric.util.loadImage(dataUrl, function(img) {
- if (callback) {
- callback(new fabric.Image(img));
- }
- });
+ var canvasEl = this.toCanvasElement(options);
+ if (callback) {
+ callback(new fabric.Image(canvasEl));
+ }
return this;
},
/**
- * Converts an object into a data-url-like string
+ * Converts an object into a HTMLCanvas element
* @param {Object} options Options object
- * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
- * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
* @param {Number} [options.multiplier=1] Multiplier to scale by
* @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
* @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
@@ -13990,11 +13977,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2
* @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
*/
- toDataURL: function(options) {
+ toCanvasElement: function(options) {
options || (options = { });
var utils = fabric.util, origParams = utils.saveObjectTransform(this),
- originalShadow = this.shadow, abs = Math.abs;
+ originalShadow = this.shadow, abs = Math.abs,
+ multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? fabric.devicePixelRatio : 1);
if (options.withoutTransform) {
utils.resetObjectTransform(this);
@@ -14020,7 +14008,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
el.width += el.width % 2 ? 2 - el.width % 2 : 0;
el.height += el.height % 2 ? 2 - el.height % 2 : 0;
var canvas = new fabric.StaticCanvas(el, {
- enableRetinaScaling: options.enableRetinaScaling,
+ enableRetinaScaling: false,
renderOnAddRemove: false,
skipOffscreen: false,
});
@@ -14031,10 +14019,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
var originalCanvas = this.canvas;
canvas.add(this);
- var data = canvas.toDataURL(options);
+ var canvasEl = canvas.toCanvasElement(multiplier || 1, options);
this.shadow = originalShadow;
- this.set(origParams).setCoords();
this.canvas = originalCanvas;
+ this.set(origParams).setCoords();
// canvas.dispose will call image.dispose that will nullify the elements
// since this canvas is a simple element for the process, we remove references
// to objects in this way in order to avoid object trashing.
@@ -14042,7 +14030,27 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
canvas.dispose();
canvas = null;
- return data;
+ return canvasEl;
+ },
+
+ /**
+ * Converts an object into a data-url-like string
+ * @param {Object} options Options object
+ * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
+ * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
+ * @param {Number} [options.multiplier=1] Multiplier to scale by
+ * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
+ * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
+ * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
+ * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
+ * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4
+ * @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4
+ * @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2
+ * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
+ */
+ toDataURL: function(options) {
+ options || (options = { });
+ return fabric.util.toDataURL(this.toCanvasElement(options), options.format || 'png', options.quality || 1);
},
/**
@@ -19931,14 +19939,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
},
_renderFill: function(ctx) {
- var w = this.width, h = this.height, sW = w * this._filterScalingX, sH = h * this._filterScalingY,
- x = -w / 2, y = -h / 2, elementToDraw = this._element;
- elementToDraw && ctx.drawImage(elementToDraw,
- this.cropX * this._filterScalingX,
- this.cropY * this._filterScalingY,
- sW,
- sH,
- x, y, w, h);
+ var elementToDraw = this._element,
+ w = this.width, h = this.height,
+ sW = Math.min(elementToDraw.naturalWidth || elementToDraw.width, w * this._filterScalingX),
+ sH = Math.min(elementToDraw.naturalHeight || elementToDraw.height, h * this._filterScalingY),
+ x = -w / 2, y = -h / 2,
+ sX = Math.max(0, this.cropX * this._filterScalingX),
+ sY = Math.max(0, this.cropY * this._filterScalingY);
+
+ elementToDraw && ctx.drawImage(elementToDraw, sX, sY, sW, sH, x, y, w, h);
},
/**
@@ -20465,51 +20474,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return pipelineState;
},
- /**
- * The same as the applyFilter method but with additional logging of WebGL
- * errors.
- */
- applyFiltersDebug: function(filters, source, width, height, targetCanvas, cacheKey) {
- // The following code is useful when debugging a specific issue but adds ~10x slowdown.
- var gl = this.gl;
- var ret = this.applyFilters(filters, source, width, height, targetCanvas, cacheKey);
- var glError = gl.getError();
- if (glError !== gl.NO_ERROR) {
- var errorString = this.glErrorToString(gl, glError);
- var error = new Error('WebGL Error ' + errorString);
- error.glErrorCode = glError;
- throw error;
- }
- return ret;
- },
-
- glErrorToString: function(context, errorCode) {
- if (!context) {
- return 'Context undefined for error code: ' + errorCode;
- }
- else if (typeof errorCode !== 'number') {
- return 'Error code is not a number';
- }
- switch (errorCode) {
- case context.NO_ERROR:
- return 'NO_ERROR';
- case context.INVALID_ENUM:
- return 'INVALID_ENUM';
- case context.INVALID_VALUE:
- return 'INVALID_VALUE';
- case context.INVALID_OPERATION:
- return 'INVALID_OPERATION';
- case context.INVALID_FRAMEBUFFER_OPERATION:
- return 'INVALID_FRAMEBUFFER_OPERATION';
- case context.OUT_OF_MEMORY:
- return 'OUT_OF_MEMORY';
- case context.CONTEXT_LOST_WEBGL:
- return 'CONTEXT_LOST_WEBGL';
- default:
- return 'UNKNOWN_ERROR';
- }
- },
-
/**
* Detach event listeners, remove references, and clean up caches.
*/
@@ -20603,9 +20567,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
if (this.gpuInfo) {
return this.gpuInfo;
}
- var gl = this.gl;
+ var gl = this.gl, gpuInfo = { renderer: '', vendor: '' };
+ if (!gl) {
+ return gpuInfo;
+ }
var ext = gl.getExtension('WEBGL_debug_renderer_info');
- var gpuInfo = { renderer: '', vendor: '' };
if (ext) {
var renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
var vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
@@ -28693,6 +28659,20 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
_dimensionAffectingProps: fabric.Text.prototype._dimensionAffectingProps.concat('width'),
+ /**
+ * Use this regular expression to split strings in breakable lines
+ * @private
+ */
+ _wordJoiners: /[ \t\r\u200B\u200C]/,
+
+ /**
+ * Use this boolean property in order to split strings that have no white space concept.
+ * this is a cheap way to help with chinese/japaense
+ * @type Boolean
+ * @since 2.6.0
+ */
+ splitByGrapheme: false,
+
/**
* Unlike superclass's version of this function, Textbox does not update
* its width.
@@ -28929,19 +28909,20 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* to.
*/
_wrapLine: function(_line, lineIndex, desiredWidth, reservedSpace) {
- var lineWidth = 0,
- graphemeLines = [],
- line = [],
+ var lineWidth = 0,
+ splitByGrapheme = this.splitByGrapheme,
+ graphemeLines = [],
+ line = [],
// spaces in different languges?
- words = _line.split(this._reSpaceAndTab),
- word = '',
- offset = 0,
- infix = ' ',
- wordWidth = 0,
- infixWidth = 0,
+ words = splitByGrapheme ? fabric.util.string.graphemeSplit(_line) : _line.split(this._wordJoiners),
+ word = '',
+ offset = 0,
+ infix = splitByGrapheme ? '' : ' ',
+ wordWidth = 0,
+ infixWidth = 0,
largestWordWidth = 0,
lineJustStarted = true,
- additionalSpace = this._getWidthOfCharSpacing(),
+ additionalSpace = splitByGrapheme ? 0 : this._getWidthOfCharSpacing(),
reservedSpace = reservedSpace || 0;
desiredWidth -= reservedSpace;
@@ -29035,7 +29016,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @return {Object} object representation of an instance
*/
toObject: function(propertiesToInclude) {
- return this.callSuper('toObject', ['minWidth'].concat(propertiesToInclude));
+ return this.callSuper('toObject', ['minWidth', 'splitByGrapheme'].concat(propertiesToInclude));
}
});
diff --git a/dist/fabric.min.js b/dist/fabric.min.js
index e1611eeff9b..628ee718052 100644
--- a/dist/fabric.min.js
+++ b/dist/fabric.min.js
@@ -1 +1 @@
-var fabric=fabric||{version:"2.4.6"};function resizeCanvasIfNeeded(t){var e=t.targetCanvas,i=e.width,r=e.height,n=t.destinationWidth,s=t.destinationHeight;i===n&&r===s||(e.width=n,e.height=s)}function copyGLTo2DDrawImage(t,e){var i=t.canvas,r=e.targetCanvas,n=r.getContext("2d");n.translate(0,r.height),n.scale(1,-1);var s=i.height-r.height;n.drawImage(i,0,s,r.width,r.height,0,0,r.width,r.height)}function copyGLTo2DPutImageData(t,e){var i=e.targetCanvas.getContext("2d"),r=e.destinationWidth,n=e.destinationHeight,s=r*n*4,o=new Uint8Array(this.imageBuffer,0,s),a=new Uint8ClampedArray(this.imageBuffer,0,s);t.readPixels(0,0,r,n,t.RGBA,t.UNSIGNED_BYTE,o);var h=new ImageData(a,r,n);i.putImageData(h,0,0)}"undefined"!=typeof exports?exports.fabric=fabric:"function"==typeof define&&define.amd&&define([],function(){return fabric}),"undefined"!=typeof document&&"undefined"!=typeof window?(fabric.document=document,fabric.window=window):(fabric.document=require("jsdom").jsdom(decodeURIComponent("%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E"),{features:{FetchExternalResources:["img"]}}),fabric.jsdomImplForWrapper=require("jsdom/lib/jsdom/living/generated/utils").implForWrapper,fabric.nodeCanvas=require("jsdom/lib/jsdom/utils").Canvas,fabric.window=fabric.document.defaultView,DOMParser=require("xmldom").DOMParser),fabric.isTouchSupported="ontouchstart"in fabric.window||"ontouchstart"in fabric.document||fabric.window&&fabric.window.navigator&&0/g,">")},graphemeSplit:function(t){var e,i=0,r=[];for(i=0;i/i,"")));if(!e||!e.documentElement)return n&&n(null),!1;C.parseSVGDocument(e.documentElement,function(t,e,i,r){n&&n(t,e,i,r)},i,r)}})},loadSVGFromString:function(t,n,e,i){var r;if(t=t.trim(),"undefined"!=typeof DOMParser){var s=new DOMParser;s&&s.parseFromString&&(r=s.parseFromString(t,"text/xml"))}else C.window.ActiveXObject&&((r=new ActiveXObject("Microsoft.XMLDOM")).async="false",r.loadXML(t.replace(//i,"")));C.parseSVGDocument(r.documentElement,function(t,e,i,r){n(t,e,i,r)},e,i)}})}("undefined"!=typeof exports?exports:this),fabric.ElementsParser=function(t,e,i,r,n){this.elements=t,this.callback=e,this.options=i,this.reviver=r,this.svgUid=i&&i.svgUid||0,this.parsingOptions=n,this.regexUrl=/^url\(['"]?#([^'"]+)['"]?\)/g},function(t){t.parse=function(){this.instances=new Array(this.elements.length),this.numElements=this.elements.length,this.createObjects()},t.createObjects=function(){var i=this;this.elements.forEach(function(t,e){t.setAttribute("svgUid",i.svgUid),i.createObject(t,e)})},t.findTag=function(t){return fabric[fabric.util.string.capitalize(t.tagName.replace("svg:",""))]},t.createObject=function(t,e){var i=this.findTag(t);if(i&&i.fromElement)try{i.fromElement(t,this.createCallback(e,t),this.options)}catch(t){fabric.log(t)}else this.checkIfDone()},t.createCallback=function(i,r){var n=this;return function(t){var e;n.resolveGradient(t,"fill"),n.resolveGradient(t,"stroke"),t instanceof fabric.Image&&t._originalElement&&(e=t.parsePreserveAspectRatioAttribute(r)),t._removeTransformMatrix(e),n.resolveClipPath(t),n.reviver&&n.reviver(r,t),n.instances[i]=t,n.checkIfDone()}},t.extractPropertyDefinition=function(t,e,i){var r=t[e];if(/^url\(/.test(r)){var n=this.regexUrl.exec(r)[1];return this.regexUrl.lastIndex=0,fabric[i][this.svgUid][n]}},t.resolveGradient=function(t,e){var i=this.extractPropertyDefinition(t,e,"gradientDefs");i&&t.set(e,fabric.Gradient.fromElement(i,t))},t.createClipPathCallback=function(t,e){return function(t){t._removeTransformMatrix(),t.fillRule=t.clipRule,e.push(t)}},t.resolveClipPath=function(t){var e,i,r,n,s=this.extractPropertyDefinition(t,"clipPath","clipPaths");if(s){r=[],i=fabric.util.invertTransform(t.calcTransformMatrix());for(var o=0;ot.x&&this.y>t.y},gte:function(t){return this.x>=t.x&&this.y>=t.y},lerp:function(t,e){return void 0===e&&(e=.5),e=Math.max(Math.min(1,e),0),new i(this.x+(t.x-this.x)*e,this.y+(t.y-this.y)*e)},distanceFrom:function(t){var e=this.x-t.x,i=this.y-t.y;return Math.sqrt(e*e+i*i)},midPointFrom:function(t){return this.lerp(t)},min:function(t){return new i(Math.min(this.x,t.x),Math.min(this.y,t.y))},max:function(t){return new i(Math.max(this.x,t.x),Math.max(this.y,t.y))},toString:function(){return this.x+","+this.y},setXY:function(t,e){return this.x=t,this.y=e,this},setX:function(t){return this.x=t,this},setY:function(t){return this.y=t,this},setFromPoint:function(t){return this.x=t.x,this.y=t.y,this},swap:function(t){var e=this.x,i=this.y;this.x=t.x,this.y=t.y,t.x=e,t.y=i},clone:function(){return new i(this.x,this.y)}}}("undefined"!=typeof exports?exports:this),function(t){"use strict";var f=t.fabric||(t.fabric={});function d(t){this.status=t,this.points=[]}f.Intersection?f.warn("fabric.Intersection is already defined"):(f.Intersection=d,f.Intersection.prototype={constructor:d,appendPoint:function(t){return this.points.push(t),this},appendPoints:function(t){return this.points=this.points.concat(t),this}},f.Intersection.intersectLineLine=function(t,e,i,r){var n,s=(r.x-i.x)*(t.y-i.y)-(r.y-i.y)*(t.x-i.x),o=(e.x-t.x)*(t.y-i.y)-(e.y-t.y)*(t.x-i.x),a=(r.y-i.y)*(e.x-t.x)-(r.x-i.x)*(e.y-t.y);if(0!==a){var h=s/a,c=o/a;0<=h&&h<=1&&0<=c&&c<=1?(n=new d("Intersection")).appendPoint(new f.Point(t.x+h*(e.x-t.x),t.y+h*(e.y-t.y))):n=new d}else n=new d(0===s||0===o?"Coincident":"Parallel");return n},f.Intersection.intersectLinePolygon=function(t,e,i){var r,n,s,o,a=new d,h=i.length;for(o=0;os.r2,h=t.width/2,c=t.height/2;for(var l in o.sort(function(t,e){return t.offset-e.offset}),"path"===t.type&&(h-=t.pathOffset.x,c-=t.pathOffset.y),s)"x1"===l||"x2"===l?s[l]+=this.offsetX-h:"y1"!==l&&"y2"!==l||(s[l]+=this.offsetY-c);if(n='id="SVGID_'+this.id+'" gradientUnits="userSpaceOnUse"',this.gradientTransform&&(n+=' gradientTransform="matrix('+this.gradientTransform.join(" ")+')" '),"linear"===this.type?r=["\n']:"radial"===this.type&&(r=["\n']),"radial"===this.type){if(a)for((o=o.concat()).reverse(),e=0,i=o.length;e\n')}return r.push("linear"===this.type?"\n":"\n"),r.join("")},toLive:function(t){var e,i,r,n=fabric.util.object.clone(this.coords);if(this.type){for("linear"===this.type?e=t.createLinearGradient(n.x1,n.y1,n.x2,n.y2):"radial"===this.type&&(e=t.createRadialGradient(n.x1,n.y1,n.r1,n.x2,n.y2,n.r2)),i=0,r=this.colorStops.length;i\n\n\n'},setOptions:function(t){for(var e in t)this[e]=t[e]},toLive:function(t){var e="function"==typeof this.source?this.source():this.source;if(!e)return"";if(void 0!==e.src){if(!e.complete)return"";if(0===e.naturalWidth||0===e.naturalHeight)return""}return t.createPattern(e,this.repeat)}})}(),function(t){"use strict";var o=t.fabric||(t.fabric={}),a=o.util.toFixed;o.Shadow?o.warn("fabric.Shadow is already defined."):(o.Shadow=o.util.createClass({color:"rgb(0,0,0)",blur:0,offsetX:0,offsetY:0,affectStroke:!1,includeDefaultValues:!0,initialize:function(t){for(var e in"string"==typeof t&&(t=this._parseShadow(t)),t)this[e]=t[e];this.id=o.Object.__uid++},_parseShadow:function(t){var e=t.trim(),i=o.Shadow.reOffsetsAndBlur.exec(e)||[];return{color:(e.replace(o.Shadow.reOffsetsAndBlur,"")||"rgb(0,0,0)").trim(),offsetX:parseInt(i[1],10)||0,offsetY:parseInt(i[2],10)||0,blur:parseInt(i[3],10)||0}},toString:function(){return[this.offsetX,this.offsetY,this.blur,this.color].join("px ")},toSVG:function(t){var e=40,i=40,r=o.Object.NUM_FRACTION_DIGITS,n=o.util.rotateVector({x:this.offsetX,y:this.offsetY},o.util.degreesToRadians(-t.angle)),s=new o.Color(this.color);return t.width&&t.height&&(e=100*a((Math.abs(n.x)+this.blur)/t.width,r)+20,i=100*a((Math.abs(n.y)+this.blur)/t.height,r)+20),t.flipX&&(n.x*=-1),t.flipY&&(n.y*=-1),'\n\t\n\t\n\t\n\t\n\t\n\t\t\n\t\t\n\t\n\n'},toObject:function(){if(this.includeDefaultValues)return{color:this.color,blur:this.blur,offsetX:this.offsetX,offsetY:this.offsetY,affectStroke:this.affectStroke};var e={},i=o.Shadow.prototype;return["color","blur","offsetX","offsetY","affectStroke"].forEach(function(t){this[t]!==i[t]&&(e[t]=this[t])},this),e}}),o.Shadow.reOffsetsAndBlur=/(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/)}("undefined"!=typeof exports?exports:this),function(){"use strict";if(fabric.StaticCanvas)fabric.warn("fabric.StaticCanvas is already defined.");else{var n=fabric.util.object.extend,t=fabric.util.getElementOffset,c=fabric.util.removeFromArray,a=fabric.util.toFixed,s=fabric.util.transformPoint,o=fabric.util.invertTransform,i=fabric.util.getNodeCanvas,r=fabric.util.createCanvasElement,e=new Error("Could not initialize `canvas` element");fabric.StaticCanvas=fabric.util.createClass(fabric.CommonMethods,{initialize:function(t,e){e||(e={}),this.renderAndResetBound=this.renderAndReset.bind(this),this.requestRenderAllBound=this.requestRenderAll.bind(this),this._initStatic(t,e)},backgroundColor:"",backgroundImage:null,overlayColor:"",overlayImage:null,includeDefaultValues:!0,stateful:!1,renderOnAddRemove:!0,clipTo:null,controlsAboveOverlay:!1,allowTouchScrolling:!1,imageSmoothingEnabled:!0,viewportTransform:fabric.iMatrix.concat(),backgroundVpt:!0,overlayVpt:!0,onBeforeScaleRotate:function(){},enableRetinaScaling:!0,vptCoords:{},skipOffscreen:!0,clipPath:void 0,_initStatic:function(t,e){var i=this.requestRenderAllBound;this._objects=[],this._createLowerCanvas(t),this._initOptions(e),this._setImageSmoothing(),this.interactive||this._initRetinaScaling(),e.overlayImage&&this.setOverlayImage(e.overlayImage,i),e.backgroundImage&&this.setBackgroundImage(e.backgroundImage,i),e.backgroundColor&&this.setBackgroundColor(e.backgroundColor,i),e.overlayColor&&this.setOverlayColor(e.overlayColor,i),this.calcOffset()},_isRetinaScaling:function(){return 1!==fabric.devicePixelRatio&&this.enableRetinaScaling},getRetinaScaling:function(){return this._isRetinaScaling()?fabric.devicePixelRatio:1},_initRetinaScaling:function(){this._isRetinaScaling()&&(this.lowerCanvasEl.setAttribute("width",this.width*fabric.devicePixelRatio),this.lowerCanvasEl.setAttribute("height",this.height*fabric.devicePixelRatio),this.contextContainer.scale(fabric.devicePixelRatio,fabric.devicePixelRatio))},calcOffset:function(){return this._offset=t(this.lowerCanvasEl),this},setOverlayImage:function(t,e,i){return this.__setBgOverlayImage("overlayImage",t,e,i)},setBackgroundImage:function(t,e,i){return this.__setBgOverlayImage("backgroundImage",t,e,i)},setOverlayColor:function(t,e){return this.__setBgOverlayColor("overlayColor",t,e)},setBackgroundColor:function(t,e){return this.__setBgOverlayColor("backgroundColor",t,e)},_setImageSmoothing:function(){var t=this.getContext();t.imageSmoothingEnabled=t.imageSmoothingEnabled||t.webkitImageSmoothingEnabled||t.mozImageSmoothingEnabled||t.msImageSmoothingEnabled||t.oImageSmoothingEnabled,t.imageSmoothingEnabled=this.imageSmoothingEnabled},__setBgOverlayImage:function(i,t,r,n){return"string"==typeof t?fabric.util.loadImage(t,function(t){if(t){var e=new fabric.Image(t,n);(this[i]=e).canvas=this}r&&r(t)},this,n&&n.crossOrigin):(n&&t.setOptions(n),(this[i]=t)&&(t.canvas=this),r&&r(t)),this},__setBgOverlayColor:function(t,e,i){return this[t]=e,this._initGradient(e,t),this._initPattern(e,t,i),this},_createCanvasElement:function(){var t=r();if(!t)throw e;if(t.style||(t.style={}),void 0===t.getContext)throw e;return t},_initOptions:function(t){var e=this.lowerCanvasEl;this._setOptions(t),this.width=this.width||parseInt(e.width,10)||0,this.height=this.height||parseInt(e.height,10)||0,this.lowerCanvasEl.style&&(e.width=this.width,e.height=this.height,e.style.width=this.width+"px",e.style.height=this.height+"px",this.viewportTransform=this.viewportTransform.slice())},_createLowerCanvas:function(t){t&&t.getContext?this.lowerCanvasEl=t:this.lowerCanvasEl=fabric.util.getById(t)||this._createCanvasElement(),fabric.util.addClass(this.lowerCanvasEl,"lower-canvas"),this.interactive&&this._applyCanvasStyle(this.lowerCanvasEl),this.contextContainer=this.lowerCanvasEl.getContext("2d")},getWidth:function(){return this.width},getHeight:function(){return this.height},setWidth:function(t,e){return this.setDimensions({width:t},e)},setHeight:function(t,e){return this.setDimensions({height:t},e)},setDimensions:function(t,e){var i;for(var r in e=e||{},t)i=t[r],e.cssOnly||(this._setBackstoreDimension(r,t[r]),i+="px",this.hasLostContext=!0),e.backstoreOnly||this._setCssDimension(r,i);return this._isCurrentlyDrawing&&this.freeDrawingBrush&&this.freeDrawingBrush._setBrushStyles(),this._initRetinaScaling(),this._setImageSmoothing(),this.calcOffset(),e.cssOnly||this.requestRenderAll(),this},_setBackstoreDimension:function(t,e){return this.lowerCanvasEl[t]=e,this.upperCanvasEl&&(this.upperCanvasEl[t]=e),this.cacheCanvasEl&&(this.cacheCanvasEl[t]=e),this[t]=e,this},_setCssDimension:function(t,e){return this.lowerCanvasEl.style[t]=e,this.upperCanvasEl&&(this.upperCanvasEl.style[t]=e),this.wrapperEl&&(this.wrapperEl.style[t]=e),this},getZoom:function(){return this.viewportTransform[0]},setViewportTransform:function(t){var e,i,r,n=this._activeObject;for(this.viewportTransform=t,i=0,r=this._objects.length;i\n'),this._setSVGBgOverlayColor(i,"backgroundColor"),this._setSVGBgOverlayImage(i,"backgroundImage",e),this._setSVGObjects(i,e),this.clipPath&&i.push("\n"),this._setSVGBgOverlayColor(i,"overlayColor"),this._setSVGBgOverlayImage(i,"overlayImage",e),i.push(""),i.join("")},_setSVGPreamble:function(t,e){e.suppressPreamble||t.push('\n','\n')},_setSVGHeader:function(t,e){var i,r=e.width||this.width,n=e.height||this.height,s='viewBox="0 0 '+this.width+" "+this.height+'" ',o=fabric.Object.NUM_FRACTION_DIGITS;e.viewBox?s='viewBox="'+e.viewBox.x+" "+e.viewBox.y+" "+e.viewBox.width+" "+e.viewBox.height+'" ':this.svgViewportTransformation&&(i=this.viewportTransform,s='viewBox="'+a(-i[4]/i[0],o)+" "+a(-i[5]/i[3],o)+" "+a(this.width/i[0],o)+" "+a(this.height/i[3],o)+'" '),t.push(""),i.join("")},_setSVGPreamble:function(t,e){e.suppressPreamble||t.push('\n','\n')},_setSVGHeader:function(t,e){var i,r=e.width||this.width,n=e.height||this.height,s='viewBox="0 0 '+this.width+" "+this.height+'" ',o=fabric.Object.NUM_FRACTION_DIGITS;e.viewBox?s='viewBox="'+e.viewBox.x+" "+e.viewBox.y+" "+e.viewBox.width+" "+e.viewBox.height+'" ':this.svgViewportTransformation&&(i=this.viewportTransform,s='viewBox="'+a(-i[4]/i[0],o)+" "+a(-i[5]/i[3],o)+" "+a(this.width/i[0],o)+" "+a(this.height/i[3],o)+'" '),t.push("