diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx
index 4935754f8..612a26529 100644
--- a/src/element/textWysiwyg.tsx
+++ b/src/element/textWysiwyg.tsx
@@ -22,7 +22,7 @@ export function textWysiwyg({
Object.assign(input.style, {
color: strokeColor,
position: "absolute",
- top: y - 8 + "px",
+ top: y + "px",
left: x + "px",
transform: "translate(-50%, -50%)",
boxShadow: "none",
@@ -36,6 +36,11 @@ export function textWysiwyg({
input.onkeydown = ev => {
if (ev.key === KEYS.ESCAPE) {
ev.preventDefault();
+ if (initText) {
+ input.value = initText;
+ handleSubmit();
+ return;
+ }
cleanup();
return;
}
diff --git a/src/element/types.ts b/src/element/types.ts
index 1662bfdf0..e350ad65b 100644
--- a/src/element/types.ts
+++ b/src/element/types.ts
@@ -5,5 +5,7 @@ export type ExcalidrawTextElement = ExcalidrawElement & {
type: "text";
font: string;
text: string;
- actualBoundingBoxAscent: number;
+ // for backward compatibility
+ actualBoundingBoxAscent?: number;
+ baseline: number;
};
diff --git a/src/index.tsx b/src/index.tsx
index 86413b6cc..3eeecb158 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -34,13 +34,12 @@ import { renderScene } from "./renderer";
import { AppState } from "./types";
import { ExcalidrawElement, ExcalidrawTextElement } from "./element/types";
-import { getDateTime, isInputLike } from "./utils";
+import { getDateTime, isInputLike, measureText } from "./utils";
import { ButtonSelect } from "./components/ButtonSelect";
import { findShapeByKey, shapesShortcutKeys } from "./shapes";
import { createHistory } from "./history";
-import "./styles.scss";
import ContextMenu from "./components/ContextMenu";
import { PanelTools } from "./components/panels/PanelTools";
import { PanelSelection } from "./components/panels/PanelSelection";
@@ -48,6 +47,8 @@ import { PanelColor } from "./components/panels/PanelColor";
import { PanelExport } from "./components/panels/PanelExport";
import { PanelCanvas } from "./components/panels/PanelCanvas";
+import "./styles.scss";
+
const { elements } = createScene();
const { history } = createHistory();
const DEFAULT_PROJECT_NAME = `excalidraw-${getDateTime()}`;
@@ -94,23 +95,16 @@ function addTextElement(
if (text === null || text === "") {
return false;
}
+
+ const metrics = measureText(text, font);
element.text = text;
element.font = font;
- const currentFont = context.font;
- context.font = element.font;
- const textMeasure = context.measureText(element.text);
- const width = textMeasure.width;
- const actualBoundingBoxAscent =
- textMeasure.actualBoundingBoxAscent || parseInt(font);
- const actualBoundingBoxDescent = textMeasure.actualBoundingBoxDescent || 0;
- element.actualBoundingBoxAscent = actualBoundingBoxAscent;
- context.font = currentFont;
- const height = actualBoundingBoxAscent + actualBoundingBoxDescent;
// Center the text
- element.x -= width / 2;
- element.y -= actualBoundingBoxAscent;
- element.width = width;
- element.height = height;
+ element.x -= metrics.width / 2;
+ element.y -= metrics.height / 2;
+ element.width = metrics.width;
+ element.height = metrics.height;
+ element.baseline = metrics.baseline;
return true;
}
@@ -965,8 +959,7 @@ class App extends React.Component<{}, AppState> {
Object.assign(element, elementAtPosition);
// x and y will change after calling addTextElement function
element.x = elementAtPosition.x + elementAtPosition.width / 2;
- element.y =
- elementAtPosition.y + elementAtPosition.actualBoundingBoxAscent;
+ element.y = elementAtPosition.y + elementAtPosition.height / 2;
initText = elementAtPosition.text;
textX =
this.state.scrollX +
@@ -977,7 +970,7 @@ class App extends React.Component<{}, AppState> {
this.state.scrollY +
elementAtPosition.y +
CANVAS_WINDOW_OFFSET_TOP +
- elementAtPosition.actualBoundingBoxAscent;
+ elementAtPosition.height / 2;
}
textWysiwyg({
@@ -1063,6 +1056,5 @@ const rootElement = document.getElementById("root");
ReactDOM.render(, rootElement);
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
const rc = rough.canvas(canvas);
-const context = canvas.getContext("2d")!;
ReactDOM.render(, rootElement);
diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts
index 3c6b1d90e..8742e8769 100644
--- a/src/renderer/renderElement.ts
+++ b/src/renderer/renderElement.ts
@@ -131,11 +131,14 @@ export function renderElement(
context.fillText(
element.text,
element.x + scrollX,
- element.y + element.actualBoundingBoxAscent + scrollY
+ element.y +
+ scrollY +
+ (element.baseline || element.actualBoundingBoxAscent || 0)
);
context.fillStyle = fillStyle;
context.font = font;
context.globalAlpha = 1;
+ console.log(element);
} else {
throw new Error("Unimplemented type " + element.type);
}
diff --git a/src/utils.ts b/src/utils.ts
index a62ecd070..7c71cc03a 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -23,3 +23,30 @@ export function isInputLike(
target instanceof HTMLSelectElement
);
}
+
+// https://github.com/grassator/canvas-text-editor/blob/master/lib/FontMetrics.js
+export function measureText(text: string, font: string) {
+ const line = document.createElement("div");
+ const body = document.body;
+ line.style.position = "absolute";
+ line.style.whiteSpace = "nowrap";
+ line.style.font = font;
+ body.appendChild(line);
+ // Now we can measure width and height of the letter
+ line.innerHTML = text;
+ const width = line.offsetWidth;
+ const height = line.offsetHeight;
+ // Now creating 1px sized item that will be aligned to baseline
+ // to calculate baseline shift
+ const span = document.createElement("span");
+ span.style.display = "inline-block";
+ span.style.overflow = "hidden";
+ span.style.width = "1px";
+ span.style.height = "1px";
+ line.appendChild(span);
+ // Baseline is important for positioning text on canvas
+ const baseline = span.offsetTop + span.offsetHeight;
+ document.body.removeChild(line);
+
+ return { width, height, baseline };
+}