Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ renderText(document, flavor, profile) -> string
renderEpub(document, profile) -> bytes
renderDocx(document, profile) -> bytes
renderPdf(document, profile) -> bytes
resolveExportProfile(profile, sourceWritingMode) -> resolved profile
listPageSizes() -> stable keys and physical dimensions
```

Bindings use idiomatic names and types for their host language, but each call
Expand Down Expand Up @@ -132,7 +134,7 @@ Deterministic transformations live in Rust:
| HTML | Rust HTML/CSS renderer |
| EPUB | Rust XHTML, metadata, CSS, and ZIP packager |
| DOCX | Rust OOXML and ZIP packager |
| PDF | Rust HTML/print-CSS renderer and Chromium controller |
| PDF | Rust profile/HTML/print-CSS preparation and Chromium controller |

Every renderer accepts the versioned Rust IR. A renderer never reparses source
text, reconstructs MDI boundaries, or delegates document semantics to a host
Expand Down Expand Up @@ -161,7 +163,8 @@ server or desktop host running the same Rust PDF API.

A binding may convert strings, bytes, errors, options, and object shapes. It
may not contain grammar tables, tokenizers, syntax fallbacks, or renderer
semantics.
semantics. Publication-profile defaults, validation, paper dimensions, and
renderer-facing layout decisions follow the same rule: they live in Rust.

The wire boundary carries explicit syntax and IR versions. Bindings reject an
unsupported IR version instead of guessing its meaning. Diagnostics retain
Expand All @@ -181,5 +184,6 @@ An implementation is part of MDI only if all of the following are true:
- all public parse results declare their syntax and IR versions;
- every binding passes the shared parse and diagnostic fixtures unchanged;
- every deterministic renderer consumes the Rust IR;
- configured EPUB/DOCX and PDF print profiles resolve through `mdi-core`;
- PDF uses HTML/CSS produced from the Rust IR and is orchestrated by Rust;
- no host-language package contains an alternative MDI tokenizer or parser.
19 changes: 18 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ cd nodejs
pnpm install --frozen-lockfile
pnpm typecheck
pnpm build
pnpm test
pnpm test:coverage
```

The PDF package tests use Chromium. Install it when working on that package:
Expand All @@ -40,6 +40,23 @@ The PDF package tests use Chromium. Install it when working on that package:
pnpm --filter @illusions-lab/mdi-to-pdf exec playwright install chromium
```

Publication changes also need the consumer-facing contract suite:

```sh
pnpm test:contracts
```

This suite validates DOCX with the .NET Open XML SDK and imports representative
documents with LibreOffice. It also checks PDF structure and page geometry,
validates EPUB with the official W3C EPUBCheck tool, and verifies HTML output.
Its local dependencies are .NET 8, LibreOffice Writer, Java 21, EPUBCheck 5.3,
and Chromium. Set `EPUBCHECK_JAR` to the downloaded EPUBCheck JAR before
running the command.

CI runs these publication contracts only after the Node.js, Rust, Swift,
Python, and Android unit/coverage jobs have all passed. A renderer change is
ready to merge only when both layers are green.

### Android

Android work requires JDK 17+, the Android SDK/NDK, Rust Android targets, and `cargo-ndk`. See [android/README.md](./android/README.md) for setup and test commands.
Expand Down
5 changes: 3 additions & 2 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@
| JavaScript / TypeScript | [`nodejs/`](./nodejs) | <a href="https://mdi.illusions.app/ja/bindings/javascript/"><img src="https://img.shields.io/badge/JavaScript%20%2F%20TypeScript%20docs-F7DF1E?style=flat-square&logo=javascript&logoColor=black" alt="JavaScript / TypeScript ドキュメント" /></a> |
| Python | [`python/`](./python) | <a href="https://mdi.illusions.app/ja/bindings/python/"><img src="https://img.shields.io/badge/Python%20docs-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python ドキュメント" /></a> |
| Swift | [`swift/`](./swift) | <a href="https://mdi.illusions.app/ja/bindings/swift/"><img src="https://img.shields.io/badge/Swift%20docs-F05138?style=flat-square&logo=swift&logoColor=white" alt="Swift ドキュメント" /></a> |
| Android / Kotlin | [`android/`](./android) | <a href="https://mdi.illusions.app/ja/bindings/android/"><img src="https://img.shields.io/badge/Android%20%2F%20Kotlin%20docs-3DDC84?style=flat-square&logo=android&logoColor=black" alt="Android / Kotlin ドキュメント" /></a> |
| Android / Kotlin *(開発中)* | [`android/`](./android) | <a href="https://mdi.illusions.app/ja/bindings/android/"><img src="https://img.shields.io/badge/Android%20%2F%20Kotlin%20docs-3DDC84?style=flat-square&logo=android&logoColor=black" alt="Android / Kotlin ドキュメント" /></a> |

> **Android / Kotlin の状態:** バインディングは現在開発中で、Maven Central では安定版の公開パッケージとしてまだ配布していません。

## リポジトリ構成

Expand Down Expand Up @@ -86,7 +87,7 @@ cargo build
cargo test
```

