Merge pull request #1567 from asturur/SVG-EXPORT

Better SVG export
This commit is contained in:
Juriy Zaytsev 2014-08-07 20:42:56 +02:00
commit 4c7ad6a82d
24 changed files with 151 additions and 270 deletions

View file

@ -1,6 +1,6 @@
language: node_js
node_js:
- "0.10"
- "0.10.29"
script: 'npm run-script build && npm test'
before_install:
- sudo apt-get update -qq

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),
stroke = this.stroke
? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
: 'none',
@ -33,6 +33,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
'stroke-linejoin: ', strokeLineJoin, '; ',
'stroke-miterlimit: ', strokeMiterLimit, '; ',
'fill: ', fill, '; ',
'fill-rule: ', fillRule, '; ',
'opacity: ', opacity, ';',
filter,
visibility
@ -44,6 +45,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String}
*/
getSvgTransform: function() {
if (this.group) return '';
var toFixed = fabric.util.toFixed,
angle = this.getAngle(),
vpt = this.getViewportTransform(),
@ -51,7 +53,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
translatePart = 'translate(' +
translatePart = this.type === 'path-group' ? '' : 'translate(' +
toFixed(center.x, NUM_FRACTION_DIGITS) +
' ' +
toFixed(center.y, NUM_FRACTION_DIGITS) +
@ -68,16 +70,24 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
' ' +
toFixed(this.scaleY * vpt[3], NUM_FRACTION_DIGITS) +
')'),
flipXPart = this.flipX ? 'matrix(-1 0 0 1 0 0) ' : '',
flipYPart = this.flipY ? 'matrix(1 0 0 -1 0 0)' : '';
addTranslateX = this.type === 'path-group' ? this.width * vpt[0] : 0,
flipXPart = this.flipX ? ' matrix(-1 0 0 1 ' + addTranslateX + ' 0) ' : '',
addTranslateY = this.type === 'path-group' ? this.height * vpt[3] : 0,
flipYPart = this.flipY ? ' matrix(1 0 0 -1 0 ' + addTranslateY + ')' : '';
return [
translatePart, anglePart, scalePart, flipXPart, flipYPart
].join('');
},
/**
* Returns transform-string for svg-export from the transform matrix of single elements
* @return {String}
*/
getSvgTransformMatrix: function() {
return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ')' : '';
},
/**
* @private
*/

View file

@ -527,7 +527,7 @@
if (heightAttr && heightAttr !== viewBoxHeight) {
scaleY = heightAttr / viewBoxHeight;
}
addSvgTransform(doc, [scaleX, 0, 0, scaleY, -minX, -minY]);
addSvgTransform(doc, [scaleX, 0, 0, scaleY, scaleX * -minX, scaleY * -minY]);
}
var descendants = fabric.util.toArray(doc.getElementsByTagName('*'));

View file

