Skip to content

Commit 915bf6c

Browse files
committed
docs: update landing page and README for v0.8.0 — Typst emitter
1 parent d9ac5e3 commit 915bf6c

2 files changed

Lines changed: 245 additions & 33 deletions

File tree

README.md

Lines changed: 161 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# odf-kit
22

3-
Create and fill OpenDocument Format files (.odt) in TypeScript/JavaScript. Build documents from scratch with a clean API, or fill existing templates with data. Works in Node.js and browsers. No LibreOffice dependency — just spec-compliant ODF files.
3+
Create, fill, read, and convert OpenDocument Format files (.odt) in TypeScript/JavaScript. Build documents from scratch with a clean API, fill existing templates with data, read .odt files into a structured model, or export to Typst for PDF generation. Works in Node.js and browsers. No LibreOffice dependency — just spec-compliant ODF files.
44

5-
**Two ways to generate documents:**
5+
**Four ways to work with .odt files:**
66

77
```typescript
88
// 1. Build from scratch
@@ -40,6 +40,44 @@ const result = fillTemplate(template, {
4040
writeFileSync("invoice.odt", result);
4141
```
4242

43+
```typescript
44+
// 3. Read and convert to HTML
45+
import { readOdt, odtToHtml } from "odf-kit/reader";
46+
import { readFileSync } from "node:fs";
47+
48+
const bytes = new Uint8Array(readFileSync("document.odt"));
49+
const html = odtToHtml(bytes, { fragment: true });
50+
51+
// Or parse to a structured document model
52+
const doc = readOdt(bytes);
53+
console.log(doc.metadata.title);
54+
for (const node of doc.body) {
55+
if (node.kind === "heading") console.log(`h${node.level}:`, node.spans[0].text);
56+
}
57+
```
58+
59+
```typescript
60+
// 4. Export to Typst for PDF generation
61+
import { odtToTypst, modelToTypst } from "odf-kit/typst";
62+
import { readFileSync, writeFileSync } from "node:fs";
63+
import { execSync } from "node:child_process";
64+
65+
const bytes = new Uint8Array(readFileSync("document.odt"));
66+
67+
// Convert to Typst markup
68+
const typ = odtToTypst(bytes);
69+
writeFileSync("document.typ", typ);
70+
71+
// Compile to PDF with the Typst CLI (installed separately)
72+
execSync("typst compile document.typ document.pdf");
73+
74+
// Or parse once and emit to multiple formats
75+
import { readOdt } from "odf-kit/reader";
76+
const model = readOdt(bytes);
77+
const html = model.toHtml({ fragment: true });
78+
const typst = modelToTypst(model);
79+
```
80+
4381
Generated `.odt` files open in LibreOffice, Apache OpenOffice, OnlyOffice, Collabora, Google Docs, Microsoft Office, and any ODF-compliant application.
4482

