diff --git a/packages/joint-core/src/dia/attributes/text.mjs b/packages/joint-core/src/dia/attributes/text.mjs index 26d9bd867..973774345 100644 --- a/packages/joint-core/src/dia/attributes/text.mjs +++ b/packages/joint-core/src/dia/attributes/text.mjs @@ -7,6 +7,8 @@ function isTextInUse(_value, _node, attrs) { return (attrs.text !== undefined); } +const FONT_ATTRIBUTES = ['font-weight', 'font-family', 'font-size', 'letter-spacing', 'text-transform']; + const textAttributesNS = { 'line-height': { @@ -122,18 +124,28 @@ const textAttributesNS = { var text = value.text; if (text === undefined) text = attrs.text; if (text !== undefined) { + const breakTextFn = value.breakText || breakText; const computedStyles = getComputedStyle(node); + const wrapFontAttributes = {}; + // The font size attributes must be set on the node + // to get the correct text wrapping. + // TODO: set the native SVG attributes before special attributes + for (let i = 0; i < FONT_ATTRIBUTES.length; i++) { + const name = FONT_ATTRIBUTES[i]; + if (name in attrs) { + node.setAttribute(name, attrs[name]); + } + // Note: computedStyles is a live object + // i.e. the properties are evaluated when accessed. + wrapFontAttributes[name] = computedStyles[name]; + } - wrappedText = breakTextFn('' + text, size, { - 'font-weight': computedStyles.fontWeight, - 'font-family': computedStyles.fontFamily, - 'text-transform': computedStyles.textTransform, - 'font-size': computedStyles.fontSize, - 'letter-spacing': computedStyles.letterSpacing, - // The `line-height` attribute in SVG is JoinJS specific. - 'lineHeight': attrs['line-height'], - }, { + // The `line-height` attribute in SVG is JoinJS specific. + // TODO: change the `lineHeight` to breakText option. + wrapFontAttributes.lineHeight = attrs['line-height']; + + wrappedText = breakTextFn('' + text, size, wrapFontAttributes, { // Provide an existing SVG Document here // instead of creating a temporary one over again. svgDocument: this.paper.svg, @@ -147,7 +159,11 @@ const textAttributesNS = { wrappedText = ''; } textAttributesNS.text.set.call(this, wrappedText, refBBox, node, attrs); - } + }, + // We expose the font attributes list to allow + // the user to take other custom font attributes into account + // when wrapping the text. + FONT_ATTRIBUTES }, 'title': { diff --git a/packages/joint-core/test/jointjs/dia/attributes.js b/packages/joint-core/test/jointjs/dia/attributes.js index fe8166db5..372b66920 100644 --- a/packages/joint-core/test/jointjs/dia/attributes.js +++ b/packages/joint-core/test/jointjs/dia/attributes.js @@ -104,8 +104,14 @@ QUnit.module('Attributes', function() { return obj.width === refBBox.width - 11 && obj.height === refBBox.height - 13; }))); - // external css styles taken into account - spy.resetHistory(); + spy.restore(); + }); + + QUnit.test('takes external CSS into account', function(assert) { + + const spy = sinon.spy(joint.util, 'breakText'); + + // no text const fontSize = '23px'; const fontFamily = 'Arial'; const fontWeight = '800'; @@ -121,7 +127,21 @@ QUnit.module('Attributes', function() { } `); paper.svg.prepend(stylesheet); - textWrap.set.call(cellView, { text: 'text', breakText: spy }, bbox, node, {}); + + const el = new joint.shapes.standard.Rectangle({ + attrs: { + label: { + text: 'text', + textWrap: { + breakText: spy + } + } + } + + }); + el.addTo(graph); + paper.requireView(el); + assert.ok(spy.calledWith( sinon.match.string, sinon.match.object, @@ -134,8 +154,8 @@ QUnit.module('Attributes', function() { obj['font-weight'] === fontWeight ); }))); - stylesheet.remove(); + stylesheet.remove(); spy.restore(); });