diff --git a/src/components/mode-tools/mode-tools.jsx b/src/components/mode-tools/mode-tools.jsx index d1b3f7861d..fae092e4c3 100644 --- a/src/components/mode-tools/mode-tools.jsx +++ b/src/components/mode-tools/mode-tools.jsx @@ -785,13 +785,13 @@ const ModeToolsComponent = props => { onClick={props.onTextAlignRight} /> - + {(props.mode === Modes.TEXT) ? ( - + ) : null} ); case Modes.BIT_RECT: @@ -895,6 +895,7 @@ ModeToolsComponent.propTypes = { onPasteFromClipboard: PropTypes.func.isRequired, onPointPoints: PropTypes.func.isRequired, onUpdateImage: PropTypes.func.isRequired, + onText2Path: PropTypes.func.isRequired, onMergeShape: PropTypes.func.isRequired, onMaskShape: PropTypes.func.isRequired, diff --git a/src/containers/mode-tools.jsx b/src/containers/mode-tools.jsx index 50b6392fba..6ebcf72b3d 100644 --- a/src/containers/mode-tools.jsx +++ b/src/containers/mode-tools.jsx @@ -47,7 +47,8 @@ class ModeTools extends React.Component { 'handleSquareEnds', 'handleMiterLineJoin', 'handleRoundLineJoin', - 'handleBevelLineJoin' + 'handleBevelLineJoin', + 'handleText2Path' ]); // defined when merging shapes @@ -330,19 +331,54 @@ class ModeTools extends React.Component { return; } - const pathData = font.getPath( - textNode.content, 0, 0, - textNode.fontSize || 16 - ).toPathData(); + // Split text by lines, because opentype generates path in one line, ignoring \n + const textPath = textNode.content.split("\n") + .map((line, i) => { + const pathData = font.getPath( + line, 0, textNode.leading * i, + textNode.fontSize || 16 + ).toPathData(); + const compound = new paper.CompoundPath(pathData); - const compound = new paper.CompoundPath(pathData); - compound.fillColor = this.fillColor || "black"; - compound.matrix = textNode.matrix.clone(); - resolve(compound); + // Copy styles of text node + ["fillColor", "strokeColor", "strokeWidth", "strokeCap", "strokeJoin", "dashArray"] + .forEach((param) => (compound[param] = textNode[param])); + compound.matrix = textNode.matrix.clone(); + + return compound; + }) + .reduce((union, path) => { + const result = union.unite(path); + union.remove(); + path.remove(); + return result; + }); + resolve(textPath); }); }); } + async handleText2Path () { + const selectedItems = getSelectedLeafItems(); + for (let i = 0; i < selectedItems.length; i++) { + if (selectedItems[i].className === "PointText") { + const path = await this.convertText2Path(selectedItems[i]); + + // Record indices + selectedItems[i].data.index = selectedItems[i].index; + + // Group item + const itemGroup = new paper.Group(path); + + // Remove path from group and insert at index of text node. + itemGroup.layer.insertChild(selectedItems[i].data.index, path); + selectedItems[i].data.index = null; + itemGroup.remove(); + } + } + this.props.onUpdateImage(); + } + async handleMergeShape (specificOperation) { const selectedItems = getSelectedRootItems(); if (selectedItems.length < 2) { @@ -507,6 +543,7 @@ class ModeTools extends React.Component { onMiterLineJoin={this.handleMiterLineJoin} onRoundLineJoin={this.handleRoundLineJoin} onBevelLineJoin={this.handleBevelLineJoin} + onText2Path={this.handleText2Path} onMergeShape={this.handleMergeShape} onMaskShape={this.handleMaskShape}