4583
## Why odf-kit?
@@ -54,7 +92,7 @@ odf-kit fills that gap with a single runtime dependency, full TypeScript types,
5492
npm install odf-kit
5593
```
5694

57-
Works in Node.js 18+ and modern browsers. ESM only.
95+
Works in Node.js 22+ and modern browsers. ESM only.
5896

5997
**Browser** — use any bundler (Vite, webpack, esbuild, Rollup):
6098

@@ -130,6 +168,8 @@ URL.revokeObjectURL(url);
130168
- **Links** — external hyperlinks, internal bookmark links, formatted link text
131169
- **Bookmarks** — named anchor points for internal navigation
132170
- **Tab stops** — left, center, right alignment with configurable positions
171+
- **Read & convert** — parse .odt files into a structured document model; convert to HTML with `odtToHtml()`
172+
- **Export to Typst** — convert any .odt file to Typst markup with `odtToTypst()`; compile to PDF with the Typst CLI
133173

134174
## Template Engine
135175

@@ -234,6 +274,78 @@ LibreOffice often fragments user-typed text like `{name}` across multiple XML el
234274

235275
Template syntax follows [Mustache](https://mustache.github.io/) conventions, proven in document templating by [docxtemplater](https://docxtemplater.com/). odf-kit's engine is a clean-room implementation purpose-built for ODF.
236276

277+
## Reading and Converting .odt Files
278+
279+
Import from `odf-kit/reader` to parse existing .odt files:
280+
281+
```typescript
282+
import { readOdt, odtToHtml } from "odf-kit/reader";
283+
import { readFileSync } from "node:fs";
284+
285+
const bytes = new Uint8Array(readFileSync("document.odt"));
286+
287+
// One-call HTML conversion
288+
const html = odtToHtml(bytes, { fragment: true });
289+
290+
// Or access the full document model
291+
const doc = readOdt(bytes);
292+
console.log(doc.metadata.title);
293+
console.log(doc.pageLayout?.orientation);
294+
295+
for (const node of doc.body) {
296+
if (node.kind === "heading") console.log(`h${node.level}:`, node.spans[0].text);
297+
if (node.kind === "paragraph") console.log(node.spans.map(s => s.text).join(""));
298+
if (node.kind === "table") console.log(`table: ${node.rows.length} rows`);
299+
}
300+
```
301+
302+
The document model covers headings, paragraphs, tables, lists, images, footnotes, bookmarks, text fields, named sections, tracked changes, page layout, and headers/footers.
303+
304+
## Exporting to Typst for PDF Generation
305+
306+
Import from `odf-kit/typst` to convert .odt files to [Typst](https://typst.app/) markup, which can then be compiled to PDF:
307+
308+
```typescript
309+
import { odtToTypst, modelToTypst } from "odf-kit/typst";
310+
import { readOdt } from "odf-kit/reader";
311+
import { readFileSync, writeFileSync } from "node:fs";
312+
import { execSync } from "node:child_process";
313+
314+
const bytes = new Uint8Array(readFileSync("document.odt"));
315+
316+
// Convenience wrapper — parse and emit in one call
317+
const typ = odtToTypst(bytes);
318+
writeFileSync("document.typ", typ);
319+
execSync("typst compile document.typ document.pdf");
320+
```
321+
322+
Use `modelToTypst()` when you already have a parsed model — parse once, emit to multiple formats without re-reading the file:
323+
324+
```typescript
325+
const model = readOdt(bytes);
326+
const html = model.toHtml({ fragment: true }); // HTML
327+
const typst = modelToTypst(model); // Typst markup
328+
```
329+
330+
Both functions are zero-dependency pure functions — no filesystem access, no child process, no Typst installation required at import time. `odf-kit/typst` works in Node.js, browsers, Deno, and any other JavaScript environment.
331+
332+
**Installing the Typst CLI** (only needed to compile to PDF):
333+
334+
```bash
335+
# macOS
336+
brew install typst
337+
338+
# Windows
339+
winget install --id Typst.Typst
340+
341+
# Linux / via npm
342+
npm install -g typst
343+
```
344+
345+
**Typst coverage:** headings, paragraphs (with text-align), bold, italic, underline, strikethrough, superscript, subscript, hyperlinks, footnotes, bookmarks as labels, text fields (page number, page count), unordered and ordered lists with nesting, tables with column widths, named sections, tracked changes (final/original/changes modes), page geometry via `#set page()`, and SpanStyle character properties (color, size, font family, highlight).
346+
347+
Images are emitted as comment placeholders — Typst does not support inline base64 data without filesystem access. Extract `ImageNode.data` from the model and write image files alongside the `.typ` output, then substitute the placeholders with `#image("filename")` calls.
348+
237349
## Quick Start — Programmatic Creation
238350

239351
### Simple document
@@ -468,6 +580,40 @@ function fillTemplate(templateBytes: Uint8Array, data: TemplateData): Uint8Array
468580
| `{object.property}` | Dot notation for nested objects |
469581
| `{#tag}...{/tag}` | Loop (array) or conditional (truthy/falsy) |
470582

