Repository navigation
Getting Started
Install Postext, lay out a first page, then write the same document as a PDF, an EPUB or a 3D book. Every snippet here also runs from a CDN (https://esm.sh/postext) with nothing to install; the Cookbook and the live examples on postext.dev are built that way.
For the ideas behind the engine, read Introduction. For every setting, see Configuration. For the full programming guide, see Programmatic Usage.
- A modern browser (Chrome, Safari or Firefox). Layout measures text with canvas font metrics, so it runs in the browser or in a Web Worker. The PDF and EPUB writers also run in Node.
- Node.js 20 or later and any package manager, if you install from npm.
npm install postextThe other packages are added when you need them:
npm install postext-pdf # PDF
npm install postext-epub # EPUB 3
npm install postext-folio three # 3D book viewer
npm install postext-citeproc # citations in CSL stylesreact (18 or later) is a peer dependency of postext, used only by postext/react; the main entry never loads it.
Postext measures text with the fonts the browser has loaded, so load them before building. The defaults are EB Garamond for the body and Open Sans for headings.
import { buildDocument, renderPage } from 'postext';
await Promise.all([
document.fonts.load('16px "EB Garamond"'),
document.fonts.load('bold 16px "Open Sans"'),
]);
const doc = buildDocument(
{ markdown: '# Hello, Postext\n\nA paragraph of body text, broken with Knuth-Plass and hyphenated.' },
{
page: { sizePreset: '17x24' },
layout: { layoutType: 'double', gutterWidth: { value: 0.75, unit: 'cm' } },
bodyText: { fontFamily: 'EB Garamond', fontSize: { value: 10, unit: 'pt' } },
},
);
// One <canvas> per page.
for (const page of doc.pages) document.body.append(renderPage(page, doc));buildDocument returns a VDTDocument: pages, columns, blocks and measured lines with their coordinates. Every output reads it:
-
renderPage(page, doc)/renderPageToCanvas(page, doc, canvas)/renderToCanvas(doc)draw pages on canvas. -
renderToHtml(doc)returns absolutely positioned HTML;renderToHtmlIndexed(doc)adds a per-block index for patching a live view. See Integrating the HTML viewer. -
renderToPdf,renderToEpubandcreateFolioFromDocumentcome from their own packages (below).
If the markdown contains maths, start the math engine first with await initMathEngine().
The PDF embeds real font files, so renderToPdf asks a font provider for the bytes of each face it sets. This one fetches static WOFF2 files from Fontsource:
import { renderToPdf, decompressWoff2 } from 'postext-pdf';
const fontProvider = async (family: string, weight: number, style: string) => {
const id = family.toLowerCase().replace(/\s+/g, '-');
const url = `https://cdn.jsdelivr.net/npm/@fontsource/${id}@latest/files/${id}-latin-${weight}-${style}.woff2`;
const res = await fetch(url);
return decompressWoff2(new Uint8Array(await res.arrayBuffer()));
};
const bytes = await renderToPdf(doc, { fontProvider });
const url = URL.createObjectURL(new Blob([bytes], { type: 'application/pdf' }));Pictures need a resourceBytes provider too. Colour space, outlines and tagged (accessible) output follow the document's pdfGeneration settings. For long books, postext-pdf/worker writes the file on a worker. See Generating PDFs.
renderToEpub takes the book's chapters in order:
import { renderToEpub } from 'postext-epub';
const epub = await renderToEpub([doc], {
layout: 'reflowable', // or 'fixed': every printed page as it is
metadata: { title: 'Hello', language: 'en' },
});See EPUB books.
import { createFolioFromDocument } from 'postext-folio';
createFolioFromDocument(document.getElementById('book')!, doc);Drag a page by its edge, click it or use the arrow keys. See A 3D book.
A .postext bundle (what the Sandbox exports) holds a book's chapters, configuration, pictures and fonts:
import { openBundle, loadBundleFonts, registerBundleImages, buildBundle, renderPage } from 'postext';
const bundle = await openBundle(file); // a File, Blob or ArrayBuffer
await loadBundleFonts(bundle);
await registerBundleImages(bundle);
const chapters = buildBundle(bundle); // one VDTDocument per chapter, each continuing the lastcreateBundle writes one from code. See Bundles.
For live previews and editors, run layout off the main thread. A newer build cancels the one in flight:
import { createLayoutWorker } from 'postext/worker';
const layout = createLayoutWorker();
await layout.registerFonts(fontPayloads); // font files as ArrayBuffers, transferred
const controller = new AbortController();
const doc = await layout.build(content, config, { signal: controller.signal });
// controller.abort() rejects the pending build with an AbortError.
layout.dispose();See Running layout in a Web Worker, including how to load the worker from a CDN.
import { createLayout } from 'postext/react';
const Article = createLayout({ markdown: '# Hello' }, { page: { sizePreset: '17x24' } });
export const Page = () => <Article className="pages" />;See React.
- Write real content → Document Format
- Tune the layout → Configuration
- See complete publications and their code → Cookbook and Showcase
- Try it without code → Sandbox
- Learn the internals → Architecture
Postext is MIT-licensed. Maintained by Ignacio Ferro and community contributors. See the live documentation for the most up-to-date reference.
Overview
Writing
- Document Format
- Hyphenation and Justification
- Chinese Layout
- Arabic Layout
- Japanese Layout
- Comics
- Composed Pages
- Sandbox
- Agent Skill
- Command Line
- WebMCP
Configuration
- Overview and index
- Page and layout
- Headers and footers
- Body text
- Hyphenation and language
- Headings
- Widows, orphans and balancing
- Lists and maths
- Notes and references
- East Asian typography
- Resources
- Styles and parts
- Callout boxes
- Comics
- Composed pages
- Fonts, colours, viewers
Code
Internals
External