WASM ブリッジのビルドには、`wasm32-unknown-unknown` Rust ターゲットと `wasm-pack` も必要です。Node ワークスペースのビルドが自動で実行します。CI Rust core を Linux、macOS、Windowsx64 と ARM64 でテストし、Chromium PDF 出力を含む JavaScript 結合テストを Linux x64 で実行します
WASM ブリッジのビルドには、`wasm32-unknown-unknown` Rust ターゲットと `wasm-pack` も必要です。Node ワークスペースのビルドが自動で実行します。CI は最初に Node.js、Rust(Linux・macOS・Windows の x64 / ARM64)、Swift、Python、Androidunit test と coverage を実行します。すべて通過してから、.NET Open XML SDK、LibreOffice、Chromium、W3C EPUBCheck、HTML contract で publication output を検証します

## リリース

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ The language toolkits below call this same parser instead of reimplementing the
| JavaScript / TypeScript | [`nodejs/`](./nodejs) | <a href="https://mdi.illusions.app/bindings/javascript/"><img src="https://img.shields.io/badge/JavaScript%20%2F%20TypeScript%20docs-F7DF1E?style=flat-square&logo=javascript&logoColor=black" alt="JavaScript and TypeScript documentation" /></a> |
| Python | [`python/`](./python) | <a href="https://mdi.illusions.app/bindings/python/"><img src="https://img.shields.io/badge/Python%20docs-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python documentation" /></a> |
| Swift | [`swift/`](./swift) | <a href="https://mdi.illusions.app/bindings/swift/"><img src="https://img.shields.io/badge/Swift%20docs-F05138?style=flat-square&logo=swift&logoColor=white" alt="Swift documentation" /></a> |
| Android / Kotlin | [`android/`](./android) | <a href="https://mdi.illusions.app/bindings/android/"><img src="https://img.shields.io/badge/Android%20%2F%20Kotlin%20docs-3DDC84?style=flat-square&logo=android&logoColor=black" alt="Android and Kotlin documentation" /></a> |
| Android / Kotlin *(in development)* | [`android/`](./android) | <a href="https://mdi.illusions.app/bindings/android/"><img src="https://img.shields.io/badge/Android%20%2F%20Kotlin%20docs-3DDC84?style=flat-square&logo=android&logoColor=black" alt="Android and Kotlin documentation" /></a> |

> **Android / Kotlin status:** the binding is still in development and is not yet published as a stable public package on Maven Central.


## Repository layout
Expand Down Expand Up @@ -89,7 +90,7 @@ cargo build
cargo test
```

Building the WASM bridge also requires the `wasm32-unknown-unknown` Rust target and `wasm-pack`; the Node workspace build runs it automatically. CI tests the Rust core on Linux, macOS, and Windows for x64 and ARM64, and runs the JavaScript integration suite including Chromium PDF output on Linux x64.
Building the WASM bridge also requires the `wasm32-unknown-unknown` Rust target and `wasm-pack`; the Node workspace build runs it automatically. CI first runs the unit and coverage suites for Node.js, Rust (Linux, macOS, and Windows on x64 and ARM64), Swift, Python, and Android. Only after they pass does it validate publication output with the .NET Open XML SDK, LibreOffice, Chromium, W3C EPUBCheck, and HTML contracts.

## Releases

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/bindings/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Without `--config`, the CLI chooses its built-in layout from front matter: `writ

When supplied, `--config` must contain `layout.system`; a profile without it is rejected. `"japanese-publisher"` is the book system: horizontal text defaults to a mirrored, left-bound `Shirokuban`/10 pt Mincho 27×26 strict grid; vertical text defaults to the mirrored, right-bound A4-landscape novel-manuscript 40×30 strict grid. `"word"` is a separate flowing system: A4, 25.4 mm margins on all four sides, no mirroring, and `gridMode: "typographic"`; it rejects strict grids.

