From caf0a904db3367ffff89f863b9287d79f9e5942b Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Thu, 20 Apr 2023 20:13:01 +0530 Subject: [PATCH] fix: preserve trailing spaces in word wrap --- src/element/textElement.ts | 42 ++++++++++++++++++++++------------- src/renderer/renderElement.ts | 40 +++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/element/textElement.ts b/src/element/textElement.ts index f01ba3e1b..d851afe54 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -384,7 +384,7 @@ export const getApproxMinLineHeight = ( let canvas: HTMLCanvasElement | undefined; -const getLineWidth = (text: string, font: FontString) => { +export const getLineWidth = (text: string, font: FontString) => { if (!canvas) { canvas = document.createElement("canvas"); } @@ -444,10 +444,9 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => { if (!Number.isFinite(maxWidth) || maxWidth < 0) { return text; } - + console.log("TEXT =", text, maxWidth); const lines: Array = []; const originalLines = text.split("\n"); - const spaceWidth = getLineWidth(" ", font); let currentLine = ""; let currentLineWidthTillNow = 0; @@ -463,7 +462,7 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => { currentLineWidthTillNow = 0; }; originalLines.forEach((originalLine) => { - const currentLineWidth = getTextWidth(originalLine, font); + const currentLineWidth = getLineWidth(originalLine, font); // Push the line if its <= maxWidth if (currentLineWidth <= maxWidth) { @@ -472,6 +471,7 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => { } const words = parseTokens(originalLine); + console.log(words, "words"); resetParams(); 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 - if (currentLineWidthTillNow + spaceWidth >= maxWidth) { + if (currentLineWidthTillNow >= maxWidth) { push(currentLine); resetParams(); // space needs to be appended before next word // as currentLine contains chars which couldn't be appended // to previous line unless the line ends with hyphen to sync // with css word-wrap - } else if (!currentLine.endsWith("-")) { + } else if (!currentLine.endsWith("-") && index < words.length) { currentLine += " "; - currentLineWidthTillNow += spaceWidth; } index++; } else { // Start appending words in a line till max width reached while (currentLineWidthTillNow < maxWidth && index < words.length) { const word = words[index]; - currentLineWidthTillNow = getLineWidth(currentLine + word, font); + currentLineWidthTillNow = getLineWidth( + `${currentLine + word}`.trimEnd(), + font, + ); if (currentLineWidthTillNow > maxWidth) { push(currentLine); @@ -535,36 +537,44 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => { break; } - index++; // 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) { + if (shouldAppendSpace && index < words.length) { currentLine += " "; } + console.log("currentLine", currentLine, currentLine.length, index); + index++; // 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(); break; } } } } + console.log("currentLine", currentLine); if (currentLine.slice(-1) === " ") { // only remove last trailing space which we have added when joining words currentLine = currentLine.slice(0, -1); push(currentLine); } }); + console.log("TEXT Words", lines); return lines.join("\n"); }; diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index f85c83a6b..619c76e74 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -37,17 +37,21 @@ import { MAX_DECIMALS_FOR_SVG_EXPORT, MIME_TYPES, SVG_NS, + TEXT_ALIGN, } from "../constants"; import { getStroke, StrokeOptions } from "perfect-freehand"; import { getBoundTextElement, + getBoundTextElementOffset, getContainerCoords, getContainerElement, getLineHeightInPx, + getLineWidth, getMaxContainerHeight, getMaxContainerWidth, } from "../element/textElement"; 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 // 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.save(); - context.font = getFontString(element); + const font = getFontString(element); + context.font = font; context.fillStyle = element.strokeColor; 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 const lines = element.text.replace(/\r\n?/g, "\n").split("\n"); - const horizontalOffset = element.textAlign === "center" ? element.width / 2 @@ -336,11 +344,35 @@ const drawElementOnCanvas = ( element.fontSize, element.lineHeight, ); + const container = getContainerElement(element); + if (container) { + console.log( + "Element width = ", + element.width, + getMaxContainerWidth(container), + ); + } const verticalOffset = element.height - element.baseline; 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( - lines[index], - horizontalOffset, + lines[index].trimEnd(), + horizontalOffset + spacesOffset, (index + 1) * lineHeightPx - verticalOffset, ); }