Fabric.js Document Engine

Search documentation

Find a page or section

中文

Canvas size limits

View as Markdown

What the HTML canvas max size is, and how the engine stays under it.

Every browser has an HTML canvas max size, and above it the canvas stays blank or the tab runs out of memory. The limit is not the same everywhere: desktop Chrome allows a side of 32,767 pixels, while iOS Safari allows about 67 million pixels in total, a square of 8,192. A design that exports fine on a laptop can then fail on an iPhone, often without an error.

Try it

Loading the live demo. Its code is below.

size-limits-demo.tsx
"use client";
 
import { isDocumentEngineError } from "fabricjs-document-engine";
import { useDocumentEngine } from "fabricjs-document-engine/react";
import { ExpandIcon, ImageIcon, MaximizeIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useEffect, useState } from "react";
 
import {
  DemoCanvas,
  DemoFrame,
  seedShapes,
  Status,
  Toolbar,
  ToolButton,
} from "./demo-ui";
import { useFabricCanvas } from "./use-fabric-canvas";
 
export default function SizeLimitsDemo() {
  const t = useTranslations("demos");
  const { elementRef, canvas } = useFabricCanvas();
  const engine = useDocumentEngine(canvas);
  const [message, setMessage] = useState<{
    tone: "ok" | "error";
    text: string;
  } | null>(null);
 
  useEffect(() => {
    if (!engine) return;
    engine.canvas.add(...seedShapes());
    engine.clearHistory();
  }, [engine]);
 
  async function check(scale: number) {
    if (!engine) return;
    const preflight = await engine.preflightExport({ format: "png", scale });
    const problem = preflight.problems[0];
    setMessage(
      problem
        ? {
            tone: "error",
            text: t("sizeLimits.refused", { code: problem.code, scale }),
          }
        : {
            tone: "ok",
            text: t("sizeLimits.fine", {
              scale,
              width: 480 * scale,
              height: 300 * scale,
            }),
          }
    );
  }
 
  function hugePage() {
    if (!engine) return;
    try {
      engine.setPage({ width: 20000, height: 20000 });
    } catch (error) {
      setMessage({
        tone: "error",
        text: t("sizeLimits.page", {
          code: isDocumentEngineError(error) ? error.code : String(error),
        }),
      });
    }
  }
 
  return (
    <DemoFrame>
      <DemoCanvas elementRef={elementRef} label={t("canvas")} />
 
      <Toolbar>
        <ToolButton
          icon={ImageIcon}
          label={t("sizeLimits.small")}
          disabled={!engine}
          onClick={() => void check(4)}
        />
        <ToolButton
          icon={MaximizeIcon}
          label={t("sizeLimits.large")}
          primary
          disabled={!engine}
          onClick={() => void check(60)}
        />
        <ToolButton
          icon={ExpandIcon}
          label={t("sizeLimits.hugePage")}
          disabled={!engine}
          onClick={hugePage}
        />
      </Toolbar>
 
      <Status tone={message?.tone ?? "idle"}>
        {message?.text ?? t("sizeLimits.hint")}
      </Status>
    </DemoFrame>
  );
}

The 4× export fits. The 60× export would be 28,800 pixels wide and is refused before any canvas is made, and so is a page of 20,000 × 20,000.

The limits

const engine = createDocumentEngine({
  canvas,
  limits: {
    maxCanvasSide: 16384,
    maxCanvasPixels: 8192 * 8192,
    maxImagePixels: 8192 * 8192,
    maxDocumentLength: 100_000_000,
  },
});
LimitDefaultChecked on
maxCanvasSide16,384The page, and PNG, JPEG, WebP and PDF exports
maxCanvasPixels67,108,864 (8,192²)The page and raster exports
maxImagePixels67,108,864Each image, after it is decoded
maxDocumentLength100,000,000The JSON of a document that is loaded

The defaults match the strictest common browser, iOS Safari. Raise them only for an editor that runs on desktop browsers. Each check is a multiplication done before any canvas exists, so it costs nothing.

Exports

const preflight = await engine.preflightExport({ format: "png", scale: 4 });
 
if (!preflight.ok) {
  const tooLarge = preflight.problems.find(
    (problem) => problem.code === "TOO_LARGE"
  );
  if (tooLarge) showMessage(tooLarge.message);
}

An export over the canvas size limit fails with EXPORT_BLOCKED and a TOO_LARGE problem, which says the size it would have had. PDF export instead draws the page at a lower resolution, so a PDF still comes out.

Resize the canvas

Use setPage to resize the canvas or change its background. The change is one undo step, and a size above the limits is refused with UNSAFE_DOCUMENT before anything changes:

try {
  engine.setPage({ width: 3000, height: 2000, background: "#ffffff" });
} catch (error) {
  if (isDocumentEngineError(error)) showMessage(error.message);
}

Images and documents

An image above maxImagePixels fails to load with the reason TOO_LARGE, and a document whose page or JSON is over the limits is refused with UNSAFE_DOCUMENT. Both are checked before Fabric decodes or creates anything. The error codes list every code, and the security page covers the other limits. Stay under the HTML canvas max size and every device can open and export the document.