From 0fc71349a602fef23d427ef2a3f5cc18f7bc3c55 Mon Sep 17 00:00:00 2001 From: Stefan Hayden Date: Sun, 17 Sep 2017 16:47:35 -0400 Subject: [PATCH] Paint order (#4303) * makes fabric aware of the paint-order svg2 spec to swap the paint order of stroke and fill --- HEADER.js | 2 +- src/mixins/object.svg_export.js | 4 ++++ src/parser.js | 12 ++++++++++++ src/shapes/circle.class.js | 6 +++--- src/shapes/ellipse.class.js | 8 ++++---- src/shapes/object.class.js | 28 +++++++++++++++++++++++++++- src/shapes/path.class.js | 4 ++-- src/shapes/polygon.class.js | 3 +-- src/shapes/polyline.class.js | 8 ++++---- src/shapes/rect.class.js | 8 ++++---- src/shapes/text.class.js | 10 ++++++++-- src/shapes/triangle.class.js | 8 ++++---- test/unit/activeselection.js | 1 + test/unit/canvas.js | 4 ++-- test/unit/canvas_static.js | 7 ++++--- test/unit/circle.js | 1 + test/unit/ellipse.js | 1 + test/unit/group.js | 1 + test/unit/image.js | 1 + test/unit/itext.js | 1 + test/unit/line.js | 1 + test/unit/object.js | 6 ++++-- test/unit/path.js | 1 + test/unit/polygon.js | 1 + test/unit/polyline.js | 1 + test/unit/rect.js | 21 +++++++++++++++++++++ test/unit/text.js | 2 ++ 27 files changed, 117 insertions(+), 34 deletions(-) diff --git a/HEADER.js b/HEADER.js index 3305dc4c632..b3a79c8b7ac 100644 --- a/HEADER.js +++ b/HEADER.js @@ -48,7 +48,7 @@ fabric.SHARED_ATTRIBUTES = [ "stroke", "stroke-dasharray", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", - "id", + "id", "paint-order", "instantiated_by_use" ]; /* _FROM_SVG_END_ */ diff --git a/src/mixins/object.svg_export.js b/src/mixins/object.svg_export.js index fb6b7f6a96a..55a1be3bc8d 100644 --- a/src/mixins/object.svg_export.js +++ b/src/mixins/object.svg_export.js @@ -195,6 +195,10 @@ markup.push(this.shadow.toSVG(this)); } return markup; + }, + + addPaintOrder: function() { + return this.paintFirst !== 'fill' ? ' paint-order="' + this.paintFirst + '" ' : ''; } }); })(); diff --git a/src/parser.js b/src/parser.js index 828af52d98e..168debb266b 100644 --- a/src/parser.js +++ b/src/parser.js @@ -35,6 +35,7 @@ 'font-size': 'fontSize', 'font-style': 'fontStyle', 'font-weight': 'fontWeight', + 'paint-order': 'paintFirst', 'stroke-dasharray': 'strokeDashArray', 'stroke-linecap': 'strokeLineCap', 'stroke-linejoin': 'strokeLineJoin', @@ -109,6 +110,17 @@ else if (attr === 'textAnchor' /* text-anchor */) { value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center'; } + else if (attr === 'paintFirst') { + var fillIndex = value.indexOf('fill'); + var strokeIndex = value.indexOf('stroke'); + var value = 'fill'; + if (fillIndex > -1 && strokeIndex > -1 && strokeIndex < fillIndex) { + value = 'stroke'; + } + else if (fillIndex === -1 && strokeIndex > -1) { + value = 'stroke'; + } + } else { parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize); } diff --git a/src/shapes/circle.class.js b/src/shapes/circle.class.js index 71f51e317bf..a8f5cb7fb14 100644 --- a/src/shapes/circle.class.js +++ b/src/shapes/circle.class.js @@ -138,13 +138,13 @@ */ _render: function(ctx) { ctx.beginPath(); - ctx.arc(0, + ctx.arc( + 0, 0, this.radius, this.startAngle, this.endAngle, false); - this._renderFill(ctx); - this._renderStroke(ctx); + this._renderPaintInOrder(ctx); }, /** diff --git a/src/shapes/ellipse.class.js b/src/shapes/ellipse.class.js index 35ba1b643df..1b381cf364b 100644 --- a/src/shapes/ellipse.class.js +++ b/src/shapes/ellipse.class.js @@ -124,8 +124,9 @@ '" ry="', this.ry, '" style="', this.getSvgStyles(), '" transform="', this.getSvgTransform(), - this.getSvgTransformMatrix(), - '"/>\n' + this.getSvgTransformMatrix(), '"', + this.addPaintOrder(), + '/>\n' ); return reviver ? reviver(markup.join('')) : markup.join(''); @@ -148,8 +149,7 @@ piBy2, false); ctx.restore(); - this._renderFill(ctx); - this._renderStroke(ctx); + this._renderPaintInOrder(ctx); }, }); diff --git a/src/shapes/object.class.js b/src/shapes/object.class.js index 483334939c7..cb6265e6ab2 100644 --- a/src/shapes/object.class.js +++ b/src/shapes/object.class.js @@ -555,6 +555,13 @@ */ __corner: 0, + /** + * Determins if the fill or the stroke is drawn first (one of "fill" or "stroke") + * @type String + * @default + */ + paintFirst: 'fill', + /** * List of properties to consider when checking if state * of an object is changed (fabric.Object#hasStateChanged) @@ -565,7 +572,7 @@ 'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' + 'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' + 'angle opacity fill globalCompositeOperation shadow clipTo visible backgroundColor ' + - 'skewX skewY fillRule' + 'skewX skewY fillRule paintFirst' ).split(' '), /** @@ -787,6 +794,7 @@ clipTo: this.clipTo && String(this.clipTo), backgroundColor: this.backgroundColor, fillRule: this.fillRule, + paintFirst: this.paintFirst, globalCompositeOperation: this.globalCompositeOperation, transformMatrix: this.transformMatrix ? this.transformMatrix.concat() : null, skewX: toFixed(this.skewX, NUM_FRACTION_DIGITS), @@ -997,6 +1005,9 @@ * @returns false */ needsItsOwnCache: function() { + if (this.paintFirst === 'stroke' && typeof this.shadow === 'object') { + return true; + } return false; }, @@ -1234,6 +1245,21 @@ return { offsetX: offsetX, offsetY: offsetY }; }, + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _renderPaintInOrder: function(ctx) { + if (this.paintFirst === 'stroke') { + this._renderStroke(ctx); + this._renderFill(ctx); + } + else { + this._renderFill(ctx); + this._renderStroke(ctx); + } + }, + /** * @private * @param {CanvasRenderingContext2D} ctx Context to render on diff --git a/src/shapes/path.class.js b/src/shapes/path.class.js index fa81a67ea76..35cb6e99c31 100644 --- a/src/shapes/path.class.js +++ b/src/shapes/path.class.js @@ -430,8 +430,7 @@ */ _render: function(ctx) { this._renderPathCommands(ctx); - this._renderFill(ctx); - this._renderStroke(ctx); + this._renderPaintInOrder(ctx); }, /** @@ -491,6 +490,7 @@ '" style="', this.getSvgStyles(), '" transform="', this.getSvgTransform(), addTransform, this.getSvgTransformMatrix(), '" stroke-linecap="round" ', + this.addPaintOrder(), '/>\n' ); diff --git a/src/shapes/polygon.class.js b/src/shapes/polygon.class.js index f1b43afe05b..6198e1c47b5 100644 --- a/src/shapes/polygon.class.js +++ b/src/shapes/polygon.class.js @@ -34,8 +34,7 @@ return; } ctx.closePath(); - this._renderFill(ctx); - this._renderStroke(ctx); + this._renderPaintInOrder(ctx); }, /** diff --git a/src/shapes/polyline.class.js b/src/shapes/polyline.class.js index e791e7e017c..95820d92fb1 100644 --- a/src/shapes/polyline.class.js +++ b/src/shapes/polyline.class.js @@ -139,8 +139,9 @@ 'points="', points.join(''), '" style="', this.getSvgStyles(), '" transform="', this.getSvgTransform(), - ' ', this.getSvgTransformMatrix(), - '"/>\n' + ' ', this.getSvgTransformMatrix(), '"', + this.addPaintOrder(), + '/>\n' ); return reviver ? reviver(markup.join('')) : markup.join(''); @@ -179,8 +180,7 @@ if (!this.commonRender(ctx)) { return; } - this._renderFill(ctx); - this._renderStroke(ctx); + this._renderPaintInOrder(ctx); }, /** diff --git a/src/shapes/rect.class.js b/src/shapes/rect.class.js index 1ae8c307c1a..206070c091d 100644 --- a/src/shapes/rect.class.js +++ b/src/shapes/rect.class.js @@ -117,8 +117,7 @@ ctx.closePath(); - this._renderFill(ctx); - this._renderStroke(ctx); + this._renderPaintInOrder(ctx); }, /** @@ -163,8 +162,9 @@ '" width="', this.width, '" height="', this.height, '" style="', this.getSvgStyles(), '" transform="', this.getSvgTransform(), - this.getSvgTransformMatrix(), - '"/>\n'); + this.getSvgTransformMatrix(), '"', + this.addPaintOrder(), + '/>\n'); return reviver ? reviver(markup.join('')) : markup.join(''); }, diff --git a/src/shapes/text.class.js b/src/shapes/text.class.js index 940b2a9c8df..b5b914419af 100644 --- a/src/shapes/text.class.js +++ b/src/shapes/text.class.js @@ -404,8 +404,14 @@ * @param {CanvasRenderingContext2D} ctx Context to render on */ _renderText: function(ctx) { - this._renderTextFill(ctx); - this._renderTextStroke(ctx); + if (this.paintFirst === 'stroke') { + this._renderTextStroke(ctx); + this._renderTextFill(ctx); + } + else { + this._renderTextFill(ctx); + this._renderTextStroke(ctx); + } }, /** diff --git a/src/shapes/triangle.class.js b/src/shapes/triangle.class.js index db46050ea52..1222f8f3f7e 100644 --- a/src/shapes/triangle.class.js +++ b/src/shapes/triangle.class.js @@ -50,8 +50,7 @@ ctx.lineTo(widthBy2, heightBy2); ctx.closePath(); - this._renderFill(ctx); - this._renderStroke(ctx); + this._renderPaintInOrder(ctx); }, /** @@ -90,8 +89,9 @@ '' + '" transform="', this.getSvgTransform(), '"', + this.addPaintOrder(), + '/>' ); return reviver ? reviver(markup.join('')) : markup.join(''); diff --git a/test/unit/activeselection.js b/test/unit/activeselection.js index 1c3362fdc98..fcf409c5243 100644 --- a/test/unit/activeselection.js +++ b/test/unit/activeselection.js @@ -82,6 +82,7 @@ 'flipY': false, 'opacity': 1, 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'transformMatrix': null, 'skewX': 0, diff --git a/test/unit/canvas.js b/test/unit/canvas.js index f644e00daa0..0ed47ed8d08 100644 --- a/test/unit/canvas.js +++ b/test/unit/canvas.js @@ -57,12 +57,12 @@ var PATH_DATALESS_JSON = '{"version":"' + fabric.version + '","objects":[{"type":"path","version":"' + fabric.version + '","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":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"sourcePath":"http://example.com/"}]}'; + '"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"sourcePath":"http://example.com/"}]}'; var RECT_JSON = '{"version":"' + fabric.version + '","objects":[{"type":"rect","version":"' + fabric.version + '","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":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}'; + '"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}'; function _createImageElement() { return fabric.isLikelyNode ? new (require(fabric.canvasModule).Image)() : fabric.document.createElement('img'); diff --git a/test/unit/canvas_static.js b/test/unit/canvas_static.js index de2e1150771..be433e4ac7f 100644 --- a/test/unit/canvas_static.js +++ b/test/unit/canvas_static.js @@ -43,17 +43,17 @@ var PATH_DATALESS_JSON = '{"version":"' + fabric.version + '","objects":[{"type":"path","version":"' + fabric.version + '","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":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"sourcePath":"http://example.com/"}]}'; + '"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"sourcePath":"http://example.com/"}]}'; var RECT_JSON = '{"version":"' + fabric.version + '","objects":[{"type":"rect","version":"' + fabric.version + '","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":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}'; + '"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}'; var RECT_JSON_WITH_PADDING = '{"version":"' + fabric.version + '","objects":[{"type":"rect","version":"' + fabric.version + '","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":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0,"padding":123,"foo":"bar"}]}'; + '"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0,"padding":123,"foo":"bar"}]}'; function getAbsolutePath(path) { var isAbsolute = /^https?:/.test(path); @@ -98,6 +98,7 @@ 'clipTo': null, 'filters': [], 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'transformMatrix': null, 'crossOrigin': '', diff --git a/test/unit/circle.js b/test/unit/circle.js index 18f35fe08f3..cc3c99d0def 100644 --- a/test/unit/circle.js +++ b/test/unit/circle.js @@ -105,6 +105,7 @@ 'backgroundColor': '', 'clipTo': null, 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'radius': 0, 'startAngle': 0, diff --git a/test/unit/ellipse.js b/test/unit/ellipse.js index 84631858f1c..38cf9a0ebfe 100644 --- a/test/unit/ellipse.js +++ b/test/unit/ellipse.js @@ -51,6 +51,7 @@ 'visible': true, 'backgroundColor': '', 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'clipTo': null, 'transformMatrix': null diff --git a/test/unit/group.js b/test/unit/group.js index a9a3414864c..9e8290b6a93 100644 --- a/test/unit/group.js +++ b/test/unit/group.js @@ -189,6 +189,7 @@ 'flipY': false, 'opacity': 1, 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'transformMatrix': null, 'skewX': 0, diff --git a/test/unit/image.js b/test/unit/image.js index b3dfd7084aa..bcbf56915ca 100644 --- a/test/unit/image.js +++ b/test/unit/image.js @@ -43,6 +43,7 @@ 'clipTo': null, 'filters': [], 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'skewX': 0, 'skewY': 0, diff --git a/test/unit/itext.js b/test/unit/itext.js index be415d3e14b..6655e938264 100644 --- a/test/unit/itext.js +++ b/test/unit/itext.js @@ -45,6 +45,7 @@ 'backgroundColor': '', 'textBackgroundColor': '', 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'skewX': 0, 'skewY': 0, diff --git a/test/unit/line.js b/test/unit/line.js index c6f510447e3..a55e6c90ede 100644 --- a/test/unit/line.js +++ b/test/unit/line.js @@ -31,6 +31,7 @@ 'clipTo': null, 'backgroundColor': '', 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'skewX': 0, 'skewY': 0, diff --git a/test/unit/object.js b/test/unit/object.js index f4124bb0917..4878976d873 100644 --- a/test/unit/object.js +++ b/test/unit/object.js @@ -150,13 +150,13 @@ var emptyObjectJSON = '{"type":"object","version":"' + fabric.version + '","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":"","fillRule":"nonzero","globalCompositeOperation":"source-over",' + + '"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over",' + '"transformMatrix":null,"skewX":0,"skewY":0}'; var augmentedJSON = '{"type":"object","version":"' + fabric.version + '","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":"","fillRule":"nonzero","globalCompositeOperation":"source-over",' + + '"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over",' + '"transformMatrix":null,"skewX":0,"skewY":0}'; var cObj = new fabric.Object(); @@ -203,6 +203,7 @@ 'backgroundColor': '', 'clipTo': null, 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'skewX': 0, 'skewY': 0, @@ -236,6 +237,7 @@ 'backgroundColor': '', 'clipTo': null, 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'transformMatrix': null, 'skewX': 0, diff --git a/test/unit/path.js b/test/unit/path.js index a79630d3edc..3e6728190d9 100644 --- a/test/unit/path.js +++ b/test/unit/path.js @@ -28,6 +28,7 @@ 'backgroundColor': '', 'clipTo': null, 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'skewX': 0, 'skewY': 0, diff --git a/test/unit/polygon.js b/test/unit/polygon.js index 38256ed9119..13058c4f0ae 100644 --- a/test/unit/polygon.js +++ b/test/unit/polygon.js @@ -35,6 +35,7 @@ 'backgroundColor': '', 'clipTo': null, 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'skewX': 0, 'skewY': 0, diff --git a/test/unit/polyline.js b/test/unit/polyline.js index 9907655b76f..d4f9bc1eea4 100644 --- a/test/unit/polyline.js +++ b/test/unit/polyline.js @@ -35,6 +35,7 @@ 'backgroundColor': '', 'clipTo': null, 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'skewX': 0, 'skewY': 0, diff --git a/test/unit/rect.js b/test/unit/rect.js index f89cd01b102..6600ff99d2e 100644 --- a/test/unit/rect.js +++ b/test/unit/rect.js @@ -27,6 +27,7 @@ 'backgroundColor': '', 'clipTo': null, 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'transformMatrix': null, 'rx': 0, @@ -192,10 +193,30 @@ assert.equal(svg, '\n'); }); + QUnit.test('toSVG with paintFirst set to stroke', function(assert) { + var rect = new fabric.Rect({ width: 100, height: 100, paintFirst: 'stroke' }); + var svg = rect.toSVG(); + assert.equal(svg, '\n'); + }); + QUnit.test('toObject without default values', function(assert) { var options = { type: 'rect', width: 69, height: 50, left: 10, top: 20, version: fabric.version, }; var rect = new fabric.Rect(options); rect.includeDefaultValues = false; assert.deepEqual(rect.toObject(), options); }); + + QUnit.test('paintFirst life cycle', function(assert) { + var done = assert.async(); + var svg = ''; + fabric.loadSVGFromString(svg, function(envlivedObjects) { + var rect = envlivedObjects[0]; + var rectObject = rect.toObject(); + var rectSvg = rect.toSVG(); + assert.equal(rect.paintFirst, 'stroke'); + assert.equal(rectObject.paintFirst, 'stroke'); + assert.ok(rectSvg.indexOf('paint-order="stroke"') > -1); + done(); + }); + }); })(); diff --git a/test/unit/text.js b/test/unit/text.js index 5243a6f8d2b..9a99b7f66ca 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -50,6 +50,7 @@ 'textAlign': 'left', 'textBackgroundColor': '', 'fillRule': 'nonzero', + 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'skewX': 0, 'skewY': 0, @@ -250,6 +251,7 @@ strokeLineJoin: 'bevil', strokeMiterLimit: 5, fontFamily: 'Monaco', + paintFirst: 'fill', fontStyle: 'italic', fontWeight: 'bold', fontSize: 123,