Fabric.js images and fonts are the most common reason a saved document looks wrong when it is opened again. An image URL expired, a font was not loaded yet, or a picture only existed in the old tab. The engine checks all of this for you.
The asset manifest
Every saved document lists the images and fonts it needs, with the ids of the objects that use them:
const manifest = engine.getAssetManifest();
// { images: [{ url, objectIds }], fonts: [{ family, weight, style, objectIds }] }Images embedded as data: URLs are left out, because they need no fetching.
Configure the checks
const engine = createDocumentEngine({
canvas,
storage,
assets: {
resolveUrl: (url) => url.replace("asset://", "https://cdn.example.com/"),
replaceMissingImage: () => "/placeholder.png",
upload: async ({ blob }) => uploadToYourBucket(blob),
loadFont: async ({ family, weight, style }) => {
const face = new FontFace(
family,
`url(/fonts/${family}-${weight}.woff2)`,
{
weight,
style,
}
);
document.fonts.add(await face.load());
},
requireFonts: false,
},
});When a document is opened
resolveUrlcan rewrite each stored URL, for example to sign it or to map ids to a CDN.loadFontruns for each font family and weight. The engine then checks the font really renders, instead of falling back quietly.- Every image loads in parallel. For each missing image,
replaceMissingImagecan return a new URL, ornullto leave it missing. - If images are still missing, loading fails with
MISSING_ASSETS.error.missingAssetslists each{ url, objectIds }, and the canvas is not touched.
A font that cannot load gives a FONT_UNAVAILABLE warning and the text uses a fallback font. Set requireFonts: true to fail with MISSING_FONTS instead.
When a document is saved
Images that exist only in this tab (blob: URLs) and embedded data: images are passed to upload once. The document stores the returned URL. Without an upload handler, blob: images give an ASSET_NOT_PORTABLE warning, because another device cannot open them.
CORS and export
An image from another site, loaded without crossOrigin: "anonymous", taints the canvas, and a PNG export fails. The engine warns with IMAGE_CROSS_ORIGIN so you can fix it early. Serve the image with an Access-Control-Allow-Origin header and load it with crossOrigin: "anonymous".
Check and replace at any time
const report = await engine.checkAssets();
report.missingImages;
report.unavailableFonts;
await engine.replaceImage("/old-logo.png", "/new-logo.png");replaceImage swaps every image with that URL. Each keeps its size on the page, and the change is one undo step. That makes it easy to fix Fabric.js images and fonts after a user reports a problem.