Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(): Add svg export for text on a path #10268

Merged
merged 10 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <kangax@gmail.com>",
"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));
})();
Loading