diff --git a/packages/element/src/newElement.ts b/packages/element/src/newElement.ts index d69f1acb62..eabb015833 100644 --- a/packages/element/src/newElement.ts +++ b/packages/element/src/newElement.ts @@ -21,7 +21,7 @@ import { getResizedElementAbsoluteCoords, } from "./bounds"; import { newElementWith } from "./mutateElement"; -import { getBoundTextMaxWidth } from "./textElement"; +import { getBoundTextMaxWidth, getInitialTextMetrics } from "./textElement"; import { normalizeText, measureText } from "./textMeasurements"; import { wrapText } from "./textWrapping"; @@ -236,40 +236,31 @@ const getTextElementPositionOffsets = ( }; }; +export type NewTextElementOptions = { + text: string; + originalText?: string; + fontSize?: number; + fontFamily?: FontFamilyValues; + textAlign?: TextAlign; + verticalAlign?: VerticalAlign; + containerId?: ExcalidrawTextContainer["id"] | null; + lineHeight?: ExcalidrawTextElement["lineHeight"]; + autoResize?: ExcalidrawTextElement["autoResize"]; +} & ElementConstructorOpts; + export const newTextElement = ( - opts: { - text: string; - originalText?: string; - fontSize?: number; - fontFamily?: FontFamilyValues; - textAlign?: TextAlign; - verticalAlign?: VerticalAlign; - containerId?: ExcalidrawTextContainer["id"] | null; - lineHeight?: ExcalidrawTextElement["lineHeight"]; - autoResize?: ExcalidrawTextElement["autoResize"]; - } & ElementConstructorOpts, + opts: NewTextElementOptions, ): NonDeleted => { const fontFamily = opts.fontFamily || DEFAULT_FONT_FAMILY; const fontSize = opts.fontSize || DEFAULT_FONT_SIZE; const lineHeight = opts.lineHeight || getLineHeight(fontFamily); const normalizedText = normalizeText(opts.text); const originalText = opts.originalText ?? normalizedText; - const shouldUseProvidedDimensions = - opts.autoResize === false && opts.width && opts.height; - const text = shouldUseProvidedDimensions - ? wrapText( - normalizedText, - getFontString({ fontFamily, fontSize }), - opts.width, - ) - : normalizedText; - - const metrics = shouldUseProvidedDimensions - ? { - width: opts.width, - height: opts.height, - } - : measureText(text, getFontString({ fontFamily, fontSize }), lineHeight); + const metrics = getInitialTextMetrics( + { ...opts, text: normalizedText }, + fontFamily, + fontSize, + ); const textAlign = opts.textAlign || DEFAULT_TEXT_ALIGN; const verticalAlign = opts.verticalAlign || DEFAULT_VERTICAL_ALIGN; const offsets = getTextElementPositionOffsets( @@ -279,7 +270,7 @@ export const newTextElement = ( const textElementProps: ExcalidrawTextElement = { ..._newElementBase("text", opts), - text, + text: normalizedText, fontSize, fontFamily, textAlign, diff --git a/packages/element/src/textElement.ts b/packages/element/src/textElement.ts index 523a8b8804..cd9a1c2bc1 100644 --- a/packages/element/src/textElement.ts +++ b/packages/element/src/textElement.ts @@ -8,6 +8,8 @@ import { getFontString, isProdEnv, invariant, + DEFAULT_FONT_FAMILY, + getLineHeight, } from "@excalidraw/common"; import { pointFrom, pointRotateRads, type Radians } from "@excalidraw/math"; @@ -30,6 +32,8 @@ import { isTextElement, } from "./typeChecks"; +import type { NewTextElementOptions } from "./newElement"; + import type { Scene } from "./Scene"; import type { MaybeTransformHandleType } from "./transformHandles"; @@ -40,6 +44,7 @@ import type { ExcalidrawTextContainer, ExcalidrawTextElement, ExcalidrawTextElementWithContainer, + FontFamilyValues, NonDeletedExcalidrawElement, } from "./types"; @@ -528,3 +533,24 @@ export const getTextFromElements = ( .join(separator); return text; }; + +/** When text is already measured and wrapped, we want to respect those dimensions */ +export const getInitialTextMetrics = ( + text: NewTextElementOptions, + fontFamily: FontFamilyValues = DEFAULT_FONT_FAMILY, + fontSize: number = DEFAULT_FONT_SIZE, +) => { + const shouldUseProvidedDimensions = + text.autoResize === false && text.width && text.height; + + return shouldUseProvidedDimensions + ? { + width: text.width, + height: text.height, + } + : measureText( + text.text, + getFontString({ fontFamily, fontSize }), + text.lineHeight ?? getLineHeight(fontFamily), + ); +}; diff --git a/packages/element/src/transform.ts b/packages/element/src/transform.ts index 370da31834..53f0bf11e5 100644 --- a/packages/element/src/transform.ts +++ b/packages/element/src/transform.ts @@ -10,10 +10,8 @@ import { arrayToMap, assertNever, cloneJSON, - getFontString, isDevEnv, toBrandedType, - getLineHeight, } from "@excalidraw/common"; import type { MarkOptional } from "@excalidraw/common/utility-types"; @@ -29,12 +27,11 @@ import { newTextElement, type ElementConstructorOpts, } from "./newElement"; -import { measureText, normalizeText } from "./textMeasurements"; import { isArrowElement } from "./typeChecks"; import { syncInvalidIndices } from "./fractionalIndex"; -import { redrawTextBoundingBox } from "./textElement"; +import { getInitialTextMetrics, redrawTextBoundingBox } from "./textElement"; import { LinearElementEditor } from "./linearElementEditor"; @@ -579,20 +576,7 @@ export const convertToExcalidrawElements = ( case "text": { const fontFamily = element?.fontFamily || DEFAULT_FONT_FAMILY; const fontSize = element?.fontSize || DEFAULT_FONT_SIZE; - const lineHeight = element?.lineHeight || getLineHeight(fontFamily); - const text = element.text ?? ""; - const shouldUseProvidedDimensions = - element.autoResize === false && element.width && element.height; - const metrics = shouldUseProvidedDimensions - ? { - width: element.width, - height: element.height, - } - : measureText( - normalizeText(text), - getFontString({ fontFamily, fontSize }), - lineHeight, - ); + const metrics = getInitialTextMetrics(element, fontFamily, fontSize); excalidrawElement = newTextElement({ width: metrics.width,