fabric 2.4.5 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.travis.yml CHANGED
@@ -75,10 +75,7 @@ jobs:
75
75
  - stage: Unit Tests
76
76
  env: CANVAS=canvas-prebuilt
77
77
  node_js: "8"
78
- cache:
79
- directories:
80
- - ''
81
- install: npm install && npm remove canvas && npm install canvas-prebuilt@^1.6.11
78
+ install: npm install && npm remove canvas && npm install canvas-prebuilt@^1.6.11 qunit@2.6.1
82
79
  addons:
83
80
  apt:
84
81
  packages: # avoid installing packages
@@ -88,14 +85,14 @@ jobs:
88
85
  script: npm run build:fast && npm run test:visual
89
86
  - stage: Visual Tests
90
87
  env: LAUNCHER=Chrome
91
- install: npm install testem@1.18.4 qunit@2.6.1
88
+ install: npm install testem@1.18.4 qunit@2.6.2
92
89
  script: npm run build:fast && testem ci --port 8080 -f testem-visual.json -l $LAUNCHER
93
90
  addons:
94
91
  apt:
95
92
  packages: # avoid installing packages
96
93
  - stage: Visual Tests
97
94
  env: LAUNCHER=Firefox
98
- install: npm install testem@1.18.4 qunit@2.6.1
95
+ install: npm install testem@1.18.4 qunit@2.6.2
99
96
  script: npm run build:fast && testem ci --port 8080 -f testem-visual.json -l $LAUNCHER
100
97
  addons:
