A design often gives text a fixed space: a headline band 128 pixels high, a name line on a badge. When the words change,
copyfitDocument makes the type smaller, only as far as it must, so every word fits the box's width and the height you
give. Font sizes, outline widths, letter spacing and the shadow scale together, so the text keeps its look.
Fit new words into a box
Build the new document at the text's full design size, then hand it to the box with the height it must fit:
// The new words at the headline's own size, 40px
const next = createDocumentFromText(words, 40, "Playfair Display");
// Made smaller only as far as it must, to fit 128 pixels high
const scale = headline.copyfitDocument(next, { containerHeight: 128 });
// scale: the factor applied to the sizes - 0.75 means the 40px words are now 30px
copyfitDocument is a method of every text box: the Konva RichText and the plain-canvas CanvasTextBox. It
replaces the box's document with the fitted one, keeps the box's width, and is one undo step. It returns the scale it
applied to the document's sizes.
Copyfit only shrinks. To let shorter words grow back, start from the full-size document each time, as above. The scale
it finds is within 1% of the largest one that fits (the tolerance option).
Note: Without
containerHeight, only the width is checked: copyfit makes sure no word is wider than the box. A box's own height always grows to fit its text, so it cannot be the limit.
Options
| Option | Default | Does |
|---|---|---|
containerHeight |
none | The height the text must fit in, in pixels |
containerWidth |
the box's width | The width to fit to. The box is set to this width. |
minScale |
0.1 |
The smallest scale allowed, as a multiple of the document's sizes (0.5 is half size). The text never goes below it, even if it then overflows. |
maxIterations |
20 |
The most trial layouts the search makes |
tolerance |
0.01 |
The search stops once the scale is known to within this fraction |
To keep text legible, express a minimum size as a scale:
headline.copyfitDocument(next, { containerHeight: 128, minScale: 12 / 40 }); // never below 12px
Copyfit measures with the fonts already loaded. If the new document uses a family that is not on the page yet, load it first so the fit is measured in the real font:
await loadFonts(next);
headline.copyfitDocument(next, { containerHeight: 128 });
Fit when a box loads
The copyfit setting shrinks a box's text once, when its fonts have loaded:
// Konva
new RichText({ x: 28, y: 32, width: 352, document: doc, copyfit: { enabled: true, containerHeight: 128, minFontSize: 12 } });
// Plain canvas
rt.addBox({ x: 28, y: 32, width: 352, document: doc, copyfit: { enabled: true, containerHeight: 128, minFontSize: 12 } });
| Field | Does |
|---|---|
enabled |
Turns the fit on |
containerHeight |
The height to fit in. On Konva, when it is left out, the box's height setting is used. A canvas box has no height setting, so set it there. |
minFontSize |
The smallest size to shrink to. If the text cannot fit at that size, it shrinks further, because fitting comes first. |
maxFontSize |
Lets the text grow, up to this size for its largest letters, to fill the height. Without it, text only shrinks. |
newText |
New words for a design made with other words: the box lays out its original text, takes the height that text needs as the space to fill, and fits newText into it. Each line becomes a paragraph in the look of the paragraph it replaces; extra lines take the first paragraph's look. |
The fitted sizes are written into the document, and the copyfit setting is then removed from the box, so a saved
design does not fit again each time it opens. A box being edited is not fitted.
Scale a document yourself
scaleDocumentTypography returns a copy of a document with its type scaled: font sizes, letter spacing, border widths, outline widths, the base size and the shadow. Line height, paragraph spacing and list indents are multiples of the font size, so they stay as they are.
import { scaleDocumentTypography } from "@mycreativeshop/rich-text/konva";
const half = scaleDocumentTypography(doc, 0.5);
It is exported from the Konva entry point, @mycreativeshop/rich-text/konva.
Check whether text fits
layoutAt(document, width) lays a document out the way a box would - its fonts, its fallback size - and returns the
result without changing the box:
const trial = headline.layoutAt(next, headline.width());
if (trial.height > 128) showWarning("The headline is too long for its space");
The result is a TextLayout plus box, the size the box would give itself:
| Field | Is |
|---|---|
lines |
Each line's text, paragraph, width, baseline and why it ended ("wrap", "paragraph", "softbreak", "end") |
graphemes |
Every letter's position and size |
contentWidth |
The widest line |
height |
The height the text needs |
longestWordWidth |
The widest single word |
box |
The { width, height } the box would take |
Two more box methods help with fitting rules: getMinimumFontSize() returns the smallest size the text is drawn at, and
getLongestWordWidth() the narrowest the box can be without a word overflowing.
Without a box
layoutText lays out a document at a width with no box and no canvas on screen: its lines, every letter's position, and the height it needs. Load the document's fonts first, or it measures in stand-in fonts:
import { layoutText, loadFonts } from "@mycreativeshop/rich-text"; // on Konva: "@mycreativeshop/rich-text/konva"
await loadFonts(doc);
const { lines, height } = layoutText(doc, { width: 352 });
Limit:
layoutTextneeds a browser. To run it on a server, run it in a headless browser.