Browse Source

fix: preserve trailing spaces in word wrap

Aakansha Doshi 2 years ago
parent
commit
caf0a904db
2 changed files with 62 additions and 20 deletions
  1. 26 16
      src/element/textElement.ts
  2. 36 4
      src/renderer/renderElement.ts

+ 26 - 16
src/element/textElement.ts

@@ -384,7 +384,7 @@ export const getApproxMinLineHeight = (
 
 
 let canvas: HTMLCanvasElement | undefined;
 let canvas: HTMLCanvasElement | undefined;
 
 
-const getLineWidth = (text: string, font: FontString) => {
+export const getLineWidth = (text: string, font: FontString) => {
   if (!canvas) {
   if (!canvas) {
     canvas = document.createElement("canvas");
     canvas = document.createElement("canvas");
   }
   }
@@ -444,10 +444,9 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
   if (!Number.isFinite(maxWidth) || maxWidth < 0) {
   if (!Number.isFinite(maxWidth) || maxWidth < 0) {
     return text;
     return text;
   }
   }
-
+  console.log("TEXT =", text, maxWidth);
   const lines: Array<string> = [];
   const lines: Array<string> = [];
   const originalLines = text.split("\n");
   const originalLines = text.split("\n");
-  const spaceWidth = getLineWidth(" ", font);
 
 
   let currentLine = "";
   let currentLine = "";
   let currentLineWidthTillNow = 0;
   let currentLineWidthTillNow = 0;
@@ -463,7 +462,7 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
     currentLineWidthTillNow = 0;
     currentLineWidthTillNow = 0;
   };
   };
   originalLines.forEach((originalLine) => {
   originalLines.forEach((originalLine) => {
-    const currentLineWidth = getTextWidth(originalLine, font);
+    const currentLineWidth = getLineWidth(originalLine, font);
 
 
     // Push the line if its <= maxWidth
     // Push the line if its <= maxWidth
     if (currentLineWidth <= maxWidth) {
     if (currentLineWidth <= maxWidth) {
@@ -472,6 +471,7 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
     }
     }
 
 
     const words = parseTokens(originalLine);
     const words = parseTokens(originalLine);
+    console.log(words, "words");
     resetParams();
     resetParams();
 
 
     let index = 0;
     let index = 0;
@@ -511,23 +511,25 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
           }
           }
         }
         }
         // push current line if appending space exceeds max width
         // push current line if appending space exceeds max width
-        if (currentLineWidthTillNow + spaceWidth >= maxWidth) {
+        if (currentLineWidthTillNow >= maxWidth) {
           push(currentLine);
           push(currentLine);
           resetParams();
           resetParams();
           // 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 unless the line ends with hyphen to sync
           // to previous line unless the line ends with hyphen to sync
           // with css word-wrap
           // with css word-wrap
-        } else if (!currentLine.endsWith("-")) {
+        } else if (!currentLine.endsWith("-") && index < words.length) {
           currentLine += " ";
           currentLine += " ";
-          currentLineWidthTillNow += spaceWidth;
         }
         }
         index++;
         index++;
       } else {
       } else {
         // Start appending words in a line till max width reached
         // Start appending words in a line till max width reached
         while (currentLineWidthTillNow < maxWidth && index < words.length) {
         while (currentLineWidthTillNow < maxWidth && index < words.length) {
           const word = words[index];
           const word = words[index];
-          currentLineWidthTillNow = getLineWidth(currentLine + word, font);
+          currentLineWidthTillNow = getLineWidth(
+            `${currentLine + word}`.trimEnd(),
+            font,
+          );
 
 
           if (currentLineWidthTillNow > maxWidth) {
           if (currentLineWidthTillNow > maxWidth) {
             push(currentLine);
             push(currentLine);
@@ -535,36 +537,44 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
 
 
             break;
             break;
           }
           }
-          index++;
 
 
           // if word ends with "-" then we don't need to add space
           // if word ends with "-" then we don't need to add space
           // to sync with css word-wrap
           // to sync with css word-wrap
           const shouldAppendSpace = !word.endsWith("-");
           const shouldAppendSpace = !word.endsWith("-");
           currentLine += word;
           currentLine += word;
 
 
-          if (shouldAppendSpace) {
+          if (shouldAppendSpace && index < words.length) {
             currentLine += " ";
             currentLine += " ";
           }
           }
+          console.log("currentLine", currentLine, currentLine.length, index);
+          index++;
 
 
           // Push the word if appending space exceeds max width
           // Push the word if appending space exceeds max width
-          if (currentLineWidthTillNow + spaceWidth >= maxWidth) {
-            if (shouldAppendSpace) {
-              lines.push(currentLine.slice(0, -1));
-            } else {
-              lines.push(currentLine);
-            }
+          if (currentLineWidthTillNow >= maxWidth) {
+            // if (
+            //   currentLineWidthTillNow + spaceWidth === maxWidth &&
+            //   index < words.length &&
+            //   words[index] === ""
+            // ) {
+            //   currentLine += " ";
+            //   index++;
+            // }
+
+            lines.push(currentLine);
             resetParams();
             resetParams();
             break;
             break;
           }
           }
         }
         }
       }
       }
     }
     }