583+
### odf-kit/reader
584+
585+
```typescript
586+
import { readOdt, odtToHtml } from "odf-kit/reader";
587+
588+
// Parse .odt bytes to a structured document model
589+
readOdt(bytes: Uint8Array, options?: ReadOdtOptions): OdtDocumentModel
590+
591+
// Convert .odt bytes directly to HTML
592+
odtToHtml(bytes: Uint8Array, options?: HtmlOptions, readOptions?: ReadOdtOptions): string
593+
```
594+
595+
`OdtDocumentModel` provides `body`, `metadata`, `pageLayout`, `header`, `footer`, and a `toHtml()` method. `BodyNode` is a discriminated union: `"paragraph"`, `"heading"`, `"list"`, `"table"`, `"section"`, `"tracked-change"`.
596+
597+
### odf-kit/typst
598+
599+
```typescript
600+
import { modelToTypst, odtToTypst } from "odf-kit/typst";
601+
602+
// Primary function — pure model walker, no re-parsing
603+
modelToTypst(model: OdtDocumentModel, options?: TypstEmitOptions): string
604+
605+
// Convenience wrapper — readOdt() + modelToTypst() in one call
606+
odtToTypst(bytes: Uint8Array, options?: TypstEmitOptions & ReadOdtOptions): string
607+
```
608+
609+
`TypstEmitOptions`:
610+
611+
| Option | Values | Default | Description |
612+
|--------|--------|---------|-------------|
613+
| `trackedChanges` | `"final"` \| `"original"` \| `"changes"` | `"final"` | How tracked changes are emitted |
614+
615+
Both functions are zero-dependency pure functions. No filesystem access, no child process, no Typst CLI required at import time.
616+
471617
### TextFormatting
472618

473619
Accepted by `addText()`, `addLink()`, and `addCell()`:
@@ -546,14 +692,24 @@ Extends `TextFormatting` with:
546692

547693
odf-kit works anywhere that supports ES2022 and ESM:
548694

549-
- **Node.js** 18 and later
695+
- **Node.js** 22 and later
550696
- **Browsers**Chrome, Firefox, Safari, Edge (all modern versions)
551697
- **Deno**, **Bun**, **Cloudflare Workers**
552698

553699
The library uses only standard JavaScript APIs (`TextEncoder`, `Uint8Array`) plus [fflate](https://github.com/101arrowz/fflate) for ZIP packaging. The TypeScript build enforces this — Node-specific APIs cannot exist in the library source code, guaranteeing cross-platform compatibility.
554700

555701
## Status
556702

703+
**v0.8.0**Typst emitter: `odf-kit/typst` sub-export with `modelToTypst()` and `odtToTypst()`. Zero new dependencies. 650 tests passing.
704+
705+
**v0.7.0**Tier 3 reader: ParagraphStyle, PageLayout, headers/footers, SectionNode, TrackedChangeNode (final/original/changes modes), image float, table column widths, graphicProps registry. 579 tests.
706+
707+
**v0.6.0**Tier 2 reader: SpanStyle (colors, fonts, sizes), ImageNode, NoteNode, BookmarkNode, FieldNode, cell/row styles, registry. 483 tests.
708+
709+
**v0.5.2**Tier 1 reader review: 7 bugs fixed, 11 new tests. ODF validator in CI.
710+
711+
**v0.5.0**Real-world document testing, generation repair plan (16 gaps fixed), template engine review (7 bugs fixed). 410 tests.
712+
557713
**v0.3.0**Template engine with loops, conditionals, dot notation, and automatic placeholder healing. 222 tests passing.
558714

559715
**v0.2.0**Migrated to fflate (zero transitive dependencies).
@@ -564,7 +720,7 @@ ODS (spreadsheets), ODP (presentations), and ODG (drawings) are planned for futu
564720

565721
## Specification Compliance
566722

567-
odf-kit targets ODF 1.2 (ISO/IEC 26300). Generated files include proper ZIP packaging (mimetype stored uncompressed as the first entry), manifest, metadata, and all required namespace declarations.
723+
odf-kit targets ODF 1.2 (ISO/IEC 26300). Generated files include proper ZIP packaging (mimetype stored uncompressed as the first entry), manifest, metadata, and all required namespace declarations. Every generated file is validated against the OASIS ODF validator on every CI push.
568724

569725
## Contributing
570726

0 commit comments

Comments
 (0)