101
98
  apt:
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.7.0]
4
+ - Add: strokeUniform property, avoid stroke scaling with paths [#5473](https://github.com/fabricjs/fabric.js/pull/5473)
5
+ - Fix: fix bug in image setSrc [#5502](https://github.com/fabricjs/fabric.js/pull/5502)
6
+ - Add: strokeUniform import/export svg [#5527](https://github.com/fabricjs/fabric.js/pull/5527)
7
+ - Fix: GraphemeSplit and toSvg for circle [#5544](https://github.com/fabricjs/fabric.js/pull/5544)
8
+ - Improvement: support running in a XML document [#5530](https://github.com/fabricjs/fabric.js/pull/5530)
9
+
10
+ ## [2.6.0]
11
+ - Fix: avoid ie11 to throw on weird draw images [#5428](https://github.com/fabricjs/fabric.js/pull/5428)
12
+ - Fix: a rare case of invisible clipPath [#5477](https://github.com/fabricjs/fabric.js/pull/5477)
13
+ - Fix: testability of code under node when webgl is involved [#5478](https://github.com/fabricjs/fabric.js/pull/5478)
14
+ - Add: Grapeheme text wrapping for Textbox (Textbox.splitByGrapheme) [#5479](https://github.com/fabricjs/fabric.js/pull/5479)
15
+ - Add: fabric.Object.toCanvasElement [#5481](https://github.com/fabricjs/fabric.js/pull/5481)
16
+
17
+ ## [2.5.0]
18
+ - Fix: textbox transform report newScaleX and newScaleY values [#5464](https://github.com/fabricjs/fabric.js/pull/5464)
19
+ - Fix: export of svg and gradient with transforms [#5456](https://github.com/fabricjs/fabric.js/pull/5456)
20
+ - Fix: detection of controls in perPixelTargetFind + cache [#5455](https://github.com/fabricjs/fabric.js/pull/5455)
21
+ - Add: added canvas.toCanvasElement method [#5452](https://github.com/fabricjs/fabric.js/pull/5452)
22
+
23
+ ## [2.4.6]
24
+ - Fix: unbreak the svg export broken in 2.4.5 [#5438](https://github.com/fabricjs/fabric.js/pull/5438)
25
+
3
26
  ## [2.4.5]
4
27
  - Fix: svg import/export for canvas+clipPath and letterspacing. [#5424](https://github.com/fabricjs/fabric.js/pull/5424)
5
28
  - Fix: avoid stroke dash from group selection to leak on upper canvas [#5392](https://github.com/fabricjs/fabric.js/pull/5392)
package/dist/fabric.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /* build: `node build.js modules=ALL exclude=gestures,accessors requirejs minifier=uglifyjs` */
2
2
  /*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
3
3
 
4
- var fabric = fabric || { version: '2.4.5' };
4
+ var fabric = fabric || { version: '2.7.0' };
5
5
  if (typeof exports !== 'undefined') {
6
6
  exports.fabric = fabric;
7
7
  }
@@ -11,7 +11,10 @@ else if (typeof define === 'function' && define.amd) {
11
11
  }
12
12
  /* _AMD_END_ */
13
13
  if (typeof document !== 'undefined' && typeof window !== 'undefined') {
14
- fabric.document = document;
14
+ if (document instanceof HTMLDocument)
15
+ fabric.document = document;
16
+ else
17
+ fabric.document = document.implementation.createHTMLDocument("");
15
18
  fabric.window = window;
16
19
  }
17
20
  else {
@@ -56,7 +59,7 @@ fabric.SHARED_ATTRIBUTES = [
56
59
  'stroke', 'stroke-dasharray', 'stroke-linecap', 'stroke-dashoffset',
57
60
  'stroke-linejoin', 'stroke-miterlimit',
58
61
  'stroke-opacity', 'stroke-width',
59
- 'id', 'paint-order',
62
+ 'id', 'paint-order', 'vector-effect',
60
63
  'instantiated_by_use', 'clip-path'
61
64
  ];
62
65
  /* _FROM_SVG_END_ */
@@ -1174,6 +1177,7 @@ fabric.CommonMethods = {
1174
1177
 
1175
1178
  /**
1176
1179
  * Creates a canvas element that is a copy of another and is also painted
1180
+ * @param {CanvasElement} canvas to copy size and content of
1177
1181
  * @static
1178
1182
  * @memberOf fabric.util
1179
1183
  * @return {CanvasElement} initialized canvas element
@@ -1186,6 +1190,19 @@ fabric.CommonMethods = {
1186
1190
  return newCanvas;
1187
1191
  },
1188
1192
 
1193
+ /**
1194
+ * since 2.6.0 moved from canvas instance to utility.
1195
+ * @param {CanvasElement} canvasEl to copy size and content of
1196
+ * @param {String} format 'jpeg' or 'png', in some browsers 'webp' is ok too
1197
+ * @param {Number} quality <= 1 and > 0
1198
+ * @static
1199
+ * @memberOf fabric.util
1200
+ * @return {String} data url
1201
+ */
1202
+ toDataURL: function(canvasEl, format, quality) {
1203
+ return canvasEl.toDataURL('image/' + format, quality);
1204
+ },
1205
+
1189
1206
  /**
1190
1207
  * Creates image element (works on client and node)
1191
1208
  * @static
@@ -3397,6 +3414,7 @@ if (typeof console !== 'undefined') {
3397
3414
  opacity: 'opacity',
3398
3415
  'clip-path': 'clipPath',
3399
3416
  'clip-rule': 'clipRule',
3417
+ 'vector-effect': 'strokeUniform'
3400
3418
  },
3401
3419
 
3402
3420
  colorAttributes = {
@@ -3428,6 +3446,9 @@ if (typeof console !== 'undefined') {
3428
3446
  if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
3429
3447
  value = '';
3430
3448
  }
3449
+ else if (attr === 'vector-effect') {
3450
+ value = value === 'non-scaling-stroke';
3451
+ }
3431
3452
  else if (attr === 'strokeDashArray') {
3432
3453
  if (value === 'none') {
3433
3454
  value = null;
@@ -5874,29 +5895,25 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
5874
5895
  var coords = clone(this.coords, true), i, len,
5875
5896
  markup, commonAttributes, colorStops = clone(this.colorStops, true),
5876
5897
  needsSwap = coords.r1 > coords.r2,
5877
- offsetX = object.width / 2, offsetY = object.height / 2;
5898
+ transform = this.gradientTransform ? this.gradientTransform.concat() : fabric.iMatrix.concat(),
5899
+ offsetX = object.width / 2 - this.offsetX, offsetY = object.height / 2 - this.offsetY;
5878
5900
  // colorStops must be sorted ascending
5879
5901
  colorStops.sort(function(a, b) {
5880
5902
  return a.offset - b.offset;
5881
5903
  });
5904
+
5882
5905
  if (object.type === 'path') {
5883
5906
  offsetX -= object.pathOffset.x;
5884
5907
  offsetY -= object.pathOffset.y;
5885
5908
  }
5886
- for (var prop in coords) {
5887
- if (prop === 'x1' || prop === 'x2') {
5888
- coords[prop] += this.offsetX - offsetX;
5889
- }
5890
- else if (prop === 'y1' || prop === 'y2') {
5891
- coords[prop] += this.offsetY - offsetY;
5892
- }
5893
- }
5909
+
5910
+ transform[4] -= offsetX;
5911
+ transform[5] -= offsetY;
5894
5912
 
5895
5913
  commonAttributes = 'id="SVGID_' + this.id +
5896
5914
  '" gradientUnits="userSpaceOnUse"';
5897
- if (this.gradientTransform) {
5898
- commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" ';
5899
- }
5915
+ commonAttributes += ' gradientTransform="matrix(' + transform.join(' ') + ')" ';
5916
+
5900
5917
  if (this.type === 'linear') {
5901
5918
  markup = [
5902
5919
  '<linearGradient ',
@@ -6631,6 +6648,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
6631
6648
 
6632
6649
  /**
6633
6650
  * Indicates whether toObject/toDatalessObject should include default values
6651
+ * if set to false, takes precedence over the object value.
6634
6652
  * @type Boolean
6635
6653
  * @default
6636
6654
  */
@@ -7713,7 +7731,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
7713
7731
  objects: this._toObjects(methodName, propertiesToInclude),
7714
7732
  };
7715
7733
  if (clipPath) {
7716
- clipPath = clipPath.toObject(propertiesToInclude);
7734
+ data.clipPath = this._toObjectMethod(clipPath, methodName, propertiesToInclude);
7717
7735
  }
7718
7736
  extend(data, this.__serializeBgOverlay(methodName, propertiesToInclude));
7719
7737
 
@@ -8331,7 +8349,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
8331
8349
  * (either those of HTMLCanvasElement itself, or rendering context)
8332
8350
  *
8333
8351
  * @param {String} methodName Method to check support for;
8334
- * Could be one of "getImageData", "toDataURL", "toDataURLWithQuality" or "setLineDash"
8352
+ * Could be one of "setLineDash"
8335
8353
  * @return {Boolean | null} `true` if method is supported (or at least exists),
8336
8354
  * `null` if canvas element or context can not be initialized
8337
8355
  */
@@ -8349,23 +8367,9 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
8349
8367
 
8350
8368
  switch (methodName) {
8351
8369
 
8352
- case 'getImageData':
8353
- return typeof ctx.getImageData !== 'undefined';
8354
-
8355
8370
  case 'setLineDash':
8356
8371
  return typeof ctx.setLineDash !== 'undefined';
8357
8372
 
8358
- case 'toDataURL':
8359
- return typeof el.toDataURL !== 'undefined';
8360
-
8361
- case 'toDataURLWithQuality':
8362
- try {
8363
- el.toDataURL('image/jpeg', 0);
8364
- return true;
8365
- }
8366
- catch (e) { }
8367
- return false;
8368
-
8369
8373
  default:
8370
8374
  return null;
8371
8375
  }
@@ -9714,13 +9718,15 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
9714
9718
  * @return {Boolean}
9715
9719
  */
9716
9720
  isTargetTransparent: function (target, x, y) {
9717
- if (target.shouldCache() && target._cacheCanvas) {
9721
+ // in case the target is the activeObject, we cannot execute this optimization
9722
+ // because we need to draw controls too.
9723
+ if (target.shouldCache() && target._cacheCanvas && target !== this._activeObject) {
9718
9724
  var normalizedPointer = this._normalizePointer(target, {x: x, y: y}),
9719
- targetRelativeX = target.cacheTranslationX + (normalizedPointer.x * target.zoomX),
9720
- targetRelativeY = target.cacheTranslationY + (normalizedPointer.y * target.zoomY);
9725
+ targetRelativeX = Math.max(target.cacheTranslationX + (normalizedPointer.x * target.zoomX), 0),
9726
+ targetRelativeY = Math.max(target.cacheTranslationY + (normalizedPointer.y * target.zoomY), 0);
9721
9727
 
9722
9728
  var isTransparent = fabric.util.isTransparent(
9723
- target._cacheContext, targetRelativeX, targetRelativeY, this.targetFindTolerance);
9729
+ target._cacheContext, Math.round(targetRelativeX), Math.round(targetRelativeY), this.targetFindTolerance);
9724
9730
 
9725
9731
  return isTransparent;
9726
9732
  }
@@ -9850,8 +9856,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
9850
9856
 
9851
9857
  /**
9852
9858
  * @private
9859
+ * @param {Boolean} alreadySelected true if target is already selected
9860
+ * @param {String} corner a string representing the corner ml, mr, tl ...
9861
+ * @param {Event} e Event object
9862
+ * @param {fabric.Object} [target] inserted back to help overriding. Unused
9853
9863
  */
9854
- _getActionFromCorner: function(alreadySelected, corner, e) {
9864
+ _getActionFromCorner: function(alreadySelected, corner, e /* target */) {
9855
9865
  if (!corner || !alreadySelected) {
9856
9866
  return 'drag';
9857
9867
  }
@@ -9882,7 +9892,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
9882
9892
 
9883
9893
  var pointer = this.getPointer(e),
9884
9894
  corner = target._findTargetCorner(this.getPointer(e, true)),
9885
- action = this._getActionFromCorner(alreadySelected, corner, e),
9895
+ action = this._getActionFromCorner(alreadySelected, corner, e, target),
9886
9896
  origin = this._getOriginFromCorner(target, corner);
9887
9897
 
9888
9898
  this._currentTransform = {
@@ -10099,12 +10109,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
10099
10109
  */
10100
10110
  _setObjectScale: function(localMouse, transform, lockScalingX, lockScalingY, by, lockScalingFlip, _dim) {
10101
10111
  var target = transform.target, forbidScalingX = false, forbidScalingY = false, scaled = false,
10102
- changeX, changeY, scaleX, scaleY;
10103
-
10104
- scaleX = localMouse.x * target.scaleX / _dim.x;
10105
- scaleY = localMouse.y * target.scaleY / _dim.y;
10106
- changeX = target.scaleX !== scaleX;
10107
- changeY = target.scaleY !== scaleY;
10112
+ scaleX = localMouse.x * target.scaleX / _dim.x,
10113
+ scaleY = localMouse.y * target.scaleY / _dim.y,
10114
+ changeX = target.scaleX !== scaleX,
10115
+ changeY = target.scaleY !== scaleY;
10108
10116
 
10109
10117
  if (lockScalingFlip && scaleX <= 0 && scaleX < target.scaleX) {
10110
10118
  forbidScalingX = true;
@@ -10124,10 +10132,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
10124
10132
  forbidScalingY || lockScalingY || (target.set('scaleY', scaleY) && (scaled = scaled || changeY));
10125
10133
  }
10126
10134
  else if (by === 'x' && !target.get('lockUniScaling')) {
10127
- forbidScalingX || lockScalingX || (target.set('scaleX', scaleX) && (scaled = scaled || changeX));
10135
+ forbidScalingX || lockScalingX || (target.set('scaleX', scaleX) && (scaled = changeX));
10128
10136
  }
10129
10137
  else if (by === 'y' && !target.get('lockUniScaling')) {
10130
- forbidScalingY || lockScalingY || (target.set('scaleY', scaleY) && (scaled = scaled || changeY));
10138
+ forbidScalingY || lockScalingY || (target.set('scaleY', scaleY) && (scaled = changeY));
10131
10139
  }
10132
10140
  transform.newScaleX = scaleX;
10133
10141
  transform.newScaleY = scaleY;
@@ -10145,15 +10153,15 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
10145
10153
  lastDist = _dim.y * transform.original.scaleY / target.scaleY +
10146
10154
  _dim.x * transform.original.scaleX / target.scaleX,
10147
10155
  scaled, signX = localMouse.x < 0 ? -1 : 1,
10148
- signY = localMouse.y < 0 ? -1 : 1;
10156
+ signY = localMouse.y < 0 ? -1 : 1, newScaleX, newScaleY;
10149
10157
 
10150
10158
  // We use transform.scaleX/Y instead of target.scaleX/Y
10151
10159
  // because the object may have a min scale and we'll loose the proportions
10152
- transform.newScaleX = signX * Math.abs(transform.original.scaleX * dist / lastDist);
10153
- transform.newScaleY = signY * Math.abs(transform.original.scaleY * dist / lastDist);
10154
- scaled = transform.newScaleX !== target.scaleX || transform.newScaleY !== target.scaleY;
10155
- target.set('scaleX', transform.newScaleX);
10156
- target.set('scaleY', transform.newScaleY);
10160
+ newScaleX = signX * Math.abs(transform.original.scaleX * dist / lastDist);
10161
+ newScaleY = signY * Math.abs(transform.original.scaleY * dist / lastDist);
10162
+ scaled = newScaleX !== target.scaleX || newScaleY !== target.scaleY;
10163
+ target.set('scaleX', newScaleX);
10164
+ target.set('scaleY', newScaleY);
10157
10165
  return scaled;
10158
10166
  },
10159
10167
 
@@ -12077,9 +12085,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
12077
12085
 
12078
12086
 
12079
12087
  (function () {
12080
-
12081
- var supportQuality = fabric.StaticCanvas.supports('toDataURLWithQuality');
12082
-
12083
12088
  fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
12084
12089
 
12085
12090
  /**
@@ -12119,69 +12124,57 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
12119
12124
 
12120
12125
  var format = options.format || 'png',
12121
12126
  quality = options.quality || 1,
12122
- multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? 1 : 1 / this.getRetinaScaling()),
12123
- cropping = {
12124
- left: options.left || 0,
12125
- top: options.top || 0,
12126
- width: options.width || 0,
12127
- height: options.height || 0,
12128
- };
12129
- return this.__toDataURLWithMultiplier(format, quality, cropping, multiplier);
12130
- },
12131
-
12132
- /**
12133
- * @private
12134
- */
12135
- __toDataURLWithMultiplier: function(format, quality, cropping, multiplier) {
12136
-
12137
- var origWidth = this.width,
12138
- origHeight = this.height,
12139
- scaledWidth = (cropping.width || this.width) * multiplier,
12127
+ multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? this.getRetinaScaling() : 1),
12128
+ canvasEl = this.toCanvasElement(multiplier, options);
12129
+ return fabric.util.toDataURL(canvasEl, format, quality);
12130
+ },
12131
+
12132
+ /**
12133
+ * Create a new HTMLCanvas element painted with the current canvas content.
12134
+ * No need to resize the actual one or repaint it.
12135
+ * Will transfer object ownership to a new canvas, paint it, and set everything back.
12136
+ * This is an intermediary step used to get to a dataUrl but also it is usefull to
12137
+ * create quick image copies of a canvas without passing for the dataUrl string
12138
+ * @param {Number} [multiplier] a zoom factor.
12139
+ * @param {Object} [cropping] Cropping informations
12140
+ * @param {Number} [cropping.left] Cropping left offset.
12141
+ * @param {Number} [cropping.top] Cropping top offset.
12142
+ * @param {Number} [cropping.width] Cropping width.
12143
+ * @param {Number} [cropping.height] Cropping height.
12144
+ */
12145
+ toCanvasElement: function(multiplier, cropping) {
12146
+ multiplier = multiplier || 1;
12147
+ cropping = cropping || { };
12148
+ var scaledWidth = (cropping.width || this.width) * multiplier,
12140
12149
  scaledHeight = (cropping.height || this.height) * multiplier,
12141
12150
  zoom = this.getZoom(),
12151
+ originalWidth = this.width,
12152
+ originalHeight = this.height,
12142
12153
  newZoom = zoom * multiplier,
12143
12154
  vp = this.viewportTransform,
12144
- translateX = (vp[4] - cropping.left) * multiplier,
12145
- translateY = (vp[5] - cropping.top) * multiplier,
12146
- newVp = [newZoom, 0, 0, newZoom, translateX, translateY],
12155
+ translateX = (vp[4] - (cropping.left || 0)) * multiplier,
12156
+ translateY = (vp[5] - (cropping.top || 0)) * multiplier,
12147
12157
  originalInteractive = this.interactive,
12148
- originalSkipOffScreen = this.skipOffscreen,
12149
- needsResize = origWidth !== scaledWidth || origHeight !== scaledHeight;
12150
-
12151
- this.viewportTransform = newVp;
12152
- this.skipOffscreen = false;
12153
- // setting interactive to false avoid exporting controls
12158
+ originalContext = this.contextContainer,
12159
+ newVp = [newZoom, 0, 0, newZoom, translateX, translateY],
12160
+ canvasEl = fabric.util.createCanvasElement();
12161
+ canvasEl.width = scaledWidth;
12162
+ canvasEl.height = scaledHeight;
12154
12163
  this.interactive = false;
12155
- if (needsResize) {
12156
- this.setDimensions({ width: scaledWidth, height: scaledHeight }, { backstoreOnly: true });
12157
- }
12158
- // call a renderAll to force sync update. This will cancel the scheduled requestRenderAll
12159
- // from setDimensions
12164
+ this.viewportTransform = newVp;
12165
+ this.width = scaledWidth;
12166
+ this.height = scaledHeight;
12167
+ this.calcViewportBoundaries();
12168
+ this.contextContainer = canvasEl.getContext('2d');
12169
+ // will be renderAllExport();
12160
12170
  this.renderAll();
12161
- var data = this.__toDataURL(format, quality, cropping);
12162
- this.interactive = originalInteractive;
12163
- this.skipOffscreen = originalSkipOffScreen;
12164
12171
  this.viewportTransform = vp;
12165
- //setDimensions with no option object is taking care of:
12166
- //this.width, this.height, this.requestRenderAll()
12167
- if (needsResize) {
12168
- this.setDimensions({ width: origWidth, height: origHeight }, { backstoreOnly: true });
12169
- }
12170
- this.renderAll();
12171
- return data;
12172
- },
12173
-
12174
- /**
12175
- * @private
12176
- */
12177
- __toDataURL: function(format, quality) {
12178
-
12179
- var canvasEl = this.contextContainer.canvas;
12180
- var data = supportQuality
12181
- ? canvasEl.toDataURL('image/' + format, quality)
12182
- : canvasEl.toDataURL('image/' + format);
12183
-
12184
- return data;
12172
+ this.width = originalWidth;
12173
+ this.height = originalHeight;
12174
+ this.calcViewportBoundaries();
12175
+ this.contextContainer = originalContext;
12176
+ this.interactive = originalInteractive;
12177
+ return canvasEl;
12185
12178
  },
12186
12179
  });
12187
12180
 
@@ -12975,6 +12968,18 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
12975
12968
  */
12976
12969
  noScaleCache: true,
12977
12970
 
12971
+ /**
12972
+ * When `false`, the stoke width will scale with the object.
12973
+ * When `true`, the stroke will always match the exact pixel size entered for stroke width.
12974
+ * default to false
12975
+ * @since 2.6.0
12976
+ * @type Boolean
12977
+ * @default false
12978
+ * @type Boolean
12979
+ * @default false
12980
+ */
12981
+ strokeUniform: false,
12982
+
12978
12983
  /**
12979
12984
  * When set to `true`, object's cache will be rerendered next render call.
12980
12985
  * since 1.7.0
@@ -13010,7 +13015,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13010
13015
  'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
13011
13016
  'stroke strokeWidth strokeDashArray strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit ' +
13012
13017
  'angle opacity fill globalCompositeOperation shadow clipTo visible backgroundColor ' +
13013
- 'skewX skewY fillRule paintFirst clipPath'
13018
+ 'skewX skewY fillRule paintFirst clipPath strokeUniform'
13014
13019
  ).split(' '),
13015
13020
 
13016
13021
  /**
@@ -13021,7 +13026,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13021
13026
  * @type Array
13022
13027
  */
13023
13028
  cacheProperties: (
13024
- 'fill stroke strokeWidth strokeDashArray width height paintFirst' +
13029
+ 'fill stroke strokeWidth strokeDashArray width height paintFirst strokeUniform' +
13025
13030
  ' strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit backgroundColor clipPath'
13026
13031
  ).split(' '),
13027
13032
 
@@ -13590,8 +13595,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13590
13595
  * @param {CanvasRenderingContext2D} ctx Context to render on
13591
13596
  */
13592
13597
  drawObject: function(ctx, forClipping) {
13593
-
13598
+ var originalFill = this.fill, originalStroke = this.stroke;
13594
13599
  if (forClipping) {
13600
+ this.fill = 'black';
13601
+ this.stroke = '';
13595
13602
  this._setClippingProperties(ctx);
13596
13603
  }
13597
13604
  else {
@@ -13601,6 +13608,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13601
13608
  }
13602
13609
  this._render(ctx);
13603
13610
  this._drawClipPath(ctx);
13611
+ this.fill = originalFill;
13612
+ this.stroke = originalStroke;
13604
13613
  },
13605
13614
 
13606
13615
  _drawClipPath: function(ctx) {
@@ -13738,6 +13747,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13738
13747
  else {
13739
13748
  alternative && alternative(ctx);
13740
13749
  }
13750
+ if (this.strokeUniform) {
13751
+ ctx.setLineDash(ctx.getLineDash().map(function(value) { return value * ctx.lineWidth; }));
13752
+ }
13741
13753
  },
13742
13754
 
13743
13755
  /**
@@ -13781,18 +13793,19 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13781
13793
  return;
13782
13794
  }
13783
13795
 
13784
- var multX = (this.canvas && this.canvas.viewportTransform[0]) || 1,
13785
- multY = (this.canvas && this.canvas.viewportTransform[3]) || 1,
13796
+ var shadow = this.shadow, canvas = this.canvas,
13797
+ multX = (canvas && canvas.viewportTransform[0]) || 1,
13798
+ multY = (canvas && canvas.viewportTransform[3]) || 1,
13786
13799
  scaling = this.getObjectScaling();
13787
- if (this.canvas && this.canvas._isRetinaScaling()) {
13800
+ if (canvas && canvas._isRetinaScaling()) {
13788
13801
  multX *= fabric.devicePixelRatio;
13789
13802
  multY *= fabric.devicePixelRatio;
13790
13803
  }
13791
- ctx.shadowColor = this.shadow.color;
13792
- ctx.shadowBlur = this.shadow.blur * fabric.browserShadowBlurConstant *
13804
+ ctx.shadowColor = shadow.color;
13805
+ ctx.shadowBlur = shadow.blur * fabric.browserShadowBlurConstant *
13793
13806
  (multX + multY) * (scaling.scaleX + scaling.scaleY) / 4;
13794
- ctx.shadowOffsetX = this.shadow.offsetX * multX * scaling.scaleX;
13795
- ctx.shadowOffsetY = this.shadow.offsetY * multY * scaling.scaleY;
13807
+ ctx.shadowOffsetX = shadow.offsetX * multX * scaling.scaleX;
13808
+ ctx.shadowOffsetY = shadow.offsetY * multY * scaling.scaleY;
13796
13809
  },
13797
13810
 
13798
13811
  /**
@@ -13812,6 +13825,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13812
13825
  * @private
13813
13826
  * @param {CanvasRenderingContext2D} ctx Context to render on
13814
13827
  * @param {Object} filler fabric.Pattern or fabric.Gradient
13828
+ * @return {Object} offset.offsetX offset for text rendering
13829
+ * @return {Object} offset.offsetY offset for text rendering
13815
13830
  */
13816
13831
  _applyPatternGradientTransform: function(ctx, filler) {
13817
13832
  if (!filler || !filler.toLive) {
@@ -13872,6 +13887,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13872
13887
  }
13873
13888
 
13874
13889
  ctx.save();
13890
+ if (this.strokeUniform) {
13891
+ ctx.scale(1 / this.scaleX, 1 / this.scaleY);
13892
+ }
13875
13893
  this._setLineDash(ctx, this.strokeDashArray, this._renderDashedStroke);
13876
13894
  this._applyPatternGradientTransform(ctx, this.stroke);
13877
13895
  ctx.stroke();
@@ -13952,6 +13970,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13952
13970
 
13953
13971
  /**
13954
13972
  * Creates an instance of fabric.Image out of an object
13973
+ * could make use of both toDataUrl or toCanvasElement.
13955
13974
  * @param {Function} callback callback, invoked with an instance as a first argument
13956
13975
  * @param {Object} [options] for clone as image, passed to toDataURL
13957
13976
  * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
@@ -13967,20 +13986,16 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13967
13986
  * @return {fabric.Object} thisArg
13968
13987
  */
13969
13988
  cloneAsImage: function(callback, options) {
13970
- var dataUrl = this.toDataURL(options);
13971
- fabric.util.loadImage(dataUrl, function(img) {
13972
- if (callback) {
13973
- callback(new fabric.Image(img));
13974
- }
13975
- });
13989
+ var canvasEl = this.toCanvasElement(options);
13990
+ if (callback) {
13991
+ callback(new fabric.Image(canvasEl));
13992
+ }
13976
13993
  return this;
13977
13994
  },
13978
13995
 
13979
13996
  /**
13980
- * Converts an object into a data-url-like string
13997
+ * Converts an object into a HTMLCanvas element
13981
13998
  * @param {Object} options Options object
13982
- * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
13983
- * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
13984
13999
  * @param {Number} [options.multiplier=1] Multiplier to scale by
13985
14000
  * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
13986
14001
  * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
@@ -13991,11 +14006,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
13991
14006
  * @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2
13992
14007
  * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
13993
14008
  */
13994
- toDataURL: function(options) {
14009
+ toCanvasElement: function(options) {
13995
14010
  options || (options = { });
13996
14011
 
13997
14012
  var utils = fabric.util, origParams = utils.saveObjectTransform(this),
13998
- originalShadow = this.shadow, abs = Math.abs;
14013
+ originalShadow = this.shadow, abs = Math.abs,
14014
+ multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? fabric.devicePixelRatio : 1);
13999
14015
 
14000
14016
  if (options.withoutTransform) {
14001
14017
  utils.resetObjectTransform(this);
@@ -14021,7 +14037,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
14021
14037
  el.width += el.width % 2 ? 2 - el.width % 2 : 0;
14022
14038
  el.height += el.height % 2 ? 2 - el.height % 2 : 0;
14023
14039
  var canvas = new fabric.StaticCanvas(el, {
14024
- enableRetinaScaling: options.enableRetinaScaling,
14040
+ enableRetinaScaling: false,
14025
14041
  renderOnAddRemove: false,
14026
14042
  skipOffscreen: false,
14027
14043
  });
@@ -14032,10 +14048,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
14032
14048
 
14033
14049
  var originalCanvas = this.canvas;
14034
14050
  canvas.add(this);
14035
- var data = canvas.toDataURL(options);
14051
+ var canvasEl = canvas.toCanvasElement(multiplier || 1, options);
14036
14052
  this.shadow = originalShadow;
14037
- this.set(origParams).setCoords();
14038
14053
  this.canvas = originalCanvas;
14054
+ this.set(origParams).setCoords();
14039
14055
  // canvas.dispose will call image.dispose that will nullify the elements
14040
14056
  // since this canvas is a simple element for the process, we remove references
14041
14057
  // to objects in this way in order to avoid object trashing.
@@ -14043,7 +14059,27 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
14043
14059
  canvas.dispose();
14044
14060
  canvas = null;
14045
14061
 
14046
- return data;
14062
+ return canvasEl;
14063
+ },
14064
+
14065
+ /**
14066
+ * Converts an object into a data-url-like string
14067
+ * @param {Object} options Options object
14068
+ * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
14069
+ * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
14070
+ * @param {Number} [options.multiplier=1] Multiplier to scale by
14071
+ * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
14072
+ * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
14073
+ * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
14074
+ * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
14075
+ * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4
14076
+ * @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4
14077
+ * @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2
14078
+ * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
14079
+ */
14080
+ toDataURL: function(options) {
14081
+ options || (options = { });
14082
+ return fabric.util.toDataURL(this.toCanvasElement(options), options.format || 'png', options.quality || 1);
14047
14083
  },
14048
14084
 
14049
14085
  /**
@@ -14785,7 +14821,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
14785
14821
  /**
14786
14822
  * Checks if object is contained within the canvas with current viewportTransform
14787
14823
  * the check is done stopping at first point that appears on screen
14788
- * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
14824
+ * @param {Boolean} [calculate] use coordinates of current position instead of .aCoords
14789
14825
  * @return {Boolean} true if object is fully or partially contained within canvas
14790
14826
  */
14791
14827
  isOnScreen: function(calculate) {
@@ -15213,12 +15249,25 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
15213
15249
  if (typeof skewY === 'undefined') {
15214
15250
  skewY = this.skewY;
15215
15251
  }
15216
- var dimensions = this._getNonTransformedDimensions();
15217
- if (skewX === 0 && skewY === 0) {
15218
- return { x: dimensions.x * this.scaleX, y: dimensions.y * this.scaleY };
15252
+ var dimensions = this._getNonTransformedDimensions(), dimX, dimY,
15253
+ noSkew = skewX === 0 && skewY === 0;
15254
+
15255
+ if (this.strokeUniform) {
15256
+ dimX = this.width;
15257
+ dimY = this.height;
15219
15258
  }
15220
- var dimX = dimensions.x / 2, dimY = dimensions.y / 2,
15221
- points = [
15259
+ else {
15260
+ dimX = dimensions.x;
15261
+ dimY = dimensions.y;
15262
+ }
15263
+ if (noSkew) {
15264
+ return this._finalizeDiemensions(dimX * this.scaleX, dimY * this.scaleY);
15265
+ }
15266
+ else {
15267
+ dimX /= 2;
15268
+ dimY /= 2;
15269
+ }
15270
+ var points = [
15222
15271
  {
15223
15272
  x: -dimX,
15224
15273
  y: -dimY
@@ -15241,9 +15290,23 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
15241
15290
  points[i] = fabric.util.transformPoint(points[i], transformMatrix);
15242
15291
  }
15243
15292
  bbox = fabric.util.makeBoundingBoxFromPoints(points);
15244
- return { x: bbox.width, y: bbox.height };
15293
+ return this._finalizeDiemensions(bbox.width, bbox.height);
15245
15294
  },
15246
15295
 
15296
+ /*
15297
+ * Calculate object bounding boxdimensions from its properties scale, skew.
15298
+ * @param Number width width of the bbox
15299
+ * @param Number height height of the bbox
15300
+ * @private
15301
+ * @return {Object} .x finalized width dimension
15302
+ * @return {Object} .y finalized height dimension
15303
+ */
15304
+ _finalizeDiemensions: function(width, height) {
15305
+ return this.strokeUniform ?
15306
+ { x: width + this.strokeWidth, y: height + this.strokeWidth }
15307
+ :
15308
+ { x: width, y: height };
15309
+ },
15247
15310
  /*
15248
15311
  * Calculate object dimensions for controls. include padding and canvas zoom
15249
15312
  * private
@@ -15556,10 +15619,12 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
15556
15619
  styleInfo = noStyle ? '' : 'style="' + this.getSvgStyles() + '" ',
15557
15620
  shadowInfo = withShadow ? 'style="' + this.getSvgFilter() + '" ' : '',
15558
15621
  clipPath = this.clipPath,
15622
+ vectorEffect = this.strokeUniform ? 'vector-effect="non-scaling-stroke" ' : '',
15559
15623
  absoluteClipPath = this.clipPath && this.clipPath.absolutePositioned,
15560
15624
  commonPieces, markup = [], clipPathMarkup,
15561
15625
  // insert commons in the markup, style and svgCommons
15562
- index = objectMarkup.indexOf('COMMON_PARTS');
15626
+ index = objectMarkup.indexOf('COMMON_PARTS'),
15627
+ additionalTransform = options.additionalTransform;
15563
15628
  if (clipPath) {
15564
15629
  clipPath.clipPathId = 'CLIPPATH_' + fabric.Object.__uid++;
15565
15630
  clipPathMarkup = '<clipPath id="' + clipPath.clipPathId + '" >\n' +
@@ -15579,7 +15644,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
15579
15644
  );
15580
15645
  commonPieces = [
15581
15646
  styleInfo,
15582
- noStyle ? '' : this.addPaintOrder(), ' '
15647
+ vectorEffect,
15648
+ noStyle ? '' : this.addPaintOrder(), ' ',
15649
+ additionalTransform ? 'transform="' + additionalTransform + '" ' : '',
15583
15650
  ].join('');
15584
15651
  objectMarkup[index] = commonPieces;
15585
15652
  if (this.fill && this.fill.toLive) {
@@ -16811,7 +16878,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
16811
16878
  '<path d="M ' + startX + ' ' + startY,
16812
16879
  ' A ' + this.radius + ' ' + this.radius,
16813
16880
  ' 0 ', +largeFlag + ' 1', ' ' + endX + ' ' + endY,
16814
- '"', 'COMMON_PARTS', ' />\n'
16881
+ '" ', 'COMMON_PARTS', ' />\n'
16815
16882
  ];
16816
16883
  }
16817
16884
  return svgString;
@@ -18249,6 +18316,16 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
18249
18316
  this._toSVG(), { reviver: reviver, additionalTransform: additionalTransform }
18250
18317
  );
18251
18318
  },
18319
+
18320
+ /**
18321
+ * Returns svg representation of an instance
18322
+ * @param {Function} [reviver] Method for further parsing of svg representation.
18323
+ * @return {String} svg representation of an instance
18324
+ */
18325
+ toSVG: function(reviver) {
18326
+ var additionalTransform = this._getOffsetTransform();
18327
+ return this._createBaseSVGMarkup(this._toSVG(), { reviver: reviver, additionalTransform: additionalTransform });
18328
+ },
18252
18329
  /* _TO_SVG_END_ */
18253
18330
 
18254
18331
  /**
@@ -19460,6 +19537,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
19460
19537
  */
19461
19538
  strokeWidth: 0,
19462
19539
 
19540
+ /**
19541
+ * When calling {@link fabric.Image.getSrc}, return value from element src with `element.getAttribute('src')`.
19542
+ * This allows for relative urls as image src.
19543
+ * @since 2.7.0
19544
+ * @type Boolean
19545
+ * @default
19546
+ */
19547
+ srcFromAttribute: false,
19548
+
19463
19549
  /**
19464
19550
  * private
19465
19551
  * contains last value of scaleX to detect
@@ -19508,7 +19594,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
19508
19594
 
19509
19595
  /**
19510
19596
  * key used to retrieve the texture representing this image
19511
- * since 2.0.0
19597
+ * @since 2.0.0
19512
19598
  * @type String
19513
19599
  * @default
19514
19600
  */
@@ -19516,7 +19602,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
19516
19602
 
19517
19603
  /**
19518
19604
  * Image crop in pixels from original image size.
19519
- * since 2.0.0
19605
+ * @since 2.0.0
19520
19606
  * @type Number
19521
19607
  * @default
19522
19608
  */
@@ -19524,7 +19610,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
19524
19610
 
19525
19611
  /**
19526
19612
  * Image crop in pixels from original image size.
19527
- * since 2.0.0
19613
+ * @since 2.0.0
19528
19614
  * @type Number
19529
19615
  * @default
19530
19616
  */
@@ -19764,7 +19850,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
19764
19850
  if (element.toDataURL) {
19765
19851
  return element.toDataURL();
19766
19852
  }
19767
- return element.src;
19853
+
19854
+ if (this.srcFromAttribute) {
19855
+ return element.getAttribute('src');
19856
+ }
19857
+ else {
19858
+ return element.src;
19859
+ }
19768
19860
  }
19769
19861
  else {
19770
19862
  return this.src || '';
@@ -19783,7 +19875,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
19783
19875
  fabric.util.loadImage(src, function(img) {
19784
19876
  this.setElement(img, options);
19785
19877
  this._setWidthHeight();
19786
- callback(this);
19878
+ callback && callback(this);
19787
19879
  }, this, options && options.crossOrigin);
19788
19880
  return this;
19789
19881
  },
@@ -19920,14 +20012,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
19920
20012
  },
19921
20013
 
19922
20014
  _renderFill: function(ctx) {
19923
- var w = this.width, h = this.height, sW = w * this._filterScalingX, sH = h * this._filterScalingY,
19924
- x = -w / 2, y = -h / 2, elementToDraw = this._element;
19925
- elementToDraw && ctx.drawImage(elementToDraw,
19926
- this.cropX * this._filterScalingX,
19927
- this.cropY * this._filterScalingY,
19928
- sW,
19929
- sH,
19930
- x, y, w, h);
20015
+ var elementToDraw = this._element,
20016
+ w = this.width, h = this.height,
20017
+ sW = Math.min(elementToDraw.naturalWidth || elementToDraw.width, w * this._filterScalingX),
20018
+ sH = Math.min(elementToDraw.naturalHeight || elementToDraw.height, h * this._filterScalingY),
20019
+ x = -w / 2, y = -h / 2,
20020
+ sX = Math.max(0, this.cropX * this._filterScalingX),
20021
+ sY = Math.max(0, this.cropY * this._filterScalingY);
20022
+
20023
+ elementToDraw && ctx.drawImage(elementToDraw, sX, sY, sW, sH, x, y, w, h);
19931
20024
  },
19932
20025
 
19933
20026
  /**
@@ -20454,51 +20547,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
20454
20547
  return pipelineState;
20455
20548
  },
20456
20549
 
20457
- /**
20458
- * The same as the applyFilter method but with additional logging of WebGL
20459
- * errors.
20460
- */
20461
- applyFiltersDebug: function(filters, source, width, height, targetCanvas, cacheKey) {
20462
- // The following code is useful when debugging a specific issue but adds ~10x slowdown.
20463
- var gl = this.gl;
20464
- var ret = this.applyFilters(filters, source, width, height, targetCanvas, cacheKey);
20465
- var glError = gl.getError();
20466
- if (glError !== gl.NO_ERROR) {
20467
- var errorString = this.glErrorToString(gl, glError);
20468
- var error = new Error('WebGL Error ' + errorString);
20469
- error.glErrorCode = glError;
20470
- throw error;
20471
- }
20472
- return ret;
20473
- },
20474
-
20475
- glErrorToString: function(context, errorCode) {
20476
- if (!context) {
20477
- return 'Context undefined for error code: ' + errorCode;
20478
- }
20479
- else if (typeof errorCode !== 'number') {
20480
- return 'Error code is not a number';
20481
- }
20482
- switch (errorCode) {
20483
- case context.NO_ERROR:
20484
- return 'NO_ERROR';
20485
- case context.INVALID_ENUM:
20486
- return 'INVALID_ENUM';
20487
- case context.INVALID_VALUE:
20488
- return 'INVALID_VALUE';
20489
- case context.INVALID_OPERATION:
20490
- return 'INVALID_OPERATION';
20491
- case context.INVALID_FRAMEBUFFER_OPERATION:
20492
- return 'INVALID_FRAMEBUFFER_OPERATION';
20493
- case context.OUT_OF_MEMORY:
20494
- return 'OUT_OF_MEMORY';
20495
- case context.CONTEXT_LOST_WEBGL:
20496
- return 'CONTEXT_LOST_WEBGL';
20497
- default:
20498
- return 'UNKNOWN_ERROR';
20499
- }
20500
- },
20501
-
20502
20550
  /**
20503
20551
  * Detach event listeners, remove references, and clean up caches.
20504
20552
  */
@@ -20592,9 +20640,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
20592
20640
  if (this.gpuInfo) {
20593
20641
  return this.gpuInfo;
20594
20642
  }
20595
- var gl = this.gl;
20643
+ var gl = this.gl, gpuInfo = { renderer: '', vendor: '' };
20644
+ if (!gl) {
20645
+ return gpuInfo;
20646
+ }
20596
20647
  var ext = gl.getExtension('WEBGL_debug_renderer_info');
20597
- var gpuInfo = { renderer: '', vendor: '' };
20598
20648
  if (ext) {
20599
20649
  var renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
20600
20650
  var vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
@@ -28682,6 +28732,20 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
28682
28732
  */
28683
28733
  _dimensionAffectingProps: fabric.Text.prototype._dimensionAffectingProps.concat('width'),
28684
28734
 
28735
+ /**
28736
+ * Use this regular expression to split strings in breakable lines
28737
+ * @private
28738
+ */
28739
+ _wordJoiners: /[ \t\r]/,
28740
+
28741
+ /**
28742
+ * Use this boolean property in order to split strings that have no white space concept.
28743
+ * this is a cheap way to help with chinese/japaense
28744
+ * @type Boolean
28745
+ * @since 2.6.0
28746
+ */
28747
+ splitByGrapheme: false,
28748
+
28685
28749
  /**
28686
28750
  * Unlike superclass's version of this function, Textbox does not update
28687
28751
  * its width.
@@ -28731,7 +28795,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
28731
28795
  charCount++;
28732
28796
  realLineCount++;
28733
28797
  }
28734
- else if (this._reSpaceAndTab.test(textInfo.graphemeText[charCount]) && i > 0) {
28798
+ else if (!this.graphemeSplit && this._reSpaceAndTab.test(textInfo.graphemeText[charCount]) && i > 0) {
28735
28799
  // this case deals with space's that are removed from end of lines when wrapping
28736
28800
  realLineCharCount++;
28737
28801
  charCount++;
@@ -28918,19 +28982,20 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
28918
28982
  * to.
28919
28983
  */
28920
28984
  _wrapLine: function(_line, lineIndex, desiredWidth, reservedSpace) {
28921
- var lineWidth = 0,
28922
- graphemeLines = [],
28923
- line = [],
28985
+ var lineWidth = 0,
28986
+ splitByGrapheme = this.splitByGrapheme,
28987
+ graphemeLines = [],
28988
+ line = [],
28924
28989
  // spaces in different languges?
28925
- words = _line.split(this._reSpaceAndTab),
28926
- word = '',
28927
- offset = 0,
28928
- infix = ' ',
28929
- wordWidth = 0,
28930
- infixWidth = 0,
28990
+ words = splitByGrapheme ? fabric.util.string.graphemeSplit(_line) : _line.split(this._wordJoiners),
28991
+ word = '',
28992
+ offset = 0,
28993
+ infix = splitByGrapheme ? '' : ' ',
28994
+ wordWidth = 0,
28995
+ infixWidth = 0,
28931
28996
  largestWordWidth = 0,
28932
28997
  lineJustStarted = true,
28933
- additionalSpace = this._getWidthOfCharSpacing(),
28998
+ additionalSpace = splitByGrapheme ? 0 : this._getWidthOfCharSpacing(),
28934
28999
  reservedSpace = reservedSpace || 0;
28935
29000
 
28936
29001
  desiredWidth -= reservedSpace;
@@ -28952,7 +29017,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
28952
29017
  lineWidth += additionalSpace;
28953
29018
  }
28954
29019
 
28955
- if (!lineJustStarted) {
29020
+ if (!lineJustStarted && !splitByGrapheme) {
28956
29021
  line.push(infix);
28957
29022
  }
28958
29023
  line = line.concat(word);
@@ -29024,7 +29089,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
29024
29089
  * @return {Object} object representation of an instance
29025
29090
  */
29026
29091
  toObject: function(propertiesToInclude) {
29027
- return this.callSuper('toObject', ['minWidth'].concat(propertiesToInclude));
29092
+ return this.callSuper('toObject', ['minWidth', 'splitByGrapheme'].concat(propertiesToInclude));
29028
29093
  }
29029
29094
  });
29030
29095
 
@@ -29052,13 +29117,18 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
29052
29117
  fabric.Canvas.prototype._setObjectScale = function(localMouse, transform,
29053
29118
  lockScalingX, lockScalingY, by, lockScalingFlip, _dim) {
29054
29119
 
29055
- var t = transform.target;
29120
+ var t = transform.target, scaled,
29121
+ scaleX = localMouse.x * t.scaleX / _dim.x,
29122
+ scaleY = localMouse.y * t.scaleY / _dim.y;
29056
29123
  if (by === 'x' && t instanceof fabric.Textbox) {
29057
29124
  var tw = t._getTransformedDimensions().x;
29058
29125
  var w = t.width * (localMouse.x / tw);
29126
+ transform.newScaleX = scaleX;
29127
+ transform.newScaleY = scaleY;
29059
29128
  if (w >= t.getMinWidth()) {
29129
+ scaled = w !== t.width;
29060
29130
  t.set('width', w);
29061
- return true;
29131
+ return scaled;
29062
29132
  }
29063
29133
  }
29064
29134
  else {
package/package.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "name": "fabric",
3
3
  "description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
4
4
  "homepage": "http://fabricjs.com/",
5
- "version": "2.4.5",
6
- "author": "Juriy Zaytsev <kangax@gmail.com>",
5
+ "version": "2.7.0",
6
+ "authors": "Juriy Zaytsev <kangax@gmail.com>",
7
7
  "contributors": [
8
8
  {
9
9
  "name": "Andrea Bogazzi",
@@ -60,7 +60,7 @@
60
60
  "testem:ci": "testem ci"
61
61
  },
62
62
  "optionalDependencies": {
63
- "canvas": "^1.6.12",
63
+ "canvas": "^1.6.13",
64
64
  "jsdom": "^9.12.0",
65
65
  "xmldom": "0.1.x"
66
66
  },
@@ -68,7 +68,7 @@
68
68
  "eslint": "4.18.x",
69
69
  "istanbul": "0.4.x",
70
70
  "onchange": "^3.x.x",
71
- "qunit": "^2.6.1",
71
+ "qunit": "2.6.2",
72
72
  "testem": "^1.18.4",
73
73
  "uglify-js": "3.3.x",
74
74
  "pixelmatch": "^4.0.2",