From b93ce24c3d2e72f3764a53e006e155c7860c815b Mon Sep 17 00:00:00 2001 From: Jon Ruskin Date: Sat, 14 Jan 2023 14:05:05 -0700 Subject: [PATCH 1/2] handle string and number font size configurations --- .../mermaid/src/diagrams/sequence/svgDraw.js | 20 +++++------ .../src/diagrams/sequence/svgDraw.spec.js | 24 +++++++++++++ packages/mermaid/src/utils.spec.js | 26 ++++++++++++++ packages/mermaid/src/utils.ts | 35 +++++++++++++++++-- 4 files changed, 92 insertions(+), 13 deletions(-) diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index ce1caea69..495865674 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -1,5 +1,6 @@ import common from '../common/common'; import { addFunction } from '../../interactionDb'; +import { parseFontSize } from '../../utils'; import { sanitizeUrl } from '@braintree/sanitize-url'; export const drawRect = function (elem, rectData) { @@ -156,6 +157,8 @@ export const drawText = function (elem, textData) { textHeight = 0; const lines = textData.text.split(common.lineBreakRegex); + const [_textFontSize, _textFontSizePx] = parseFontSize(textData.fontSize); + let textElems = []; let dy = 0; let yfunc = () => textData.y; @@ -215,12 +218,8 @@ export const drawText = function (elem, textData) { } } for (let [i, line] of lines.entries()) { - if ( - textData.textMargin !== undefined && - textData.textMargin === 0 && - textData.fontSize !== undefined - ) { - dy = i * textData.fontSize; + if (textData.textMargin !== undefined && textData.textMargin === 0 && _textFontSize !== null) { + dy = i * _textFontSize; } const textElem = elem.append('text'); @@ -235,8 +234,8 @@ export const drawText = function (elem, textData) { if (textData.fontFamily !== undefined) { textElem.style('font-family', textData.fontFamily); } - if (textData.fontSize !== undefined) { - textElem.style('font-size', textData.fontSize); + if (_textFontSizePx !== null) { + textElem.style('font-size', _textFontSizePx); } if (textData.fontWeight !== undefined) { textElem.style('font-weight', textData.fontWeight); @@ -840,8 +839,7 @@ const _drawTextCandidateFunc = (function () { function byTspan(content, g, x, y, width, height, textAttrs, conf) { const { actorFontSize, actorFontFamily, actorFontWeight } = conf; - let _actorFontSize = - actorFontSize && actorFontSize.replace ? actorFontSize.replace('px', '') : actorFontSize; + const [_actorFontSize, _actorFontSizePx] = parseFontSize(actorFontSize); const lines = content.split(common.lineBreakRegex); for (let i = 0; i < lines.length; i++) { @@ -851,7 +849,7 @@ const _drawTextCandidateFunc = (function () { .attr('x', x + width / 2) .attr('y', y) .style('text-anchor', 'middle') - .style('font-size', actorFontSize) + .style('font-size', _actorFontSizePx) .style('font-weight', actorFontWeight) .style('font-family', actorFontFamily); text diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.spec.js b/packages/mermaid/src/diagrams/sequence/svgDraw.spec.js index 8e5f5f32b..ed60285ed 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.spec.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.spec.js @@ -125,6 +125,30 @@ describe('svgDraw', function () { expect(text3.attr).toHaveBeenCalledWith('y', 10); expect(text3.text).toHaveBeenCalledWith('fine lines'); }); + it('should work with numeral font sizes', function () { + const svg = MockD3('svg'); + svgDraw.drawText(svg, { + x: 10, + y: 10, + dy: '1em', + text: 'One fine text message', + class: 'noteText', + fontFamily: 'courier', + fontSize: 10, + fontWeight: '500', + }); + expect(svg.__children.length).toBe(1); + const text = svg.__children[0]; + expect(text.__name).toBe('text'); + expect(text.attr).toHaveBeenCalledWith('x', 10); + expect(text.attr).toHaveBeenCalledWith('y', 10); + expect(text.attr).toHaveBeenCalledWith('dy', '1em'); + expect(text.attr).toHaveBeenCalledWith('class', 'noteText'); + expect(text.text).toHaveBeenCalledWith('One fine text message'); + expect(text.style).toHaveBeenCalledWith('font-family', 'courier'); + expect(text.style).toHaveBeenCalledWith('font-size', '10px'); + expect(text.style).toHaveBeenCalledWith('font-weight', '500'); + }); }); describe('drawBackgroundRect', function () { it('should append a rect before the previous element within a given bound', function () { diff --git a/packages/mermaid/src/utils.spec.js b/packages/mermaid/src/utils.spec.js index 7ee6aa000..f8bf613fd 100644 --- a/packages/mermaid/src/utils.spec.js +++ b/packages/mermaid/src/utils.spec.js @@ -402,3 +402,29 @@ describe('when inserting titles', function () { expect(titleAttrSpy).toHaveBeenCalledWith('class', 'testClass'); }); }); + +describe('when parsing font sizes', function () { + it('parses number inputs', function () { + expect(utils.parseFontSize(14)).toEqual([14, '14px']); + }); + + it('parses string em inputs', function () { + expect(utils.parseFontSize('14em')).toEqual([14, '14em']); + }); + + it('parses string px inputs', function () { + expect(utils.parseFontSize('14px')).toEqual([14, '14px']); + }); + + it('parses string inputs without units', function () { + expect(utils.parseFontSize('14')).toEqual([14, '14px']); + }); + + it('handles undefined input', function () { + expect(utils.parseFontSize(undefined)).toEqual([null, null]); + }); + + it('handles unparseable input', function () { + expect(utils.parseFontSize({ fontSize: 14 })).toEqual([null, null]); + }); +}); diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index 4c2f844e2..a4ef5f630 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -543,12 +543,14 @@ export const drawSimpleText = function ( // Remove and ignore br:s const nText = textData.text.replace(common.lineBreakRegex, ' '); + const [, _fontSizePx] = parseFontSize(textData.fontSize); + const textElem = elem.append('text'); textElem.attr('x', textData.x); textElem.attr('y', textData.y); textElem.style('text-anchor', textData.anchor); textElem.style('font-family', textData.fontFamily); - textElem.style('font-size', textData.fontSize); + textElem.style('font-size', _fontSizePx); textElem.style('font-weight', textData.fontWeight); textElem.attr('fill', textData.fill); if (textData.class !== undefined) { @@ -722,6 +724,8 @@ export const calculateTextDimensions: ( return { width: 0, height: 0 }; } + const [, _fontSizePx] = parseFontSize(fontSize); + // We can't really know if the user supplied font family will render on the user agent; // thus, we'll take the max width between the user supplied font family, and a default // of sans-serif. @@ -745,7 +749,7 @@ export const calculateTextDimensions: ( const textObj = getTextObj(); textObj.text = line; const textElem = drawSimpleText(g, textObj) - .style('font-size', fontSize) + .style('font-size', _fontSizePx) .style('font-weight', fontWeight) .style('font-family', fontFamily); @@ -941,6 +945,32 @@ export const insertTitle = ( .attr('class', cssClass); }; +/** + * Parses a raw fontSize configuration value into a number and string value. + * + * @param fontSize - a string or number font size configuration value + * + * @returns parsed number and string style font size values, or nulls if a number value can't + * be parsed from an input string. + */ +export const parseFontSize = (fontSize: string | number | undefined): [number?, string?] => { + // if the font size is a number, assume a px string representation + if (typeof fontSize === 'number') { + return [fontSize, fontSize + 'px']; + } + + const fontSizeNumber = parseInt(fontSize, 10); + if (Number.isNaN(fontSizeNumber)) { + // if a number value can't be parsed, return null for both values + return [null, null]; + } else if (fontSize === String(fontSizeNumber)) { + // if a string input doesn't contain any units, assume px units + return [fontSizeNumber, fontSize + 'px']; + } else { + return [fontSizeNumber, fontSize]; + } +}; + export default { assignWithDepth, wrapLabel, @@ -964,4 +994,5 @@ export default { directiveSanitizer, sanitizeCss, insertTitle, + parseFontSize, }; From 9629c8d8d6a01dfda90fbc8d3eefe4eb8f1a8620 Mon Sep 17 00:00:00 2001 From: Jon Ruskin Date: Tue, 17 Jan 2023 08:31:36 -0700 Subject: [PATCH 2/2] use undefined not null --- packages/mermaid/src/diagrams/sequence/svgDraw.js | 8 ++++++-- packages/mermaid/src/utils.spec.js | 4 ++-- packages/mermaid/src/utils.ts | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index 495865674..220db2209 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -218,7 +218,11 @@ export const drawText = function (elem, textData) { } } for (let [i, line] of lines.entries()) { - if (textData.textMargin !== undefined && textData.textMargin === 0 && _textFontSize !== null) { + if ( + textData.textMargin !== undefined && + textData.textMargin === 0 && + _textFontSize !== undefined + ) { dy = i * _textFontSize; } @@ -234,7 +238,7 @@ export const drawText = function (elem, textData) { if (textData.fontFamily !== undefined) { textElem.style('font-family', textData.fontFamily); } - if (_textFontSizePx !== null) { + if (_textFontSizePx !== undefined) { textElem.style('font-size', _textFontSizePx); } if (textData.fontWeight !== undefined) { diff --git a/packages/mermaid/src/utils.spec.js b/packages/mermaid/src/utils.spec.js index f8bf613fd..0f0bc1e92 100644 --- a/packages/mermaid/src/utils.spec.js +++ b/packages/mermaid/src/utils.spec.js @@ -421,10 +421,10 @@ describe('when parsing font sizes', function () { }); it('handles undefined input', function () { - expect(utils.parseFontSize(undefined)).toEqual([null, null]); + expect(utils.parseFontSize(undefined)).toEqual([undefined, undefined]); }); it('handles unparseable input', function () { - expect(utils.parseFontSize({ fontSize: 14 })).toEqual([null, null]); + expect(utils.parseFontSize({ fontSize: 14 })).toEqual([undefined, undefined]); }); }); diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index a4ef5f630..876c81543 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -962,7 +962,7 @@ export const parseFontSize = (fontSize: string | number | undefined): [number?, const fontSizeNumber = parseInt(fontSize, 10); if (Number.isNaN(fontSizeNumber)) { // if a number value can't be parsed, return null for both values - return [null, null]; + return [undefined, undefined]; } else if (fontSize === String(fontSizeNumber)) { // if a string input doesn't contain any units, assume px units return [fontSizeNumber, fontSize + 'px'];