Export
Export is where "one layout for everything" pays off. The PDF writer doesn't re-flow anything: it draws the exact pages the editor shows, fragment by fragment.
PDF in progress
@nextgensoftwares/folio-export-pdf writes a PDF from a DocumentLayout:
import { exportPdf } from '@nextgensoftwares/folio-export-pdf';
const bytes = await exportPdf(editor.layout, {
fonts: async (font) => fontBytesFor(font.family, font.weight, font.style), // bytes of one face
shaper: fonts, // the FontEngine layout used (same face selection)
images: async (src) => ({ data: await fetchBytes(src), type: 'png' }),
title: 'Physics I',
onProgress: (done, total) => setProgress(done / total),
});- Every fragment is drawn at its laid-out position with HarfBuzz-shaped glyphs (so Arabic shaping and ligatures match the screen) in embedded, subset fonts with a ToUnicode map, so text stays selectable and searchable.
- Pages are streamed: each page's lazy fragments are read, drawn, compressed and dropped before the next one, and the writer yields to the event loop every 25 pages (
yieldEvery) so a browser UI stays responsive. Long exports can be cancelled (signal). - Links become PDF link annotations (http, https, mailto, tel and ftp only).
- Math is drawn from a host typesetter (
math), images fromimages(default:data:URLs), SVG through a host rasterizer (rasterizeSvg).customfragments are drawn bypainterskeyed by fragment type. - In the browser,
pdfExportPlugin({ faces })adds an Export ▸ PDF toolbar entry that exports the current layout and downloads it.
See Exporters for the plugin API and @nextgensoftwares/folio-export-pdf for every option.
DOCX in progress
@nextgensoftwares/folio-export-docx (DOCX export from Folio documents) is being built. DOCX is an export of the model, not of the pages: Word re-flows the document itself, so the mapping targets Word's structure (styles, paragraph properties such as keepNext / keepLines / pageBreakBefore, numbering, tables, headers and footers).
@nextgensoftwares/folio-docx is the Word importer (see Import). The older @nextgensoftwares/folio-pdf package is a stub whose functions throw "not implemented".
Floating media
Media placement (wrap, float, alignment, distT/B/L/R, offsetX/Y, anchor) is read with the same placementOf the layout uses, so both exports agree with the editor:
- PDF draws the laid-out pages, so floats come for free: text lines are already narrowed beside square floats, and each page lists
behindobjects first andfrontobjects last, so drawing in order stacks them correctly. - DOCX writes Word anchors (
wp:anchor): square →wrapSquarewith text on the free side (wrapText="right"for a left float), top-and-bottom →wrapTopAndBottom, behind / in front →wrapNonewithbehindDoc1 / 0. Square and top-and-bottom are aligned left/center/right in the column; behind/front useposOffset(aligned x +offsetX; y =offsetYfrom the paragraph, or from the page margin whenanchor: 'margin'). Distances map todistT/B/L/Rin EMU. Folio's floated object is its own block, so the anchor sits in an empty 1pt paragraph (keepNext) right before the text it wraps: Word wraps the following paragraphs the same way and keeps the object with them across page breaks. Inline media stayswp:inline; floats nested in lists, quotes or table cells stay inline, as in layout. Video/audio/file cards export through their print fallback and are not anchored yet.
Plugins and export
Plugins contribute export mappings through FolioPlugin.exporters, keyed by exporter name and then node or fragment type. They are opaque to the core; each exporter defines what it expects. For example @nextgensoftwares/folio-plugin-media provides print fallbacks for video, audio and file cards (a poster or title, a detail line and the link), because a static page can't play a video.
Server-side export
Layout is DOM-free, so the whole pipeline (fonts, layout, PDF) runs in Node. Load the same font files with FontEngine, lay out with the same theme, and pass the layout to exportPdf. Only math and SVG need host typesetting / rasterizing, which you can provide with any Node library.