mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-05-15 01:03:10 +00:00
a test
This commit is contained in:
commit
0a83d45b70
17 changed files with 334 additions and 279 deletions
|
|
@ -85,14 +85,14 @@ jobs:
|
|||
script: npm run build:fast && npm run test:visual
|
||||
- stage: Visual Tests
|
||||
env: LAUNCHER=Chrome
|
||||
install: npm install testem@1.18.4 qunit@2.6.1
|
||||
install: npm install testem@1.18.4 qunit@2.6.2
|
||||
script: npm run build:fast && testem ci --port 8080 -f testem-visual.json -l $LAUNCHER
|
||||
addons:
|
||||
apt:
|
||||
packages: # avoid installing packages
|
||||
- stage: Visual Tests
|
||||
env: LAUNCHER=Firefox
|
||||
install: npm install testem@1.18.4 qunit@2.6.1
|
||||
install: npm install testem@1.18.4 qunit@2.6.2
|
||||
script: npm run build:fast && testem ci --port 8080 -f testem-visual.json -l $LAUNCHER
|
||||
addons:
|
||||
apt:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "fabric",
|
||||
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
|
||||
"homepage": "http://fabricjs.com/",
|
||||
"version": "2.5.0",
|
||||
"version": "2.6.0",
|
||||
"authors": "Juriy Zaytsev <kangax@gmail.com>",
|
||||
"contributors": [
|
||||
{
|
||||
|
|
@ -68,7 +68,7 @@
|
|||
"eslint": "4.18.x",
|
||||
"istanbul": "0.4.x",
|
||||
"onchange": "^3.x.x",
|
||||
"qunit": "^2.6.1",
|
||||
"qunit": "2.6.2",
|
||||
"testem": "^1.18.4",
|
||||
"uglify-js": "3.3.x",
|
||||
"pixelmatch": "^4.0.2",
|
||||
|
|
|
|||
|
|
@ -219,51 +219,6 @@
|
|||
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.
|
||||
*/
|
||||
|
|
@ -357,9 +312,11 @@
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
(function () {
|
||||
|
||||
var supportQuality = fabric.StaticCanvas.supports('toDataURLWithQuality');
|
||||
|
||||
fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
|
||||
|
||||
/**
|
||||
|
|
@ -43,7 +40,7 @@
|
|||
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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -93,17 +90,6 @@
|
|||
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);
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -45,6 +45,15 @@
|
|||
*/
|
||||
strokeWidth: 0,
|
||||
|
||||
/**
|
||||
* When calling {@link fabric.Image.getSrc}, return value from element src with `element.getAttribute('src')`.
|
||||
* This allows for relative urls as image src.
|
||||
* @since 2.7.0
|
||||
* @type Boolean
|
||||
* @default
|
||||
*/
|
||||
srcFromAttribute: false,
|
||||
|
||||
/**
|
||||
* private
|
||||
* contains last value of scaleX to detect
|
||||
|
|
@ -93,7 +102,7 @@
|
|||
|
||||
/**
|
||||
* key used to retrieve the texture representing this image
|
||||
* since 2.0.0
|
||||
* @since 2.0.0
|
||||
* @type String
|
||||
* @default
|
||||
*/
|
||||
|
|
@ -101,7 +110,7 @@
|
|||
|
||||
/**
|
||||
* Image crop in pixels from original image size.
|
||||
* since 2.0.0
|
||||
* @since 2.0.0
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
|
|
@ -109,7 +118,7 @@
|
|||
|
||||
/**
|
||||
* Image crop in pixels from original image size.
|
||||
* since 2.0.0
|
||||
* @since 2.0.0
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
|
|
@ -349,7 +358,13 @@
|
|||
if (element.toDataURL) {
|
||||
return element.toDataURL();
|
||||
}
|
||||
return element.src;
|
||||
|
||||
if (this.srcFromAttribute) {
|
||||
return element.getAttribute('src');
|
||||
}
|
||||
else {
|
||||
return element.src;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return this.src || '';
|
||||
|
|
@ -505,14 +520,15 @@
|
|||
},
|
||||
|
||||
_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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1187,8 +1187,10 @@
|
|||
* @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 {
|
||||
|
|
@ -1198,6 +1200,8 @@
|
|||
}
|
||||
this._render(ctx);
|
||||
this._drawClipPath(ctx);
|
||||
this.fill = originalFill;
|
||||
this.stroke = originalStroke;
|
||||
},
|
||||
|
||||
_drawClipPath: function(ctx) {
|
||||
|
|
@ -1558,6 +1562,7 @@
|
|||
|
||||
/**
|
||||
* 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"
|
||||
|
|
@ -1573,20 +1578,16 @@
|
|||
* @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
|
||||
|
|
@ -1597,11 +1598,12 @@
|
|||
* @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);
|
||||
|
|
@ -1627,7 +1629,7 @@
|
|||
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,
|
||||
});
|
||||
|
|
@ -1638,10 +1640,10 @@
|
|||
|
||||
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.
|
||||
|
|
@ -1649,7 +1651,27 @@
|
|||
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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -64,6 +64,20 @@
|
|||
*/
|
||||
_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.
|
||||
|
|
@ -300,19 +314,20 @@
|
|||
* 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;
|
||||
|
|
@ -406,7 +421,7 @@
|
|||
* @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));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1795,7 +1795,7 @@
|
|||
* (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
|
||||
*/
|
||||
|
|
@ -1813,23 +1813,9 @@
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -583,6 +583,7 @@
|
|||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -595,6 +596,19 @@
|
|||
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
|
||||
|
|
|
|||
|
|
@ -1170,16 +1170,11 @@
|
|||
|
||||
QUnit.test('toDataURL', function(assert) {
|
||||
assert.ok(typeof canvas.toDataURL === 'function');
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
}
|
||||
else {
|
||||
var dataURL = canvas.toDataURL();
|
||||
// don't compare actual data url, as it is often browser-dependent
|
||||
// this.assertIdentical(emptyImageCanvasData, canvas.toDataURL('png'));
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
}
|
||||
var dataURL = canvas.toDataURL();
|
||||
// don't compare actual data url, as it is often browser-dependent
|
||||
// this.assertIdentical(emptyImageCanvasData, canvas.toDataURL('png'));
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
});
|
||||
|
||||
// QUnit.test('getPointer', function(assert) {
|
||||
|
|
|
|||
|
|
@ -482,20 +482,15 @@
|
|||
|
||||
QUnit.test('toDataURL', function(assert) {
|
||||
assert.ok(typeof canvas.toDataURL === 'function');
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
}
|
||||
else {
|
||||
var rect = new fabric.Rect({width: 100, height: 100, fill: 'red', top: 0, left: 0});
|
||||
canvas.add(rect);
|
||||
var dataURL = canvas.toDataURL();
|
||||
// don't compare actual data url, as it is often browser-dependent
|
||||
// this.assertIdentical(emptyImageCanvasData, canvas.toDataURL('png'));
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
//we can just compare that the dataUrl generated differs from the dataURl of an empty canvas.
|
||||
assert.equal(dataURL.substring(200, 210) !== 'AAAAAAAAAA', true);
|
||||
}
|
||||
var rect = new fabric.Rect({width: 100, height: 100, fill: 'red', top: 0, left: 0});
|
||||
canvas.add(rect);
|
||||
var dataURL = canvas.toDataURL();
|
||||
// don't compare actual data url, as it is often browser-dependent
|
||||
// this.assertIdentical(emptyImageCanvasData, canvas.toDataURL('png'));
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
//we can just compare that the dataUrl generated differs from the dataURl of an empty canvas.
|
||||
assert.equal(dataURL.substring(200, 210) !== 'AAAAAAAAAA', true);
|
||||
});
|
||||
|
||||
QUnit.test('toDataURL with enableRetinaScaling: true and no multiplier', function(assert) {
|
||||
|
|
@ -604,39 +599,28 @@
|
|||
});
|
||||
|
||||
QUnit.test('toDataURL jpeg', function(assert) {
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
try {
|
||||
var dataURL = canvas.toDataURL({ format: 'jpeg' });
|
||||
assert.equal(dataURL.substring(0, 22), 'data:image/jpeg;base64');
|
||||
}
|
||||
else {
|
||||
try {
|
||||
var dataURL = canvas.toDataURL({ format: 'jpeg' });
|
||||
assert.equal(dataURL.substring(0, 22), 'data:image/jpeg;base64');
|
||||
}
|
||||
// node-canvas does not support jpeg data urls
|
||||
catch (err) {
|
||||
assert.ok(true);
|
||||
}
|
||||
// node-canvas does not support jpeg data urls
|
||||
catch (err) {
|
||||
assert.ok(true);
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('toDataURL cropping', function(assert) {
|
||||
var done = assert.async();
|
||||
assert.ok(typeof canvas.toDataURL === 'function');
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
done();
|
||||
}
|
||||
else {
|
||||
var croppingWidth = 75,
|
||||
croppingHeight = 50,
|
||||
dataURL = canvas.toDataURL({width: croppingWidth, height: croppingHeight});
|
||||
var croppingWidth = 75,
|
||||
croppingHeight = 50,
|
||||
dataURL = canvas.toDataURL({width: croppingWidth, height: croppingHeight});
|
||||
|
||||
fabric.Image.fromURL(dataURL, function (img) {
|
||||
assert.equal(img.width, croppingWidth, 'Width of exported image should correspond to cropping width');
|
||||
assert.equal(img.height, croppingHeight, 'Height of exported image should correspond to cropping height');
|
||||
done();
|
||||
});
|
||||
}
|
||||
fabric.Image.fromURL(dataURL, function (img) {
|
||||
assert.equal(img.width, croppingWidth, 'Width of exported image should correspond to cropping width');
|
||||
assert.equal(img.height, croppingHeight, 'Height of exported image should correspond to cropping height');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('centerObjectH', function(assert) {
|
||||
|
|
|
|||
|
|
@ -25,57 +25,59 @@
|
|||
}
|
||||
|
||||
var IMG_SRC = fabric.isLikelyNode ? (__dirname + '/../fixtures/test_image.gif') : getAbsolutePath('../fixtures/test_image.gif'),
|
||||
IMG_SRC_REL = fabric.isLikelyNode ? (__dirname + '/../fixtures/test_image.gif') : '/fixtures/test_image.gif',
|
||||
IMG_WIDTH = 276,
|
||||
IMG_HEIGHT = 110;
|
||||
|
||||
var REFERENCE_IMG_OBJECT = {
|
||||
'version': fabric.version,
|
||||
'type': 'image',
|
||||
'originX': 'left',
|
||||
'originY': 'top',
|
||||
'left': 0,
|
||||
'top': 0,
|
||||
'width': IMG_WIDTH, // node-canvas doesn't seem to allow setting width/height on image objects
|
||||
'height': IMG_HEIGHT, // or does it now?
|
||||
'fill': 'rgb(0,0,0)',
|
||||
'stroke': null,
|
||||
'strokeWidth': 0,
|
||||
'strokeDashArray': null,
|
||||
'strokeLineCap': 'butt',
|
||||
'strokeDashOffset': 0,
|
||||
'strokeLineJoin': 'miter',
|
||||
'strokeMiterLimit': 4,
|
||||
'scaleX': 1,
|
||||
'scaleY': 1,
|
||||
'angle': 0,
|
||||
'flipX': false,
|
||||
'flipY': false,
|
||||
'opacity': 1,
|
||||
'src': IMG_SRC,
|
||||
'shadow': null,
|
||||
'visible': true,
|
||||
'backgroundColor': '',
|
||||
'clipTo': null,
|
||||
'filters': [],
|
||||
'fillRule': 'nonzero',
|
||||
'paintFirst': 'fill',
|
||||
'globalCompositeOperation': 'source-over',
|
||||
'skewX': 0,
|
||||
'skewY': 0,
|
||||
'transformMatrix': null,
|
||||
'crossOrigin': '',
|
||||
'cropX': 0,
|
||||
'cropY': 0
|
||||
version: fabric.version,
|
||||
type: 'image',
|
||||
originX: 'left',
|
||||
originY: 'top',
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: IMG_WIDTH, // node-canvas doesn't seem to allow setting width/height on image objects
|
||||
height: IMG_HEIGHT, // or does it now?
|
||||
fill: 'rgb(0,0,0)',
|
||||
stroke: null,
|
||||
strokeWidth: 0,
|
||||
strokeDashArray: null,
|
||||
strokeLineCap: 'butt',
|
||||
strokeDashOffset: 0,
|
||||
strokeLineJoin: 'miter',
|
||||
strokeMiterLimit: 4,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
angle: 0,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
opacity: 1,
|
||||
src: IMG_SRC,
|
||||
shadow: null,
|
||||
visible: true,
|
||||
backgroundColor: '',
|
||||
clipTo: null,
|
||||
filters: [],
|
||||
fillRule: 'nonzero',
|
||||
paintFirst: 'fill',
|
||||
globalCompositeOperation: 'source-over',
|
||||
skewX: 0,
|
||||
skewY: 0,
|
||||
transformMatrix: null,
|
||||
crossOrigin: '',
|
||||
cropX: 0,
|
||||
cropY: 0
|
||||
};
|
||||
|
||||
function _createImageElement() {
|
||||
return fabric.document.createElement('img');
|
||||
}
|
||||
|
||||
function _createImageObject(width, height, callback, options) {
|
||||
function _createImageObject(width, height, callback, options, src) {
|
||||
options = options || {};
|
||||
src = src || IMG_SRC;
|
||||
var elImage = _createImageElement();
|
||||
setSrc(elImage, IMG_SRC, function() {
|
||||
setSrc(elImage, src, function() {
|
||||
options.width = width;
|
||||
options.height = height;
|
||||
callback(new fabric.Image(elImage, options));
|
||||
|
|
@ -90,6 +92,10 @@
|
|||
return _createImageObject(IMG_WIDTH / 2, IMG_HEIGHT / 2, callback, options);
|
||||
}
|
||||
|
||||
function createImageObjectWithSrc(callback, options, src) {
|
||||
return _createImageObject(IMG_WIDTH, IMG_HEIGHT, callback, options, src);
|
||||
}
|
||||
|
||||
function setSrc(img, src, callback) {
|
||||
img.onload = function() {
|
||||
callback && callback();
|
||||
|
|
@ -294,6 +300,19 @@
|
|||
});
|
||||
});
|
||||
|
||||
QUnit.test('getSrc with srcFromAttribute', function(assert) {
|
||||
var done = assert.async();
|
||||
createImageObjectWithSrc(function(image) {
|
||||
assert.equal(image.getSrc(), IMG_SRC_REL);
|
||||
done();
|
||||
},
|
||||
{
|
||||
srcFromAttribute: true
|
||||
},
|
||||
IMG_SRC_REL
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('getElement', function(assert) {
|
||||
var elImage = _createImageElement();
|
||||
var image = new fabric.Image(elImage);
|
||||
|
|
@ -780,4 +799,26 @@
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('_renderFill respects source boundaries ', function (assert) {
|
||||
fabric.Image.prototype._renderFill.call({
|
||||
cropX: -1,
|
||||
cropY: -1,
|
||||
_filterScalingX: 1,
|
||||
_filterScalingY: 1,
|
||||
width: 300,
|
||||
height: 300,
|
||||
_element: {
|
||||
naturalWidth: 200,
|
||||
height: 200,
|
||||
},
|
||||
}, {
|
||||
drawImage: function(src, sX, sY, sW, sH) {
|
||||
assert.ok(sX >= 0, 'sX should be positive');
|
||||
assert.ok(sY >= 0, 'sY should be positive');
|
||||
assert.ok(sW <= 200, 'sW should not be larger than image width');
|
||||
assert.ok(sH <= 200, 'sH should not be larger than image height');
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -410,74 +410,71 @@
|
|||
QUnit.test('cloneAsImage', function(assert) {
|
||||
var done = assert.async();
|
||||
var cObj = new fabric.Rect({ width: 100, height: 100, fill: 'red', strokeWidth: 0 });
|
||||
|
||||
assert.ok(typeof cObj.cloneAsImage === 'function');
|
||||
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
fabric.log('`toDataURL` is not supported by this environment; skipping `cloneAsImage` test (as it relies on `toDataURL`)');
|
||||
cObj.cloneAsImage(function(image) {
|
||||
assert.ok(image);
|
||||
assert.ok(image instanceof fabric.Image);
|
||||
assert.equal(image.width, 100, 'the image has same dimension of object');
|
||||
done();
|
||||
}
|
||||
else {
|
||||
cObj.cloneAsImage(function(image) {
|
||||
assert.ok(image);
|
||||
assert.ok(image instanceof fabric.Image);
|
||||
assert.equal(image.width, 100, 'the image has same dimension of object');
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('cloneAsImage with retina scaling enabled', function(assert) {
|
||||
var done = assert.async();
|
||||
var cObj = new fabric.Rect({ width: 100, height: 100, fill: 'red', strokeWidth: 0 });
|
||||
fabric.devicePixelRatio = 2;
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
fabric.log('`toDataURL` is not supported by this environment; skipping `cloneAsImage` test (as it relies on `toDataURL`)');
|
||||
cObj.cloneAsImage(function(image) {
|
||||
assert.ok(image);
|
||||
assert.ok(image instanceof fabric.Image);
|
||||
assert.equal(image.width, 200, 'the image has been scaled by retina');
|
||||
fabric.devicePixelRatio = 1;
|
||||
done();
|
||||
}
|
||||
else {
|
||||
cObj.cloneAsImage(function(image) {
|
||||
assert.ok(image);
|
||||
assert.ok(image instanceof fabric.Image);
|
||||
assert.equal(image.width, 200, 'the image has been scaled by retina');
|
||||
fabric.devicePixelRatio = 1;
|
||||
done();
|
||||
}, { enableRetinaScaling: true });
|
||||
}
|
||||
}, { enableRetinaScaling: true });
|
||||
});
|
||||
|
||||
QUnit.test('toDataURL', function(assert) {
|
||||
// var data =
|
||||
// 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQA'+
|
||||
// 'AABkCAYAAABw4pVUAAAA+UlEQVR4nO3RoRHAQBDEsOu/6YR+B2s'+
|
||||
// 'gIO4Z3919pMwDMCRtHoAhafMADEmbB2BI2jwAQ9LmARiSNg/AkLR5AI'+
|
||||
// 'akzQMwJG0egCFp8wAMSZsHYEjaPABD0uYBGJI2D8CQtHkAhqTNAzAkbR'+
|
||||
// '6AIWnzAAxJmwdgSNo8AEPS5gEYkjYPwJC0eQCGpM0DMCRtHoAhafMADEm'+
|
||||
// 'bB2BI2jwAQ9LmARiSNg/AkLR5AIakzQMwJG0egCFp8wAMSZsHYEjaPABD0'+
|
||||
// 'uYBGJI2D8CQtHkAhqTNAzAkbR6AIWnzAAxJmwdgSNo8AEPS5gEYkjYPw'+
|
||||
// 'JC0eQCGpM0DMCRtHsDjB5K06yueJFXJAAAAAElFTkSuQmCC';
|
||||
QUnit.test('toCanvasElement', function(assert) {
|
||||
var cObj = new fabric.Rect({
|
||||
width: 100, height: 100, fill: 'red', strokeWidth: 0
|
||||
});
|
||||
|
||||
assert.ok(typeof cObj.toCanvasElement === 'function');
|
||||
|
||||
var canvasEl = cObj.toCanvasElement();
|
||||
|
||||
assert.ok(typeof canvasEl.getContext === 'function', 'the element returned is a canvas');
|
||||
});
|
||||
|
||||
QUnit.test('toCanvasElement does not modify oCoords on zoomed canvas', function(assert) {
|
||||
var cObj = new fabric.Rect({
|
||||
width: 100, height: 100, fill: 'red', strokeWidth: 0
|
||||
});
|
||||
canvas.setZoom(2);
|
||||
canvas.add(cObj);
|
||||
var originaloCoords = cObj.oCoords;
|
||||
var originalaCoords = cObj.aCoords;
|
||||
cObj.toCanvasElement();
|
||||
assert.deepEqual(cObj.oCoords, originaloCoords, 'cObj did not get object coords changed');
|
||||
assert.deepEqual(cObj.aCoords, originalaCoords, 'cObj did not get absolute coords changed');
|
||||
});
|
||||
|
||||
|
||||
QUnit.test('toDataURL', function(assert) {
|
||||
var cObj = new fabric.Rect({
|
||||
width: 100, height: 100, fill: 'red', strokeWidth: 0
|
||||
});
|
||||
|
||||
assert.ok(typeof cObj.toDataURL === 'function');
|
||||
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
}
|
||||
else {
|
||||
var dataURL = cObj.toDataURL();
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
var dataURL = cObj.toDataURL();
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
|
||||
try {
|
||||
dataURL = cObj.toDataURL({ format: 'jpeg' });
|
||||
assert.equal(dataURL.substring(0, 22), 'data:image/jpeg;base64');
|
||||
}
|
||||
catch (err) {
|
||||
fabric.log('jpeg toDataURL not supported');
|
||||
}
|
||||
try {
|
||||
dataURL = cObj.toDataURL({ format: 'jpeg' });
|
||||
assert.equal(dataURL.substring(0, 22), 'data:image/jpeg;base64');
|
||||
}
|
||||
catch (err) {
|
||||
fabric.log('jpeg toDataURL not supported');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -496,16 +493,10 @@
|
|||
width: 100, height: 100, fill: 'red'
|
||||
});
|
||||
canvas.add(cObj);
|
||||
var objCanvas = cObj.canvas;
|
||||
cObj.toDataURL();
|
||||
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
}
|
||||
else {
|
||||
var objCanvas = cObj.canvas;
|
||||
cObj.toDataURL();
|
||||
|
||||
assert.equal(objCanvas, cObj.canvas);
|
||||
}
|
||||
assert.equal(objCanvas, cObj.canvas);
|
||||
});
|
||||
|
||||
QUnit.test('isType', function(assert) {
|
||||
|
|
|
|||
|
|
@ -31,17 +31,17 @@
|
|||
opacity: 1,
|
||||
shadow: null,
|
||||
visible: true,
|
||||
clipTo: null,
|
||||
text: 'x',
|
||||
fontSize: 40,
|
||||
fontWeight: 'normal',
|
||||
fontFamily: 'Times New Roman',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.16,
|
||||
underline: false,
|
||||
overline: false,
|
||||
linethrough: false,
|
||||
textAlign: 'left',
|
||||
clipTo: null,
|
||||
text: 'x',
|
||||
fontSize: 40,
|
||||
fontWeight: 'normal',
|
||||
fontFamily: 'Times New Roman',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.16,
|
||||
underline: false,
|
||||
overline: false,
|
||||
linethrough: false,
|
||||
textAlign: 'left',
|
||||
backgroundColor: '',
|
||||
textBackgroundColor: '',
|
||||
fillRule: 'nonzero',
|
||||
|
|
@ -52,7 +52,8 @@
|
|||
transformMatrix: null,
|
||||
charSpacing: 0,
|
||||
styles: { },
|
||||
minWidth: 20
|
||||
minWidth: 20,
|
||||
splitByGrapheme: false,
|
||||
};
|
||||
|
||||
QUnit.test('constructor', function(assert) {
|
||||
|
|
@ -129,6 +130,30 @@
|
|||
textbox.initDimensions();
|
||||
assert.equal(textbox.textLines[0], 'xa', 'first line match expectations spacing 800');
|
||||
});
|
||||
QUnit.test('wrapping with different things', function(assert) {
|
||||
var textbox = new fabric.Textbox('xa\u200Bxb\u200Bxc\u200Cxd\u200Cxe ya yb id', {
|
||||
width: 16,
|
||||
});
|
||||
assert.equal(textbox.textLines[0], 'xa', '0 line match expectations');
|
||||
assert.equal(textbox.textLines[1], 'xb', '1 line match expectations');
|
||||
assert.equal(textbox.textLines[2], 'xc', '2 line match expectations');
|
||||
assert.equal(textbox.textLines[3], 'xd', '3 line match expectations');
|
||||
assert.equal(textbox.textLines[4], 'xe', '4 line match expectations');
|
||||
assert.equal(textbox.textLines[5], 'ya', '5 line match expectations');
|
||||
assert.equal(textbox.textLines[6], 'yb', '6 line match expectations');
|
||||
});
|
||||
QUnit.test('wrapping with splitByGrapheme', function(assert) {
|
||||
var textbox = new fabric.Textbox('xaxbxcxdxeyaybid', {
|
||||
width: 1,
|
||||
splitByGrapheme: true,
|
||||
});
|
||||
assert.equal(textbox.textLines[0], 'x', '0 line match expectations splitByGrapheme');
|
||||
assert.equal(textbox.textLines[1], 'a', '1 line match expectations splitByGrapheme');
|
||||
assert.equal(textbox.textLines[2], 'x', '2 line match expectations splitByGrapheme');
|
||||
assert.equal(textbox.textLines[3], 'b', '3 line match expectations splitByGrapheme');
|
||||
assert.equal(textbox.textLines[4], 'x', '4 line match expectations splitByGrapheme');
|
||||
assert.equal(textbox.textLines[5], 'c', '5 line match expectations splitByGrapheme');
|
||||
});
|
||||
QUnit.test('wrapping with custom space', function(assert) {
|
||||
var textbox = new fabric.Textbox('xa xb xc xd xe ya yb id', {
|
||||
width: 2000,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,22 @@
|
|||
percentage: 0.06,
|
||||
});
|
||||
|
||||
function clipping02(canvas, callback) {
|
||||
var clipPath = new fabric.Circle({ radius: 50, strokeWidth: 40, top: -50, left: -50, fill: '' });
|
||||
var obj = new fabric.Rect({ top: 0, left: 0, strokeWidth: 0, width: 200, height: 200, fill: 'rgba(0,255,0,0.5)'});
|
||||
obj.clipPath = clipPath;
|
||||
canvas.add(obj);
|
||||
canvas.renderAll();
|
||||
callback(canvas.lowerCanvasEl);
|
||||
}
|
||||
|
||||
tests.push({
|
||||
test: 'falsy values for fill are handled',
|
||||
code: clipping02,
|
||||
golden: 'clipping01.png',
|
||||
percentage: 0.06,
|
||||
});
|
||||
|
||||
function clipping1(canvas, callback) {
|
||||
var zoom = 20;
|
||||
canvas.setZoom(zoom);
|
||||
|
|
|
|||
Loading…
Reference in a new issue