From e5bc67640c72a58ebab569685266e25c48853ad9 Mon Sep 17 00:00:00 2001 From: oterral Date: Thu, 11 Jul 2024 22:34:09 +0200 Subject: [PATCH] fix: fix text style kml parsing as richt text (#727) --- package.json | 2 +- src/utils/KML.js | 31 ++++++- src/utils/KML.test.js | 193 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 214 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index a44d96df..5a3cf60c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "react-spatial", "license": "MIT", "description": "Components to build React map apps.", - "version": "1.11.4", + "version": "1.11.5-beta.7", "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", diff --git a/src/utils/KML.js b/src/utils/KML.js index 9f2b20a1..e31b1d8f 100644 --- a/src/utils/KML.js +++ b/src/utils/KML.js @@ -164,9 +164,30 @@ const sanitizeFeature = (feature, doNotRevert32pxScaling = false) => { feature.set("name", name); } + // For backward compatibility we translate the bold and italic textFont property to a textArray prop + const font = feature.get("textFont") || "normal 16px Helvetica"; + + // Since we use rich text in mapset editor we use a text array instead, + // it's only necessary when there is new lines in the text + // Manage new lines + if (/\n/.test(name)) { + const array = []; + const split = name.split("\n"); + split.forEach((txt, idx) => { + array.push(txt || "\u200B", txt ? font : ""); + + if (idx < split.length - 1) { + array.push("\n", ""); + } + }); + name = array; + } else { + name = [name, font]; + } + text = new Text({ - font: feature.get("textFont") || "normal 16px Helvetica", - text: feature.get("name"), + font: font.replace(/bold/g, "normal"), // We manage bold in textArray + text: name, fill: style.getText().getFill(), // rotation unsupported by KML, taken instead from custom field. rotation: feature.get("textRotation") || 0, @@ -483,14 +504,16 @@ const writeFeatures = (layer, featureProjection, mapResolution) => { .map((t, idx) => { return idx % 2 === 0 ? t : ""; }) - .join(""); + + .join("") + .replaceAll("\u200B", ""); } // We add the current text as features's name so it will be added as Placemark's name in the kml if (kmlText) { // If we see spaces at the beginning or at the end we add a empty // white space at the beginning and at the end. - if (/^\s|\s$/g.test(kmlText)) { + if (/^(\s|\n)|(\n|\s)$/g.test(kmlText)) { clone.set("name", `\u200B${kmlText}\u200B`); } else { clone.set("name", kmlText); diff --git a/src/utils/KML.test.js b/src/utils/KML.test.js index cde2b862..ea78e2ec 100644 --- a/src/utils/KML.test.js +++ b/src/utils/KML.test.js @@ -164,11 +164,16 @@ describe("KML", () => { right + + + ["bar","normal 16px arial"] + + rgba(255,255,255,0.01) - bold 16px arial + normal 16px arial -90 @@ -203,8 +208,8 @@ describe("KML", () => { // Text const styleText = style.getText(); - expect(styleText.getText()).toBe("bar"); - expect(styleText.getFont()).toEqual("bold 16px arial"); + expect(styleText.getText()).toEqual(["bar", "normal 16px arial"]); + expect(styleText.getFont()).toEqual("normal 16px arial"); expect(styleText.getFill()).toEqual({ color_: [32, 52, 126, 1], patternImage_: null, @@ -248,8 +253,13 @@ describe("KML", () => { + + + [" bar ","normal 16px arial"] + + - bold 16px arial + normal 16px arial @@ -266,7 +276,7 @@ describe("KML", () => { // Text const styleText = style.getText(); - expect(styleText.getText()).toBe(" bar "); // Avoid trim spaces using unicode \u200B + expect(styleText.getText()).toEqual([" bar ", "normal 16px arial"]); // Avoid trim spaces using unicode \u200B expectWriteResult(feats, str); }); @@ -291,7 +301,7 @@ describe("KML", () => { ["\\n",""," ","","foo","bold 16px arial"," ","","\\n",""] - bold 16px arial + normal 16px arial @@ -323,10 +333,179 @@ describe("KML", () => { "\n", "", ]); - expect(styleText.getFont()).toEqual("bold 16px arial"); + expect(styleText.getFont()).toEqual("normal 16px arial"); expectWriteResult(feats, str); }); + test("should read/rewrite old TextStyle as textArray as ExtendedData.", () => { + const str = ` + + + lala + + \u200B bar \u200B + + + + normal 16px arial + + + + 0,0,0 + + + + + `; + const feats = KML.readFeatures(str); + const style = feats[0].getStyleFunction()(feats[0], 1); + expect(feats.length).toBe(1); + expect(style instanceof Style).toBe(true); + + // Text + const styleText = style.getText(); + + // Make sure it is an array, the toEqual on nextline is not working properly because the array is converted to a string for comparaison. + expect(Array.isArray(styleText.getText())).toBe(true); + expect(styleText.getText()).toEqual([" bar ", "normal 16px arial"]); + expect(styleText.getFont()).toEqual("normal 16px arial"); + + const strAfter = ` + + + lala + + \u200B bar \u200B + + + + + [" bar ","normal 16px arial"] + + + + normal 16px arial + + + + 0,0,0 + + + + + `; + expectWriteResult(feats, strAfter); + }); + + test("should read/write old bold TextStyle as textArray as ExtendedData.", () => { + const str = ` + + + lala + + \u200B\n foo \n\n \n\u200B + + + + bold 16px arial + + + + 0,0,0 + + + + + `; + const feats = KML.readFeatures(str); + const style = feats[0].getStyleFunction()(feats[0], 1); + expect(feats.length).toBe(1); + expect(style instanceof Style).toBe(true); + + // Text + const styleText = style.getText(); + + // Make sure it is an array, the toEqual on nextline is not working properly because the array is converted to a string for comparaison. + expect(Array.isArray(styleText.getText())).toBe(true); + expect(styleText.getText()).toEqual([ + "\u200B", + "", + "\n", + "", + " foo ", + "bold 16px arial", + "\n", + "", + "\u200B", + "", + "\n", + "", + " ", + "bold 16px arial", + "\n", + "", + "\u200B", + "", + ]); + expect(styleText.getFont()).toEqual("normal 16px arial"); + + const strAfter = ` + + + lala + + \u200B\n foo \n\n \n\u200B + + + + ["\u200B","","\\n",""," foo ","bold 16px arial","\\n","","\u200B","","\\n",""," ","bold 16px arial","\\n","","\u200B",""] + + + normal 16px arial + + + + 0,0,0 + + + + + `; + expectWriteResult(feats, strAfter); + }); + test("should read and write lineDash and fillPattern style for polygon", () => { const str = `