Fonts
Four families ship with the package, so nothing needs registering:
| Family | Faces | Use it with |
|---|---|---|
| Inter | regular, italic, bold | the default (font-sans) |
| Inter Display | bold | font-display, for large headings |
| Roboto Mono | regular | font-mono |
| Noto Sans | regular | automatic, for characters Inter lacks (fallbackFonts) |
font-semibold and other weights without a face of their own use the nearest one (bold), as in
CSS.
No serif face ships with the package, so font-serif sets Noto Sans. For serif text, register
a serif family and use it with fontFamily or font-[…], or set --font-serif in the theme you
pass to initTailwind({ css }) from @inkoxide/tailwind.
Add your own fonts with Font.register:
import { Font } from '@inkoxide/render';
Font.register({ family: 'Lora', src: './fonts/Lora-Regular.ttf' });
Font.register({
family: 'Source Serif',
fonts: [
{ src: './fonts/SourceSerif-Regular.ttf' },
{ src: './fonts/SourceSerif-Bold.ttf', fontWeight: 'bold' },
{ src: 'https://example.com/fonts/SourceSerif-Italic.ttf', fontStyle: 'italic' },
],
});
Then use it with style={{ fontFamily: 'Lora' }}, or with className="font-[Lora]".
-
Sources.
srcis a file path, afile:,https:orhttp:URL, or the bytes (Uint8ArrayorArrayBuffer). Preferhttps:: a font fetched overhttp:can be changed on the way. Formats are TrueType (.ttf), OpenType (.otf), WOFF (.woff) and WOFF2 (.woff2). Variable fonts are not supported: register the static instances. Font collections (.ttc) are not supported either: register each font as its own file. -
Loading.
registeris synchronous. The file is read or fetched just before the next render, and a font that cannot be loaded fails that render withINVALID_FONT. The fonts registered with it still load, and later renders can use them. The face that failed is then dropped, and is not tried again: later renders succeed, and text in it falls back as for a face never registered (to the nearest face of its family, else toInter), with only a warning in thewarningsofgenerate. Register it again, from the same source or another, to try once more. To refuse to render without it, load your fonts at startup withawait init()and stop when it throws. A URL is fetched with a 30-second limit for the whole download and a 50 MiB size limit, and redirects are followed; renders wait for it, so for a font over that size or from a slow host, read the bytes yourself and register them. -
Weights. Weights are numbers (1–1000, as in CSS) or names (
'light','medium','bold'). Missing weights and styles fall back to the nearest one, as in CSS. -
Colour emoji. Register a colour emoji font, such as Noto Color Emoji, and list it in
fallbackFonts:Font.register({ family: 'Noto Color Emoji', src: './fonts/Noto-COLRv1.ttf' }); <Document fallbackFonts={['NotoSans-Regular', 'Noto-Color-Emoji-400']}>…</Document>An emoji (
😀,❤️, a flag, a keycap, a skin tone, a sequence such as👩💻) goes to the first colour font infallbackFontsthat has it, then to the run's font, then to the other fallbacks. Followed by U+FE0E (text presentation), or as a character that is text by default (❤,©), it is text like any other. Colour glyphs come from the font'sCOLRtable (versions 0 and 1) or its PNG bitmaps (CBDT, as in Noto Color Emoji's bitmap build, orsbix); a font with bitmaps and no outlines works too, but cannot be a form field's font. They are drawn as images that copy and search as their text.COLRlayers in the foreground colour take the run'scolor, as in browsers (in the text of an SVG image, black); the colour's alpha fades the whole glyph. Gradients that sweep around a point draw with their first colour, and blend modes draw as normal. A colour glyph that is malformed or takes too much work to draw (over 10 000 drawing steps) draws without colour, and so do colour glyphs past the first 4096 of a render (one that takes the run's colour counts once per colour) or its first 32 MB of colour glyph images. In a font with bitmaps and no outlines, which has nothing else to draw them with, such glyphs are not drawn, but they still copy and search as their text. Fonts with onlySVGglyphs are not drawn in colour. PDF/A-1b forbids transparency: there, a colour glyph that needs it (a bitmap with an alpha channel, as in every emoji bitmap font, or a layer with an opacity) fails the render withPDFA_CONFLICTat the path of its run (at thesrcof an SVG whose text has it), and inline images and emoji images that need it fail it at theirsrcoremojiImageskey. OpaqueCOLRglyphs draw in colour there. -
Emoji from images.
Font.registerEmojiSourcedraws emoji from an image set instead:{ url, format }adds each emoji's code points and the format to the URL (https://cdn.example.com/emoji/1f469-200d-1f4bb.png;withVariationSelectors: truekeepsfe0fin them), and{ builder }turns the code points into any imagesrc. The images load like<Image src>: a URL must be allowed byremoteImages(else the render fails, naming the emoji), a path needslocalImages, and images fetched from URLs are kept between renders (16 MB of them, each render still holding them to its ownremoteImages.maxBytes). An emoji image is 1em high with its aspect ratio, its bottom 0.1em below the baseline (raised or lowered with the text byverticalAlign: 'super'or'sub'), and copies as the emoji. An emoji whose image cannot be loaded, or is not an image the engine can draw (other bytes, an image over the size limits), is drawn by the fonts, with a warning; a ZWJ sequence (👩💻) without an image of its own uses the images of its parts.nullremoves the source.Font.registerEmojiSource({ url: 'https://cdn.example.com/emoji/72x72/', format: 'png' }); -
Faces. A face is a family, a weight and a style. Registering one again from the same source (the same path, URL or bytes) does nothing, and from another source throws. A face the bundled fonts have (
Inter400) cannot be replaced: register your build under another family name. You can add the faces they lack, such asInterbold italic. Two family names that differ only in spaces and hyphens (Brand Two,Brand-Two) give the same font id to faces of the same weight and style, so such a face cannot be registered under both:Brand Two400 andBrand-Two400 throw,Brand Two400 andBrand-Two700 do not.
Synced from packages/render/GUIDE.md in the inkoxide repository.