In the browser
The same templates render in a browser, with the engine as WebAssembly in a Web Worker, so the
page keeps responding while it renders. Install the module next to the package, and import
from @inkoxide/render/browser:
npm install @inkoxide/render @inkoxide/wasm
import { PdfDownloadLink, PdfViewer, usePdf } from '@inkoxide/render/browser';
function InvoicePage({ invoice }: { invoice: InvoiceData }) {
const doc = <Invoice invoice={invoice} />;
return (
<>
<PdfDownloadLink document={doc} fileName="invoice.pdf">
{({ loading }) => (loading ? 'Rendering…' : 'Download')}
</PdfDownloadLink>
<PdfViewer document={doc} style={{ width: '100%', height: 800 }} />
</>
);
}
usePdf(element, options?)renders when the component mounts and returns{ blob, url, loading, error, update };update(element?)renders again. A render that starts stops the one still running, and the object URL is revoked when a new one replaces it.<PdfDownloadLink>is a link that downloads the PDF, and<PdfViewer>an<iframe>with the browser's own viewer. Each renders when it mounts: give it anotherkeyto render again.renderToBufferandrenderToBlobrender outside React components, andgeneraterenders a template. Asignalin their options, ortimeoutMs(a time in ms), stops a render: its worker ends, and the next render starts a new one. A render that times out rejects with aDOMExceptionnamedTimeoutError.init(options?)starts the engine, and every render calls it; only the options of the first call count.wasmis where the module comes from (a URL, aResponse, its bytes or a compiledWebAssembly.Module);assetsis a folder to serve the bundled fonts and hyphenation patterns from:fonts/andhyphenation/from@inkoxide/render'sassets/, with the.binfiles of@inkoxide/wasm'shyphenation/inhyphenation/too;worker: falserenders on the calling thread.clearCaches()empties the engine's caches of decoded images, font subsets and SVG text. The memory stays with the engine, which reuses it for the next renders; the PDFs are the same.
Setting up. Bundlers copy the module, the worker and the bundled fonts, which the packages
refer to with new URL(…, import.meta.url). With Vite, set worker: { format: 'es' }, and
leave @inkoxide/render and @inkoxide/wasm out of optimizeDeps, so that those URLs stay
next to their files.
examples/browser is a page
set up that way.
Fonts and images. A bundled font is downloaded the first time a document uses it, as a
TrueType file, for the same PDF as on the server. Font.register takes a URL (relative to the
page) or the bytes; a file path (a file: URL, a drive letter or a backslash) fails the render
with INVALID_FONT. A WOFF2 font gives other PDF bytes than its TrueType file;
WOFF gives the same. Images come as bytes in images, or from the hosts allowed in
remoteImages: the requests carry no cookies, need CORS, and a redirect is refused.
localImages is not available.
Content Security Policy. The engine needs script-src 'wasm-unsafe-eval' and a worker of
the page's origin (worker-src 'self'). Nothing needs 'unsafe-eval', and the page needs no
cross-origin isolation (COOP and COEP).
What differs from Node.
- Renders run in one worker, one at a time, on one thread:
threadsmay only be1. renderToFile,renderToStreamand image files are not available.- The engine module is about 1.2 MB to download with brotli, once. A bundled font (65 to 245 KB with brotli) is downloaded when a document first draws with it: Noto Sans, the fallback, only for a character Inter lacks. A language's hyphenation patterns (1 to 350 KB) are downloaded when a document first hyphenates text in it.
- The PDF has the bytes Node renders, except where SVG markers are rotated: their numbers can differ in the last digits, since the browser's math library rounds differently.
- A bug in the engine fails the render with
INTERNAL; the next render runs in a new instance.
Synced from packages/render/GUIDE.md in the inkoxide repository.