@ -79,15 +79,19 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var markup = this._createBaseSVGMarkup();
var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
if (this.group) {
x = this.left + this.radius;
y = this.top + this.radius;
}
markup.push(
'<circle ',
'cx="0" cy="0" ',
'cx="' + x + '" cy="' + y + '" ',
'r="', this.radius,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
'"/>'
' ', this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
@ -101,11 +105,9 @@
*/
_render: function(ctx, noTransform) {
ctx.beginPath();
// multiply by currently set alpha (the one that was set by path group where this object is contained, for example)
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
ctx.arc(noTransform ? this.left : 0, noTransform ? this.top : 0, this.radius, 0, piBy2, false);
ctx.arc(noTransform ? this.left + this.radius : 0, noTransform ? this.top + this.radius : 0, this.radius, 0, piBy2, false);
this._renderFill(ctx);
this.stroke && this._renderStroke(ctx);
this._renderStroke(ctx);
},
/**
@ -169,22 +171,13 @@
throw new Error('value of `r` attribute is required and can not be negative');
}
if (!('left' in parsedAttributes)) {
parsedAttributes.left = 0;
}
if (!('top' in parsedAttributes)) {
parsedAttributes.top = 0;
}
if (!('transformMatrix' in parsedAttributes)) {
parsedAttributes.left -= options.width ? (options.width / 2) : 0;
parsedAttributes.top -= options.height ? (options.height / 2) : 0;
}
parsedAttributes.left = parsedAttributes.left || 0;
parsedAttributes.top = parsedAttributes.top || 0;
var obj = new fabric.Circle(extend(parsedAttributes, options));
obj.cx = parseFloat(element.getAttribute('cx')) || 0;
obj.cy = parseFloat(element.getAttribute('cy')) || 0;
obj.left -= obj.radius;
obj.top -= obj.radius;
return obj;
};

View file

@ -77,34 +77,26 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var markup = this._createBaseSVGMarkup();
var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
if (this.group) {
x = this.left + this.rx;
y = this.top + this.ry;
}
markup.push(
'<ellipse ',
'rx="', this.get('rx'),
'" ry="', this.get('ry'),
'cx="', x, '" cy="', y, '" ',
'rx="', this.rx,
'" ry="', this.ry,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
'"/>'
this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
},
/* _TO_SVG_END_ */
/**
* Renders this instance on a given context
* @param {CanvasRenderingContext2D} ctx context to render on
* @param {Boolean} [noTransform] When true, context is not transformed
*/
render: function(ctx, noTransform) {
// do not use `get` for perf. reasons
if (this.rx === 0 || this.ry === 0) {
return;
}
return this.callSuper('render', ctx, noTransform);
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx context to render on
@ -112,10 +104,9 @@
*/
_render: function(ctx, noTransform) {
ctx.beginPath();
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
ctx.save();
ctx.transform(1, 0, 0, this.ry/this.rx, 0, 0);
ctx.arc(noTransform ? this.left : 0, noTransform ? this.top * this.rx/this.ry : 0, this.rx, 0, piBy2, false);
ctx.arc(noTransform ? this.left + this.rx : 0, noTransform ? (this.top + this.ry) * this.rx/this.ry : 0, this.rx, 0, piBy2, false);
ctx.restore();
this._renderFill(ctx);
this._renderStroke(ctx);
@ -152,21 +143,13 @@
var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES);
if (!('left' in parsedAttributes)) {
parsedAttributes.left = 0;
}
if (!('top' in parsedAttributes)) {
parsedAttributes.top = 0;
}
if (!('transformMatrix' in parsedAttributes)) {
parsedAttributes.left -= options.width ? (options.width / 2) : 0;
parsedAttributes.top -= options.height ? (options.height / 2) : 0;
}
parsedAttributes.left = parsedAttributes.left || 0;
parsedAttributes.top = parsedAttributes.top || 0;
var ellipse = new fabric.Ellipse(extend(parsedAttributes, options));
ellipse.cx = parseFloat(element.getAttribute('cx')) || 0;
ellipse.cy = parseFloat(element.getAttribute('cy')) || 0;
ellipse.top -= ellipse.ry;
ellipse.left -= ellipse.rx;
return ellipse;
};
/* _FROM_SVG_END_ */

View file

@ -482,7 +482,7 @@
//jscs:disable validateIndentation
'<g ',
'transform="', this.getSvgTransform(),
'">'
'">\n'
//jscs:enable validateIndentation
];
@ -490,7 +490,7 @@
markup.push(this._objects[i].toSVG(reviver));
}
markup.push('</g>');
markup.push('</g>\n');
return reviver ? reviver(markup.join('')) : markup.join('');
},

View file

