Fonts and rendering

Vector text

Try it in the demo →

By default the library draws every letter as a shape read from the font file, instead of asking the browser to draw text. Each letter is measured and drawn from the same file, so every browser draws the same pixels, line breaks are the same everywhere, letters stay sharp at any zoom, and print matches the screen.

Render mode

import { configureRichText } from "@mycreativeshop/rich-text"; // on Konva: "@mycreativeshop/rich-text/konva"

configureRichText({ renderMode: "vector" }); // the default
configureRichText({ renderMode: "native" }); // the browser draws the text
Mode Letters drawn by Characters the font lacks
"vector" The library, as shapes from the font file Google's Noto fonts and our emoji set, the same everywhere (see Languages and emoji)
"native" The browser The fonts on the device, which differ between devices

Set the mode before the first text box exists. Once a box exists the mode is fixed for the page, and a later change is ignored with a console warning. A print server must use the same mode as the editor.

In vector mode, text the library cannot draw as shapes, such as color fonts and scripts the Noto fonts do not cover, is drawn by the browser on its own.

Compare with native mode

On Konva, box.compareWithNativeLayout() tells you whether a box would break its lines differently in native mode. It returns null when the modes cannot differ: not in vector mode, or no character in the box is drawn from the fallback fonts or the emoji set. Otherwise it returns the lines in each mode, linesChanged, and the characters drawn from the fallback fonts. Use it when your app moves from native to vector text, to point people at the boxes that changed:

const comparison = await headline.compareWithNativeLayout();
if (comparison?.linesChanged) console.log(comparison.vector, comparison.native);

Zoom

Zoom is your own view's scale. The letters are drawn again from their shapes at each scale, so they stay sharp:

// Konva
stage.scale({ x: 4, y: 4 });

// Plain canvas
ctx.setTransform(4 * ratio, 0, 0, 4 * ratio, 0, 0);
rt.paint(ctx);
rt.paintOverlay(ctx);

On a plain canvas, pass the same transform to rt.fromEvent(e, canvas, sceneToCanvas) so clicks land where the text is drawn. The cursor and the handles keep their size on screen at any zoom.

Text versions

When a change to how text is laid out would move existing text, it ships as a new text version, and saved text keeps drawing exactly as it was saved. See Upgrading.

Exporting images and print

Text in an exported image or PDF is the same as on screen: the same fonts, the same line breaks, the same pixels. Wait for fonts first, so nothing is captured in a stand-in font.

Konva

cloneStageForExport waits for every text box's fonts, then copies the stage with each box exactly as it is laid out on screen:

import { cloneStageForExport } from "@mycreativeshop/rich-text/konva";

const copy = await cloneStageForExport(stage);
const png = copy.toDataURL({ pixelRatio: 3 });
copy.destroy();

The copy is kept out of Konva.stages, and its image fills are loaded before it draws. Destroy it when you are done.

To export without a copy, wait yourself with richTextReady:

import { richTextReady } from "@mycreativeshop/rich-text/konva";

await richTextReady(stage); // or a layer, or a group
const png = layer.toDataURL({ pixelRatio: 3 });

A box created just before an export, for a thumbnail for example, needs the same wait: await box.fontsReady().

Plain canvas

Wait for every box, then paint onto your own canvas at any scale:

await rt.ready();

const out = document.createElement("canvas");
out.width = 1275 * 3;
out.height = 1650 * 3;
const ctx = out.getContext("2d")!;
ctx.setTransform(3, 0, 0, 3, 0, 0);
drawYourScene(ctx);
rt.paint(ctx); // the text only: no cursor, selection or handles
const png = out.toDataURL("image/png");

Print

A print renderer that runs the library in a headless browser on a server produces exactly the editor's lines, because both draw from the font files. Set the same render mode there as in the editor, load the document's fonts with loadFonts, and paint. A design records the addresses of its custom fonts (fontMetadata), so the server needs no font lookup for them.

Note: A font that never arrives does not hold an export forever. The waits give up after a time limit, the text draws in a stand-in font, and onFontError tells you which face (see Fonts).

Note: On a domain your license does not cover, the license mark is drawn in exported images too.

In the API reference