diff --git a/src/element/textElement.ts b/src/element/textElement.ts index fd501c1414..77987a55e3 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -289,6 +289,43 @@ export const measureText = ( return { width, height }; }; +export const measureBaseline = ( + 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"; + } + + //@ts-ignore + container.style.lineHeight = 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 baseline = span.offsetTop + span.offsetHeight; + //document.body.removeChild(container); + return baseline; +}; + /** * To get unitless line-height (if unknown) we can calculate it by dividing * height-per-line by fontSize. diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index 4036719df0..4103b3fa38 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -34,6 +34,7 @@ import { wrapText, getMaxContainerHeight, getMaxContainerWidth, + measureBaseline, } from "./textElement"; import { actionDecreaseFontSize, @@ -269,6 +270,17 @@ export const textWysiwyg = ({ } else { textElementWidth += 0.5; } + const baseline = measureBaseline( + updatedTextElement.text, + getFontString(updatedTextElement), + updatedTextElement.lineHeight, + !!container, + ); + + const offset = + (updatedTextElement.height - baseline - 10) * appState.zoom.value; + + const top = viewportY + offset; // Make sure text editor height doesn't go beyond viewport const editorMaxHeight = (appState.height - viewportY) / appState.zoom.value; @@ -279,7 +291,7 @@ export const textWysiwyg = ({ width: `${textElementWidth}px`, height: `${textElementHeight}px`, left: `${viewportX}px`, - top: `${viewportY}px`, + top: `${top}px`, transform: getTransform( textElementWidth, textElementHeight, diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index 0861315cce..d6d4079c98 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -200,6 +200,16 @@ const drawImagePlaceholder = ( size, ); }; +//@ts-ignore +const drawLine = (x, y, width, height, stroke, context) => { + context.lineWidth = "2"; + context.strokeStyle = stroke; + context.beginPath(); + context.moveTo(x, y); + context.lineTo(x + width, y); + context.closePath(); + context.stroke(); +}; const drawElementOnCanvas = ( element: NonDeletedExcalidrawElement, @@ -274,6 +284,29 @@ const drawElementOnCanvas = ( context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr"); context.save(); context.font = getFontString(element); + context.textBaseline = "alphabetic"; + const metrics = context.measureText(element.text); + + // drawLine(0, 0, metrics.width, element.height, "green", context); + + // drawLine( + // 0, + // -metrics.actualBoundingBoxAscent, + // metrics.width, + // element.height, + // "magenta", + // context, + // ); + + // drawLine( + // 0, + // metrics.actualBoundingBoxDescent, + // metrics.width, + // element.height, + // "magenta", + // context, + // ); + context.fillStyle = element.strokeColor; context.textAlign = element.textAlign as CanvasTextAlign; @@ -286,18 +319,17 @@ const drawElementOnCanvas = ( : element.textAlign === "right" ? element.width : 0; - context.textBaseline = "bottom"; - const lineHeightPx = getLineHeightInPx( element.fontSize, element.lineHeight, ); + const verticalOffset = 10; for (let index = 0; index < lines.length; index++) { context.fillText( lines[index], horizontalOffset, - (index + 1) * lineHeightPx, + (index + 1) * lineHeightPx - verticalOffset, ); } context.restore();