Skip to content

Commit

Permalink
Merge pull request #7 from kangax/master
Browse files Browse the repository at this point in the history
update to master
  • Loading branch information
asturur committed Dec 13, 2015
2 parents c461792 + 0255ce9 commit 52391f0
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 22 deletions.
13 changes: 8 additions & 5 deletions src/shadow.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,21 +111,24 @@
* @return {String} SVG representation of a shadow
*/
toSVG: function(object) {
var fBoxX = 40, fBoxY = 40;
var fBoxX = 40, fBoxY = 40, NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
offset = fabric.util.rotateVector({x: this.offsetX, y: this.offsetY},
fabric.util.degreesToRadians(-object.angle)), BLUR_BOX = 20;

if (object.width && object.height) {
//http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
// we add some extra space to filter box to contain the blur ( 20 )
fBoxX = toFixed((Math.abs(this.offsetX) + this.blur) / object.width, 2) * 100 + 20;
fBoxY = toFixed((Math.abs(this.offsetY) + this.blur) / object.height, 2) * 100 + 20;
fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
}

return (
'<filter id="SVGID_' + this.id + '" y="-' + fBoxY + '%" height="' + (100 + 2 * fBoxY) + '%" ' +
'x="-' + fBoxX + '%" width="' + (100 + 2 * fBoxX) + '%" ' + '>\n' +
'\t<feGaussianBlur in="SourceAlpha" stdDeviation="' +
toFixed(this.blur ? this.blur / 2 : 0, 3) + '"></feGaussianBlur>\n' +
'\t<feOffset dx="' + this.offsetX + '" dy="' + this.offsetY + '" result="oBlur" ></feOffset>\n' +
toFixed(this.blur ? this.blur / 2 : 0, NUM_FRACTION_DIGITS) + '"></feGaussianBlur>\n' +
'\t<feOffset dx="' + toFixed(offset.x, NUM_FRACTION_DIGITS) +
'" dy="' + toFixed(offset.y, NUM_FRACTION_DIGITS) + '" result="oBlur" ></feOffset>\n' +
'\t<feFlood flood-color="' + this.color + '"/>\n' +
'\t<feComposite in2="oBlur" operator="in" />\n' +
'\t<feMerge>\n' +
Expand Down
6 changes: 3 additions & 3 deletions src/shapes/itext.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -1008,7 +1008,7 @@
return this.__widthOfSpace[lineIndex];
}
var line = this._textLines[lineIndex],
wordsWidth = this._getWidthOfWords(ctx, line, lineIndex),
wordsWidth = this._getWidthOfWords(ctx, line, lineIndex, 0),
widthDiff = this.width - wordsWidth,
numSpaces = line.length - line.replace(this._reSpacesAndTabs, '').length,
width = widthDiff / numSpaces;
Expand All @@ -1022,14 +1022,14 @@
* @param {Number} line
* @param {Number} lineIndex
*/
_getWidthOfWords: function (ctx, line, lineIndex) {
_getWidthOfWords: function (ctx, line, lineIndex, charOffset) {
var width = 0;

for (var charIndex = 0; charIndex < line.length; charIndex++) {
var _char = line[charIndex];

if (!_char.match(/\s/)) {
width += this._getWidthOfChar(ctx, _char, lineIndex, charIndex);
width += this._getWidthOfChar(ctx, _char, lineIndex, charIndex + charOffset);
}
}

Expand Down
54 changes: 45 additions & 9 deletions src/shapes/text.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,19 +479,20 @@

// stretch the line
var words = line.split(/\s+/),
wordsWidth = this._getWidthOfWords(ctx, line, lineIndex),
charOffset = 0,
wordsWidth = this._getWidthOfWords(ctx, line, lineIndex, 0),
widthDiff = this.width - wordsWidth,
numSpaces = words.length - 1,
spaceWidth = numSpaces > 0 ? widthDiff / numSpaces : 0,
leftOffset = 0, charOffset = 0, word;
leftOffset = 0, word;

for (var i = 0, len = words.length; i < len; i++) {
while (line[charOffset] === ' ' && charOffset < line.length) {
charOffset++;
}
word = words[i];
this._renderChars(method, ctx, word, left + leftOffset, top, lineIndex, charOffset);
leftOffset += ctx.measureText(word).width + spaceWidth;
leftOffset += this._getWidthOfWords(ctx, word, lineIndex, charOffset) + spaceWidth;
charOffset += word.length;
}
},
Expand Down Expand Up @@ -640,7 +641,7 @@
ctx.fillStyle = this.textBackgroundColor;
for (var i = 0, len = this._textLines.length; i < len; i++) {
if (this._textLines[i] !== '') {
lineWidth = this._getLineWidth(ctx, i);
lineWidth = this.textAlign === 'justify' ? this.width : this._getLineWidth(ctx, i);
lineLeftOffset = this._getLineLeftOffset(lineWidth);
ctx.fillRect(
this._getLeftOffset() + lineLeftOffset,
Expand Down Expand Up @@ -709,7 +710,7 @@
width = 0;
}
else if (this.textAlign === 'justify' && this._cacheLinesWidth) {
wordCount = line.split(' ');
wordCount = line.split(/\s+/);
//consider not justify last line, not for now.
if (wordCount.length > 1) {
width = this.width;
Expand Down Expand Up @@ -889,9 +890,9 @@
(this.fontStyle ? 'font-style="' + this.fontStyle + '" ': ''),
(this.fontWeight ? 'font-weight="' + this.fontWeight + '" ': ''),
(this.textDecoration ? 'text-decoration="' + this.textDecoration + '" ': ''),
'style="', this.getSvgStyles(), '" >',
'style="', this.getSvgStyles(), '" >\n',
textAndBg.textSpans.join(''),
'</text>\n',
'\t\t</text>\n',
'\t</g>\n'
);
},
Expand Down Expand Up @@ -927,8 +928,13 @@
_setSVGTextLineText: function(i, textSpans, height, textLeftOffset, textTopOffset) {
var yPos = this.fontSize * (this._fontSizeMult - this._fontSizeFraction)
- textTopOffset + height - this.height / 2;
if (this.textAlign === 'justify') {
// i call from here to do not intefere with IText
this._setSVGTextLineJustifed(i, textSpans, yPos, textLeftOffset);
return;
}
textSpans.push(
'<tspan x="',
'\t\t\t<tspan x="',
toFixed(textLeftOffset + this._getLineLeftOffset(this._getLineWidth(this.ctx, i)), NUM_FRACTION_DIGITS), '" ',
'y="',
toFixed(yPos, NUM_FRACTION_DIGITS),
Expand All @@ -937,10 +943,40 @@
// on containing <text> one doesn't work in Illustrator
this._getFillAttributes(this.fill), '>',
fabric.util.string.escapeXml(this._textLines[i]),
'</tspan>'
'</tspan>\n'
);
},

_setSVGTextLineJustifed: function(i, textSpans, yPos, textLeftOffset) {
var ctx = fabric.util.createCanvasElement().getContext('2d');
this._setTextStyles(ctx);
var line = this._textLines[i],
words = line.split(/\s+/),
wordsWidth = this._getWidthOfWords(ctx, line),
widthDiff = this.width - wordsWidth,
numSpaces = words.length - 1,
spaceWidth = numSpaces > 0 ? widthDiff / numSpaces : 0,
word, attributes = this._getFillAttributes(this.fill);
textLeftOffset += this._getLineLeftOffset(this._getLineWidth(ctx, i))
for (var i = 0, len = words.length; i < len; i++) {
word = words[i];
textSpans.push(
'\t\t\t<tspan x="',
toFixed(textLeftOffset, NUM_FRACTION_DIGITS), '" ',
'y="',
toFixed(yPos, NUM_FRACTION_DIGITS),
'" ',
// doing this on <tspan> elements since setting opacity
// on containing <text> one doesn't work in Illustrator
attributes, '>',
fabric.util.string.escapeXml(word),
'</tspan>\n'
);
textLeftOffset += this._getWidthOfWords(ctx, word) + spaceWidth;
}

},

_setSVGTextLineBg: function(textBgRects, i, textLeftOffset, textTopOffset, height) {
textBgRects.push(
'\t\t<rect ',
Expand Down
24 changes: 21 additions & 3 deletions src/util/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,29 @@
*/
rotatePoint: function(point, origin, radians) {
point.subtractEquals(origin);
var v = fabric.util.rotateVector(point, radians);
return new fabric.Point(v.x, v.y).addEquals(origin);
},

/**
* Rotates `vector` with `radians`
* @static
* @memberOf fabric.util
* @param {Object} vector The vector to rotate
* @param {Object.x} x coordinate of vector
* @param {Object.y} y coordinate of vector
* @param {Number} radians The radians of the angle for the rotation
* @return {Object} The new rotated point
*/
rotateVector: function(vector, radians) {
var sin = Math.sin(radians),
cos = Math.cos(radians),
rx = point.x * cos - point.y * sin,
ry = point.x * sin + point.y * cos;
return new fabric.Point(rx, ry).addEquals(origin);
rx = vector.x * cos - vector.y * sin,
ry = vector.x * sin + vector.y * cos;
return {
x: rx,
y: ry
};
},

/**
Expand Down
2 changes: 1 addition & 1 deletion test/unit/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@

equal(group.set('opacity', 0.12345), group, 'should be chainable');
equal(group.get('opacity'), 0.12345, 'group\'s "own" property should be set properly');
equal(firstObject.get('opacity'), 0.12345, 'objects\' value should be set properly');
equal(firstObject.get('opacity'), 1, 'objects\' value of non delegated property should stay same');

group.set('left', 1234);
equal(group.get('left'), 1234, 'group\'s own "left" property should be set properly');
Expand Down
10 changes: 10 additions & 0 deletions test/unit/shadow.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,5 +170,15 @@
shadow.color = '#000000';
equal(shadow.toSVG(object), '<filter id="SVGID_0" y="-40%" height="180%" x="-40%" width="180%" >\n\t<feGaussianBlur in="SourceAlpha" stdDeviation="1"></feGaussianBlur>\n\t<feOffset dx="10" dy="-10" result="oBlur" ></feOffset>\n\t<feFlood flood-color="#000000"/>\n\t<feComposite in2="oBlur" operator="in" />\n\t<feMerge>\n\t\t<feMergeNode></feMergeNode>\n\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n\t</feMerge>\n</filter>\n');
});

test('toSVG with rotated object', function() {
// reset uid
fabric.Object.__uid = 0;

var shadow = new fabric.Shadow({color: '#FF0000', offsetX: 10, offsetY: 10, blur: 2});
var object = new fabric.Object({fill: '#FF0000', angle: 45});

equal(shadow.toSVG(object), '<filter id="SVGID_0" y="-40%" height="180%" x="-40%" width="180%" >\n\t<feGaussianBlur in="SourceAlpha" stdDeviation="1"></feGaussianBlur>\n\t<feOffset dx="14.14" dy="0" result="oBlur" ></feOffset>\n\t<feFlood flood-color="#FF0000"/>\n\t<feComposite in2="oBlur" operator="in" />\n\t<feMerge>\n\t\t<feMergeNode></feMergeNode>\n\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n\t</feMerge>\n</filter>\n');
});

})();
15 changes: 14 additions & 1 deletion test/unit/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
'transformMatrix': null
};

var TEXT_SVG = '\t<g transform="translate(10.5 26.72)">\n\t\t<text font-family="Times New Roman" font-size="40" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" ><tspan x="-10" y="8.98" fill="rgb(0,0,0)">x</tspan></text>\n\t</g>\n';
var TEXT_SVG = '\t<g transform="translate(10.5 26.72)">\n\t\t<text font-family="Times New Roman" font-size="40" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" >\n\t\t\t<tspan x="-10" y="8.98" fill="rgb(0,0,0)">x</tspan>\n\t\t</text>\n\t</g>\n';
var TEXT_SVG_JUSTIFIED = '\t<g transform="translate(50.5 26.72)">\n\t\t<text font-family="Times New Roman" font-size="40" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" >\n\t\t\t<tspan x="-50" y="8.98" fill="rgb(0,0,0)">x</tspan>\n\t\t\t<tspan x="30" y="8.98" fill="rgb(0,0,0)">y</tspan>\n\t\t</text>\n\t</g>\n';

test('constructor', function() {
ok(fabric.Text);
Expand Down Expand Up @@ -261,5 +262,17 @@

equal(removeTranslate(text.toSVG()), removeTranslate(TEXT_SVG.replace('font-family="Times New Roman"', 'font-family="\'Arial Black\', Arial"')));
});
test('toSVG justified', function() {
var text = new fabric.Text('x y');

function removeTranslate(str) {
return str.replace(/translate\(.*?\)/, '');
}

// temp workaround for text objects not obtaining width under node
text.width = 100;
text.textAlign = 'justify'
equal(removeTranslate(text.toSVG()), removeTranslate(TEXT_SVG_JUSTIFIED));
});

})();

0 comments on commit 52391f0

Please sign in to comment.