Browse Source

fix: Text-only measurements off by a pixel

Daniel J. Geiger 1 year ago
parent
commit
4d6d6cf129

+ 14 - 6
src/excalidraw-app/subtypes/mathjax/implementation.tsx

@@ -621,8 +621,6 @@ const measureMarkup = (
   container.appendChild(span);
   // Baseline is important for positioning text on canvas
   const baseline = span.offsetTop + span.offsetHeight;
-  const width = container.offsetWidth + 1;
-  const height = container.offsetHeight;
 
   const containerRect = container.getBoundingClientRect();
   // Compute for each SVG or Text child node of line (the last
@@ -673,6 +671,12 @@ const measureMarkup = (
     childMetrics.push({ x: 0, y: 0, width: 0, height: 0 });
   }
   document.body.removeChild(container);
+  let width = 0;
+  let height = 0;
+  childMetrics.forEach((metrics) => (width += metrics.width));
+  childMetrics.forEach(
+    (metrics) => (height = Math.max(height, metrics.height)),
+  );
   return { width, height, baseline, childMetrics };
 };
 
@@ -735,10 +739,14 @@ const getMetrics = (
     imageHeight += height;
   }
   const lastLineMetrics = lineMetrics[lineMetrics.length - 1];
+  const imageBaseline = Math.max(
+    0,
+    imageHeight - lastLineMetrics.height + lastLineMetrics.baseline - 1,
+  );
   const imageMetrics = {
     width: imageWidth,
     height: imageHeight,
-    baseline: imageHeight - lastLineMetrics.height + lastLineMetrics.baseline,
+    baseline: imageBaseline,
   };
   const metrics = { markupMetrics, lineMetrics, imageMetrics };
   if (isMathJaxLoaded) {
@@ -778,17 +786,17 @@ const renderMath = (
   );
   const width = parentWidth ?? metrics.imageMetrics.width;
 
-  let y = 0;
+  let y = -1;
   for (let index = 0; index < markup.length; index++) {
     const lineMetrics = metrics.lineMetrics[index];
     const lineMarkupMetrics = metrics.markupMetrics[index];
     const rtl = isRTL(mathLines[index]);
     const x =
       textAlign === "right"
-        ? width - lineMetrics.width
+        ? width - lineMetrics.width + 1
         : textAlign === "left"
         ? 0
-        : (width - lineMetrics.width) / 2;
+        : (width - lineMetrics.width + 1) / 2;
     // Drop any empty strings from this line to match childMetrics
     const content = markup[index].filter((value) => value !== "");
     for (let i = 0; i < content.length; i += 1) {

+ 21 - 0
src/excalidraw-app/subtypes/mathjax/tests/implementation.test.tsx

@@ -0,0 +1,21 @@
+import { render } from "../../../../tests/test-utils";
+import { API } from "../../../../tests/helpers/api";
+import ExcalidrawApp from "../../../";
+
+import { measureTextElement } from "../../../../element/textElement";
+import { ensureSubtypesLoaded } from "../../../../subtypes";
+
+describe("mathjax", () => {
+  it("text-only measurements match", async () => {
+    await render(<ExcalidrawApp />);
+    await ensureSubtypesLoaded(["math"]);
+    const text = "A quick brown fox jumps over the lazy dog.";
+    const elements = [
+      API.createElement({ type: "text", id: "A", text, subtype: "math" }),
+      API.createElement({ type: "text", id: "B", text }),
+    ];
+    const metrics1 = measureTextElement(elements[0]);
+    const metrics2 = measureTextElement(elements[1]);
+    expect(metrics1).toStrictEqual(metrics2);
+  });
+});