diff --git a/src/element/textElement.ts b/src/element/textElement.ts index 1b391b38b..26fe4cbb5 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -347,6 +347,41 @@ export const measureBaseline = ( return baseline; }; +export const measureDOMHeight = ( + text: string, + font: FontString, + lineHeight: ExcalidrawTextElement["lineHeight"], + wrapInContainer?: boolean, +) => { + const container = document.createElement("div"); + container.style.position = "absolute"; + container.style.whiteSpace = "pre"; + container.style.font = font; + container.style.minHeight = "1em"; + if (wrapInContainer) { + container.style.overflow = "hidden"; + container.style.wordBreak = "break-word"; + container.style.whiteSpace = "pre-wrap"; + } + + container.style.lineHeight = String(lineHeight); + + container.innerText = text; + + // Baseline is important for positioning text on canvas + document.body.appendChild(container); + + const span = document.createElement("span"); + span.style.display = "inline-block"; + span.style.overflow = "hidden"; + span.style.width = "1px"; + span.style.height = "1px"; + container.appendChild(span); + const domHeight = container.offsetHeight; + + document.body.removeChild(container); + return domHeight; +}; /** * To get unitless line-height (if unknown) we can calculate it by dividing * height-per-line by fontSize. @@ -366,7 +401,9 @@ export const getLineHeightInPx = ( fontSize: ExcalidrawTextElement["fontSize"], lineHeight: ExcalidrawTextElement["lineHeight"], ) => { - return fontSize * lineHeight; + const res = fontSize * lineHeight; + + return res; }; // FIXME rename to getApproxMinContainerHeight diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index d9a7505f9..717791a98 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -11,7 +11,7 @@ import { isBoundToContainer, isTextElement, } from "./typeChecks"; -import { CLASSES, VERTICAL_ALIGN } from "../constants"; +import { CLASSES, isSafari, VERTICAL_ALIGN } from "../constants"; import { ExcalidrawElement, ExcalidrawLinearElement, @@ -35,6 +35,8 @@ import { getMaxContainerHeight, getMaxContainerWidth, computeContainerDimensionForBoundText, + measureDOMHeight, + splitIntoLines, } from "./textElement"; import { actionDecreaseFontSize, @@ -271,13 +273,25 @@ export const textWysiwyg = ({ } else { textElementWidth += 0.5; } + const domHeight = measureDOMHeight( + updatedTextElement.text, + getFontString(updatedTextElement), + updatedTextElement.lineHeight, + ); + + let lineHeight = element.lineHeight; + if (isSafari && domHeight > textElementHeight) { + lineHeight = (Math.floor(element.lineHeight * element.fontSize) / + element.fontSize) as ExcalidrawTextElement["lineHeight"]; + } + // Make sure text editor height doesn't go beyond viewport const editorMaxHeight = (appState.height - viewportY) / appState.zoom.value; Object.assign(editable.style, { font: getFontString(updatedTextElement), // must be defined *after* font ¯\_(ツ)_/¯ - lineHeight: element.lineHeight, + lineHeight, width: `${textElementWidth}px`, height: `${textElementHeight}px`, left: `${viewportX}px`,