Skip to content

Commit

Permalink
fix(): Add svg export for text on a path (#10268)
Browse files Browse the repository at this point in the history
  • Loading branch information
asturur authored Nov 13, 2024
1 parent 58bc07d commit 61fc46f
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 17 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [5.4.1]

- fix() Fix the svg export of text with path [`#10268`](https://github.com/fabricjs/fabric.js/pull/10268)

## [5.4.0]

- fix() fix an issue with offScreen detection and background/overlay Vpt setting [`#8896`](https://github.com/fabricjs/fabric.js/pull/8896)
Expand Down
2 changes: 1 addition & 1 deletion HEADER.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */

var fabric = fabric || { version: '5.4.0' };
var fabric = fabric || { version: '5.4.1' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
Expand Down
39 changes: 32 additions & 7 deletions dist/fabric.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* build: `node build.js modules=ALL exclude=gestures,accessors,erasing requirejs minifier=uglifyjs` */
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */

var fabric = fabric || { version: '5.4.0' };
var fabric = fabric || { version: '5.4.1' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
Expand Down Expand Up @@ -30392,6 +30392,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
/* _TO_SVG_START_ */
(function() {
var toFixed = fabric.util.toFixed,
radiansToDegrees = fabric.util.radiansToDegrees,
calcRotateMatrix = fabric.util.calcRotateMatrix,
transformPoint = fabric.util.transformPoint,
multipleSpacesRegex = / +/g;

fabric.util.object.extend(fabric.Text.prototype, /** @lends fabric.Text.prototype */ {
Expand All @@ -30413,10 +30416,20 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
return this._createBaseSVGMarkup(
var textSvg = this._createBaseSVGMarkup(
this._toSVG(),
{ reviver: reviver, noStyle: true, withShadow: true }
);
if (this.path) {
return (
textSvg +
this._createBaseSVGMarkup(this.path._toSVG(), {
reviver: reviver,
withShadow: true,
})
);
}
return textSvg;
},

/**
Expand Down Expand Up @@ -30482,19 +30495,31 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
/**
* @private
*/
_createTextCharSpan: function(_char, styleDecl, left, top) {
_createTextCharSpan: function(_char, styleDecl, left, top, charBox) {
var shouldUseWhitespace = _char !== _char.trim() || _char.match(multipleSpacesRegex),
styleProps = this.getSvgSpanStyles(styleDecl, shouldUseWhitespace),
fillStyles = styleProps ? 'style="' + styleProps + '"' : '',
dy = styleDecl.deltaY, dySpan = '',
NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
angleAttr = '';
if (dy) {
dySpan = ' dy="' + toFixed(dy, NUM_FRACTION_DIGITS) + '" ';
}
if (charBox.renderLeft !== undefined) {
var angle = charBox.angle;
angleAttr = ' rotate="' + toFixed(radiansToDegrees(angle), fabric.Object.NUM_FRACTION_DIGITS) + '" ';
var wBy2 = charBox.width / 2,
m = calcRotateMatrix({ angle: radiansToDegrees(angle) });
m[4] = charBox.renderLeft;
m[5] = charBox.renderTop;
var renderPoint = transformPoint({ x: -wBy2, y: 0 }, m);
left = renderPoint.x;
top = renderPoint.y;
}
return [
'<tspan x="', toFixed(left, NUM_FRACTION_DIGITS), '" y="',
toFixed(top, NUM_FRACTION_DIGITS), '" ', dySpan,
fillStyles, '>',
fillStyles, angleAttr, '>',
fabric.util.string.escapeXml(_char),
'</tspan>'
].join('');
Expand All @@ -30514,7 +30539,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot

textTopOffset += lineHeight * (1 - this._fontSizeFraction) / this.lineHeight;
for (var i = 0, len = line.length - 1; i <= len; i++) {
timeToRender = i === len || this.charSpacing;
timeToRender = i === len || this.charSpacing || this.path;
charsToRender += line[i];
charBox = this.__charBounds[lineIndex][i];
if (boxWidth === 0) {
Expand All @@ -30537,7 +30562,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
if (timeToRender) {
style = this._getStyleDeclaration(lineIndex, i) || { };
textSpans.push(this._createTextCharSpan(charsToRender, style, textLeftOffset, textTopOffset));
textSpans.push(this._createTextCharSpan(charsToRender, style, textLeftOffset, textTopOffset, charBox));
charsToRender = '';
actualStyle = nextStyle;
textLeftOffset += boxWidth;
Expand Down
2 changes: 1 addition & 1 deletion dist/fabric.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "fabric",
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
"homepage": "http://fabricjs.com/",
"version": "5.4.0",
"version": "5.4.1",
"author": "Juriy Zaytsev <[email protected]>",
"contributors": [
{
Expand Down
37 changes: 31 additions & 6 deletions src/mixins/itext.svg_export.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/* _TO_SVG_START_ */
(function() {
var toFixed = fabric.util.toFixed,
radiansToDegrees = fabric.util.radiansToDegrees,
calcRotateMatrix = fabric.util.calcRotateMatrix,
transformPoint = fabric.util.transformPoint,
multipleSpacesRegex = / +/g;

fabric.util.object.extend(fabric.Text.prototype, /** @lends fabric.Text.prototype */ {
Expand All @@ -22,10 +25,20 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
return this._createBaseSVGMarkup(
var textSvg = this._createBaseSVGMarkup(
this._toSVG(),
{ reviver: reviver, noStyle: true, withShadow: true }
);
if (this.path) {
return (
textSvg +
this._createBaseSVGMarkup(this.path._toSVG(), {
reviver: reviver,
withShadow: true,
})
);
}
return textSvg;
},

/**
Expand Down Expand Up @@ -91,19 +104,31 @@
/**
* @private
*/
_createTextCharSpan: function(_char, styleDecl, left, top) {
_createTextCharSpan: function(_char, styleDecl, left, top, charBox) {
var shouldUseWhitespace = _char !== _char.trim() || _char.match(multipleSpacesRegex),
styleProps = this.getSvgSpanStyles(styleDecl, shouldUseWhitespace),
fillStyles = styleProps ? 'style="' + styleProps + '"' : '',
dy = styleDecl.deltaY, dySpan = '',
NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
angleAttr = '';
if (dy) {
dySpan = ' dy="' + toFixed(dy, NUM_FRACTION_DIGITS) + '" ';
}
if (charBox.renderLeft !== undefined) {
var angle = charBox.angle;
angleAttr = ' rotate="' + toFixed(radiansToDegrees(angle), fabric.Object.NUM_FRACTION_DIGITS) + '" ';
var wBy2 = charBox.width / 2,
m = calcRotateMatrix({ angle: radiansToDegrees(angle) });
m[4] = charBox.renderLeft;
m[5] = charBox.renderTop;
var renderPoint = transformPoint({ x: -wBy2, y: 0 }, m);
left = renderPoint.x;
top = renderPoint.y;
}
return [
'<tspan x="', toFixed(left, NUM_FRACTION_DIGITS), '" y="',
toFixed(top, NUM_FRACTION_DIGITS), '" ', dySpan,
fillStyles, '>',
fillStyles, angleAttr, '>',
fabric.util.string.escapeXml(_char),
'</tspan>'
].join('');
Expand All @@ -123,7 +148,7 @@

textTopOffset += lineHeight * (1 - this._fontSizeFraction) / this.lineHeight;
for (var i = 0, len = line.length - 1; i <= len; i++) {
timeToRender = i === len || this.charSpacing;
timeToRender = i === len || this.charSpacing || this.path;
charsToRender += line[i];
charBox = this.__charBounds[lineIndex][i];
if (boxWidth === 0) {
Expand All @@ -146,7 +171,7 @@
}
if (timeToRender) {
style = this._getStyleDeclaration(lineIndex, i) || { };
textSpans.push(this._createTextCharSpan(charsToRender, style, textLeftOffset, textTopOffset));
textSpans.push(this._createTextCharSpan(charsToRender, style, textLeftOffset, textTopOffset, charBox));
charsToRender = '';
actualStyle = nextStyle;
textLeftOffset += boxWidth;
Expand Down
Binary file added test/visual/golden/textWithPathSvg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions test/visual/z_svg_export.js
Original file line number Diff line number Diff line change
Expand Up @@ -444,5 +444,58 @@
width: 100,
height: 100,
});

function textWithPath(canvas, callback) {
// eslint-disable-next-line max-len
var circlePath = new fabric.Path('M 9.184850993605149e-15 150 A 150 150 0 0 1 9.184850993605149e-15 150 A 150 150 0 0 1 -48.70492038070252 141.8725862550952 A 150 150 0 0 1 -92.13190690345013 118.37107640945909 A 150 150 0 0 1 -125.57497173937925 82.04222371836408 A 150 150 0 0 1 -145.41003989089958 36.82282307111986 A 150 150 0 0 1 -149.4876739510005 -12.386901820849799 A 150 150 0 0 1 -137.36599899825862 -60.25431369794538 A 150 150 0 0 1 -110.35858660096977 -101.59223574386112 A 150 150 0 0 1 -71.39210895556111 -131.9210626809733 A 150 150 0 0 1 -24.68918854211018 -147.95419551040834 A 150 150 0 0 1 24.689188542109864 -147.9541955104084 A 150 150 0 0 1 71.39210895556072 -131.92106268097353 A 150 150 0 0 1 110.3585866009695 -101.59223574386141 A 150 150 0 0 1 137.36599899825842 -60.254313697945854 A 150 150 0 0 1 149.48767395100043 -12.386901820850317 A 150 150 0 0 1 145.4100398908997 36.82282307111929 A 150 150 0 0 1 125.57497173937968 82.04222371836342 A 150 150 0 0 1 92.13190690345085 118.37107640945852 A 150 150 0 0 1 48.70492038070333 141.87258625509492 A 150 150 0 0 1 9.785115956531574e-13 150', { visible: false });
var text = new fabric.Text('testing 123 123 123 ', {
left: 30,
top: 30,
fill: '',
stroke: 'red',
objectCaching: false,
path: circlePath,
styles: {
0: {
0: {
fontSize: 60,
fill: 'blue',
},
1: {
fontSize: 90,
fill: 'green',
},
2: {
fontSize: 20,
fill: 'Yellow',
},
3: {
fontWeigth: 'bold',
fill: 'transparent',
strokeWidth: 4,
strole: 'blue',
},
4: {
fontWeigth: 'bold',
fill: 'transparent',
strokeWidth: 4,
strole: 'blue',
},
},
},
});
canvas.add(text);
toSVGCanvas(canvas, callback);
}

tests.push({
test: 'Text with a path has working svg export',
code: textWithPath,
golden: 'textWithPathSvg.png',
percentage: 0.06,
disabled: fabric.isLikelyNode,
width: 400,
height: 400,
});
tests.forEach(visualTestLoop(QUnit));
})();

0 comments on commit 61fc46f

Please sign in to comment.