@ -112,46 +112,6 @@
};
},
/**
* Renders image on a specified context
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Boolean} [noTransform] When true, context is not transformed
*/
render: function(ctx, noTransform) {
// do not render if object is not visible
if (!this.visible) {
return;
}
ctx.save();
var m = this.transformMatrix,
isInPathGroup = this.group && this.group.type === 'path-group';
// this._resetWidthHeight();
if (isInPathGroup) {
ctx.translate(-this.group.width/2, -this.group.height/2);
}
if (m) {
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
if (!noTransform) {
this.transform(ctx);
}
if (isInPathGroup) {
ctx.translate(this.width/2, this.height/2);
}
this._setShadow(ctx);
this.clipTo && fabric.util.clipContext(this, ctx);
this._render(ctx);
if (this.shadow && !this.shadow.affectStroke) {
this._removeShadow(ctx);
}
this._renderStroke(ctx);
this.clipTo && ctx.restore();
ctx.restore();
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
@ -209,20 +169,23 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var markup = [];
var markup = [], x = -this.width / 2, y = -this.height / 2;
if (this.group) {
x = this.left;
y = this.top;
}
markup.push(
'<g transform="', this.getSvgTransform(), '">',
'<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
'<image xlink:href="', this.getSvgSrc(),
'" x="', x, '" y="', y,
'" style="', this.getSvgStyles(),
// we're essentially moving origin of transformation from top/left corner to the center of the shape
// by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
// so that object's center aligns with container's left/top
'" transform="translate(' + (-this.width/2) + ' ' + (-this.height/2) + ')',
'" width="', this.width,
'" height="', this.height,
'" preserveAspectRatio="none"',
'></image>'
'></image>\n'
);
if (this.stroke || this.strokeDashArray) {
@ -230,15 +193,15 @@
this.fill = null;
markup.push(
'<rect ',
'x="', (-1 * this.width / 2), '" y="', (-1 * this.height / 2),
'x="', x, '" y="', y,
'" width="', this.width, '" height="', this.height,
'" style="', this.getSvgStyles(),
'"/>'
'"/>\n'
);
this.fill = origFill;
}
markup.push('</g>');
markup.push('</g>\n');
return reviver ? reviver(markup.join('')) : markup.join('');
},
@ -332,15 +295,16 @@
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
_render: function(ctx, noTransform) {
this._element &&
ctx.drawImage(
this._element,
-this.width / 2,
-this.height / 2,
noTransform ? this.left : -this.width/2,
noTransform ? this.top : -this.height/2,
this.width,
this.height
);
this._renderStroke(ctx);
},
/**

View file

@ -150,11 +150,10 @@
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
_render: function(ctx, noTransform) {
ctx.beginPath();
var isInPathGroup = this.group && this.group.type === 'path-group';
if (isInPathGroup) {
if (noTransform) {
// Line coords are distances from left-top of canvas to origin of line.
//
// To render line in a path-group, we need to translate them to
@ -164,9 +163,6 @@
cp.x,
cp.y
);
if (!this.transformMatrix) {
ctx.translate(-this.group.width / 2, -this.group.height / 2);
}
}
if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) {
@ -234,16 +230,22 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var markup = this._createBaseSVGMarkup();
var markup = this._createBaseSVGMarkup(), addTranslate = '';
if (!this.group) {
var x = - this.width / 2 - (this.x1 > this.x2 ? this.x2 : this.x1),
y = - this.height / 2 - (this.y1 > this.y2 ? this.y2 : this.y1);
addTranslate = 'translate(' + x + ', ' + y + ') ';
}
markup.push(
'<line ',
'x1="', this.get('x1'),
'" y1="', this.get('y1'),
'" x2="', this.get('x2'),
'" y2="', this.get('y2'),
'x1="', this.x1,
'" y1="', this.y1,
'" x2="', this.x2,
'" y2="', this.y2,
'" style="', this.getSvgStyles(),
'"/>'
'" transform="', this.getSvgTransform(), addTranslate,
this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');

View file

@ -960,12 +960,14 @@
this._setStrokeStyles(ctx);
this._setFillStyles(ctx);
var m = this.transformMatrix;
if (m && this.group) {
if (this.group && this.group.type === 'path-group') {
ctx.translate(-this.group.width/2, -this.group.height/2);
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
var m = this.transformMatrix;
if (m) {
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
}
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
this._setShadow(ctx);
this.clipTo && fabric.util.clipContext(this, ctx);
this._render(ctx, noTransform);

View file

@ -545,15 +545,13 @@
var path = chunks.join(' ');
markup.push(
//jscs:disable validateIndentation
'<g transform="', (this.group ? '' : this.getSvgTransform()), '">',
'<path ',
'd="', path,
'" style="', this.getSvgStyles(),
'" transform="translate(', (-this.width / 2), ' ', (-this.height/2), ')',
'" stroke-linecap="round" ',
'/>',
'</g>'
//jscs:disable validateIndentation
'<path ',
'd="', path,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
this.getSvgTransformMatrix(), '" stroke-linecap="round" ',
'/>\n'
//jscs:enable validateIndentation
);

View file

@ -149,19 +149,20 @@
*/
toSVG: function(reviver) {
var objects = this.getObjects(),
translatePart = 'translate(' + this.left + ' ' + this.top + ')',
markup = [
//jscs:disable validateIndentation
'<g ',
'style="', this.getSvgStyles(), '" ',
'transform="', this.getSvgTransform(), '" ',
'>'
'transform="', translatePart, this.getSvgTransform(), '" ',
'>\n'
//jscs:enable validateIndentation
];
for (var i = 0, len = objects.length; i < len; i++) {
markup.push(objects[i].toSVG(reviver));
}
markup.push('</g>');
markup.push('</g>\n');
return reviver ? reviver(markup.join('')) : markup.join('');
},

View file

@ -111,7 +111,8 @@
'points="', points.join(''),
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
'"/>'
' ', this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
@ -125,7 +126,6 @@
_render: function(ctx) {
var point;
ctx.beginPath();
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
ctx.moveTo(this.points[0].x, this.points[0].y);
for (var i = 0, len = this.points.length; i < len; i++) {
point = this.points[i];
@ -184,6 +184,7 @@
if (!element) {
return null;
}
options || (options = { });
var points = fabric.parsePointsAttribute(element.getAttribute('points')),
@ -193,9 +194,6 @@
return null;
}
if (!('transformMatrix' in parsedAttributes)) {
fabric.util.normalizePoints(points, options);
}
return new fabric.Polygon(points, extend(parsedAttributes, options), true);
};
/* _FROM_SVG_END_ */

View file

@ -95,7 +95,8 @@
'points="', points.join(''),
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
'"/>'
' ', this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
@ -169,9 +170,10 @@
var points = fabric.parsePointsAttribute(element.getAttribute('points')),
parsedAttributes = fabric.parseAttributes(element, fabric.Polyline.ATTRIBUTE_NAMES);
if (!('transformMatrix' in parsedAttributes)) {
fabric.util.normalizePoints(points, options);
if (points === null) {
return null;
}
return new fabric.Polyline(points, fabric.util.object.extend(parsedAttributes, options), true);
};
/* _FROM_SVG_END_ */

View file

@ -50,17 +50,6 @@
*/
ry: 0,
/**
* @type Number
* @default
*/
x: 0,
/**
* @type Number
* @default
*/
y: 0,
/**
* Used to specify dash pattern for stroke on this object
@ -79,8 +68,6 @@
this.callSuper('initialize', options);
this._initRxRy();
this.x = options.x || 0;
this.y = options.y || 0;
},
/**
@ -100,7 +87,7 @@
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
_render: function(ctx, noTransform) {
// optimize 1x1 case (used in spray brush)
if (this.width === 1 && this.height === 1) {
@ -112,24 +99,15 @@
ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
w = this.width,
h = this.height,
x = -w / 2,
y = -h / 2,
isInPathGroup = this.group && this.group.type === 'path-group',
x = noTransform ? this.left : 0,
y = noTransform ? this.top : 0,
isRounded = rx !== 0 || ry !== 0,
k = 1 - 0.5522847498 /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */;
ctx.beginPath();
ctx.globalAlpha = isInPathGroup ? (ctx.globalAlpha * this.opacity) : this.opacity;
if (this.transformMatrix && isInPathGroup) {
ctx.translate(
this.width / 2 + this.x,
this.height / 2 + this.y);
}
if (!this.transformMatrix && isInPathGroup) {
ctx.translate(
-this.group.width / 2 + this.width / 2 + this.x,
-this.group.height / 2 + this.height / 2 + this.y);
if (!noTransform) {
ctx.translate(-this.width / 2, -this.height / 2);
}
ctx.moveTo(x + rx, y);
@ -170,22 +148,6 @@
ctx.closePath();
},
/**
* Since coordinate system differs from that of SVG
* @private
*/
_normalizeLeftTopProperties: function(parsedAttributes) {
if ('left' in parsedAttributes) {
this.set('left', parsedAttributes.left + this.getWidth() / 2);
}
this.set('x', parsedAttributes.left || 0);
if ('top' in parsedAttributes) {
this.set('top', parsedAttributes.top + this.getHeight() / 2);
}
this.set('y', parsedAttributes.top || 0);
return this;
},
/**
* Returns object representation of an instance
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
@ -194,9 +156,7 @@
toObject: function(propertiesToInclude) {
var object = extend(this.callSuper('toObject', propertiesToInclude), {
rx: this.get('rx') || 0,
ry: this.get('ry') || 0,
x: this.get('x'),
y: this.get('y')
ry: this.get('ry') || 0
});
if (!this.includeDefaultValues) {
this._removeDefaultValues(object);
@ -211,16 +171,20 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var markup = this._createBaseSVGMarkup();
var markup = this._createBaseSVGMarkup(), x = this.left, y = this.top;
if (!this.group) {
x = -this.width / 2;
y = -this.height / 2;
}
markup.push(
'<rect ',
'x="', (-1 * this.width / 2), '" y="', (-1 * this.height / 2),
'x="', x, '" y="', y,
'" rx="', this.get('rx'), '" ry="', this.get('ry'),
'" width="', this.width, '" height="', this.height,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
'"/>');
this.getSvgTransformMatrix(),
'"/>\n');
return reviver ? reviver(markup.join('')) : markup.join('');
},
@ -244,15 +208,6 @@
*/
fabric.Rect.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y rx ry width height'.split(' '));
/**
* @private
*/
function _setDefaultLeftTopValues(attributes) {
attributes.left = attributes.left || 0;
attributes.top = attributes.top || 0;
return attributes;
}
/**
* Returns {@link fabric.Rect} instance from an SVG element
* @static
@ -265,14 +220,14 @@
if (!element) {
return null;
}
options = options || { };
var parsedAttributes = fabric.parseAttributes(element, fabric.Rect.ATTRIBUTE_NAMES);
parsedAttributes = _setDefaultLeftTopValues(parsedAttributes);
var rect = new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
rect._normalizeLeftTopProperties(parsedAttributes);
return rect;
parsedAttributes.left = parsedAttributes.left || 0;
parsedAttributes.top = parsedAttributes.top || 0;
return new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
};
/* _FROM_SVG_END_ */

View file

@ -849,8 +849,8 @@
: (this.height/2) - (textLines.length * this.fontSize) - this._totalLineHeight;
return {
textLeft: textLeft,
textTop: textTop,
textLeft: textLeft + (this.group ? this.left : 0),
textTop: textTop + (this.group ? this.top : 0),
lineTop: lineTop
};
},
@ -860,7 +860,7 @@
*/
_wrapSVGTextAndBg: function(markup, textAndBg, shadowSpans, offsets) {
markup.push(
'<g transform="', this.getSvgTransform(), '">',
'<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
textAndBg.textBgRects.join(''),
'<text ',
(this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g,'\'') + '" ': ''),
@ -873,8 +873,8 @@
'transform="translate(', toFixed(offsets.textLeft, 2), ' ', toFixed(offsets.textTop, 2), ')">',
shadowSpans.join(''),
textAndBg.textSpans.join(''),
'</text>',
'</g>'
'</text>\n',
'</g>\n'
);
},
@ -992,7 +992,7 @@
toFixed(this._boundaries[i].width, 2),
'" height="',
toFixed(this._boundaries[i].height, 2),
'"></rect>');
'"></rect>\n');
},
_setSVGBg: function(textBgRects) {

View file

@ -505,26 +505,6 @@
return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
},
/**
* Normalizes polygon/polyline points according to their dimensions
* @param {Array} points
* @param {Object} options
*/
normalizePoints: function(points, options) {
var minX = fabric.util.array.min(points, 'x'),
minY = fabric.util.array.min(points, 'y');
minX = minX < 0 ? minX : 0;
minY = minX < 0 ? minY : 0;
for (var i = 0, len = points.length; i < len; i++) {
// normalize coordinates, according to containing box
// (dimensions of which are passed via `options`)
points[i].x -= (options.width / 2 + minX) || 0;
points[i].y -= (options.height / 2 + minY) || 0;
}
},
/**
* Returns true if context has transparent pixel
* at specified location (taking tolerance into account)

View file

@ -46,7 +46,7 @@
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,"x":0,"y":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
'"visible":true,"clipTo":null,"backgroundColor":"","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

@ -33,12 +33,12 @@
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,"x":0,"y":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","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,"x":0,"y":0}],"background":""}';
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","padding":123,"foo":"bar","rx":0,"ry":0}],"background":""}';
function getAbsolutePath(path) {
var isAbsolute = /^https?:/.test(path);

View file

@ -152,8 +152,8 @@
ok(oCircle instanceof fabric.Circle);
equal(oCircle.get('radius'), radius);
equal(oCircle.get('left'), left);
equal(oCircle.get('top'), top);
equal(oCircle.get('left'), left - radius);
equal(oCircle.get('top'), top - radius);
equal(oCircle.get('fill'), fill);
equal(oCircle.get('opacity'), opacity);
equal(oCircle.get('strokeWidth'), strokeWidth);

View file

@ -111,8 +111,8 @@
equal(oEllipse.get('rx'), rx);
equal(oEllipse.get('ry'), ry);
equal(oEllipse.get('left'), left);
equal(oEllipse.get('top'), top);
equal(oEllipse.get('left'), left - rx);
equal(oEllipse.get('top'), top - ry);
equal(oEllipse.get('fill'), fill);
equal(oEllipse.get('opacity'), opacity);
equal(oEllipse.get('strokeWidth'), strokeWidth);

View file

@ -384,7 +384,7 @@ test('toObject without default values', function() {
var group = makeGroupWith2Objects();
ok(typeof group.toSVG == 'function');
var expectedSVG = '<g transform="translate(130 160)"><rect x="-15" y="-5" 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); opacity: 1;" transform="translate(25 -25)"/><rect x="-5" y="-20" 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); opacity: 1;" transform="translate(-35 10)"/></g>';
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';
equal(group.toSVG(), expectedSVG);
});

View file

@ -116,16 +116,13 @@
deepEqual(polylineWithAttrs.get('transformMatrix'), [ 2, 0, 0, 2, -10, -20 ]);
var elPolylineWithoutPoints = fabric.document.createElement('polyline');
equal(fabric.Polyline.fromElement(elPolylineWithoutPoints), null);
var error;
try {
fabric.Polyline.fromElement(elPolylineWithoutPoints);
}
catch(err) {
error = err;
}
var elPolylineWithEmptyPoints = fabric.document.createElement('polyline');
elPolylineWithEmptyPoints.setAttribute('points', '');
ok(typeof error !== 'undefined', 'missing points attribute should result in error');
equal(fabric.Polyline.fromElement(), null);
equal(fabric.Polyline.fromElement(elPolylineWithEmptyPoints), null);
equal(fabric.Polyline.fromElement(), null);
});
})();

View file

@ -27,8 +27,6 @@
'clipTo': null,
'rx': 0,
'ry': 0,
'x': 0,
'y': 0
};
QUnit.module('fabric.Rect');
@ -99,8 +97,8 @@
ok(rectWithAttrs instanceof fabric.Rect);
var expectedObject = fabric.util.object.extend(REFERENCE_RECT, {
left: 121,
top: 186.5,
left: 10,
top: 20,
width: 222,
height: 333,
fill: 'rgb(255,255,255)',
@ -112,9 +110,7 @@
strokeLineJoin: 'bevil',
strokeMiterLimit: 5,
rx: 11,
ry: 12,
x: 10,
y: 20
ry: 12
});
deepEqual(rectWithAttrs.toObject(), expectedObject);
});
@ -135,7 +131,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); opacity: 1;" transform="translate(50 50)"/>');
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');
});
test('toObject without default values', function() {

View file

@ -46,7 +46,7 @@
'useNative': true
};
var TEXT_SVG = '<g transform="translate(10 26)"><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); opacity: 1;" transform="translate(-10 39)"><tspan x="0" y="-26" fill="rgb(0,0,0)">x</tspan></text></g>';
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';
test('constructor', function() {
ok(fabric.Text);