Przeglądaj źródła

fix: support breaking words containing hyphen - (#6014)

* fix: support breaking words containing hyphen -

* fix

* add spec

* fix

* fix

* fix

* fix and add spec

* improve code and add more tests
Aakansha Doshi 2 lat temu
rodzic
commit
5ddb28d378
2 zmienionych plików z 89 dodań i 9 usunięć
  1. 51 0
      src/element/textElement.test.ts
  2. 38 9
      src/element/textElement.ts

+ 51 - 0
src/element/textElement.test.ts

@@ -9,6 +9,7 @@ import {
   detectLineHeight,
   detectLineHeight,
   getLineHeightInPx,
   getLineHeightInPx,
   getDefaultLineHeight,
   getDefaultLineHeight,
+  parseTokens,
 } from "./textElement";
 } from "./textElement";
 import { FontString } from "./types";
 import { FontString } from "./types";
 
 
@@ -183,6 +184,56 @@ now`,
     expect(wrapText(text, font, -1)).toEqual(text);
     expect(wrapText(text, font, -1)).toEqual(text);
     expect(wrapText(text, font, Infinity)).toEqual(text);
     expect(wrapText(text, font, Infinity)).toEqual(text);
   });
   });
+
+  it("should wrap the text correctly when text contains hyphen", () => {
+    let text =
+      "Wikipedia is hosted by Wikimedia- Foundation, a non-profit organization that also hosts a range-of other projects";
+    const res = wrapText(text, font, 110);
+    expect(res).toBe(
+      `Wikipedia \nis hosted \nby \nWikimedia-\nFoundation,\na non-\nprofit \norganizati\non that \nalso hosts\na range-of\nother \nprojects`,
+    );
+
+    text = "Hello thereusing-now";
+    expect(wrapText(text, font, 100)).toEqual("Hello \nthereusin\ng-now");
+  });
+});
+
+describe("Test parseTokens", () => {
+  it("should split into tokens correctly", () => {
+    let text = "Excalidraw is a virtual collaborative whiteboard";
+    expect(parseTokens(text)).toEqual([
+      "Excalidraw",
+      "is",
+      "a",
+      "virtual",
+      "collaborative",
+      "whiteboard",
+    ]);
+
+    text =
+      "Wikipedia is hosted by Wikimedia- Foundation, a non-profit organization that also hosts a range-of other projects";
+    expect(parseTokens(text)).toEqual([
+      "Wikipedia",
+      "is",
+      "hosted",
+      "by",
+      "Wikimedia-",
+      "",
+      "Foundation,",
+      "a",
+      "non-",
+      "profit",
+      "organization",
+      "that",
+      "also",
+      "hosts",
+      "a",
+      "range-",
+      "of",
+      "other",
+      "projects",
+    ]);
+  });
 });
 });
 
 
 describe("Test measureText", () => {
 describe("Test measureText", () => {

+ 38 - 9
src/element/textElement.ts

@@ -419,6 +419,24 @@ export const getTextHeight = (
   return getLineHeightInPx(fontSize, lineHeight) * lineCount;
   return getLineHeightInPx(fontSize, lineHeight) * lineCount;
 };
 };
 
 
+export const parseTokens = (text: string) => {
+  // Splitting words containing "-" as those are treated as separate words
+  // by css wrapping algorithm eg non-profit => non-, profit
+  const words = text.split("-");
+  if (words.length > 1) {
+    // non-proft org => ['non-', 'profit org']
+    words.forEach((word, index) => {
+      if (index !== words.length - 1) {
+        words[index] = word += "-";
+      }
+    });
+  }
+  // Joining the words with space and splitting them again with space to get the
+  // final list of tokens
+  // ['non-', 'profit org'] =>,'non- proft org' => ['non-','profit','org']
+  return words.join(" ").split(" ");
+};
+
 export const wrapText = (text: string, font: FontString, maxWidth: number) => {
 export const wrapText = (text: string, font: FontString, maxWidth: number) => {
   // if maxWidth is not finite or NaN which can happen in case of bugs in
   // if maxWidth is not finite or NaN which can happen in case of bugs in
   // computation, we need to make sure we don't continue as we'll end up
   // computation, we need to make sure we don't continue as we'll end up
@@ -444,17 +462,16 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
     currentLine = "";
     currentLine = "";
     currentLineWidthTillNow = 0;
     currentLineWidthTillNow = 0;
   };
   };
-
   originalLines.forEach((originalLine) => {
   originalLines.forEach((originalLine) => {
     const currentLineWidth = getTextWidth(originalLine, font);
     const currentLineWidth = getTextWidth(originalLine, font);
 
 
-    //Push the line if its <= maxWidth
+    // Push the line if its <= maxWidth
     if (currentLineWidth <= maxWidth) {
     if (currentLineWidth <= maxWidth) {
       lines.push(originalLine);
       lines.push(originalLine);
       return; // continue
       return; // continue
     }
     }
-    const words = originalLine.split(" ");
 
 
+    const words = parseTokens(originalLine);
     resetParams();
     resetParams();
 
 
     let index = 0;
     let index = 0;
@@ -472,6 +489,7 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
       else if (currentWordWidth > maxWidth) {
       else if (currentWordWidth > maxWidth) {
         // push current line since the current word exceeds the max width
         // push current line since the current word exceeds the max width
         // so will be appended in next line
         // so will be appended in next line
+
         push(currentLine);
         push(currentLine);
 
 
         resetParams();
         resetParams();
@@ -492,15 +510,15 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
             currentLine += currentChar;
             currentLine += currentChar;
           }
           }
         }
         }
-
         // push current line if appending space exceeds max width
         // push current line if appending space exceeds max width
         if (currentLineWidthTillNow + spaceWidth >= maxWidth) {
         if (currentLineWidthTillNow + spaceWidth >= maxWidth) {
           push(currentLine);
           push(currentLine);
           resetParams();
           resetParams();
-        } else {
           // space needs to be appended before next word
           // space needs to be appended before next word
           // as currentLine contains chars which couldn't be appended
           // as currentLine contains chars which couldn't be appended
-          // to previous line
+          // to previous line unless the line ends with hyphen to sync
+          // with css word-wrap
+        } else if (!currentLine.endsWith("-")) {
           currentLine += " ";
           currentLine += " ";
           currentLineWidthTillNow += spaceWidth;
           currentLineWidthTillNow += spaceWidth;
         }
         }
@@ -518,12 +536,23 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
             break;
             break;
           }
           }
           index++;
           index++;
-          currentLine += `${word} `;
+
+          // if word ends with "-" then we don't need to add space
+          // to sync with css word-wrap
+          const shouldAppendSpace = !word.endsWith("-");
+          currentLine += word;
+
+          if (shouldAppendSpace) {
+            currentLine += " ";
+          }
 
 
           // Push the word if appending space exceeds max width
           // Push the word if appending space exceeds max width
           if (currentLineWidthTillNow + spaceWidth >= maxWidth) {
           if (currentLineWidthTillNow + spaceWidth >= maxWidth) {
-            const word = currentLine.slice(0, -1);
-            push(word);
+            if (shouldAppendSpace) {
+              lines.push(currentLine.slice(0, -1));
+            } else {
+              lines.push(currentLine);
+            }
             resetParams();
             resetParams();
             break;
             break;
           }
           }