Onykia Docs

Rendering

The engine separates what gets compiled (setTarget) from how you turn the result into pixels or files (render and export).

Targets

core.setTarget(target) keeps one artifact pipeline warm. It accepts 'pdf' | 'svg' | 'png' | 'html' | 'none' ('none' = diagnostics only, no artifact). It's independent of export() - preview as SVG, still export PDF.

The render loop

onPages fires after every compile that changes layout, with each page's size in points. Render from there:

let pages: PageInfo[] = [];
core.onPages(({ pages: next }) => {
  pages = next;
  if (pages.length > 0) void draw();
});

Canvas (raster)

renderToCanvas draws one <canvas> per page into the container, replacing its children. It oversamples by devicePixelRatio for hi-DPI sharpness (override with dpr).

import { renderToCanvas } from '@mudomi/onykia-engine';
 
await renderToCanvas(core, { container: previewEl, pages, fit: 'width' });

Sizing is either { fit: 'width' } (re-run on resize) or { zoom } (CSS pixels per Typst point) - there is no fit: 'height' or fit: 'page'. To paint a single page yourself:

import { paint } from '@mudomi/onykia-engine';
 
const result = await core.render(0, 2); // page 0 at zoom 2
paint(canvas, result);

SVG

Export each page and inline the markup:

const decoder = new TextDecoder();
const svgs = await Promise.all(
  pages.map(async (_, index) => decoder.decode((await core.export({ format: 'svg', index })).data)),
);
previewEl.innerHTML = svgs.join('\n');

HTML

HTML export returns a full document; render it in an iframe:

const { data } = await core.export({ format: 'html' });
const iframe = document.createElement('iframe');
iframe.srcdoc = new TextDecoder().decode(data);
previewEl.replaceChildren(iframe);

Outline

onOutline emits the document's heading outline after every compile - the same set #outline() renders. Use it for a navigation sidebar.

core.onOutline(({ entries }) => renderToc(entries));

Each entry is { level, title, position, children? }. position is { page, x, y } - page is 0-based (it indexes straight into the pages array), x/y are in points.

Export

core.export(args) returns { data: Uint8Array, mime: string }.

formatscopecall
pdfwhole documentcore.export({ format: 'pdf', standards? })
htmlwhole documentcore.export({ format: 'html' })
svgone page (zero-based)core.export({ format: 'svg', index })
pngone page (zero-based)core.export({ format: 'png', index, ppi? })
const { data, mime } = await core.export({ format: 'pdf' });
const url = URL.createObjectURL(new Blob([new Uint8Array(data)], { type: mime }));
// download via an <a download href={url}> element

png takes an optional ppi (default 144). pdf takes an optional standards array to produce archival/accessible output, e.g. ['a-2b'] for PDF/A-2b.

PDF/A has hard requirements - notably every font must be embeddable. When export can't meet a standard it rejects with the compiler's actual reason and hints; surface that message to the user.

On this page