Semantic MDI parsing and source-span diagnostics remain Rust-owned. Profile values are publication policy: EPUB/DOCX adapters use them to package the parsed IR, while PDF geometry and Chromium layout are host concerns. This keeps application UI preferences and machine-specific browser behavior out of the parser.
Rust owns semantic parsing, source-span diagnostics, profile validation, the paper catalogue, and configured EPUB/DOCX generation. For PDF it also prepares the print HTML and geometry; the host supplies the machine-specific Chromium process. Application UI preferences stay outside both layers.

## PDF and DOCX limits

Expand Down
8 changes: 4 additions & 4 deletions docs/src/content/docs/bindings/javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ await writeFile("book.epub", renderEpub(source));
await writeFile("book.docx", renderDocx(source));
```

Use the two-argument overloads (or their explicit `WithProfile` names) for a publication export. They are asynchronous because `@illusions-lab/mdi` converts the already-parsed Rust IR to the publication adapters; it does **not** parse MDI source again in JavaScript.
Use the two-argument overloads (or their explicit `WithProfile` names) for a publication export. Their Promise-shaped API is kept for compatibility, while profile validation and EPUB/DOCX generation run in Rust. JavaScript neither reparses MDI nor keeps another document generator.

```ts
import { renderEpub, renderDocx } from "@illusions-lab/mdi";
Expand Down Expand Up @@ -87,9 +87,9 @@ Both configured calls also accept the full nested `ExportProfile` schema through

Every configured export must state `layout.system`. Choose `"japanese-publisher"` for a mirrored Japanese book: horizontal text defaults to 10 pt Mincho on `Shirokuban`, with a strict 27-character × 26-line left-bound grid; vertical text defaults to the A4-landscape novel manuscript, a strict 40-character × 30-line right-bound grid. Choose `"word"` for Word-style flowing pages: its default is A4 with 25.4 mm on every side, no mirror margins, and `gridMode: "typographic"`; `"word"` rejects `"strict"`.

## What remains the semantic owner
## Where each responsibility lives

Rust owns parsing, diagnostics, source spans, and the semantic MDI-to-HTML/baseline-export decisions. Publication settings belong to the adapter layer: EPUB/DOCX profile settings shape an archive, while paper geometry, Chromium behavior, and application UI preferences belong to the host. The configured DOCX exporter represents page breaks, vertical text, ordinary paragraph formatting, ruby/tate-chu-yoko/no-break/kern/blank constructs as far as OOXML permits, but it is not a byte-for-byte visual equivalent of browser HTML. Test the generated DOCX in the target Word-compatible reader when those Japanese composition details are critical.
Rust owns parsing, diagnostics, source spans, profile validation, the canonical paper catalogue, and configured EPUB/DOCX generation. For PDF, Rust prepares the styled HTML, page geometry, and page-number templates; the host only controls Chromium and application UI. The configured DOCX exporter represents page breaks, vertical text, ordinary paragraph formatting, ruby/tate-chu-yoko/no-break/kern/blank constructs as far as OOXML permits, but it is not a byte-for-byte visual equivalent of browser HTML. Test the generated DOCX in the Word-compatible reader your users rely on when those Japanese composition details are critical.

## HTML and PDF hosts