+    console.log("currentLine", currentLine);
     if (currentLine.slice(-1) === " ") {
     if (currentLine.slice(-1) === " ") {
       // only remove last trailing space which we have added when joining words
       // only remove last trailing space which we have added when joining words
       currentLine = currentLine.slice(0, -1);
       currentLine = currentLine.slice(0, -1);
       push(currentLine);
       push(currentLine);
     }
     }
   });
   });
+  console.log("TEXT Words", lines);
   return lines.join("\n");
   return lines.join("\n");
 };
 };
 
 

+ 36 - 4
src/renderer/renderElement.ts

@@ -37,17 +37,21 @@ import {
   MAX_DECIMALS_FOR_SVG_EXPORT,
   MAX_DECIMALS_FOR_SVG_EXPORT,
   MIME_TYPES,
   MIME_TYPES,
   SVG_NS,
   SVG_NS,
+  TEXT_ALIGN,
 } from "../constants";
 } from "../constants";
 import { getStroke, StrokeOptions } from "perfect-freehand";
 import { getStroke, StrokeOptions } from "perfect-freehand";
 import {
 import {
   getBoundTextElement,
   getBoundTextElement,
+  getBoundTextElementOffset,
   getContainerCoords,
   getContainerCoords,
   getContainerElement,
   getContainerElement,
   getLineHeightInPx,
   getLineHeightInPx,
+  getLineWidth,
   getMaxContainerHeight,
   getMaxContainerHeight,
   getMaxContainerWidth,
   getMaxContainerWidth,
 } from "../element/textElement";
 } from "../element/textElement";
 import { LinearElementEditor } from "../element/linearElementEditor";
 import { LinearElementEditor } from "../element/linearElementEditor";
+import heILJsonE0bd304682986695208c from "../packages/excalidraw/dist/excalidraw-assets-dev/locales/he-IL-json-e0bd304682986695208c";
 
 
 // using a stronger invert (100% vs our regular 93%) and saturate
 // using a stronger invert (100% vs our regular 93%) and saturate
 // as a temp hack to make images in dark theme look closer to original
 // as a temp hack to make images in dark theme look closer to original
@@ -319,13 +323,17 @@ const drawElementOnCanvas = (
         }
         }
         context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr");
         context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr");
         context.save();
         context.save();
-        context.font = getFontString(element);
+        const font = getFontString(element);
+        context.font = font;
         context.fillStyle = element.strokeColor;
         context.fillStyle = element.strokeColor;
         context.textAlign = element.textAlign as CanvasTextAlign;
         context.textAlign = element.textAlign as CanvasTextAlign;
 
 
+        context.fillStyle = "yellow";
+        context.fillRect(0, 0, element.width, element.height);
+        context.fillStyle = element.strokeColor;
+
         // Canvas does not support multiline text by default
         // Canvas does not support multiline text by default
         const lines = element.text.replace(/\r\n?/g, "\n").split("\n");
         const lines = element.text.replace(/\r\n?/g, "\n").split("\n");
-
         const horizontalOffset =
         const horizontalOffset =
           element.textAlign === "center"
           element.textAlign === "center"
             ? element.width / 2
             ? element.width / 2
@@ -336,11 +344,35 @@ const drawElementOnCanvas = (
           element.fontSize,
           element.fontSize,
           element.lineHeight,
           element.lineHeight,
         );
         );
+        const container = getContainerElement(element);
+        if (container) {
+          console.log(
+            "Element width = ",
+            element.width,
+            getMaxContainerWidth(container),
+          );
+        }
         const verticalOffset = element.height - element.baseline;
         const verticalOffset = element.height - element.baseline;
         for (let index = 0; index < lines.length; index++) {
         for (let index = 0; index < lines.length; index++) {
+          const trailingSpacesWidth =
+            getLineWidth(lines[index], font) -
+            getLineWidth(lines[index].trimEnd(), font);
+          console.log(trailingSpacesWidth, "width");
+          const maxWidth = container
+            ? getMaxContainerWidth(container)
+            : element.width;
+          const availableWidth =
+            maxWidth - getLineWidth(lines[index].trimEnd(), font);
+          let spacesOffset = 0;
+          if (element.textAlign === TEXT_ALIGN.CENTER) {
+            spacesOffset = -trailingSpacesWidth / 2;
+          } else if (element.textAlign === TEXT_ALIGN.RIGHT) {
+            spacesOffset = -Math.min(availableWidth, trailingSpacesWidth);
+          }
+          console.log(spacesOffset, "spacesOffset", trailingSpacesWidth);
           context.fillText(
           context.fillText(
-            lines[index],
-            horizontalOffset,
+            lines[index].trimEnd(),
+            horizontalOffset + spacesOffset,
             (index + 1) * lineHeightPx - verticalOffset,
             (index + 1) * lineHeightPx - verticalOffset,
           );
           );
         }
         }