diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/customWidthsMap.js b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/customWidthsMap.js
new file mode 100644
index 00000000000..78883b7e639
--- /dev/null
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/customWidthsMap.js
@@ -0,0 +1,583 @@
+const widthsMap = {
+ "lucida sans unicode": {
+ "0": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "1": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "2": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "3": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "4": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "5": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "6": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "7": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "8": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "9": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ " ": [
+ 32,
+ 32,
+ 32,
+ 32
+ ],
+ "!": [
+ 32,
+ 32,
+ 32,
+ 32
+ ],
+ "\"": [
+ 37,
+ 37,
+ 37,
+ 37
+ ],
+ "#": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "$": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "%": [
+ 67,
+ 67,
+ 67,
+ 67
+ ],
+ "&": [
+ 70,
+ 70,
+ 70,
+ 70
+ ],
+ "'": [
+ 23,
+ 23,
+ 23,
+ 23
+ ],
+ "(": [
+ 33,
+ 33,
+ 33,
+ 33
+ ],
+ ")": [
+ 33,
+ 33,
+ 33,
+ 33
+ ],
+ "*": [
+ 48,
+ 48,
+ 48,
+ 48
+ ],
+ "+": [
+ 80,
+ 80,
+ 80,
+ 80
+ ],
+ ",": [
+ 32,
+ 32,
+ 32,
+ 32
+ ],
+ "-": [
+ 58,
+ 58,
+ 58,
+ 58
+ ],
+ ".": [
+ 32,
+ 32,
+ 32,
+ 32
+ ],
+ "/": [
+ 52,
+ 52,
+ 52,
+ 52
+ ],
+ ":": [
+ 32,
+ 32,
+ 32,
+ 32
+ ],
+ ";": [
+ 32,
+ 32,
+ 32,
+ 32
+ ],
+ "<": [
+ 80,
+ 80,
+ 80,
+ 80
+ ],
+ "=": [
+ 80,
+ 80,
+ 80,
+ 80
+ ],
+ ">": [
+ 80,
+ 80,
+ 80,
+ 80
+ ],
+ "?": [
+ 42,
+ 42,
+ 42,
+ 42
+ ],
+ "@": [
+ 86,
+ 86,
+ 86,
+ 86
+ ],
+ "A": [
+ 69,
+ 69,
+ 69,
+ 69
+ ],
+ "B": [
+ 58,
+ 58,
+ 58,
+ 58
+ ],
+ "C": [
+ 69,
+ 69,
+ 69,
+ 69
+ ],
+ "D": [
+ 75,
+ 75,
+ 75,
+ 75
+ ],
+ "E": [
+ 54,
+ 54,
+ 54,
+ 54
+ ],
+ "F": [
+ 54,
+ 54,
+ 54,
+ 54
+ ],
+ "G": [
+ 72,
+ 72,
+ 72,
+ 72
+ ],
+ "H": [
+ 74,
+ 74,
+ 74,
+ 74
+ ],
+ "I": [
+ 29,
+ 29,
+ 29,
+ 29
+ ],
+ "J": [
+ 31,
+ 31,
+ 31,
+ 31
+ ],
+ "K": [
+ 65,
+ 65,
+ 65,
+ 65
+ ],
+ "L": [
+ 53,
+ 53,
+ 53,
+ 53
+ ],
+ "M": [
+ 86,
+ 86,
+ 86,
+ 86
+ ],
+ "N": [
+ 74,
+ 74,
+ 74,
+ 74
+ ],
+ "O": [
+ 78,
+ 78,
+ 78,
+ 78
+ ],
+ "P": [
+ 55,
+ 55,
+ 55,
+ 55
+ ],
+ "Q": [
+ 78,
+ 78,
+ 78,
+ 78
+ ],
+ "R": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "S": [
+ 54,
+ 54,
+ 54,
+ 54
+ ],
+ "T": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "U": [
+ 69,
+ 69,
+ 69,
+ 69
+ ],
+ "V": [
+ 65,
+ 65,
+ 65,
+ 65
+ ],
+ "W": [
+ 86,
+ 86,
+ 86,
+ 86
+ ],
+ "X": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "Y": [
+ 62,
+ 62,
+ 62,
+ 62
+ ],
+ "Z": [
+ 60,
+ 60,
+ 60,
+ 60
+ ],
+ "[": [
+ 33,
+ 33,
+ 33,
+ 33
+ ],
+ "\\": [
+ 52,
+ 52,
+ 52,
+ 52
+ ],
+ "]": [
+ 33,
+ 33,
+ 33,
+ 33
+ ],
+ "^": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "_": [
+ 50,
+ 50,
+ 50,
+ 50
+ ],
+ "`": [
+ 61,
+ 61,
+ 61,
+ 61
+ ],
+ "a": [
+ 55,
+ 55,
+ 55,
+ 55
+ ],
+ "b": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "c": [
+ 51,
+ 51,
+ 51,
+ 51
+ ],
+ "d": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "e": [
+ 56,
+ 56,
+ 56,
+ 56
+ ],
+ "f": [
+ 37,
+ 37,
+ 37,
+ 37
+ ],
+ "g": [
+ 62,
+ 62,
+ 62,
+ 62
+ ],
+ "h": [
+ 62,
+ 62,
+ 62,
+ 62
+ ],
+ "i": [
+ 29,
+ 29,
+ 29,
+ 29
+ ],
+ "j": [
+ 30,
+ 30,
+ 30,
+ 30
+ ],
+ "k": [
+ 58,
+ 58,
+ 58,
+ 58
+ ],
+ "l": [
+ 29,
+ 29,
+ 29,
+ 29
+ ],
+ "m": [
+ 93,
+ 93,
+ 93,
+ 93
+ ],
+ "n": [
+ 62,
+ 62,
+ 62,
+ 62
+ ],
+ "o": [
+ 61,
+ 61,
+ 61,
+ 61
+ ],
+ "p": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "q": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "r": [
+ 41,
+ 41,
+ 41,
+ 41
+ ],
+ "s": [
+ 51,
+ 51,
+ 51,
+ 51
+ ],
+ "t": [
+ 37,
+ 37,
+ 37,
+ 37
+ ],
+ "u": [
+ 62,
+ 62,
+ 62,
+ 62
+ ],
+ "v": [
+ 52,
+ 52,
+ 52,
+ 52
+ ],
+ "w": [
+ 77,
+ 77,
+ 77,
+ 77
+ ],
+ "x": [
+ 61,
+ 61,
+ 61,
+ 61
+ ],
+ "y": [
+ 52,
+ 52,
+ 52,
+ 52
+ ],
+ "z": [
+ 57,
+ 57,
+ 57,
+ 57
+ ],
+ "{": [
+ 33,
+ 33,
+ 33,
+ 33
+ ],
+ "|": [
+ 37,
+ 37,
+ 37,
+ 37
+ ],
+ "}": [
+ 33,
+ 33,
+ 33,
+ 33
+ ],
+ "~": [
+ 63,
+ 63,
+ 63,
+ 63
+ ],
+ "→": [
+ 94,
+ 94,
+ 94,
+ 94
+ ]
+ }
+};
+
+exports.default = widthsMap;
+module.exports = exports["default"];
\ No newline at end of file
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/jsdom-exporter.js b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/jsdom-exporter.js
index e1dcacbfe2b..9d1088f2584 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/jsdom-exporter.js
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/jsdom-exporter.js
@@ -8,6 +8,12 @@
const jsdom = require('jsdom');
const fs = require('fs');
const path = require('path');
+const customWidthsMap = require('./customWidthsMap.js');
+const defaultWidthsMap = require('string-pixel-width/lib/widthsMap.js');
+const pixelWidth = require('string-pixel-width');
+
+// Combine the default font widths map from string-pixel-width with our additions
+const widthsMap = { ...defaultWidthsMap, ...customWidthsMap };
const { JSDOM } = jsdom;
@@ -25,7 +31,7 @@ const doc = win.document;
global.Node = win.Node
// Require Highcharts with the window shim
-const Highcharts = require('highcharts/highstock')(win);
+const Highcharts = require('highcharts/highstock.src')(win);
require("highcharts/modules/accessibility")(Highcharts);
require("highcharts/highcharts-more")(Highcharts);
require("highcharts/highcharts-3d")(Highcharts);
@@ -45,101 +51,207 @@ require("highcharts/modules/bullet")(Highcharts);
win.Date = Date;
+function processTextNodes(element, cb) {
+ for (var childNode of element.childNodes) {
+ if (childNode.nodeType === Node.ELEMENT_NODE) {
+ processTextNodes(childNode, cb);
+ } else if (childNode.nodeType === Node.TEXT_NODE) {
+ cb(childNode, element);
+ }
+ }
+}
+
+/**
+ * Search up parent chain for an element with the requested attribute set
+ *
+ * @param {*} ele
+ * @param {*} attr
+ * @returns Attribute, possibly inherited, or undefined if not found
+ */
+function findStyleAttr(ele, attr) {
+ while (ele) {
+ if (ele.style[attr]) {
+ return ele.style[attr];
+ }
+ ele = ele.parentElement;
+ }
+}
+
+const sizableFonts = Object.keys(widthsMap);
+
+/**
+ * The string-pixel-width library supports a limited set of fonts. Search a font-family
+ * list for a usable font, or find the next-best fallback
+ */
+function findSizableFont(fontFamily = "") {
+ let fonts = fontFamily.split(",")
+ .map(s => s.trim())
+ .map(s => s.replace(/^"(.*)"$/, '$1')) // Un-quote any quoted entries
+ .map(s => s.toLowerCase());
+
+ // Search the font list for one in our list of sizable fonts
+ let usableFont = fonts.find(f => sizableFonts.includes(f));
+
+ if (!usableFont) {
+ // None of those are sizable. Go with Highcharts default font
+ usableFont = 'times new roman';
+ }
+
+ return usableFont;
+}
+
+function removeHighchartsTextOutlines(elem) {
+ let children = [].slice.call(
+ elem.children.length ? elem.children : [elem]
+ );
+ children.forEach(child => {
+ if (child.getAttribute('class') === 'highcharts-text-outline') {
+ child.parentNode.removeChild(child);
+ }
+ });
+
+}
+
// Do some modifications to the jsdom document in order to get the SVG bounding
// boxes right.
-let oldCreateElementNS = doc.createElementNS;
-doc.createElementNS = (ns, tagName) => {
- let elem = oldCreateElementNS.call(doc, ns, tagName);
- if (ns !== 'http://www.w3.org/2000/svg') {
- return elem;
- }
- /**
- * Pass Highcharts' test for SVG capabilities
- * @returns {undefined}
- */
- elem.createSVGRect = () => { };
- /**
- * jsdom doesn't compute layout (see
- * https://github.com/tmpvar/jsdom/issues/135). This getBBox implementation
- * provides just enough information to get Highcharts to render text boxes
- * correctly, and is not intended to work like a general getBBox
- * implementation. The height of the boxes are computed from the sum of
- * tspans and their font sizes. The width is based on an average width for
- * each glyph. It could easily be improved to take font-weight into account.
- * For a more exact result we could to create a map over glyph widths for
- * several fonts and sizes, but it may not be necessary for the purpose.
- * If the width for the element is zero, then the height is also
- * set to zero, in order to not reserve any vertical space for elements
- * without content.
- * @returns {Object} The bounding box
- */
- elem.getBBox = () => {
- let lineWidth = 0,
- width = 0,
- height = 0;
-
- let children = [].slice.call(
- elem.children.length ? elem.children : [elem]
- );
+/**
+ * Pass Highcharts' test for SVG capabilities
+ * @returns {undefined}
+ */
+win.SVGElement.prototype.createSVGRect = function() { };
+/**
+ * jsdom doesn't compute layout (see
+ * https://github.com/tmpvar/jsdom/issues/135). This getBBox implementation
+ * provides just enough information to get Highcharts to render text boxes
+ * correctly, and is not intended to work like a general getBBox
+ * implementation. The height of the boxes are computed from the sum of
+ * tspans and their font sizes. The width is based on an average width for
+ * each glyph. It could easily be improved to take font-weight into account.
+ * For a more exact result we could to create a map over glyph widths for
+ * several fonts and sizes, but it may not be necessary for the purpose.
+ * If the width for the element is zero, then the height is also
+ * set to zero, in order to not reserve any vertical space for elements
+ * without content.
+ * @returns {Object} The bounding box
+ */
+win.SVGElement.prototype.getBBox = function() {
+ let lineWidth = 0,
+ lineHeight = 0,
+ width = 0,
+ height = 0,
+ newLine = false;
- children
- .filter(child => {
- if (child.getAttribute('class') === 'highcharts-text-outline') {
- child.parentNode.removeChild(child);
- return false;
- }
- return true;
- })
- .forEach(child => {
- let fontSize = child.style.fontSize || elem.style.fontSize,
- lineHeight,
- textLength;
-
- // The font size and lineHeight is based on empirical values,
- // copied from the SVGRenderer.fontMetrics function in
- // Highcharts.
- if (/px/.test(fontSize)) {
- fontSize = parseInt(fontSize, 10);
- } else {
- fontSize = /em/.test(fontSize) ?
- parseFloat(fontSize) * 12 :
- 12;
- }
- lineHeight = fontSize < 24 ?
- fontSize + 3 :
- Math.round(fontSize * 1.2);
- textLength = child.textContent.length * fontSize * 0.55;
-
- // Tspans on the same line
- if (child.getAttribute('dx') !== '0') {
- height += lineHeight;
- }
+ removeHighchartsTextOutlines(this);
- // New line
- if (child.getAttribute('dy') !== null) {
- lineWidth = 0;
- }
+ processTextNodes(this, (textNode, child) => {
+ if (child.tagName === 'title') {
+ return;
+ }
+ let fontSize = findStyleAttr(child, 'fontSize'),
+ fontFamily = findStyleAttr(child, 'fontFamily'),
+ textLength;
- lineWidth += textLength;
- width = Math.max(width, lineWidth);
+ // The font size and lineHeight is based on empirical values,
+ // copied from the SVGRenderer.fontMetrics function in
+ // Highcharts.
+ if (/px/.test(fontSize)) {
+ fontSize = parseInt(fontSize, 10);
+ } else {
+ fontSize = /em/.test(fontSize) ?
+ parseFloat(fontSize) * 12 :
+ 12;
+ }
+ let nodeHeight = fontSize < 24 ?
+ fontSize + 3 :
+ Math.round(fontSize * 1.2);
+ lineHeight = Math.max(lineHeight, nodeHeight);
+ let fontToUse = findSizableFont(fontFamily);
+ textLength = pixelWidth(textNode.data, { size: fontSize, font: fontToUse, map: widthsMap });
- }
- );
+ // In practice, dy is used to trigger a new line
+ if (child.getAttribute('dy') !== null) {
+ lineWidth = 0;
+ newLine = true;
+ }
+
+ lineWidth += textLength;
+ width = Math.max(width, lineWidth);
+
+ if (newLine) {
+ height += lineHeight;
+ newLine = false;
+ lineHeight = 0;
+ }
+ });
+
+ // Add the height of the ongoing line
+ height += lineHeight;
- // If the width of the text box is 0, always return a 0 height (since the element indeed consumes no space)
- // Returning a non-zero height causes Highcharts to allocate vertical space in the chart for text that doesn't
- // exist
- let retHeight = width == 0 ? 0 : height;
- return {
- x: 0,
- y: 0,
- width: width,
- height: retHeight
- };
+ // If the width of the text box is 0, always return a 0 height (since the element indeed consumes no space)
+ // Returning a non-zero height causes Highcharts to allocate vertical space in the chart for text that doesn't
+ // exist
+ let retHeight = width == 0 ? 0 : height;
+ return {
+ x: 0,
+ y: 0,
+ width: width,
+ height: retHeight
};
- return elem;
};
+/**
+ * Estimate the rendered length of a substring of text. Uses a similar strategy to getBBox,
+ * above.
+ *
+ * @param {integer} charnum Starting character position
+ * @param {integer} numchars Number of characters to count
+ * @returns Rendered length of the substring (estimated)
+ */
+win.SVGElement.prototype.getSubStringLength = function(charnum, numchars) {
+ let offset = charnum,
+ remaining = numchars,
+ textLength = 0;
+
+ removeHighchartsTextOutlines(this);
+
+ processTextNodes(this, (textNode, child) => {
+ if (child.tagName === 'title') {
+ return;
+ }
+
+ if (remaining <= 0) {
+ return;
+ }
+ let childLength = textNode.length;
+
+ if (childLength <= offset) {
+ offset -= childLength;
+ } else {
+ let usedLength = Math.min(childLength - offset, remaining);
+ remaining -= usedLength;
+
+ let fontSize = findStyleAttr(child, 'fontSize'),
+ fontFamily = findStyleAttr(child, 'fontFamily');
+
+ // The font size is based on empirical values,
+ // copied from the SVGRenderer.fontMetrics function in
+ // Highcharts.
+ if (/px/.test(fontSize)) {
+ fontSize = parseInt(fontSize, 10);
+ } else {
+ fontSize = /em/.test(fontSize) ?
+ parseFloat(fontSize) * 12 :
+ 12;
+ }
+ let textToSize = textNode.data.substring(offset, offset + usedLength);
+ let fontToUse = findSizableFont(fontFamily);
+ let measuredWidth = pixelWidth(textToSize, { size: fontSize, font: fontToUse, map: widthsMap });
+ textLength += measuredWidth;
+ }
+ });
+
+ return textLength;
+}
const inflateFunctions = (jsonConfiguration) => {
Object.entries(jsonConfiguration).forEach(([attr, targetProperty]) => {
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/package.json b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/package.json
index c534f44234e..b1e89fd433f 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/package.json
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/package.json
@@ -5,16 +5,18 @@
"scripts": {
"test": "mocha",
"test:watch": "mocha --watch",
- "build": "webpack --config webpack.config.js"
+ "build": "webpack --config webpack.config.js"
},
"dependencies": {
"highcharts": "9.2.2",
- "jsdom": "16.5.3"
+ "jsdom": "16.5.3",
+ "string-pixel-width": "1.11.0"
},
"devDependencies": {
"chai": "4.3.4",
"mocha": "8.4.0",
"mock-fs": "5.0.0",
+ "rewire": "7.0.0",
"webpack": "5.76.0",
"webpack-cli": "4.9.2"
},
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/java/com/vaadin/flow/component/charts/export/SVGGeneratorTest.java b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/java/com/vaadin/flow/component/charts/export/SVGGeneratorTest.java
index 727f075cfe1..dae4e4f09a3 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/java/com/vaadin/flow/component/charts/export/SVGGeneratorTest.java
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/java/com/vaadin/flow/component/charts/export/SVGGeneratorTest.java
@@ -261,8 +261,9 @@ private Configuration createColumnWithoutTitle() {
XAxis x = new XAxis();
x.setCategories("January is a long month", "February is rather boring",
- "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
- "Dec");
+ "Mar", "Apr", "May", "Jun",
+ "Jul is a month to enjoy really nice weather", "Aug", "Sep",
+ "Oct", "Nov", "Dec");
configuration.addxAxis(x);
YAxis y = new YAxis();
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/column-without-title.svg b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/column-without-title.svg
index 02a0c50e992..4d9ec97de60 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/column-without-title.svg
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/column-without-title.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-height.svg b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-height.svg
index 9ba44bd101d..8cc9a23abca 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-height.svg
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-height.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-lang.svg b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-lang.svg
index 0f0d1c27947..013f52a3ee2 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-lang.svg
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-lang.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-width.svg b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-width.svg
index ee9f080c048..a4999078ca2 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-width.svg
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/custom-width.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/empty.svg b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/empty.svg
index 20ada559d65..2eb2dbcd1f9 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/empty.svg
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/empty.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/enabled-functions.svg b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/enabled-functions.svg
index 2a0f9494181..0bc1cdece79 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/enabled-functions.svg
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/enabled-functions.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/lumo-dark.svg b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/lumo-dark.svg
index 32b3f9b865e..298b8b31f65 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/lumo-dark.svg
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/lumo-dark.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/pie.svg b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/pie.svg
index fd29933e4f3..1f2d416cd1c 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/pie.svg
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/pie.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/timeline.svg b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/timeline.svg
index d88b9e06019..0df3e1b1f6d 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/timeline.svg
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/test/resources/timeline.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/test/index.test.js b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/test/index.test.js
index e7654e06b58..3c414fd2ce2 100644
--- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/test/index.test.js
+++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/test/index.test.js
@@ -1,8 +1,13 @@
const { expect } = require('chai')
+const rewire = require('rewire');
const { JSDOM } = require('jsdom');
const mock = require('mock-fs')
+const pixelWidth = require('string-pixel-width');
-const jsdomExporter = require('../jsdom-exporter.js')
+const jsdomExporter = rewire('../jsdom-exporter.js')
+
+const exporterDom = jsdomExporter.__get__("dom");
+const widthsMap = jsdomExporter.__get__("widthsMap");
/**
*
@@ -157,3 +162,22 @@ describe('timeline', () => {
expect(document.querySelector('.highcharts-navigator')).to.be.not.null;
});
});
+
+describe('getSubStringLength', () => {
+ it('should measure strings split across multiple elements', () => {
+ let window = exporterDom.window;
+ let document = window.document;
+ let container = document.getElementById('container');
+ container.innerHTML = `
+
+ `;
+ let text = container.querySelector('text');
+ expect(text).to.be.not.null;
+ expect(text.getSubStringLength(6, 3)).to.equal(pixelWidth("678", { size: 12, font: 'arial', map: widthsMap }))
+ expect(text.getSubStringLength(1, 2)).to.equal(pixelWidth("12", { size: 12, font: 'arial', map: widthsMap }))
+ expect(text.getSubStringLength(2, 6)).to.equal(pixelWidth("234567", { size: 12, font: 'arial', map: widthsMap }))
+ expect(text.getSubStringLength(0, 20)).to.equal(pixelWidth("0123456789", { size: 12, font: 'arial', map: widthsMap }))
+ });
+});
\ No newline at end of file