Expand All @@ -105,7 +105,7 @@ const request = preparePdfExport(source, profile);
const pdf = await renderPdfWithChromium(source, profile);
```

Install `@illusions-lab/mdi-to-pdf` alongside `@illusions-lab/mdi` for the default Node/Playwright adapter. An Electron host may instead pass `{ renderHtmlToPdf(html, profile, sourceWritingMode) }` to `renderPdfWithChromium`. PDF profiles cover paper, landscape, margins, writing direction, font, font size/line spacing, character/line grids, indentation, and page-number settings. Browser/WASM consumers can use the baseline renderer APIs from the main entry point, but configured publication adapters and PDF are Node/Electron host workflows; send `preparePdfExport()` to a capable host.
Install `@illusions-lab/mdi-to-pdf` alongside `@illusions-lab/mdi` for the default Node/Playwright host. An Electron host may instead pass `{ renderHtmlToPdf(html, profile, sourceWritingMode) }` to `renderPdfWithChromium`. Rust resolves PDF paper, landscape, margins, writing direction, font, font size/line spacing, character/line grids, indentation, and page-number settings. Browser/WASM consumers can create configured EPUB/DOCX locally; PDF alone must send `preparePdfExport()` to a Node, Electron, Tauri, or CLI host that can launch Chromium.

## Other exports and errors

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/bindings/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Everything listed above is real, published, and tested — the package's own tes

- **No PDF function yet.** Unlike the [JavaScript/WASM binding](/bindings/javascript/), Python *can* spawn a subprocess — there's no fundamental barrier like WASM's — but `mdi.render_pdf` simply isn't exposed in this package today. Use the [CLI](/bindings/cli/) for PDF output from a Python-adjacent workflow in the meantime.
- **No grammar of its own.** Every function calls straight into the same `mdi-core` crate every other binding uses; a discrepancy between this binding and the CLI or Rust directly would be a bug in the ~60-line wrapper, not an independent parser to fix.
- **No export-profile application.** `render_epub`/`render_docx` take only `source`, matching Rust's own current "baseline" renderers (see [Rust Core API status](/core/rust-api/#not-yet-implemented)) — profile-driven cover images, chapter splitting, and page geometry aren't wired through this binding either.
- **No export-profile arguments yet.** `render_epub`/`render_docx` currently take only `source`. Rust already provides the configured EPUB/DOCX implementation; the Python wrapper has not exposed those profile and cover parameters yet.

## Next steps

Expand Down
9 changes: 5 additions & 4 deletions docs/src/content/docs/core/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ This is stricter than "Rust is the reference implementation." A reference implem
▼ ▼ ▼
Rust renderers language bindings ecosystem adapters
HTML/TXT/EPUB/DOCX/MDI Node.js (real) mdast/remark (real)
Python / Swift / Kotlin (real)
Python / Swift (real)
Android / Kotlin (development)
└── HTML + print CSS ──▶ Chromium ──▶ PDF
```

Every arrow above is a real, checkable relationship today. Rust, Node.js, Swift, Kotlin, and Python all use the same Rust-authoritative grammar and document IR — see [Bindings](/bindings/javascript/) for each platform.
Every released binding above is a real, checkable relationship today. Rust, Node.js, Swift, and Python use the same Rust-authoritative grammar and document IR. Android / Kotlin is still in development and is not yet published as a stable public package on Maven Central — see [Bindings](/bindings/javascript/) for each platform.

## Why one pass, not one pass per layer

Expand Down Expand Up @@ -75,14 +76,14 @@ An implementation is part of MDI only if all of the following hold:
| Layer | Status |
| --- | --- |
| `mdi-core`: full CommonMark + GFM + front matter + MDI in one Rust parse | **Implemented.** See [Rust Core API status](/core/rust-api/) for the exact function list. |
| Rust-native `renderHtml`/`renderText`/`renderEpub`/`renderDocx` | **Implemented** (baseline). Cover media, detailed DOCX typography, and full export-profile/pagination parity are later extensions of these same APIs, not separate milestones. |
| Rust-native `renderHtml`/`renderText`/`renderEpub`/`renderDocx` | **Implemented.** Baseline and profile-configured EPUB/DOCX use the same Rust renderers; cover media, chapter splitting, typography, page geometry, and numbering are available today. |
| Rust-orchestrated PDF via Chromium | **Implemented.** See [Rendering model](/core/rendering/#the-chromiumpdf-boundary). |
| `@illusions-lab/mdi` (JavaScript/WASM) | **Implemented** — calls Rust directly for every operation; see [Bindings: JavaScript](/bindings/javascript/). |
| `@illusions-lab/mdi-remark` (mdast adapter) | **Implemented** as a one-way adapter (MDI → mdast); see [Ecosystem: Remark](/ecosystem/remark/) for exactly what "one-way" means today. |
| `@illusions-lab/mdi-cli` | **Implemented**, calling Rust directly for every format except the final Chromium print step; see [Bindings: CLI](/bindings/cli/). |
| Python binding (PyO3) | **Implemented.** Published on PyPI as [`illusion-markdown`](https://pypi.org/project/illusion-markdown/) (import name `mdi`); calls the same Rust core directly. See [Bindings: Python](/bindings/python/). |
| Swift binding | **Implemented.** See [Bindings: Swift](/bindings/swift/). |
| Android / Kotlin binding | **Implemented.** See [Bindings: Android / Kotlin](/bindings/android/). |
| Android / Kotlin binding | **In development.** Source and local verification are available, but it is not yet published as a stable public package on Maven Central. See [Bindings: Android / Kotlin](/bindings/android/). |

The repository's [`ARCHITECTURE.md`](https://github.com/illusions-lab/MDI/blob/main/ARCHITECTURE.md) is the normative version of this contract; this page explains and status-checks it.

Expand Down
Loading
Loading