Merge pull request #646 from Kienz/parseSVGOpacity

Parse SVG stroke-opacity and fill-opacity. Add hsl/hsla support.
This commit is contained in:
Juriy Zaytsev 2013-05-25 15:08:17 -07:00
commit 13326cde52
16 changed files with 649 additions and 290 deletions

View file

@ -33,6 +33,7 @@
/**
* @private
* @param {String|Array} color Color value to parse
*/
_tryParsingColor: function(color) {
var source;
@ -46,11 +47,58 @@
if (!source) {
source = Color.sourceFromRgb(color);
}
if (!source) {
source = Color.sourceFromHsl(color);
}
if (source) {
this.setSource(source);
}
},
/**
* Adapted from <a href="https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html">https://github.com/mjijackson</a>
* @private
* @param {Number} r Red color value
* @param {Number} g Green color value
* @param {Number} b Blue color value
* @return {Array} Hsl color
*/
_rgbToHsl: function(r, g, b) {
r /= 255, g /= 255, b /= 255;
var h, s, l,
max = fabric.util.array.max([r, g, b]),
min = fabric.util.array.min([r, g, b]);
l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
}
else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [
Math.round(h * 360),
Math.round(s * 100),
Math.round(l * 100)
];
},
/**
* Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
* @return {Array}
@ -85,6 +133,28 @@
return 'rgba(' + source[0] + ',' + source[1] + ',' + source[2] + ',' + source[3] + ')';
},
/**
* Returns color represenation in HSL format
* @return {String} ex: hsl(0-360,0%-100%,0%-100%)
*/
toHsl: function() {
var source = this.getSource(),
hsl = this._rgbToHsl(source[0], source[1], source[2]);
return 'hsl(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%)';
},
/**
* Returns color represenation in HSLA format
* @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)
*/
toHsla: function() {
var source = this.getSource(),
hsl = this._rgbToHsl(source[0], source[1], source[2]);
return 'hsla(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%,' + source[3] + ')';
},
/**
* Returns color represenation in HEX format
* @return {String} ex: FF5555
@ -114,7 +184,7 @@
/**
* Sets value of alpha channel for this color
* @param {Number} 0-1
* @param {Number} alpha 0-1
* @return {fabric.Color} thisArg
*/
setAlpha: function(alpha) {
@ -138,6 +208,7 @@
/**
* Transforms color to its black and white representation
* @param {Number} threshold
* @return {fabric.Color} thisArg
*/
toBlackWhite: function(threshold) {
@ -179,11 +250,18 @@
};
/**
* Regex matching color in RGB or RGBA formats (ex: rgb(0, 0, 0), rgb(255, 100, 10, 0.5), rgb(1,1,1))
* Regex matching color in RGB or RGBA formats (ex: rgb(0, 0, 0), rgba(255, 100, 10, 0.5), rgba( 255 , 100 , 10 , 0.5 ), rgb(1,1,1), rgba(100%, 60%, 10%, 0.5))
* @static
* @field
*/
fabric.Color.reRGBa = /^rgba?\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(\d+(?:\.\d+)?))?\)$/;
fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}\%?)\s*,\s*(\d{1,3}\%?)\s*,\s*(\d{1,3}\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
/**
* Regex matching color in HSL or HSLA formats (ex: hsl(200, 80%, 10%), hsla(300, 50%, 80%, 0.5), hsla( 300 , 50% , 80% , 0.5 ))
* @static
* @field
*/
fabric.Color.reHSLa = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
/**
* Regex matching color in HEX format (ex: #FF5555, 010155, aff)
@ -216,6 +294,22 @@
'yellow': '#FFFF00'
};
/**
* @private
* @param {Number} p
* @param {Number} q
* @param {Number} t
* @return {Number}
*/
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
/**
* Returns new color object, when given a color in RGB format
* @param {String} color ex: rgb(0-255,0-255,0-255)
@ -227,16 +321,20 @@
/**
* Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format
* @param {String} color ex: rgb(0-255,0-255,0-255)
* @param {String} color ex: rgb(0-255,0-255,0-255), rgb(0%-100%,0%-100%,0%-100%)
* @return {Array} source
*/
fabric.Color.sourceFromRgb = function(color) {
var match = color.match(Color.reRGBa);
if (match) {
var r = parseInt(match[1], 10) / (/%$/.test(match[1]) ? 100 : 1) * (/%$/.test(match[1]) ? 255 : 1),
g = parseInt(match[2], 10) / (/%$/.test(match[2]) ? 100 : 1) * (/%$/.test(match[2]) ? 255 : 1),
b = parseInt(match[3], 10) / (/%$/.test(match[3]) ? 100 : 1) * (/%$/.test(match[3]) ? 255 : 1);
return [
parseInt(match[1], 10),
parseInt(match[2], 10),
parseInt(match[3], 10),
parseInt(r, 10),
parseInt(g, 10),
parseInt(b, 10),
match[4] ? parseFloat(match[4]) : 1
];
}
@ -251,6 +349,60 @@
*/
fabric.Color.fromRgba = Color.fromRgb;
/**
* Returns new color object, when given a color in HSL format
* @param {String} color ex: hsl(0-260,0%-100%,0%-100%)
* @return {fabric.Color}
*/
fabric.Color.fromHsl = function(color) {
return Color.fromSource(Color.sourceFromHsl(color));
};
/**
* Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in HSL or HSLA format.
* Adapted from <a href="https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html">https://github.com/mjijackson</a>
* @param {String} color ex: hsl(0-360,0%-100%,0%-100%) or hsla(0-360,0%-100%,0%-100%, 0-1)
* @return {Array} source
* @see http://http://www.w3.org/TR/css3-color/#hsl-color
*/
fabric.Color.sourceFromHsl = function(color) {
var match = color.match(Color.reHSLa);
if (!match) return;
var h = (((parseFloat(match[1]) % 360) + 360) % 360) / 360,
s = parseFloat(match[2]) / (/%$/.test(match[2]) ? 100 : 1),
l = parseFloat(match[3]) / (/%$/.test(match[3]) ? 100 : 1),
r, g, b;
if (s === 0) {
r = g = b = l;
}
else {
var q = l <= 0.5 ? l * (s + 1) : l + s - l * s;
var p = l * 2 - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [
Math.round(r * 255),
Math.round(g * 255),
Math.round(b * 255),
match[4] ? parseFloat(match[4]) : 1
];
};
/**
* Returns new color object, when given a color in HSLA format
* @static
* @function
* @param {String} color
* @return {fabric.Color}
*/
fabric.Color.fromHsla = Color.fromHsl;
/**
* Returns new color object, when given a color in HEX format
* @static
@ -286,6 +438,7 @@
/**
* Returns new color object, when given color in array representation (ex: [200, 100, 100, 0.5])
* @static
* @param {Array} source
* @return {fabric.Color}
*/
fabric.Color.fromSource = function(source) {

View file

@ -31,7 +31,7 @@
* Constructor
* @param {HTMLImageElement | String} element Image element
* @param {Object} [options] Options object
* @return {fabric.Image}
* @return {fabric.Image} thisArg
*/
initialize: function(element, options) {
options || (options = { });
@ -51,7 +51,7 @@
/**
* Returns image element which this instance if based on
* @return {HTMLImageElement} image element
* @return {HTMLImageElement} Image element
*/
getElement: function() {
return this._element;
@ -136,9 +136,10 @@
ctx.strokeStyle = this.stroke.toLive
? this.stroke.toLive(ctx)
: this.stroke;
ctx.beginPath();
ctx.strokeRect(-this.width / 2, -this.height / 2, this.width, this.height);
ctx.beginPath();
ctx.closePath();
ctx.restore();
},
@ -152,6 +153,7 @@
w = this.width,
h = this.height;
ctx.save();
ctx.lineWidth = this.strokeWidth;
ctx.lineCap = this.strokeLineCap;
ctx.lineJoin = this.strokeLineJoin;
@ -159,12 +161,14 @@
ctx.strokeStyle = this.stroke.toLive
? this.stroke.toLive(ctx)
: this.stroke;
ctx.beginPath();
fabric.util.drawDashedLine(ctx, x, y, x+w, y, this.strokeDashArray);
fabric.util.drawDashedLine(ctx, x+w, y, x+w, y+h, this.strokeDashArray);
fabric.util.drawDashedLine(ctx, x+w, y+h, x, y+h, this.strokeDashArray);
fabric.util.drawDashedLine(ctx, x, y+h, x, y, this.strokeDashArray);
ctx.closePath();
ctx.restore();
},
/**
@ -372,7 +376,7 @@
/**
* Returns complexity of an instance
* @return {Number} complexity
* @return {Number} complexity of this instance
*/
complexity: function() {
return 1;
@ -455,7 +459,7 @@
* @param {SVGElement} element Element to parse
* @param {Function} callback Callback to execute when fabric.Image object is created
* @param {Object} [options] Options object
* @return {fabric.Image}
* @return {fabric.Image} Instance of fabric.Image
*/
fabric.Image.fromElement = function(element, callback, options) {
var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES);

View file

@ -167,7 +167,7 @@
overlayFill: null,
/**
* When `true`, an object is rendered via stroke and this property specifies its color
* When defined, an object is rendered via stroke and this property specifies its color
* @type String
* @default
*/
@ -413,7 +413,7 @@
/**
* Transforms context when rendering an object
* @param {CanvasRenderingContext2D} ctx Context
* @param {Boolean} when true, context is transformed to object's top/left corner. This is used when rendering text on Node
* @param {Boolean} fromLeft When true, context is transformed to object's top/left corner. This is used when rendering text on Node
*/
transform: function(ctx, fromLeft) {
ctx.globalAlpha = this.opacity;
@ -429,8 +429,8 @@
/**
* Returns an object representation of an instance
* @param {Array} propertiesToInclude
* @return {Object} object representation of an instance
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} Object representation of an instance
*/
toObject: function(propertiesToInclude) {
@ -478,8 +478,8 @@
/**
* Returns (dataless) object representation of an instance
* @param {Array} [propertiesToInclude]
* @return {Object} object representation of an instance
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} Object representation of an instance
*/
toDatalessObject: function(propertiesToInclude) {
// will be overwritten by subclasses
@ -540,6 +540,7 @@
/**
* @private
* @param {Object} object
*/
_removeDefaultValues: function(object) {
var defaultOptions = fabric.Object.prototype.options;
@ -572,7 +573,7 @@
/**
* Sets property to a given value. When changing position/dimension -related properties (left, top, scale, angle, etc.) `set` does not update position of object's borders/controls. If you need to update those, call `setCoords()`.
* @param {String} name
* @param {String|Object} key (if object, iterate over the object properties)
* @param {Object|Function} value (if function, the value is passed into it and its return value is used as a new one)
* @return {fabric.Object} thisArg
* @chainable
@ -596,8 +597,9 @@
/**
* @private
* @param key
* @param value
* @param {String} key
* @param {Any} value
* @return {fabric.Object} thisArg
*/
_set: function(key, value) {
var shouldConstrainValue = (key === 'scaleX' || key === 'scaleY');
@ -667,7 +669,6 @@
this.transform(ctx);
}
ctx.save();
if (this.stroke) {
ctx.lineWidth = this.strokeWidth;
ctx.lineCap = this.strokeLineCap;
@ -697,7 +698,6 @@
this._render(ctx, noTransform);
this.clipTo && ctx.restore();
this._removeShadow(ctx);
ctx.restore();
if (this.active && !noTransform) {
this.drawBorders(ctx);
@ -757,6 +757,7 @@
_renderStroke: function(ctx) {
if (!this.stroke) return;
ctx.save();
if (this.strokeDashArray) {
// Spec requires the concatenation of two copies the dash list when the number of elements is odd
if (1 & this.strokeDashArray.length) {
@ -776,12 +777,13 @@
this._stroke ? this._stroke(ctx) : ctx.stroke();
}
this._removeShadow(ctx);
ctx.restore();
},
/**
* Clones an instance
* @param {Function} callback Callback is invoked with a clone as a first argument
* @param {Array} propertiesToInclude
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the outpu
* @return {fabric.Object} clone of an instance
*/
clone: function(callback, propertiesToInclude) {
@ -808,7 +810,7 @@
/**
* Converts an object into a data-url-like string
* @param {Object} options
* @param {Object} options Options object
*
* `format` the format of the output image. Either "jpeg" or "png".
* `quality` quality level (0..1)
@ -876,7 +878,7 @@
/**
* Returns complexity of an instance
* @return {Number} complexity
* @return {Number} complexity of this instance
*/
complexity: function() {
return 0;
@ -884,7 +886,7 @@
/**
* Returns a JSON representation of an instance
* @param {Array} propertiesToInclude Any properties that you might want to additionally include in the output
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} JSON
*/
toJSON: function(propertiesToInclude) {

View file

@ -11,34 +11,41 @@
extend = fabric.util.object.extend,
capitalize = fabric.util.string.capitalize,
clone = fabric.util.object.clone,
toFixed = fabric.util.toFixed,
multiplyTransformMatrices = fabric.util.multiplyTransformMatrices;
fabric.SHARED_ATTRIBUTES = [
"transform",
"fill", "fill-rule", "fill-opacity",
"fill", "fill-opacity", "fill-rule",
"opacity",
"stroke", "stroke-dasharray", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-width"
"stroke", "stroke-dasharray", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width"
];
var attributesMap = {
'fill-opacity': 'fillOpacity',
'fill-rule': 'fillRule',
'font-family': 'fontFamily',
'font-size': 'fontSize',
'font-style': 'fontStyle',
'font-weight': 'fontWeight',
'cx': 'left',
'x': 'left',
'cy': 'top',
'y': 'top',
'r': 'radius',
'fill-opacity': 'opacity',
'fill-rule': 'fillRule',
'stroke-width': 'strokeWidth',
'stroke-dasharray': 'strokeDashArray',
'stroke-linecap': 'strokeLineCap',
'stroke-linejoin': 'strokeLineJoin',
'stroke-miterlimit':'strokeMiterLimit',
'transform': 'transformMatrix',
'stroke-opacity': 'strokeOpacity',
'stroke-width': 'strokeWidth',
'text-decoration': 'textDecoration',
'font-size': 'fontSize',
'font-weight': 'fontWeight',
'font-style': 'fontStyle',
'font-family': 'fontFamily'
'cy': 'top',
'y': 'top',
'transform': 'transformMatrix'
};
var colorAttributes = {
'stroke': 'strokeOpacity',
'fill': 'fillOpacity'
};
function normalizeAttr(attr) {
@ -77,6 +84,22 @@
return (!isArray && isNaN(parsed) ? value : parsed);
}
/**
* @private
* @param {Object} attributes Array of attributes to parse
*/
function _setStrokeFillOpacity(attributes) {
for (var attr in colorAttributes) {
if (!attributes[attr] || typeof attributes[colorAttributes[attr]] === 'undefined') continue;
var color = new fabric.Color(attributes[attr]);
attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
delete attributes[colorAttributes[attr]];
}
return attributes;
}
/**
* Returns an object of attributes' name/value, given element and an array of attribute names;
* Parses parent "g" nodes recursively upwards.
@ -116,7 +139,7 @@
ownAttributes = extend(ownAttributes,
extend(getGlobalStylesForElement(element), fabric.parseStyleAttribute(element)));
return extend(parentAttributes, ownAttributes);
return _setStrokeFillOpacity(extend(parentAttributes, ownAttributes));
}
/**

View file

@ -172,6 +172,7 @@
* Constructor
* @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
* @param {Object} [options] Options object
* @return {fabric.Path} thisArg
*/
initialize: function(path, options) {
options = options || { };
@ -203,6 +204,7 @@
/**
* @private
* @param {Object} [options] Options object
*/
_initializePath: function (options) {
var isWidthSet = 'width' in options,
@ -232,6 +234,7 @@
/**
* @private
* @param {Boolean} positionSet When false, path offset is returned otherwise 0
*/
_calculatePathOffset: function (positionSet) {
return {
@ -242,6 +245,7 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx context to render path on
*/
_render: function(ctx) {
var current, // current instruction
@ -540,7 +544,6 @@
}
// ctx.globalCompositeOperation = this.fillRule;
ctx.save();
if (this.overlayFill) {
ctx.fillStyle = this.overlayFill;
}
@ -569,7 +572,6 @@
this._renderStroke(ctx);
this.clipTo && ctx.restore();
this._removeShadow(ctx);
ctx.restore();
if (!noTransform && this.active) {
this.drawBorders(ctx);
@ -589,7 +591,7 @@
/**
* Returns object representation of an instance
* @param {Array} propertiesToInclude
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} object representation of an instance
*/
toObject: function(propertiesToInclude) {
@ -607,7 +609,7 @@
/**
* Returns dataless object representation of an instance
* @param {Array} propertiesToInclude
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} object representation of an instance
*/
toDatalessObject: function(propertiesToInclude) {
@ -657,7 +659,7 @@
/**
* Returns number representation of an instance complexity
* @return {Number} complexity
* @return {Number} complexity of this instance
*/
complexity: function() {
return this.path.length;

View file

@ -37,8 +37,6 @@
'textAlign',
'fontStyle',
'lineHeight',
'stroke',
'strokeWidth',
'backgroundColor',
'textBackgroundColor',
'useNative'
@ -52,6 +50,14 @@
*/
fabric.Text = fabric.util.createClass(fabric.Object, /** @lends fabric.Text.prototype */ {
/**
* Type of an object
* @type String
* @default
*/
type: 'text',
/**
* Font size (in pixels)
* @type Number
@ -74,7 +80,7 @@
fontFamily: 'Times New Roman',
/**
* Text decoration (e.g. underline, overline)
* Text decoration Possible values: "", "underline", "overline" or "line-through".
* @type String
* @default
*/
@ -95,7 +101,7 @@
textAlign: 'left',
/**
* Font style (e.g. italic)
* Font style . Possible values: "", "normal", "italic" or "oblique".
* @type String
* @default
*/
@ -108,20 +114,6 @@
*/
lineHeight: 1.3,
/**
* Stroke style. When specified, text is rendered with stroke
* @type String
* @default
*/
stroke: '',
/**
* Stroke width
* @type Number
* @default
*/
strokeWidth: 1,
/**
* Background color of an entire text box
* @type String
@ -143,13 +135,6 @@
*/
path: null,
/**
* Type of an object
* @type String
* @default
*/
type: 'text',
/**
* Indicates whether canvas native text methods should be used to render text (otherwise, Cufon is used)
* @type Boolean
@ -166,8 +151,8 @@
/**
* Constructor
* @param {String} text
* @param {Object} [options]
* @param {String} text Text string
* @param {Object} [options] Options object
* @return {fabric.Text} thisArg
*/
initialize: function(text, options) {
@ -224,6 +209,7 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_renderViaCufon: function(ctx) {
var o = Cufon.textOptions || (Cufon.textOptions = { });
@ -316,6 +302,8 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Array} textLines Array of all text lines
*/
_setBoundaries: function(ctx, textLines) {
this._boundaries = [ ];
@ -335,6 +323,7 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_setTextStyles: function(ctx) {
if (this.fill) {
@ -358,6 +347,9 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Array} textLines Array of all text lines
* @return {Number} Height of fabric.Text object
*/
_getTextHeight: function(ctx, textLines) {
return this.fontSize * textLines.length * this.lineHeight;
@ -365,6 +357,9 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Array} textLines Array of all text lines
* @return {Number} Maximum width of fabric.Text object
*/
_getTextWidth: function(ctx, textLines) {
var maxWidth = ctx.measureText(textLines[0]).width;
@ -380,46 +375,46 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_setTextShadow: function(ctx) {
if (this.textShadow) {
if (!this.textShadow) return;
// "rgba(0,0,0,0.2) 2px 2px 10px"
// "rgb(0, 100, 0) 0 0 5px"
// "red 2px 2px 1px"
// "#f55 123 345 567"
var reOffsetsAndBlur = /\s+(-?\d+)(?:px)?\s+(-?\d+)(?:px)?\s+(\d+)(?:px)?\s*/;
// "rgba(0,0,0,0.2) 2px 2px 10px"
// "rgb(0, 100, 0) 0 0 5px"
// "red 2px 2px 1px"
// "#f55 123 345 567"
var reOffsetsAndBlur = /\s+(-?\d+)(?:px)?\s+(-?\d+)(?:px)?\s+(\d+)(?:px)?\s*/;
var shadowDeclaration = this.textShadow;
var offsetsAndBlur = reOffsetsAndBlur.exec(this.textShadow);
var shadowColor = shadowDeclaration.replace(reOffsetsAndBlur, '');
var shadowDeclaration = this.textShadow;
var offsetsAndBlur = reOffsetsAndBlur.exec(this.textShadow);
var shadowColor = shadowDeclaration.replace(reOffsetsAndBlur, '');
ctx.save();
ctx.shadowColor = shadowColor;
ctx.shadowOffsetX = parseInt(offsetsAndBlur[1], 10);
ctx.shadowOffsetY = parseInt(offsetsAndBlur[2], 10);
ctx.shadowBlur = parseInt(offsetsAndBlur[3], 10);
ctx.save();
ctx.shadowColor = shadowColor;
ctx.shadowOffsetX = parseInt(offsetsAndBlur[1], 10);
ctx.shadowOffsetY = parseInt(offsetsAndBlur[2], 10);
ctx.shadowBlur = parseInt(offsetsAndBlur[3], 10);
this._shadows = [{
blur: ctx.shadowBlur,
color: ctx.shadowColor,
offX: ctx.shadowOffsetX,
offY: ctx.shadowOffsetY
}];
this._shadows = [{
blur: ctx.shadowBlur,
color: ctx.shadowColor,
offX: ctx.shadowOffsetX,
offY: ctx.shadowOffsetY
}];
this._shadowOffsets = [[
parseInt(ctx.shadowOffsetX, 10), parseInt(ctx.shadowOffsetY, 10)
]];
}
this._shadowOffsets = [[
parseInt(ctx.shadowOffsetX, 10), parseInt(ctx.shadowOffsetY, 10)
]];
},
/**
* @private
* @param method
* @param ctx
* @param line
* @param left
* param top
* @param {String} method Method name ("fillText" or "strokeText")
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {String} line Text to render
* @param {Number} left Left position of text
* @param {Number} top Top position of text
*/
_drawTextLine: function(method, ctx, line, left, top) {
@ -452,6 +447,10 @@
}
},
/**
* @private
* @return {Number} Left offset
*/
_getLeftOffset: function() {
if (fabric.isLikelyNode && (this.originX === 'left' || this.originX === 'center')) {
return 0;
@ -459,6 +458,10 @@
return -this.width / 2;
},
/**
* @private
* @return {Number} Top offset
*/
_getTopOffset: function() {
if (fabric.isLikelyNode && (this.originY === 'top' || this.originY === 'center')) {
return 0;
@ -468,51 +471,59 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Array} textLines Array of all text lines
*/
_renderTextFill: function(ctx, textLines) {
if (this.fill) {
this._boundaries = [ ];
for (var i = 0, len = textLines.length; i < len; i++) {
this._drawTextLine(
'fillText',
ctx,
textLines[i],
this._getLeftOffset(),
this._getTopOffset() + ((i + 1) * this.fontSize * this.lineHeight)
);
}
if (!this.fill) return;
this._boundaries = [ ];
for (var i = 0, len = textLines.length; i < len; i++) {
this._drawTextLine(
'fillText',
ctx,
textLines[i],
this._getLeftOffset(),
this._getTopOffset() + ((i + 1) * this.fontSize * this.lineHeight)
);
}
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Array} textLines Array of all text lines
*/
_renderTextStroke: function(ctx, textLines) {
if (this.stroke) {
if (this.strokeDashArray) {
// Spec requires the concatenation of two copies the dash list when the number of elements is odd
if (1 & this.strokeDashArray.length) {
this.strokeDashArray.push.apply(this.strokeDashArray, this.strokeDashArray);
}
supportsLineDash && ctx.setLineDash(this.strokeDashArray);
}
if (!this.stroke) return;
ctx.beginPath();
for (var i = 0, len = textLines.length; i < len; i++) {
this._drawTextLine(
'strokeText',
ctx,
textLines[i],
this._getLeftOffset(),
this._getTopOffset() + ((i + 1) * this.fontSize * this.lineHeight)
);
ctx.save();
if (this.strokeDashArray) {
// Spec requires the concatenation of two copies the dash list when the number of elements is odd
if (1 & this.strokeDashArray.length) {
this.strokeDashArray.push.apply(this.strokeDashArray, this.strokeDashArray);
}
ctx.closePath();
supportsLineDash && ctx.setLineDash(this.strokeDashArray);
}
ctx.beginPath();
for (var i = 0, len = textLines.length; i < len; i++) {
this._drawTextLine(
'strokeText',
ctx,
textLines[i],
this._getLeftOffset(),
this._getTopOffset() + ((i + 1) * this.fontSize * this.lineHeight)
);
}
ctx.closePath();
ctx.restore();
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Array} textLines Array of all text lines
*/
_renderTextBackground: function(ctx, textLines) {
this._renderTextBoxBackground(ctx);
@ -521,52 +532,57 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_renderTextBoxBackground: function(ctx) {
if (this.backgroundColor) {
ctx.save();
ctx.fillStyle = this.backgroundColor;
if (!this.backgroundColor) return;
ctx.fillRect(
this._getLeftOffset(),
this._getTopOffset(),
this.width,
this.height
);
ctx.save();
ctx.fillStyle = this.backgroundColor;
ctx.restore();
}
ctx.fillRect(
this._getLeftOffset(),
this._getTopOffset(),
this.width,
this.height
);
ctx.restore();
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Array} textLines Array of all text lines
*/
_renderTextLinesBackground: function(ctx, textLines) {
if (this.textBackgroundColor) {
ctx.save();
ctx.fillStyle = this.textBackgroundColor;
if (!this.textBackgroundColor) return;
for (var i = 0, len = textLines.length; i < len; i++) {
ctx.save();
ctx.fillStyle = this.textBackgroundColor;
if (textLines[i] !== '') {
for (var i = 0, len = textLines.length; i < len; i++) {
var lineWidth = this._getLineWidth(ctx, textLines[i]);
var lineLeftOffset = this._getLineLeftOffset(lineWidth);
if (textLines[i] !== '') {
ctx.fillRect(
this._getLeftOffset() + lineLeftOffset,
this._getTopOffset() + (i * this.fontSize * this.lineHeight),
lineWidth,
this.fontSize * this.lineHeight
);
}
var lineWidth = this._getLineWidth(ctx, textLines[i]);
var lineLeftOffset = this._getLineLeftOffset(lineWidth);
ctx.fillRect(
this._getLeftOffset() + lineLeftOffset,
this._getTopOffset() + (i * this.fontSize * this.lineHeight),
lineWidth,
this.fontSize * this.lineHeight
);
}
ctx.restore();
}
ctx.restore();
},
/**
* @private
* @param {Number} lineWidth Width of text line
* @return {Number} Line left offset
*/
_getLineLeftOffset: function(lineWidth) {
if (this.textAlign === 'center') {
@ -580,8 +596,9 @@
/**
* @private
* @param ctx
* @param line
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {String} line Text line
* @return {Number} Line width
*/
_getLineWidth: function(ctx, line) {
return this.textAlign === 'justify'
@ -591,8 +608,11 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Array} textLines Array of all text lines
*/
_renderTextDecoration: function(ctx, textLines) {
if (!this.textDecoration) return;
var halfOfVerticalBox = this.originY === 'top' ? 0 : this._getTextHeight(ctx, textLines) / 2;
var _this = this;
@ -664,7 +684,7 @@
/**
* Renders text instance on a specified context
* @param ctx {CanvasRenderingContext2D} context to render on
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Boolean} [noTransform] When true, context is not transformed
*/
render: function(ctx, noTransform) {
@ -682,8 +702,8 @@
/**
* Returns object representation of an instance
* @param {Array} propertiesToInclude
* @return {Object} object representation of an instance
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} Object representation of an instance
*/
toObject: function(propertiesToInclude) {
return extend(this.callSuper('toObject', propertiesToInclude), {
@ -697,8 +717,6 @@
textShadow: this.textShadow,
textAlign: this.textAlign,
path: this.path,
stroke: this.stroke,
strokeWidth: this.strokeWidth,
backgroundColor: this.backgroundColor,
textBackgroundColor: this.textBackgroundColor,
useNative: this.useNative
@ -749,6 +767,9 @@
/**
* @private
* @param {Number} lineTopOffset Line top offset
* @param {Array} textLines Array of all text lines
* @return {Array}
*/
_getSVGShadows: function(lineTopOffset, textLines) {
var shadowSpans = [], j, i, jlen, ilen, lineTopOffsetMultiplier = 1;
@ -785,6 +806,10 @@
/**
* @private
* @param {Number} lineTopOffset Line top offset
* @param {Number} textLeftOffset Text left offset
* @param {Array} textLines Array of all text lines
* @return {Object}
*/
_getSVGTextAndBg: function(lineTopOffset, textLeftOffset, textLines) {
var textSpans = [ ], textBgRects = [ ], i, lineLeftOffset, len, lineTopOffsetMultiplier = 1;
@ -854,6 +879,8 @@
* we work around it by "moving" alpha channel into opacity attribute and setting fill's alpha to 1
*
* @private
* @param {Any} value
* @return {String}
*/
_getFillAttributes: function(value) {
var fillColor = (value && typeof value === 'string') ? new fabric.Color(value) : '';
@ -921,8 +948,8 @@
/**
* Returns fabric.Text instance from an object representation
* @static
* @param {Object} object to create an instance from
* @return {fabric.Text} an instance
* @param object {Object} object Object to create an instance from
* @return {fabric.Text} Instance of fabric.Text
*/
fabric.Text.fromObject = function(object) {
return new fabric.Text(object.text, clone(object));
@ -931,9 +958,9 @@
/**
* Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>)
* @static
* @param element
* @param options
* @return {fabric.Text} an instance
* @param {SVGElement} element Element to parse
* @param {Object} [options] Options object
* @return {fabric.Text} Instance of fabric.Text
*/
fabric.Text.fromElement = function(element, options) {

View file

@ -44,11 +44,11 @@
ok(typeof circle.setRadius == 'function');
circle.setRadius(20);
equal(20, circle.getRadiusX());
equal(20, circle.getRadiusY());
equal(circle.getRadiusX(), 20);
equal(circle.getRadiusY(), 20);
equal(40, circle.getWidth());
equal(40, circle.getHeight());
equal(circle.getWidth(), 40);
equal(circle.getHeight(), 40);
});
test('complexity', function() {
@ -97,8 +97,8 @@
circle.set('left', 100).set('top', 200).set('radius', 15);
var augmentedProperties = fabric.util.object.extend(fabric.util.object.clone(defaultProperties), {
left: 100,
top: 200,
left: 100,
top: 200,
radius: 15
});
@ -113,7 +113,7 @@
left = 12,
top = 15,
fill = 'ff5555',
fillOpacity = 0.5,
opacity = 0.5,
strokeWidth = 2,
strokeDashArray = [5, 2],
strokeLineCap = 'round',
@ -125,7 +125,7 @@
elCircle.setAttribute('cx', left);
elCircle.setAttribute('cy', top);
elCircle.setAttribute('fill', fill);
elCircle.setAttribute('fill-opacity', fillOpacity);
elCircle.setAttribute('opacity', opacity);
elCircle.setAttribute('stroke-width', strokeWidth);
elCircle.setAttribute('stroke-dasharray', '5, 2');
elCircle.setAttribute('stroke-linecap', strokeLineCap);
@ -139,7 +139,7 @@
equal(oCircle.get('left'), left);
equal(oCircle.get('top'), top);
equal(oCircle.get('fill'), fill);
equal(oCircle.get('opacity'), fillOpacity);
equal(oCircle.get('opacity'), opacity);
equal(oCircle.get('strokeWidth'), strokeWidth);
deepEqual(oCircle.get('strokeDashArray'), strokeDashArray);
equal(oCircle.get('strokeLineCap'), strokeLineCap);
@ -193,7 +193,7 @@
var expected = circle.toObject();
var actual = fabric.Circle.fromObject(expected).toObject();
deepEqual(expected, actual);
deepEqual(actual, expected);
});
test('cloning and radius, width, height', function() {
@ -202,9 +202,9 @@
var clone = circle.clone();
equal(40, clone.getWidth());
equal(40, clone.getHeight());
equal(clone.getWidth(), 40);
equal(clone.getHeight(), 40);
equal(10, clone.radius);
equal(clone.radius, 10);
});
})();

View file

@ -12,6 +12,16 @@
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toRgb(), 'rgb(100,100,100)');
var oColor = new fabric.Color('rgba(100,100,100, 0.5)');
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toRgba(), 'rgba(100,100,100,0.5)');
var oColor = new fabric.Color('hsl(262,80%,12%)');
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toHsl(), 'hsl(262,80%,12%)');
});
test('getSource', function() {
@ -43,6 +53,22 @@
equal(oColor.toRgba(), 'rgba(0,0,0,0.5)');
});
test('toHsl', function() {
var oColor = new fabric.Color('ffffff');
ok(typeof oColor.toHsl == 'function');
equal(oColor.toHsl(), 'hsl(0,0%,100%)');
oColor.setSource([0,0,0,0.5]);
equal(oColor.toHsl(), 'hsl(0,0%,0%)');
});
test('toHsla', function() {
var oColor = new fabric.Color('ffffff');
ok(typeof oColor.toHsla == 'function');
equal(oColor.toHsla(), 'hsla(0,0%,100%,1)');
oColor.setSource([0,0,0,0.5]);
equal(oColor.toHsla(), 'hsla(0,0%,0%,0.5)');
});
test('toHex', function() {
var oColor = new fabric.Color('ffffff');
ok(typeof oColor.toHex == 'function');
@ -100,6 +126,36 @@
equal(oColor.toHex(), 'FFFFFF');
});
test('fromRgb (with whitespaces)', function() {
ok(typeof fabric.Color.fromRgb == 'function');
var originalRgb = 'rgb( 255 , 255 , 255 )';
var oColor = fabric.Color.fromRgb(originalRgb);
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toRgb(), 'rgb(255,255,255)');
equal(oColor.toHex(), 'FFFFFF');
});
test('fromRgb (percentage values)', function() {
ok(typeof fabric.Color.fromRgb == 'function');
var originalRgb = 'rgb(100%,100%,100%)';
var oColor = fabric.Color.fromRgb(originalRgb);
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toRgb(), 'rgb(255,255,255)');
equal(oColor.toHex(), 'FFFFFF');
});
test('fromRgb (percentage values with whitespaces)', function() {
ok(typeof fabric.Color.fromRgb == 'function');
var originalRgb = 'rgb( 100% , 100% , 100% )';
var oColor = fabric.Color.fromRgb(originalRgb);
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toRgb(), 'rgb(255,255,255)');
equal(oColor.toHex(), 'FFFFFF');
});
test('fromRgba', function() {
ok(typeof fabric.Color.fromRgba == 'function');
var originalRgba = 'rgba(255,255,255,0.5)';
@ -111,6 +167,78 @@
equal(oColor.getAlpha(), 0.5, 'alpha should be set properly');
});
test('fromRgba (with whitespaces)', function() {
var originalRgba = 'rgba( 255 , 255 , 255 , 0.5 )';
oColor = fabric.Color.fromRgba(originalRgba);
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toRgba(), 'rgba(255,255,255,0.5)');
equal(oColor.toHex(), 'FFFFFF');
equal(oColor.getAlpha(), 0.5, 'alpha should be set properly');
});
test('fromRgba (percentage values)', function() {
var originalRgba = 'rgba(100%,100%,100%,0.5)';
oColor = fabric.Color.fromRgba(originalRgba);
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toRgba(), 'rgba(255,255,255,0.5)');
equal(oColor.toHex(), 'FFFFFF');
equal(oColor.getAlpha(), 0.5, 'alpha should be set properly');
});
test('fromRgba (percentage values with whitespaces)', function() {
var originalRgba = 'rgba( 100% , 100% , 100% , 0.5 )';
oColor = fabric.Color.fromRgba(originalRgba);
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toRgba(), 'rgba(255,255,255,0.5)');
equal(oColor.toHex(), 'FFFFFF');
equal(oColor.getAlpha(), 0.5, 'alpha should be set properly');
});
test('fromHsl', function() {
ok(typeof fabric.Color.fromHsl == 'function');
var originalHsl = 'hsl(262,80%,12%)';
var oColor = fabric.Color.fromHsl(originalHsl);
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toHsl(), originalHsl);
equal(oColor.toHex(), '180637');
});
test('fromHsl (with whitespaces)', function() {
ok(typeof fabric.Color.fromHsl == 'function');
var originalHsl = 'hsl( 262 , 80% , 12% )';
var oColor = fabric.Color.fromHsl(originalHsl);
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toHsl(), 'hsl(262,80%,12%)');
equal(oColor.toHex(), '180637');
});
test('fromHsla', function() {
ok(typeof fabric.Color.fromHsla == 'function');
var originalHsla = 'hsla(262,80%,12%,0.2)';
var oColor = fabric.Color.fromHsla(originalHsla);
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toHsla(), originalHsla);
equal(oColor.toHex(), '180637');
equal(oColor.getAlpha(), 0.2, 'alpha should be set properly');
});
test('fromHsla (with whitespaces)', function() {
ok(typeof fabric.Color.fromHsla == 'function');
var originalHsla = 'hsla( 262 , 80% , 12% , 0.2 )';
var oColor = fabric.Color.fromHsla(originalHsla);
ok(oColor);
ok(oColor instanceof fabric.Color);
equal(oColor.toHsla(), 'hsla(262,80%,12%,0.2)');
equal(oColor.toHex(), '180637');
equal(oColor.getAlpha(), 0.2, 'alpha should be set properly');
});
test('fromHex', function() {
ok(typeof fabric.Color.fromHex == 'function');
var originalHex = 'FF5555';
@ -123,24 +251,30 @@
test('sourceFromRgb', function() {
ok(typeof fabric.Color.sourceFromRgb == 'function');
deepEqual([255,255,255,1], fabric.Color.sourceFromRgb('rgb(255,255,255)'));
deepEqual([100,150,200,1], fabric.Color.sourceFromRgb('rgb(100,150,200)'));
deepEqual(fabric.Color.sourceFromRgb('rgb(255,255,255)'), [255,255,255,1]);
deepEqual(fabric.Color.sourceFromRgb('rgb(100,150,200)'), [100,150,200,1]);
});
test('sourceFromHsl', function() {
ok(typeof fabric.Color.sourceFromHsl == 'function');
deepEqual(fabric.Color.sourceFromHsl('hsl(360,100%,100%)'), [255,255,255,1]);
deepEqual(fabric.Color.sourceFromHsl('hsl(180,50%,40%)'), [51,153,153,1]);
});
test('sourceFromHex', function() {
ok(typeof fabric.Color.sourceFromHex == 'function');
// uppercase
deepEqual([255,255,255,1], fabric.Color.sourceFromHex('FFFFFF'));
deepEqual([255,255,255,1], fabric.Color.sourceFromHex('FFF'));
deepEqual([255,255,255,1], fabric.Color.sourceFromHex('#FFFFFF'));
deepEqual([255,255,255,1], fabric.Color.sourceFromHex('#FFF'));
deepEqual(fabric.Color.sourceFromHex('FFFFFF'), [255,255,255,1]);
deepEqual(fabric.Color.sourceFromHex('FFF'), [255,255,255,1]);
deepEqual(fabric.Color.sourceFromHex('#FFFFFF'), [255,255,255,1]);
deepEqual(fabric.Color.sourceFromHex('#FFF'), [255,255,255,1]);
// lowercase
deepEqual([255,255,255,1], fabric.Color.sourceFromHex('#ffffff'));
deepEqual([255,255,255,1], fabric.Color.sourceFromHex('#fff'));
deepEqual([255,255,255,1], fabric.Color.sourceFromHex('ffffff'));
deepEqual([255,255,255,1], fabric.Color.sourceFromHex('fff'));
deepEqual(fabric.Color.sourceFromHex('#ffffff'), [255,255,255,1]);
deepEqual(fabric.Color.sourceFromHex('#fff'), [255,255,255,1]);
deepEqual(fabric.Color.sourceFromHex('ffffff'), [255,255,255,1]);
deepEqual(fabric.Color.sourceFromHex('fff'), [255,255,255,1]);
});
test('fromSource', function() {

View file

@ -55,7 +55,7 @@
'visible': true
};
ok(typeof ellipse.toObject == 'function');
deepEqual(defaultProperties, ellipse.toObject());
deepEqual(ellipse.toObject(), defaultProperties);
ellipse.set('left', 100).set('top', 200).set('rx', 15).set('ry', 25);
@ -66,7 +66,7 @@
ry: 25
});
deepEqual(augmentedProperties, ellipse.toObject());
deepEqual(ellipse.toObject(), augmentedProperties);
});
test('render', function() {
@ -92,7 +92,7 @@
left = 12,
top = 15,
fill = 'ff5555',
fillOpacity = 0.5,
opacity = 0.5,
strokeWidth = 2,
strokeDashArray = [5, 2],
strokeLineCap = 'round',
@ -104,7 +104,7 @@
elEllipse.setAttribute('cx', left);
elEllipse.setAttribute('cy', top);
elEllipse.setAttribute('fill', fill);
elEllipse.setAttribute('fill-opacity', fillOpacity);
elEllipse.setAttribute('opacity', opacity);
elEllipse.setAttribute('stroke-width', strokeWidth);
elEllipse.setAttribute('stroke-dasharray', '5, 2');
elEllipse.setAttribute('stroke-linecap', strokeLineCap);
@ -119,7 +119,7 @@
equal(oEllipse.get('left'), left);
equal(oEllipse.get('top'), top);
equal(oEllipse.get('fill'), fill);
equal(oEllipse.get('opacity'), fillOpacity);
equal(oEllipse.get('opacity'), opacity);
equal(oEllipse.get('strokeWidth'), strokeWidth);
deepEqual(oEllipse.get('strokeDashArray'), strokeDashArray);
equal(oEllipse.get('strokeLineCap'), strokeLineCap);
@ -150,6 +150,6 @@
var expected = ellipse.toObject();
var actual = fabric.Ellipse.fromObject(expected).toObject();
deepEqual(expected, actual);
deepEqual(actual, expected);
});
})();

View file

@ -8,7 +8,7 @@
'cy': 103,
'y': 104,
'r': 105,
'fill-opacity': 0.45,
'opacity': 0.45,
'fill-rule': 'foo',
'stroke-width': 4
};
@ -26,17 +26,17 @@
ok(fabric.parseAttributes);
var element = makeElement();
var attributeNames = 'cx cy x y r fill-opacity fill-rule stroke-width transform fill fill-rule'.split(' ');
var attributeNames = 'cx cy x y r opacity fill-rule stroke-width transform fill fill-rule'.split(' ');
var parsedAttributes = fabric.parseAttributes(element, attributeNames);
deepEqual({
deepEqual(parsedAttributes, {
left: 102,
top: 104,
radius: 105,
opacity: 0.45,
fillRule: 'foo',
strokeWidth: 4
}, parsedAttributes);
});
});
test('parseAttributesNoneValues', function() {
@ -44,27 +44,27 @@
element.setAttribute('fill', 'none');
element.setAttribute('stroke', 'none');
deepEqual({ fill: '', stroke: '' }, fabric.parseAttributes(element, 'fill stroke'.split(' ')));
deepEqual(fabric.parseAttributes(element, 'fill stroke'.split(' ')), { fill: '', stroke: '' });
});
test('parseAttributesFillRule', function() {
var element = fabric.document.createElement('path');
element.setAttribute('fill-rule', 'evenodd');
deepEqual({ fillRule: 'destination-over' }, fabric.parseAttributes(element, ['fill-rule']));
deepEqual(fabric.parseAttributes(element, ['fill-rule']), { fillRule: 'destination-over' });
});
test('parseAttributesFillRuleWithoutTransformation', function() {
var element = fabric.document.createElement('path');
element.setAttribute('fill-rule', 'inherit');
deepEqual({ fillRule: 'inherit' }, fabric.parseAttributes(element, ['fill-rule']));
deepEqual(fabric.parseAttributes(element, ['fill-rule']), { fillRule: 'inherit' });
});
test('parseAttributesTransform', function() {
var element = fabric.document.createElement('path');
element.setAttribute('transform', 'translate(5, 10)');
deepEqual({ transformMatrix: [1, 0, 0, 1, 5, 10] }, fabric.parseAttributes(element, ['transform']));
deepEqual(fabric.parseAttributes(element, ['transform']), { transformMatrix: [1, 0, 0, 1, 5, 10] });
});
test('parseAttributesWithParent', function() {
@ -79,8 +79,8 @@
parent.setAttribute('y', '200');
grandParent.setAttribute('fill', 'red');
deepEqual({ fill: 'red', left: 100, top: 200 },
fabric.parseAttributes(element, 'x y fill'.split(' ')));
deepEqual(fabric.parseAttributes(element, 'x y fill'.split(' ')),
{ fill: 'red', left: 100, top: 200 });
});
asyncTest('parseElements', function() {
@ -132,7 +132,7 @@
'width': 103.45,
'height': 20
};
deepEqual(expectedObject, fabric.parseStyleAttribute(element));
deepEqual(fabric.parseStyleAttribute(element), expectedObject);
});
test('parseStyleAttribute with one pair', function() {
@ -142,7 +142,7 @@
var expectedObject = {
'left': 10
};
deepEqual(expectedObject, fabric.parseStyleAttribute(element));
deepEqual(fabric.parseStyleAttribute(element), expectedObject);
});
test('parseStyleAttribute with value normalization', function() {
@ -152,7 +152,7 @@
var expectedObject = {
'fill': ''
};
deepEqual(expectedObject, fabric.parseStyleAttribute(element));
deepEqual(fabric.parseStyleAttribute(element), expectedObject);
});
test('parseStyleAttribute with short font declaration', function() {
@ -164,7 +164,7 @@
'fontStyle': 'italic',
'fontFamily': 'Arial,Helvetica,sans-serif'
};
deepEqual(expectedObject, fabric.parseStyleAttribute(element));
deepEqual(fabric.parseStyleAttribute(element), expectedObject);
});
test('parseAttributes (style to have higher priority than attribute)', function() {
@ -175,7 +175,21 @@
var expectedObject = {
'fill': 'red'
};
deepEqual(expectedObject, fabric.parseAttributes(element, ['path']));
deepEqual(fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES), expectedObject);
});
test('parseAttributes stroke-opacity and fill-opacity', function() {
var element = fabric.document.createElement('path');
element.setAttribute('style', 'fill:rgb(100,200,50);fill-opacity:0.2;');
element.setAttribute('stroke', 'green');
element.setAttribute('stroke-opacity', '0.5');
element.setAttribute('fill', 'green');
var expectedObject = {
'fill': 'rgba(100,200,50,0.2)',
'stroke': 'rgba(0,128,0,0.5)'
};
deepEqual(fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES), expectedObject);
});
test('parsePointsAttribute', function() {
@ -210,44 +224,44 @@
element.setAttribute('transform', 'translate(5,10)');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual([1,0,0,1,5,10], parsedValue);
deepEqual(parsedValue, [1,0,0,1,5,10]);
element.setAttribute('transform', 'translate(-10,-20)');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual([1,0,0,1,-10,-20], parsedValue);
deepEqual(parsedValue, [1,0,0,1,-10,-20]);
var ANGLE = 90;
element.setAttribute('transform', 'rotate(' + ANGLE + ')');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual([Math.cos(ANGLE), Math.sin(ANGLE), -Math.sin(ANGLE), Math.cos(ANGLE), 0, 0], parsedValue);
deepEqual(parsedValue, [Math.cos(ANGLE), Math.sin(ANGLE), -Math.sin(ANGLE), Math.cos(ANGLE), 0, 0]);
element.setAttribute('transform', 'scale(3.5)');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual([3.5,0,0,3.5,0,0], parsedValue);
deepEqual(parsedValue, [3.5,0,0,3.5,0,0]);
element.setAttribute('transform', 'scale(2 13)');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual([2,0,0,13,0,0], parsedValue);
deepEqual(parsedValue, [2,0,0,13,0,0]);
element.setAttribute('transform', 'skewX(2)');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual([1,0,2,1,0,0], parsedValue);
deepEqual(parsedValue, [1,0,2,1,0,0]);
element.setAttribute('transform', 'skewY(234.111)');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual([1,234.111,0,1,0,0], parsedValue);
deepEqual(parsedValue, [1,234.111,0,1,0,0]);
element.setAttribute('transform', 'matrix(1,2,3,4,5,6)');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual([1,2,3,4,5,6], parsedValue);
deepEqual(parsedValue, [1,2,3,4,5,6]);
element.setAttribute('transform', 'translate(21,31) translate(11,22)');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual([1,0,0,1,32,53], parsedValue);
deepEqual(parsedValue, [1,0,0,1,32,53]);
element.setAttribute('transform', 'scale(2 13) translate(5,15) skewX(11.22)');
var parsedValue = fabric.parseTransformAttribute(element.getAttribute('transform'));
deepEqual([2,0,22.44,13,10,195], parsedValue);
deepEqual(parsedValue, [2,0,22.44,13,10,195]);
});
@ -321,7 +335,7 @@
el.setAttribute('opacity', opacityValue);
var obj = fabric.Rect.fromElement(el);
equal(parseFloat(opacityValue), obj.opacity,
equal(obj.opacity, parseFloat(opacityValue),
'opacity should be parsed correctly from "opacity" attribute of ' + tagNames[i] + ' element');
}
});

View file

@ -78,25 +78,25 @@
test('toString', function() {
var path = makePathObject();
ok(typeof path.toString == 'function');
equal('#<fabric.Path (4): { "top": 200, "left": 200 }>', path.toString());
equal(path.toString(), '#<fabric.Path (4): { "top": 200, "left": 200 }>');
});
test('toObject', function() {
var path = makePathObject();
ok(typeof path.toObject == 'function');
deepEqual(REFERENCE_PATH_OBJECT, path.toObject());
deepEqual(path.toObject(), REFERENCE_PATH_OBJECT);
});
test('toDatalessObject', function() {
var path = makePathObject();
ok(typeof path.toDatalessObject == 'function');
deepEqual(REFERENCE_PATH_OBJECT, path.toDatalessObject());
deepEqual(path.toDatalessObject(), REFERENCE_PATH_OBJECT);
var src = 'http://example.com/';
path.setSourcePath(src);
deepEqual(fabric.util.object.extend(fabric.util.object.clone(REFERENCE_PATH_OBJECT), {
path: src
}), path.toDatalessObject());
deepEqual(path.toDatalessObject(), fabric.util.object.extend(fabric.util.object.clone(REFERENCE_PATH_OBJECT), {
path: src
}));
});
test('complexity', function() {
@ -108,7 +108,7 @@
ok(typeof fabric.Path.fromObject == 'function');
var path = fabric.Path.fromObject(REFERENCE_PATH_OBJECT);
ok(path instanceof fabric.Path);
deepEqual(REFERENCE_PATH_OBJECT, path.toObject());
deepEqual(path.toObject(), REFERENCE_PATH_OBJECT);
});
test('fromElement', function() {
@ -117,7 +117,7 @@
elPath.setAttribute('d', 'M 100 100 L 300 100 L 200 300 z');
elPath.setAttribute('fill', 'red');
elPath.setAttribute('fill-opacity', '1');
elPath.setAttribute('opacity', '1');
elPath.setAttribute('stroke', 'blue');
elPath.setAttribute('stroke-width', '1');
elPath.setAttribute('stroke-dasharray', '5, 2');
@ -132,13 +132,13 @@
var path = fabric.Path.fromElement(elPath);
ok(path instanceof fabric.Path);
deepEqual(fabric.util.object.extend(REFERENCE_PATH_OBJECT, {
deepEqual(path.toObject(), fabric.util.object.extend(REFERENCE_PATH_OBJECT, {
strokeDashArray: [5, 2],
strokeLineCap: 'round',
strokeLineJoin: 'bevil',
strokeMiterLimit: 5,
transformMatrix: [2, 0, 0, 2, 0, 0]
}), path.toObject());
}));
var ANGLE = 90;
@ -146,8 +146,8 @@
path = fabric.Path.fromElement(elPath);
deepEqual(
[ Math.cos(ANGLE), Math.sin(ANGLE), -Math.sin(ANGLE), Math.cos(ANGLE), 0, 0 ],
path.get('transformMatrix')
path.get('transformMatrix'),
[ Math.cos(ANGLE), Math.sin(ANGLE), -Math.sin(ANGLE), Math.cos(ANGLE), 0, 0 ]
);
});
@ -155,16 +155,16 @@
var el = getPathElement('M100 100 l 200 200 300 300 400 -50 z');
var obj = fabric.Path.fromElement(el);
deepEqual(['M', 100, 100], obj.path[0]);
deepEqual(['l', 200, 200], obj.path[1]);
deepEqual(['l', 300, 300], obj.path[2]);
deepEqual(['l', 400, -50], obj.path[3]);
deepEqual(obj.path[0], ['M', 100, 100]);
deepEqual(obj.path[1], ['l', 200, 200]);
deepEqual(obj.path[2], ['l', 300, 300]);
deepEqual(obj.path[3], ['l', 400, -50]);
el = getPathElement('c 0,-53.25604 43.17254,-96.42858 96.42857,-96.42857 53.25603,0 96.42857,43.17254 96.42857,96.42857');
obj = fabric.Path.fromElement(el);
deepEqual(['c', 0, -53.25604, 43.17254, -96.42858, 96.42857, -96.42857], obj.path[0]);
deepEqual(['c', 53.25603, 0, 96.42857, 43.17254, 96.42857, 96.42857], obj.path[1]);
deepEqual(obj.path[0], ['c', 0, -53.25604, 43.17254, -96.42858, 96.42857, -96.42857]);
deepEqual(obj.path[1], ['c', 53.25603, 0, 96.42857, 43.17254, 96.42857, 96.42857]);
});
test('compressed path commands', function() {
@ -172,9 +172,9 @@
var el = getPathElement('M56.224 84.12c-.047.132-.138.221-.322.215.046-.131.137-.221.322-.215z');
var obj = fabric.Path.fromElement(el);
deepEqual(['M', 56.224, 84.12], obj.path[0]);
deepEqual(['c', -0.047, 0.132, -0.138, 0.221, -0.322, 0.215], obj.path[1]);
deepEqual(['c', 0.046, -0.131, 0.137, -0.221, 0.322, -0.215], obj.path[2]);
deepEqual(['z'], obj.path[3]);
deepEqual(obj.path[0], ['M', 56.224, 84.12]);
deepEqual(obj.path[1], ['c', -0.047, 0.132, -0.138, 0.221, -0.322, 0.215]);
deepEqual(obj.path[2], ['c', 0.046, -0.131, 0.137, -0.221, 0.322, -0.215]);
deepEqual(obj.path[3], ['z']);
});
})();

View file

@ -38,7 +38,7 @@
el.setAttribute('d', path);
el.setAttribute('fill', 'rgb(255,0,0)');
el.setAttribute('stroke', 'blue');
el.setAttribute('troke-width', 3);
el.setAttribute('stroke-width', 3);
return el;
}
@ -77,7 +77,7 @@
paths[0].group = null;
paths[1].group = null;
deepEqual(paths, pathGroup.getObjects());
deepEqual(pathGroup.getObjects(), paths);
});
test('toObject', function() {
@ -115,7 +115,7 @@
'paths': 'http://example.com/',
'sourcePath': 'http://example.com/'
});
deepEqual(expectedObject, pathGroup.toDatalessObject());
deepEqual(pathGroup.toDatalessObject(), expectedObject);
});
test('toString', function() {

View file

@ -51,7 +51,7 @@
ok(polygon instanceof fabric.Object);
equal(polygon.type, 'polygon');
deepEqual([ { x: -5, y: -5 }, { x: 5, y: 5 } ], polygon.get('points'));
deepEqual(polygon.get('points'), [ { x: -5, y: -5 }, { x: 5, y: 5 } ]);
});
test('complexity', function() {
@ -74,7 +74,7 @@
ok(typeof fabric.Polygon.fromObject == 'function');
var polygon = fabric.Polygon.fromObject(REFERENCE_OBJECT);
ok(polygon instanceof fabric.Polygon);
deepEqual(REFERENCE_OBJECT, polygon.toObject());
deepEqual(polygon.toObject(), REFERENCE_OBJECT);
});
test('fromElement', function() {
@ -93,12 +93,12 @@
points: [ { x: 10, y: 12 }, { x: 20, y: 22 } ]
});
deepEqual(expected, polygon.toObject());
deepEqual(polygon.toObject(), expected);
var elPolygonWithAttrs = fabric.document.createElement('polygon');
elPolygonWithAttrs.setAttribute('points', '10,10 20,20 30,30 10,10');
elPolygonWithAttrs.setAttribute('fill', 'rgb(255,255,255)');
elPolygonWithAttrs.setAttribute('fill-opacity', '0.34');
elPolygonWithAttrs.setAttribute('opacity', '0.34');
elPolygonWithAttrs.setAttribute('stroke-width', '3');
elPolygonWithAttrs.setAttribute('stroke', 'blue');
elPolygonWithAttrs.setAttribute('transform', 'translate(-10,-20) scale(2)');
@ -115,21 +115,21 @@
{ x: 10, y: 10 }
];
deepEqual(fabric.util.object.extend(REFERENCE_OBJECT, {
'width': 20,
'height': 20,
'fill': 'rgb(255,255,255)',
'stroke': 'blue',
'strokeWidth': 3,
'strokeDashArray': [5, 2],
'strokeLineCap': 'round',
'strokeLineJoin': 'bevil',
deepEqual(polygonWithAttrs.toObject(), fabric.util.object.extend(REFERENCE_OBJECT, {
'width': 20,
'height': 20,
'fill': 'rgb(255,255,255)',
'stroke': 'blue',
'strokeWidth': 3,
'strokeDashArray': [5, 2],
'strokeLineCap': 'round',
'strokeLineJoin': 'bevil',
'strokeMiterLimit': 5,
'opacity': 0.34,
'points': expectedPoints
}), polygonWithAttrs.toObject());
'opacity': 0.34,
'points': expectedPoints
}));
deepEqual([ 2, 0, 0, 2, -10, -20 ], polygonWithAttrs.get('transformMatrix'));
deepEqual(polygonWithAttrs.get('transformMatrix'), [ 2, 0, 0, 2, -10, -20 ]);
var elPolygonWithoutPoints = fabric.document.createElement('polygon');

View file

@ -91,7 +91,7 @@
var elPolylineWithAttrs = fabric.document.createElement('polyline');
elPolylineWithAttrs.setAttribute('points', '10,10 20,20 30,30 10,10');
elPolylineWithAttrs.setAttribute('fill', 'rgb(255,255,255)');
elPolylineWithAttrs.setAttribute('fill-opacity', '0.34');
elPolylineWithAttrs.setAttribute('opacity', '0.34');
elPolylineWithAttrs.setAttribute('stroke-width', '3');
elPolylineWithAttrs.setAttribute('stroke', 'blue');
elPolylineWithAttrs.setAttribute('transform', 'translate(-10,-20) scale(2)');
@ -118,7 +118,7 @@
'points': expectedPoints
}));
deepEqual([ 2, 0, 0, 2, -10, -20 ], polylineWithAttrs.get('transformMatrix'));
deepEqual(polylineWithAttrs.get('transformMatrix'), [ 2, 0, 0, 2, -10, -20 ]);
var elPolylineWithoutPoints = fabric.document.createElement('polyline');

View file

@ -89,7 +89,7 @@
elRectWithAttrs.setAttribute('rx', 11);
elRectWithAttrs.setAttribute('ry', 12);
elRectWithAttrs.setAttribute('fill', 'rgb(255,255,255)');
elRectWithAttrs.setAttribute('fill-opacity', 0.45);
elRectWithAttrs.setAttribute('opacity', 0.45);
elRectWithAttrs.setAttribute('stroke', 'blue');
elRectWithAttrs.setAttribute('stroke-width', 3);
elRectWithAttrs.setAttribute('stroke-dasharray', '5, 2');
@ -102,20 +102,20 @@
ok(rectWithAttrs instanceof fabric.Rect);
var expectedObject = fabric.util.object.extend(REFERENCE_RECT, {
left: 121,
top: 186.5,
width: 222,
height: 333,
fill: 'rgb(255,255,255)',
opacity: 0.45,
stroke: 'blue',
strokeWidth: 3,
strokeDashArray: [5, 2],
strokeLineCap: 'round',
strokeLineJoin: 'bevil',
left: 121,
top: 186.5,
width: 222,
height: 333,
fill: 'rgb(255,255,255)',
opacity: 0.45,
stroke: 'blue',
strokeWidth: 3,
strokeDashArray: [5, 2],
strokeLineCap: 'round',
strokeLineJoin: 'bevil',
strokeMiterLimit: 5,
rx: 11,
ry: 12
rx: 11,
ry: 12
});
deepEqual(rectWithAttrs.toObject(), expectedObject);
});
@ -136,6 +136,6 @@
var rect = new fabric.Rect({ width: 100, height: 100, rx: 20, ry: 30 });
var svg = rect.toSVG();
equal('<rect x="-50" y="-50" rx="20" ry="30" width="100" height="100" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(0 0)"/>', svg);
equal(svg, '<rect x="-50" y="-50" rx="20" ry="30" width="100" height="100" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(0 0)"/>');
});
})();

