Merge pull request #1694 from asturur/FillRule-misunderstanding

Fill rule misunderstanding
This commit is contained in:
Juriy Zaytsev 2014-09-23 11:32:06 +02:00
commit ec1db65110
20 changed files with 498 additions and 460 deletions

View file

@ -10,7 +10,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
var fill = this.fill
? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
: 'none',
fillRule = (this.fillRule === 'destination-over' ? 'evenodd' : this.fillRule),
fillRule = this.fillRule,
stroke = this.stroke
? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
: 'none',

View file

@ -63,9 +63,6 @@
if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
value = '';
}
else if (attr === 'fillRule') {
value = (value === 'evenodd') ? 'destination-over' : value;
}
else if (attr === 'strokeDashArray') {
value = value.replace(/,/g, ' ').split(/\s+/).map(function(n) {
return parseInt(n);

View file

@ -458,10 +458,19 @@
/**
* Fill rule used to fill an object
* accepted values are nonzero, evenodd
* <b>Backwards incompatibility note:</b> This property was used for setting globalCompositeOperation until v1.4.12 (use `fabric.Object#globalCompositeOperation` instead)
* @type String
* @default
*/
fillRule: 'source-over',
fillRule: 'nonzero',
/**
* Composite rule used for canvas globalCompositeOperation
* @type String
* @default
*/
globalCompositeOperation: 'source-over',
/**
* Background color of an object. Only works with text objects at the moment.
@ -674,7 +683,7 @@
stateProperties: (
'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' +
'angle opacity fill fillRule shadow clipTo visible backgroundColor'
'angle opacity fill fillRule globalCompositeOperation shadow clipTo visible backgroundColor'
).split(' '),
/**
@ -765,30 +774,32 @@
var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
object = {
type: this.type,
originX: this.originX,
originY: this.originY,
left: toFixed(this.left, NUM_FRACTION_DIGITS),
top: toFixed(this.top, NUM_FRACTION_DIGITS),
width: toFixed(this.width, NUM_FRACTION_DIGITS),
height: toFixed(this.height, NUM_FRACTION_DIGITS),
fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
strokeDashArray: this.strokeDashArray,
strokeLineCap: this.strokeLineCap,
strokeLineJoin: this.strokeLineJoin,
strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
flipX: this.flipX,
flipY: this.flipY,
opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
visible: this.visible,
clipTo: this.clipTo && String(this.clipTo),
backgroundColor: this.backgroundColor
type: this.type,
originX: this.originX,
originY: this.originY,
left: toFixed(this.left, NUM_FRACTION_DIGITS),
top: toFixed(this.top, NUM_FRACTION_DIGITS),
width: toFixed(this.width, NUM_FRACTION_DIGITS),
height: toFixed(this.height, NUM_FRACTION_DIGITS),
fill: (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
stroke: (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
strokeDashArray: this.strokeDashArray,
strokeLineCap: this.strokeLineCap,
strokeLineJoin: this.strokeLineJoin,
strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
angle: toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
flipX: this.flipX,
flipY: this.flipY,
opacity: toFixed(this.opacity, NUM_FRACTION_DIGITS),
shadow: (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
visible: this.visible,
clipTo: this.clipTo && String(this.clipTo),
backgroundColor: this.backgroundColor,
fillRule: this.fillRule,
globalCompositeOperation: this.globalCompositeOperation
};
if (!this.includeDefaultValues) {
@ -959,7 +970,7 @@
ctx.save();
//setup fill rule for current object
this._setupFillRule(ctx);
this._setupCompositeOperation(ctx);
this._transform(ctx, noTransform);
this._setStrokeStyles(ctx);
@ -977,7 +988,7 @@
this._render(ctx, noTransform);
this.clipTo && ctx.restore();
this._removeShadow(ctx);
this._restoreFillRule(ctx);
this._restoreCompositeOperation(ctx);
ctx.restore();
},
@ -1099,7 +1110,7 @@
-this.width / 2 + this.fill.offsetX || 0,
-this.height / 2 + this.fill.offsetY || 0);
}
if (this.fillRule === 'destination-over') {
if (this.fillRule === 'evenodd') {
ctx.fill('evenodd');
}
else {
@ -1476,13 +1487,13 @@
/**
* Sets canvas globalCompositeOperation for specific object
* custom composition operation for the particular object can be specifed using fillRule property
* custom composition operation for the particular object can be specifed using globalCompositeOperation property
* @param {CanvasRenderingContext2D} ctx Rendering canvas context
*/
_setupFillRule: function (ctx) {
if (this.fillRule) {
this._prevFillRule = ctx.globalCompositeOperation;
ctx.globalCompositeOperation = this.fillRule;
_setupCompositeOperation: function (ctx) {
if (this.globalCompositeOperation) {
this._prevGlobalCompositeOperation = ctx.globalCompositeOperation;
ctx.globalCompositeOperation = this.globalCompositeOperation;
}
},
@ -1490,9 +1501,9 @@
* Restores previously saved canvas globalCompositeOperation after obeject rendering
* @param {CanvasRenderingContext2D} ctx Rendering canvas context
*/
_restoreFillRule: function (ctx) {
if (this.fillRule && this._prevFillRule) {
ctx.globalCompositeOperation = this._prevFillRule;
_restoreCompositeOperation: function (ctx) {
if (this.globalCompositeOperation && this._prevGlobalCompositeOperation) {
ctx.globalCompositeOperation = this._prevGlobalCompositeOperation;
}
}
});

View file

@ -392,10 +392,10 @@
_renderText: function(ctx, textLines) {
ctx.save();
this._setShadow(ctx);
this._setupFillRule(ctx);
this._setupCompositeOperation(ctx);
this._renderTextFill(ctx, textLines);
this._renderTextStroke(ctx, textLines);
this._restoreFillRule(ctx);
this._restoreCompositeOperation(ctx);
this._removeShadow(ctx);
ctx.restore();
},

View file

@ -41,12 +41,12 @@
var PATH_DATALESS_JSON = '{"objects":[{"type":"path","originX":"left","originY":"top","left":100,"top":100,"width":200,"height":200,"fill":"rgb(0,0,0)",'+
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","path":"http://example.com/","pathOffset":{"x":200,"y":200}}],"background":""}';
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","path":"http://example.com/","pathOffset":{"x":200,"y":200}}],"background":""}';
var RECT_JSON = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":10,"fill":"rgb(0,0,0)",'+
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
'"shadow":null,'+
'"visible":true,"clipTo":null,"backgroundColor":"","rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
'"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
var el = fabric.document.createElement('canvas');
el.width = 600; el.height = 600;

View file

@ -28,17 +28,17 @@
var PATH_DATALESS_JSON = '{"objects":[{"type":"path","originX":"left","originY":"top","left":100,"top":100,"width":200,"height":200,"fill":"rgb(0,0,0)",'+
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","path":"http://example.com/","pathOffset":{"x":200,"y":200}}],"background":""}';
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","path":"http://example.com/","pathOffset":{"x":200,"y":200}}],"background":""}';
var RECT_JSON = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":10,"fill":"rgb(0,0,0)",'+
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
var RECT_JSON_WITH_PADDING = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":20,"fill":"rgb(0,0,0)",'+
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","padding":123,"foo":"bar","rx":0,"ry":0}],"background":""}';
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","padding":123,"foo":"bar","rx":0,"ry":0}],"background":""}';
function getAbsolutePath(path) {
var isAbsolute = /^https?:/.test(path);
@ -55,33 +55,35 @@
IMG_HEIGHT = 110;
var REFERENCE_IMG_OBJECT = {
'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': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'src': fabric.isLikelyNode ? undefined : IMG_SRC,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'filters': [],
'crossOrigin': ''
'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': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'src': fabric.isLikelyNode ? undefined : IMG_SRC,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'filters': [],
'crossOrigin': '',
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over'
};
function _createImageElement() {

View file

@ -79,33 +79,35 @@
test('toObject', function() {
var circle = new fabric.Circle();
var defaultProperties = {
'type': 'circle',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 0,
'height': 0,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'radius': 0,
'startAngle': 0,
'endAngle': 2 * Math.PI
'type': 'circle',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 0,
'height': 0,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over',
'radius': 0,
'startAngle': 0,
'endAngle': 2 * Math.PI
};
ok(typeof circle.toObject == 'function');
deepEqual(circle.toObject(), defaultProperties);

View file

@ -22,32 +22,34 @@
test('toObject', function() {
var ellipse = new fabric.Ellipse();
var defaultProperties = {
'type': 'ellipse',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 0,
'height': 0,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'rx': 0,
'ry': 0,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null
'type': 'ellipse',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 0,
'height': 0,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'rx': 0,
'ry': 0,
'shadow': null,
'visible': true,
'backgroundColor': '',
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over',
'clipTo': null
};
ok(typeof ellipse.toObject == 'function');
deepEqual(ellipse.toObject(), defaultProperties);

View file

@ -154,31 +154,33 @@
var clone = group.toObject();
var expectedObject = {
'type': 'group',
'originX': 'left',
'originY': 'top',
'left': 90,
'top': 130,
'width': 80,
'height': 60,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'objects': clone.objects
'type': 'group',
'originX': 'left',
'originY': 'top',
'left': 90,
'top': 130,
'width': 80,
'height': 60,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over',
'objects': clone.objects
};
deepEqual(clone, expectedObject);
@ -384,7 +386,7 @@ test('toObject without default values', function() {
var group = makeGroupWith2Objects();
ok(typeof group.toSVG == 'function');
var expectedSVG = '<g transform="translate(130 160)">\n<rect x="10" y="-30" rx="0" ry="0" width="30" height="10" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform=""/>\n<rect x="-40" y="-10" rx="0" ry="0" width="10" height="40" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform=""/>\n</g>\n';
var expectedSVG = '<g transform="translate(130 160)">\n<rect x="10" y="-30" rx="0" ry="0" width="30" height="10" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=""/>\n<rect x="-40" y="-10" rx="0" ry="0" width="10" height="40" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=""/>\n</g>\n';
equal(group.toSVG(), expectedSVG);
});

View file

@ -15,33 +15,35 @@
IMG_HEIGHT = 110;
var REFERENCE_IMG_OBJECT = {
'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': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'src': fabric.isLikelyNode ? undefined : IMG_SRC,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'filters': [],
'crossOrigin': ''
'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': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'src': fabric.isLikelyNode ? undefined : IMG_SRC,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'filters': [],
'crossOrigin': '',
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over'
};
function _createImageElement() {

View file

@ -3,42 +3,44 @@
QUnit.module('fabric.IText');
var ITEXT_OBJECT = {
'type': 'text',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 20,
'height': 52,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'clipTo': null,
'text': 'x',
'fontSize': 40,
'fontWeight': 'normal',
'fontFamily': 'Times New Roman',
'fontStyle': '',
'lineHeight': 1.3,
'textDecoration': '',
'textAlign': 'left',
'path': null,
'backgroundColor': '',
'textBackgroundColor': '',
'useNative': true,
styles: { }
'type': 'text',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 20,
'height': 52,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'clipTo': null,
'text': 'x',
'fontSize': 40,
'fontWeight': 'normal',
'fontFamily': 'Times New Roman',
'fontStyle': '',
'lineHeight': 1.3,
'textDecoration': '',
'textAlign': 'left',
'path': null,
'backgroundColor': '',
'textBackgroundColor': '',
'useNative': true,
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over',
styles: { }
};
test('constructor', function() {

View file

@ -1,34 +1,36 @@
(function(){
var LINE_OBJECT = {
'type': 'line',
'originX': 'left',
'originY': 'top',
'left': 11,
'top': 12,
'width': 2,
'height': 2,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'x1': 11,
'y1': 12,
'x2': 13,
'y2': 14,
'shadow': null,
'visible': true,
'clipTo': null,
'backgroundColor': ''
'type': 'line',
'originX': 'left',
'originY': 'top',
'left': 11,
'top': 12,
'width': 2,
'height': 2,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'x1': 11,
'y1': 12,
'x2': 13,
'y2': 14,
'shadow': null,
'visible': true,
'clipTo': null,
'backgroundColor': '',
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over'
};
QUnit.module('fabric.Line');

View file

@ -153,12 +153,12 @@
var emptyObjectJSON = '{"type":"object","originX":"left","originY":"top","left":0,"top":0,"width":0,"height":0,"fill":"rgb(0,0,0)",'+
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":""}';
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over"}';
var augmentedJSON = '{"type":"object","originX":"left","originY":"top","left":0,"top":0,"width":122,"height":0,"fill":"rgb(0,0,0)",'+
'"stroke":null,"strokeWidth":1,"strokeDashArray":[5,2],"strokeLineCap":"round","strokeLineJoin":"bevil","strokeMiterLimit":5,'+
'"scaleX":1.3,"scaleY":1,"angle":0,"flipX":false,"flipY":true,"opacity":0.88,'+
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":""}';
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over"}';
var cObj = new fabric.Object();
ok(typeof cObj.toJSON == 'function');
@ -178,57 +178,61 @@
test('toObject', function() {
var emptyObjectRepr = {
'type': 'object',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 0,
'height': 0,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null
'type': 'object',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 0,
'height': 0,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over'
};
var augmentedObjectRepr = {
'type': 'object',
'originX': 'left',
'originY': 'top',
'left': 10,
'top': 20,
'width': 30,
'height': 40,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': [5, 2],
'strokeLineCap': 'round',
'strokeLineJoin': 'bevil',
'strokeMiterLimit': 5,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': true,
'flipY': false,
'opacity': 0.13,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null
'type': 'object',
'originX': 'left',
'originY': 'top',
'left': 10,
'top': 20,
'width': 30,
'height': 40,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': [5, 2],
'strokeLineCap': 'round',
'strokeLineJoin': 'bevil',
'strokeMiterLimit': 5,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': true,
'flipY': false,
'opacity': 0.13,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over'
};
var cObj = new fabric.Object();

View file

@ -51,7 +51,7 @@
var element = fabric.document.createElement('path');
element.setAttribute('fill-rule', 'evenodd');
deepEqual(fabric.parseAttributes(element, ['fill-rule']), { fillRule: 'destination-over' });
deepEqual(fabric.parseAttributes(element, ['fill-rule']), { fillRule: 'evenodd' });
});
test('parseAttributesFillRuleWithoutTransformation', function() {

View file

@ -1,32 +1,34 @@
(function() {
var REFERENCE_PATH_OBJECT = {
'type': 'path',
'originX': 'left',
'originY': 'top',
'left': 100,
'top': 100,
'width': 200,
'height': 200,
'fill': 'red',
'stroke': 'blue',
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'path': [['M', 100, 100], ['L', 300, 100], ['L', 200, 300], ['z']],
'pathOffset': { x: 200, y: 200 },
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null
'type': 'path',
'originX': 'left',
'originY': 'top',
'left': 100,
'top': 100,
'width': 200,
'height': 200,
'fill': 'red',
'stroke': 'blue',
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'path': [['M', 100, 100], ['L', 300, 100], ['L', 200, 300], ['z']],
'pathOffset': { x: 200, y: 200 },
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over'
};
function getPathElement(path) {

View file

@ -1,31 +1,33 @@
(function(){
var REFERENCE_PATH_GROUP_OBJECT = {
'type': 'path-group',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 0,
'height': 0,
'fill': '',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'clipTo': null,
'backgroundColor': '',
'paths': getPathObjects()
'type': 'path-group',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 0,
'height': 0,
'fill': '',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'clipTo': null,
'backgroundColor': '',
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over',
'paths': getPathObjects()
};
function getPathElement(path) {

View file

@ -8,31 +8,33 @@
}
var REFERENCE_OBJECT = {
'type': 'polygon',
'originX': 'left',
'originY': 'top',
'left': 10,
'top': 12,
'width': 10,
'height': 10,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'points': getPoints(),
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null
'type': 'polygon',
'originX': 'left',
'originY': 'top',
'left': 10,
'top': 12,
'width': 10,
'height': 10,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'points': getPoints(),
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over'
};
QUnit.module('fabric.Polygon');

View file

@ -8,31 +8,33 @@
}
var REFERENCE_OBJECT = {
'type': 'polyline',
'originX': 'left',
'originY': 'top',
'left': 10,
'top': 12,
'width': 10,
'height': 10,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'points': getPoints(),
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null
'type': 'polyline',
'originX': 'left',
'originY': 'top',
'left': 10,
'top': 12,
'width': 10,
'height': 10,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'points': getPoints(),
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over'
};
QUnit.module('fabric.Polyline');

View file

@ -1,32 +1,34 @@
(function() {
var REFERENCE_RECT = {
'type': 'rect',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 0,
'height': 0,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'rx': 0,
'ry': 0,
'type': 'rect',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': 0,
'height': 0,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over',
'rx': 0,
'ry': 0,
};
QUnit.module('fabric.Rect');
@ -131,7 +133,7 @@
var rect = new fabric.Rect({ width: 100, height: 100, rx: 20, ry: 30 });
var svg = rect.toSVG();
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); fill-rule: source-over; opacity: 1;" transform="translate(50 50)"/>\n');
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); fill-rule: nonzero; opacity: 1;" transform="translate(50 50)"/>\n');
});
test('toObject without default values', function() {

View file

@ -9,44 +9,46 @@
var CHAR_WIDTH = 20;
var REFERENCE_TEXT_OBJECT = {
'type': 'text',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': CHAR_WIDTH,
'height': 52,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'clipTo': null,
'backgroundColor': '',
'text': 'x',
'fontSize': 40,
'fontWeight': 'normal',
'fontFamily': 'Times New Roman',
'fontStyle': '',
'lineHeight': 1.3,
'textDecoration': '',
'textAlign': 'left',
'path': null,
'textBackgroundColor': '',
'useNative': true
'type': 'text',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': CHAR_WIDTH,
'height': 52,
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 1,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 10,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'shadow': null,
'visible': true,
'clipTo': null,
'backgroundColor': '',
'text': 'x',
'fontSize': 40,
'fontWeight': 'normal',
'fontFamily': 'Times New Roman',
'fontStyle': '',
'lineHeight': 1.3,
'textDecoration': '',
'textAlign': 'left',
'path': null,
'textBackgroundColor': '',
'useNative': true,
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over'
};
var TEXT_SVG = '<g transform="translate(10 26)">\n<text font-family="Times New Roman" font-size="40" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform="translate(-10 39)"><tspan x="0" y="-26" fill="rgb(0,0,0)">x</tspan></text>\n</g>\n';
var TEXT_SVG = '<g transform="translate(10 26)">\n<text font-family="Times New Roman" font-size="40" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform="translate(-10 39)"><tspan x="0" y="-26" fill="rgb(0,0,0)">x</tspan></text>\n</g>\n';
test('constructor', function() {
ok(fabric.Text);