View file

@ -16,7 +16,7 @@
'height': 52,
'fill': 'rgb(0,0,0)',
'overlayFill': null,
'stroke': '',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
@ -98,9 +98,9 @@
text.set({ opacity: 0.123, fill: 'red', fontFamily: 'blah' });
equal(0.123, text.getOpacity());
equal('red', text.getFill());
equal('blah', text.get('fontFamily'));
equal(text.getOpacity(), 0.123);
equal(text.getFill(), 'red');
equal(text.get('fontFamily'), 'blah');
});
test('setColor', function(){
@ -166,7 +166,7 @@
elTextWithAttrs.setAttribute('x', 10);
elTextWithAttrs.setAttribute('y', 20);
elTextWithAttrs.setAttribute('fill', 'rgb(255,255,255)');
elTextWithAttrs.setAttribute('fill-opacity', 0.45);
elTextWithAttrs.setAttribute('opacity', 0.45);
elTextWithAttrs.setAttribute('stroke', 'blue');
elTextWithAttrs.setAttribute('stroke-width', 3);
elTextWithAttrs.setAttribute('stroke-dasharray', '5, 2');
@ -215,10 +215,10 @@
test('dimensions after text change', function() {
var text = new fabric.Text('x');
equal(20, text.width);
equal(text.width, 20);
text.setText('xx');
equal(40, text.width);
equal(text.width, 40);
});
test('setting fontFamily', function() {
@ -226,7 +226,7 @@
text.path = 'foobar.js';
text.set('fontFamily', 'foobar');
equal('foobar', text.get('fontFamily'));
equal(text.get('fontFamily'), 'foobar');
});
})();