From f30829bc7c9c4b43bbbd9743a56a3e0ba82c8539 Mon Sep 17 00:00:00 2001
From: Iktahana <171251543+Iktahana@users.noreply.github.com>
Date: Thu, 23 Jul 2026 17:19:13 +0900
Subject: [PATCH] docs: explain Rust-owned publication exports
---
ARCHITECTURE.md | 8 +++--
CONTRIBUTING.md | 19 ++++++++++-
README.ja.md | 5 +--
README.md | 5 +--
docs/src/content/docs/bindings/cli.md | 2 +-
docs/src/content/docs/bindings/javascript.md | 8 ++---
docs/src/content/docs/bindings/python.md | 2 +-
docs/src/content/docs/core/architecture.md | 9 ++---
docs/src/content/docs/core/rendering.md | 24 +++++++++-----
docs/src/content/docs/core/rust-api.md | 15 +++++++--
.../content/docs/ecosystem/export-profiles.md | 9 +++--
docs/src/content/docs/ecosystem/outputs.md | 13 +++++---
docs/src/content/docs/index.mdx | 4 +--
docs/src/content/docs/ja/bindings/cli.md | 2 +-
.../content/docs/ja/bindings/javascript.md | 8 ++---
docs/src/content/docs/ja/bindings/python.md | 2 +-
docs/src/content/docs/ja/core/architecture.md | 9 ++---
docs/src/content/docs/ja/core/rendering.md | 24 +++++++++-----
docs/src/content/docs/ja/core/rust-api.md | 18 ++++++++--
.../docs/ja/ecosystem/export-profiles.md | 9 +++--
docs/src/content/docs/ja/ecosystem/outputs.md | 13 +++++---
docs/src/content/docs/ja/index.mdx | 4 +--
docs/src/content/docs/zh-tw/bindings/cli.md | 2 +-
.../content/docs/zh-tw/bindings/javascript.md | 8 ++---
.../src/content/docs/zh-tw/bindings/python.md | 2 +-
.../content/docs/zh-tw/core/architecture.md | 9 ++---
docs/src/content/docs/zh-tw/core/rendering.md | 23 ++++++++-----
docs/src/content/docs/zh-tw/core/rust-api.md | 17 ++++++++--
.../docs/zh-tw/ecosystem/export-profiles.md | 9 +++--
.../content/docs/zh-tw/ecosystem/outputs.md | 12 ++++---
docs/src/content/docs/zh-tw/index.mdx | 2 +-
mdi-core/README.md | 7 ++++
nodejs/packages/cli/README.md | 17 +++++-----
nodejs/packages/export-profile/README.md | 15 +++++----
nodejs/packages/mdast-util-mdi/src/source.ts | 2 +-
nodejs/packages/mdi/README.md | 33 +++++++++++--------
nodejs/packages/mdi/src/index.test.ts | 2 +-
nodejs/packages/mdi/src/index.ts | 29 +++++++---------
nodejs/packages/to-docx/README.md | 12 ++++---
nodejs/packages/to-epub/README.md | 11 ++++---
nodejs/packages/to-pdf/README.md | 9 ++---
41 files changed, 276 insertions(+), 157 deletions(-)
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 297448f..3af69c4 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -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
@@ -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
@@ -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
@@ -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.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 54550dc..1ed8936 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -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:
@@ -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.
diff --git a/README.ja.md b/README.ja.md
index 77dda45..6cbc7bf 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -38,8 +38,9 @@
| JavaScript / TypeScript | [`nodejs/`](./nodejs) |
|
| Python | [`python/`](./python) |
|
| Swift | [`swift/`](./swift) |
|
-| Android / Kotlin | [`android/`](./android) |
|
+| Android / Kotlin *(開発中)* | [`android/`](./android) |
|
+> **Android / Kotlin の状態:** バインディングは現在開発中で、Maven Central では安定版の公開パッケージとしてまだ配布していません。
## リポジトリ構成
@@ -86,7 +87,7 @@ cargo build
cargo test
```
-WASM ブリッジのビルドには、`wasm32-unknown-unknown` Rust ターゲットと `wasm-pack` も必要です。Node ワークスペースのビルドが自動で実行します。CI は Rust core を Linux、macOS、Windows の x64 と 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、Android の unit test と coverage を実行します。すべて通過してから、.NET Open XML SDK、LibreOffice、Chromium、W3C EPUBCheck、HTML contract で publication output を検証します。
## リリース
diff --git a/README.md b/README.md
index 8f42aee..29cceda 100644
--- a/README.md
+++ b/README.md
@@ -39,8 +39,9 @@ The language toolkits below call this same parser instead of reimplementing the
| JavaScript / TypeScript | [`nodejs/`](./nodejs) |
|
| Python | [`python/`](./python) |
|
| Swift | [`swift/`](./swift) |
|
-| Android / Kotlin | [`android/`](./android) |
|
+| Android / Kotlin *(in development)* | [`android/`](./android) |
|
+> **Android / Kotlin status:** the binding is still in development and is not yet published as a stable public package on Maven Central.
## Repository layout
@@ -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
diff --git a/docs/src/content/docs/bindings/cli.md b/docs/src/content/docs/bindings/cli.md
index 35fdb97..ab141ba 100644
--- a/docs/src/content/docs/bindings/cli.md
+++ b/docs/src/content/docs/bindings/cli.md
@@ -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
diff --git a/docs/src/content/docs/bindings/javascript.md b/docs/src/content/docs/bindings/javascript.md
index f879ae9..1a5cb7e 100644
--- a/docs/src/content/docs/bindings/javascript.md
+++ b/docs/src/content/docs/bindings/javascript.md
@@ -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";
@@ -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
@@ -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
diff --git a/docs/src/content/docs/bindings/python.md b/docs/src/content/docs/bindings/python.md
index 9b45d58..73d1819 100644
--- a/docs/src/content/docs/bindings/python.md
+++ b/docs/src/content/docs/bindings/python.md
@@ -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
diff --git a/docs/src/content/docs/core/architecture.md b/docs/src/content/docs/core/architecture.md
index 67eb0f2..93aa1cf 100644
--- a/docs/src/content/docs/core/architecture.md
+++ b/docs/src/content/docs/core/architecture.md
@@ -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
@@ -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.
diff --git a/docs/src/content/docs/core/rendering.md b/docs/src/content/docs/core/rendering.md
index 46330a5..978f2a6 100644
--- a/docs/src/content/docs/core/rendering.md
+++ b/docs/src/content/docs/core/rendering.md
@@ -1,15 +1,15 @@
---
title: Rendering model and the Chromium/PDF boundary
-description: How Rust-owned MDI semantics flow to baseline exports, configured publication adapters, and a host PDF engine.
+description: How Rust turns one MDI document into baseline and configured exports, and where a PDF host takes over.
---
**Prerequisites:** [Document IR](/core/document-ir/) and [Export profiles](/ecosystem/export-profiles/).
-## One semantic source, two layers of settings
+## One semantic source, one publication contract
-Rust owns the `.mdi` parse, diagnostic codes and UTF-8 spans, and semantic HTML/baseline EPUB/DOCX/TXT output. It is the authority for ruby, tate-chu-yoko, no-break, kerning, blank paragraphs, page breaks, and all other MDI meaning.
+Rust owns the `.mdi` parse, diagnostic codes and UTF-8 spans, semantic output, and publication-profile resolution. Ruby, tate-chu-yoko, no-break, kerning, blank paragraphs, page breaks, paper dimensions, and EPUB/DOCX layout are therefore decided in one place.
-Publication settings are deliberately separate. EPUB/DOCX adapters apply metadata, chapters, typography, page setup, and numbering to that already-parsed IR. A host applies browser/PDF settings such as Chromium location and application UI preferences. This avoids putting platform-specific print policy into the parser.
+The profile remains separate from document meaning: changing a font or page size never changes how MDI is parsed. Rust validates that profile and applies metadata, chapters, typography, page setup, and numbering. A PDF host is responsible only for locating or launching Chromium and returning its bytes; application UI preferences remain with the application.
Configured publication exports require one explicit `layout.system`. `japanese-publisher` is the strict Japanese book contract: horizontal pages use `Shirokuban`, 10 pt Mincho, mirrored spreads, left binding, and 27×26; vertical pages use the A4-landscape novel manuscript, mirrored spreads, right binding, and 40×30. `word` is intentionally incompatible: A4, 25.4 mm on all four sides, no mirroring, and flowing `typographic` layout; it rejects `strict`.
@@ -19,7 +19,7 @@ Configured publication exports require one explicit `layout.system`. `japanese-p
| TXT | `renderTextFormat(source, format, indentPrefix)` | caller supplies text indentation. |
| EPUB | `renderEpub(source)` | `await renderEpub(source, { profile, cover })` or aliases such as `title`, `chapterSplitLevel`, and `coverImage`. |
| DOCX | `renderDocx(source)` | `await renderDocx(source, profile)` with metadata, page, type, and page-number values. |
-| PDF | — | `@illusions-lab/mdi/node` hands Rust HTML and an `ExportProfile` to a Chromium-capable host. |
+| PDF | — | `@illusions-lab/mdi/node` hands Rust-prepared HTML, page geometry, and page-number templates to a Chromium-capable host. |
## Diagnostics, headings, and semantic HTML
@@ -29,7 +29,7 @@ HTML is semantic and stable: `renderHtml` returns a complete page including MDI
## EPUB and DOCX
-The single-argument calls are synchronous Rust baseline exports: useful when front matter is enough and no publication profile is required. The configured calls are asynchronous because the package structurally converts the Rust IR to the EPUB/DOCX publication adapters — MDI source is not reparsed in JavaScript.
+The single-argument calls are synchronous Rust baseline exports: useful when front matter is enough and no publication profile is required. The configured JavaScript calls retain their asynchronous shape for compatibility, but profile validation and EPUB/DOCX archive generation now run in Rust. JavaScript does not keep a parallel page-size table or document generator.
Configured EPUB accepts title/author/publisher/language/date/identifier, vertical writing, font family, text indent, `h1`/`h2`/`h3`/`none` chapter splitting, and a JPEG/PNG cover. Configured DOCX accepts metadata; page size, orientation and four margins; writing direction, font family, font size, line spacing and indent; and page-number visibility, position and `simple`/`dash`/`fraction` format.
@@ -37,9 +37,17 @@ DOCX maps page breaks, writing direction, ordinary paragraphs, and available inl
## The Chromium/PDF boundary
-`preparePdfExport(source, profile)` in `@illusions-lab/mdi/node` returns Rust-produced HTML, the profile, and front-matter writing direction. Electron can print that request through its own BrowserWindow. `renderPdfWithChromium(source, profile)` instead loads the optional, separately installable `@illusions-lab/mdi-to-pdf` Node adapter (Playwright) unless you pass an Electron-compatible `{ renderHtmlToPdf }` adapter.
+`preparePdfExport(source, profile)` in `@illusions-lab/mdi/node` returns Rust-prepared HTML and resolved print data. Electron can print that request through its own BrowserWindow. `renderPdfWithChromium(source, profile)` instead loads the optional, separately installable `@illusions-lab/mdi-to-pdf` Node host (Playwright) unless you pass an Electron-compatible `{ renderHtmlToPdf }` implementation.
-The PDF adapter owns Chromium execution and applies paper size, landscape, margins, vertical/horizontal flow, font family/size/line spacing, characters-per-line, lines-per-page, first-line indentation, and page numbers. Browser WASM cannot spawn Chromium; browser code should prepare the request and send it to a Node, Electron, Tauri, or CLI host. Chromium receives completed HTML/CSS, never `.mdi` source, so it cannot change MDI syntax meaning.
+Rust resolves and applies paper size, landscape, margins, vertical/horizontal flow, font family/size/line spacing, characters-per-line, lines-per-page, first-line indentation, and page-number templates. The host owns Chromium execution. Browser WASM cannot spawn Chromium, so browser code should prepare the request and send it to a Node, Electron, Tauri, or CLI host. Chromium receives completed HTML/CSS and print data, never `.mdi` source.
+
+## What the contracts check
+
+Unit and coverage tests run first. Once they pass, the publication suite opens
+DOCX with the .NET Open XML SDK and LibreOffice, checks PDF structure and page
+geometry, validates EPUB with W3C EPUBCheck, and verifies HTML. This catches
+files that are valid ZIP archives but still fail in the applications readers
+actually use.
## Next steps
diff --git a/docs/src/content/docs/core/rust-api.md b/docs/src/content/docs/core/rust-api.md
index 8feca28..3f73a17 100644
--- a/docs/src/content/docs/core/rust-api.md
+++ b/docs/src/content/docs/core/rust-api.md
@@ -31,13 +31,22 @@ This page lists only symbols present in [`mdi-core/src/lib.rs`](https://github.c
- `render_text(source: &str) -> String` / `render_text_document(document: &Document) -> String` — deterministic plain text (the `txt` flavor, hardcoded).
- `render_text_format(source: &str, format: TextFormat, indent_prefix: &str) -> String` — any of the five TXT flavors. `TextFormat` is `Plain | Ruby | Narou | Kakuyomu | Aozora`, parsed from the binding-facing strings `txt`/`txt-ruby`/`narou`/`kakuyomu`/`aozora` via `TextFormat::parse`.
- `render_epub(source: &str) -> Result, String>` / `render_epub_document(document: &Document) -> Result, String>` — a complete EPUB 3 archive.
+- `render_epub_with_profile(source: &str, profile_json: &str, cover: Option<&EpubCover>) -> Result, String>` / `render_epub_document_with_profile(...)` — configured EPUB metadata, typography, chapter splitting, and optional PNG/JPEG cover data.
- `render_docx(source: &str) -> Result, String>` / `render_docx_document(document: &Document) -> Result, String>` — a complete DOCX archive.
+- `render_docx_with_profile(source: &str, profile_json: &str) -> Result, String>` / `render_docx_document_with_profile(...)` — configured OOXML page geometry, typography, grids, mirrored margins, and page numbering.
- `render_pdf(source: &str, options: &PdfOptions) -> Result, String>` — Rust-rendered HTML, laid out by a local Chromium. See [Rendering model: the Chromium/PDF boundary](/core/rendering/#the-chromiumpdf-boundary).
- `find_chromium() -> Option` — best-effort search for a local Chromium-family executable, used when `PdfOptions.chromium_path` is `None`.
+## Publication profiles
+
+- `resolve_export_profile(...)` / `resolve_export_profile_json(...)` — validate and fill the canonical profile, optionally inheriting a document writing mode.
+- `page_dimensions(page_size: &str) -> Option<(f64, f64)>` / `page_size_catalog_json() -> Result` — the canonical 67-size physical paper catalogue used by bindings and renderers.
+- `prepare_chromium_print_profile(...)` / its JSON and resolved variants — return styled HTML, millimetre page geometry, margins, and page-number templates for a Chromium host.
+- `apply_pdf_profile(...)` / `apply_pdf_profile_json(...)` — apply the resolved print CSS without launching a browser.
+
## Public data types
-`ParseOutput`, `ParserCapabilities`, `Diagnostic`, `DiagnosticSeverity`, `SourceSpan`, `Document`, `Frontmatter`, `FrontmatterEntry`, `PdfOptions` (current-generation, `Document`-based API); `MdiSyntaxDocument`, `MdiBlock`, `PagebreakVariant`, `Inline`, `RubyReading` (the older, `parse_mdi_syntax`-only shape — `Inline`/`RubyReading` are also reused internally to build the current-generation `Document`'s MDI nodes, but their `serde` output is what appears inside `Document.children`, not `MdiSyntaxDocument`).
+`ParseOutput`, `ParserCapabilities`, `Diagnostic`, `DiagnosticSeverity`, `SourceSpan`, `Document`, `Frontmatter`, `FrontmatterEntry`, `PdfOptions`, `EpubCover`, `ResolvedExportProfile` and its nested profile/Chromium print types (current-generation API); `MdiSyntaxDocument`, `MdiBlock`, `PagebreakVariant`, `Inline`, `RubyReading` (the older, `parse_mdi_syntax`-only shape — `Inline`/`RubyReading` are also reused internally to build the current-generation `Document`'s MDI nodes, but their `serde` output is what appears inside `Document.children`, not `MdiSyntaxDocument`).
## Not yet implemented
@@ -45,8 +54,8 @@ These exist as concepts in `ARCHITECTURE.md`/`SYNTAX.md` but have **no correspon
- A **standalone validation API** distinct from `parse_output`. Today, the only validation *is* whatever diagnostics `parse_output` returns as part of parsing; there's no separate `validate(document, options)` call.
- A **normalize** API distinct from `serialize_mdi`. Serialization already applies MDI's recommended-form normalization as a side effect of round-tripping; there's no separate function you'd call just to normalize without also serializing.
-- **Export-profile-aware EPUB/DOCX rendering** — cover images, configurable chapter-split level, page geometry, and font selection for those two formats. `render_epub`/`render_docx` currently only read front-matter metadata (title/author/lang/writing-mode), not an [export profile](/ecosystem/export-profiles/).
-- **Full DOCX typography** — ruby runs, boten character styles, and page geometry in the OOXML output; today `render_docx` flattens MDI typography to plain text runs, the same way `render_text` does.
+
+DOCX now emits native or portable OOXML representations for ruby, tate-chu-yoko, emphasis, kerning, page geometry, and other supported constructs. Word-compatible readers can still differ in Japanese line composition, so this is format support rather than a promise of pixel-identical browser layout.
## Next steps
diff --git a/docs/src/content/docs/ecosystem/export-profiles.md b/docs/src/content/docs/ecosystem/export-profiles.md
index e809f44..4b3186a 100644
--- a/docs/src/content/docs/ecosystem/export-profiles.md
+++ b/docs/src/content/docs/ecosystem/export-profiles.md
@@ -5,7 +5,7 @@ description: The real, validated ExportProfile schema — every field, its defau
**Prerequisites:** [Getting Started](/guides/getting-started/).
-An export profile configures **presentation** — page size, fonts, margins, indentation, page numbers — for a specific output. It never changes MDI syntax or meaning; the same source with two different profiles produces two different-looking PDFs of the *same document*. The package is `@illusions-lab/mdi-export-profile`; `resolveExportProfile` validates every field and throws a descriptive `Error` rather than silently accepting malformed layout data (see below).
+An export profile configures **presentation** — page size, fonts, margins, indentation, page numbers — for a specific output. It never changes MDI syntax or meaning; the same source with two different profiles produces two different-looking PDFs of the *same document*. The package is `@illusions-lab/mdi-export-profile`; its JavaScript functions pass validation and default resolution to `mdi-core`, then return a friendly typed result or a descriptive `Error`.
## The schema
@@ -48,7 +48,7 @@ An export profile configures **presentation** — page size, fonts, margins, ind
| `typesetting.fontFamily` | `string` | Mincho fallback stack | Must be a non-empty string; whitespace-only falls back to default. |
| `typesetting.textIndentEm` | `number` | `1` | `0`–`4`. |
| `typesetting.fullwidthSpaceIndent` | `boolean` | `false` | — |
-| `pagination.pageSize` | one of `PAGE_SIZES` | publisher: `"Shirokuban"`; word: `"A4"` | Must be a key of the exported `PAGE_DIMENSIONS` map. |
+| `pagination.pageSize` | one of `PAGE_SIZES` | publisher: `"Shirokuban"`; word: `"A4"` | Must be one of the 67 Rust-owned catalogue keys exposed through `PAGE_DIMENSIONS`. |
| `pagination.landscape` | `boolean` | `false` | — |
| `pagination.charactersPerLine` / `linesPerPage` | `number` | publisher horizontal: `27`×`26`; vertical: `40`×`30`; word: informational | `10`–`400`. |
| `pagination.gridMode` | `"strict" \| "typographic"` | publisher: strict; word: typographic | `word` rejects strict; strict rejects explicit line spacing. |
@@ -90,7 +90,10 @@ const resolved = resolveExportProfile(profile); // every field filled in, fully
| Full-width-space indent | Yes | Yes (1–4 spaces via `text.indentCount`) | EPUB / DOCX: Yes |
| Cover image, chapter split level | — | — | EPUB: Yes |
-The adapters package Rust-owned parsed IR; layout policy remains outside the MDI parser. EPUB cannot promise fixed physical pages because it is reflowable.
+`mdi-core` owns the defaults, validation rules, physical paper dimensions,
+and renderer-facing profile. The JavaScript package adds TypeScript types and
+Japanese UI labels without copying the layout table. EPUB cannot promise fixed
+physical pages because it is reflowable.
## Next steps
diff --git a/docs/src/content/docs/ecosystem/outputs.md b/docs/src/content/docs/ecosystem/outputs.md
index a4a8467..0993e7b 100644
--- a/docs/src/content/docs/ecosystem/outputs.md
+++ b/docs/src/content/docs/ecosystem/outputs.md
@@ -15,15 +15,20 @@ Every output below is a transformation of the **same** MDI document IR (see [Doc
| DOCX | `@illusions-lab/mdi` (`renderDocx`) | A real OOXML (WordprocessingML) document, zipped by Rust |
| PDF | `@illusions-lab/mdi-to-pdf` | Rust-rendered HTML/print CSS, laid out and rasterized by a locally installed Chromium-family browser |
-Four of these five are produced **entirely inside Rust** — the CLI and the JavaScript package call straight into `mdi-core`'s renderer functions with no intermediate JavaScript rendering step. PDF is the one exception, and only because generating a PDF requires launching an OS process (Chromium), which the Rust core itself does (via `render_pdf`/`find_chromium`) when called from a native host, and which the Node.js CLI reaches through `@illusions-lab/mdi-to-pdf`.
+Four of these five are produced **entirely inside Rust**, including profile-configured EPUB and DOCX. The CLI and JavaScript package call straight into `mdi-core`; their JavaScript layers normalize arguments but do not render documents. PDF uses Rust-resolved profile data and Rust-prepared HTML/print CSS, then asks a native or Node host to launch Chromium.
## Legacy compatibility packages
-`@illusions-lab/mdi-to-hast`, `@illusions-lab/mdi-to-html`, `@illusions-lab/mdi-to-epub`, and `@illusions-lab/mdi-to-docx` still exist and are still published — they predate the current Rust-native renderers and operate on `mdast`/HAST trees instead of calling Rust's render functions directly. They remain useful for `unified`-ecosystem consumers who already have an `mdast` tree (via [the remark adapter](/ecosystem/remark/)) and want to render it without a second parse. **The CLI itself no longer uses them** for its own `build` command — see [Bindings: CLI](/bindings/cli/) for exactly which Rust function backs each `--to` value today. One concrete, current difference worth knowing: `@illusions-lab/mdi-to-hast`'s stylesheet matches `SYNTAX.md` more closely than the CSS Rust's own `render_html` embeds — see [Migration and compatibility: stylesheet parity](/ecosystem/compatibility/#stylesheet-parity).
+`@illusions-lab/mdi-to-hast`, `@illusions-lab/mdi-to-html`, `@illusions-lab/mdi-to-epub`, and `@illusions-lab/mdi-to-docx` remain published for `unified` consumers that already hold an `mdast`/HAST tree. The EPUB and DOCX compatibility entries serialize that tree and delegate final generation to Rust; they no longer carry independent archive generators. **The CLI itself does not use these tree-facing entries** for `build`. One current difference worth knowing: `@illusions-lab/mdi-to-hast`'s stylesheet matches `SYNTAX.md` more closely than the CSS Rust's own `render_html` embeds — see [Migration and compatibility: stylesheet parity](/ecosystem/compatibility/#stylesheet-parity).
-## What's still pending across every renderer
+## Profile-configured output
-Cover images, configurable chapter-split level, and full page-geometry/font control for EPUB and DOCX are not yet wired to an [export profile](/ecosystem/export-profiles/) — both renderers currently only read metadata (`title`/`author`/`lang`/`writing-mode`) straight from front matter. This is tracked explicitly on [Rust Core API status: not yet implemented](/core/rust-api/#not-yet-implemented), not silently absent.
+Configured EPUB supports metadata, writing mode, typography, chapter splitting,
+and PNG/JPEG cover images. Configured DOCX supports metadata, page geometry,
+mirrored margins, writing mode, typography, strict or flowing grids, and page
+numbers. Both routes resolve the same [export profile](/ecosystem/export-profiles/)
+in Rust, so future Python, Swift, or Android APIs can expose the same behavior
+without recreating it.
## Next steps
diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx
index c428bee..dd4cec4 100644
--- a/docs/src/content/docs/index.mdx
+++ b/docs/src/content/docs/index.mdx
@@ -23,7 +23,7 @@ hero:
import { Card, CardGrid, Code } from '@astrojs/starlight/components';
-
+
@@ -36,7 +36,7 @@ import { Card, CardGrid, Code } from '@astrojs/starlight/components';
`renderHtml`, `renderTextFormat` (five flavors, including 小説家になろう and 青空文庫 conventions), `renderEpub`, `renderDocx`, and Rust-rendered PDF via Chromium — all implemented today, not planned. See [HTML / TXT / EPUB / DOCX / PDF outputs](/ecosystem/outputs/).
- Rust, Node.js, Swift, Kotlin, and Python are supported today, all backed by the same Rust core. See [Bindings](/bindings/javascript/) for platform-specific setup and APIs.
+ Rust, Node.js, Swift, and Python are supported today, all backed by the same Rust core. The Android / Kotlin binding is still in development and is not yet available as a stable public package on Maven Central. See [Bindings](/bindings/javascript/) for platform-specific setup and APIs.
diff --git a/docs/src/content/docs/ja/bindings/cli.md b/docs/src/content/docs/ja/bindings/cli.md
index 9bbe9d4..e920f34 100644
--- a/docs/src/content/docs/ja/bindings/cli.md
+++ b/docs/src/content/docs/ja/bindings/cli.md
@@ -47,4 +47,4 @@ input は UTF-8 です。`--to` は必須、`-o` は output path を上書きし
明示した `--config` には `layout.system` が必須で、ない profile は reject されます。`"japanese-publisher"` は book 用の system です。横書きは `Shirokuban`・10 pt 明朝体・mirror 付き左綴じ 27×26 strict grid、縦書きは A4 landscape 小説原稿・mirror 付き右綴じ 40×30 strict grid が default です。`"word"` は別の flowing system で、A4、四辺 25.4 mm、mirror なし、`gridMode: "typographic"` です。strict grid は使えません。
-MDI parsing/diagnostic/span は Rust の責務です。profile は publication policy、PDF geometry と Chromium layout は host policy です。PDF は `@illusions-lab/mdi-to-pdf` と local Chromium を要し、Chromium は `.mdi` ではなく完成 HTML を受け取ります。DOCX は page/type/numbering を設定できますが、ruby、tate-chu-yoko、禁則/改行禁止、kern、blank paragraph が browser と pixel-identical である保証はありません。対象 reader で確認してください。
+MDI parsing、diagnostic、span、profile validation、紙面 catalogue、設定付き EPUB/DOCX generation は Rust の責務です。PDF でも print HTML と geometry は Rust が準備し、host は machine 固有の Chromium process を提供します。Chromium は `.mdi` ではなく完成 HTML を受け取ります。DOCX は page/type/numbering を設定できますが、ruby、tate-chu-yoko、禁則/改行禁止、kern、blank paragraph が browser と pixel-identical である保証はありません。対象 reader で確認してください。
diff --git a/docs/src/content/docs/ja/bindings/javascript.md b/docs/src/content/docs/ja/bindings/javascript.md
index f70d77b..329e043 100644
--- a/docs/src/content/docs/ja/bindings/javascript.md
+++ b/docs/src/content/docs/ja/bindings/javascript.md
@@ -40,7 +40,7 @@ await writeFile("book.epub", renderEpub(source));
await writeFile("book.docx", renderDocx(source));
```
-publication 設定が必要なら二引数 overload(または `WithProfile`)を `await` します。これは Rust IR を EPUB/DOCX adapter に構造変換するため asynchronous ですが、JavaScript で MDI source を再 parse しません。
+publication 設定が必要なら二引数 overload(または `WithProfile`)を `await` します。Promise 形式は互換性のために残していますが、profile の検証と EPUB/DOCX の生成は Rust で行います。JavaScript は MDI を再 parse せず、別の document generator も持ちません。
```ts
const epub = await renderEpub(source, {
@@ -66,9 +66,9 @@ EPUB は metadata、縦書き、font、indent、`h1`/`h2`/`h3`/`none` chapter sp
設定付き export は必ず `layout.system` を指定します。`"japanese-publisher"` は mirrored の和文 book 用で、横書きは 10 pt 明朝体の `Shirokuban`・左綴じ 27 字 × 26 行 strict grid、縦書きは A4 landscape の小説原稿・右綴じ 40 字 × 30 行 strict grid が default です。`"word"` は Word 形式の flowing page 用で、default は A4、四辺 25.4 mm、mirror なし、`gridMode: "typographic"` です。`"word"` は `"strict"` を reject します。
-## 設定の所有者と DOCX の限界
+## 各層の担当と DOCX の限界
-Rust が grammar、diagnostic、span、semantic HTML/baseline export を所有します。EPUB/DOCX の profile は publication adapter の責務であり、紙面、Chromium、application UI の選択は host の責務です。DOCX は page break、縦書き、paragraph/run を OOXML に落としますが、ruby、tate-chu-yoko、禁則/改行禁止、kern、強制 blank paragraph を browser HTML と pixel-identical に再現する約束ではありません。重要な組版は対象 Word-compatible reader で確認してください。
+Rust が grammar、diagnostic、span、profile validation、canonical な紙面 catalogue、設定付き EPUB/DOCX generation を担当します。PDF でも styled HTML、page geometry、page-number template は Rust が準備し、host は Chromium と application UI だけを扱います。DOCX は page break、縦書き、paragraph/run を OOXML に落としますが、ruby、tate-chu-yoko、禁則/改行禁止、kern、強制 blank paragraph を browser HTML と pixel-identical に再現する約束ではありません。重要な組版は利用者が使う Word-compatible reader で確認してください。
## HTML/PDF host
@@ -79,6 +79,6 @@ const request = preparePdfExport(source, profile); // Electron の print API に
const pdf = await renderPdfWithChromium(source, profile);
```
-Node の default PDF adapter には別途 `npm install @illusions-lab/mdi-to-pdf` が必要です。Electron は `{ renderHtmlToPdf(html, profile, sourceWritingMode) }` adapter を渡せます。PDF profile は paper、landscape、margin、縦横、font、font size/line spacing、文字数/行数、indent、page number を扱います。browser/WASM は main entry point の baseline renderer を使えますが、設定付き publication adapter と PDF は Node/Electron host workflow です。`preparePdfExport()` を Node/Electron/Tauri/CLI host に送ります。
+Node の default PDF host には別途 `npm install @illusions-lab/mdi-to-pdf` が必要です。Electron は `{ renderHtmlToPdf(html, profile, sourceWritingMode) }` を渡せます。PDF の paper、landscape、margin、縦横、font、font size/line spacing、文字数/行数、indent、page number は Rust が解決します。browser/WASM でも設定付き EPUB/DOCX は生成できます。Chromium を起動する PDF だけは `preparePdfExport()` を Node/Electron/Tauri/CLI host に送ります。
非 string source と不正 option は `TypeError` です。diagnostic は document feedback として扱い、I/O/archive/host renderer の failure だけを `try`/`catch` してください。span は JavaScript index ではなく UTF-8 **byte** offset です。
diff --git a/docs/src/content/docs/ja/bindings/python.md b/docs/src/content/docs/ja/bindings/python.md
index d21abf1..d350b51 100644
--- a/docs/src/content/docs/ja/bindings/python.md
+++ b/docs/src/content/docs/ja/bindings/python.md
@@ -73,7 +73,7 @@ Python binding は **実装済み・公開済み・テスト済み** です。te
- **PDF function はまだありません。** Python には WASM のような原理的制限はありませんが、現在 `mdi.render_pdf` は export されていません。Python 隣接 workflow の PDF は [CLI](/ja/bindings/cli/) を使ってください。
- **独自 grammar はありません。** CLI/Rust と差があれば短い wrapper か core のバグです。
-- **export profile を適用しません。** `render_epub`/`render_docx` は source だけを受け、cover、chapter split、page geometry はまだ binding 経由で渡せません。
+- **export profile の引数はまだありません。** `render_epub`/`render_docx` は現在 source だけを受け取ります。設定付き EPUB/DOCX の実装は既に Rust にあり、Python wrapper が profile と cover の引数をまだ公開していない状態です。
## 次へ
diff --git a/docs/src/content/docs/ja/core/architecture.md b/docs/src/content/docs/ja/core/architecture.md
index d508d16..34fc1be 100644
--- a/docs/src/content/docs/ja/core/architecture.md
+++ b/docs/src/content/docs/ja/core/architecture.md
@@ -20,12 +20,13 @@ description: 実行可能な文法は一つ、バージョン付き IR も一つ
```text
.mdi source → mdi-core (Rust) → versioned Document IR
├→ Rust: HTML / TXT / EPUB / DOCX / PDF
- ├→ Node.js / Python / Swift / Kotlin(実装済み)
+ ├→ Node.js / Python / Swift(実装済み)
+ ├→ Android / Kotlin(開発中)
└→ remark/mdast adapter (実装済み)
HTML + print CSS → Chromium → PDF
```
-Rust、Node.js、Swift、Kotlin、Python はいずれもこの同じ Rust core を呼びます。具体的な API は [Bindings](/ja/bindings/javascript/) を参照してください。
+公開済みの Rust、Node.js、Swift、Python はいずれもこの同じ Rust core を呼びます。Android / Kotlin は開発中で、Maven Central では安定版の公開 package としてまだ配布していません。具体的な API は [Bindings](/ja/bindings/javascript/) を参照してください。
## なぜ一回の解析なのか
@@ -56,12 +57,12 @@ binding は各言語らしい入出力へ変換できます。たとえば EPUB/
| 層 | 状態 |
| --- | --- |
| `mdi-core` の完全な一回解析 | **実装済み** |
-| Rust の HTML / TXT / EPUB / DOCX renderer | **実装済み**(baseline) |
+| Rust の HTML / TXT / EPUB / DOCX renderer | **実装済み。**Baseline と設定付き EPUB/DOCX は同じ Rust renderer を使い、cover、chapter、typography、page geometry、page number も扱います。 |
| Chromium を Rust が起動する PDF | **実装済み**。 [レンダリング](/ja/core/rendering/) |
| JavaScript/WASM、CLI、remark adapter | **実装済み** |
| Python PyO3 binding | **実装済み**。PyPI: `illusion-markdown` |
| Swift binding | **実装済み**。 [Swift](/ja/bindings/swift/) |
-| Android / Kotlin binding | **実装済み**。 [Android / Kotlin](/ja/bindings/android/) |
+| Android / Kotlin binding | **開発中**。source とローカル検証手順はありますが、Maven Central では安定版の公開 package としてまだ配布していません。 [Android / Kotlin](/ja/bindings/android/) |
## 次へ
diff --git a/docs/src/content/docs/ja/core/rendering.md b/docs/src/content/docs/ja/core/rendering.md
index ddca5a1..9077180 100644
--- a/docs/src/content/docs/ja/core/rendering.md
+++ b/docs/src/content/docs/ja/core/rendering.md
@@ -1,15 +1,15 @@
---
title: レンダリングモデルと Chromium/PDF の境界
-description: Rust-owned MDI semantics が baseline export、設定付き publication adapter、PDF host に流れる仕組み。
+description: 一つの MDI document を Rust が baseline/設定付き output にし、PDF host がどこから引き継ぐか。
---
**前提:** [Document IR](/ja/core/document-ir/)、[Export profiles](/ja/ecosystem/export-profiles/)。
-## 一つの意味、二層の設定
+## 一つの意味、一つの publication contract
-Rust は `.mdi` parse、diagnostic code と UTF-8 span、semantic HTML/baseline EPUB/DOCX/TXT を所有します。ruby、tate-chu-yoko、改行禁止、kern、blank paragraph、pagebreak の意味を決める唯一の authority です。
+Rust は `.mdi` parse、diagnostic code と UTF-8 span、semantic output、publication profile の解決を担います。ruby、tate-chu-yoko、改行禁止、kern、blank paragraph、pagebreak に加え、紙面寸法と EPUB/DOCX layout も一か所で決まります。
-EPUB/DOCX adapter は既に parse 済みの Rust IR に metadata、chapter、typography、page setup、numbering を適用します。Chromium path や application UI preference は host の設定です。platform 固有の print policy を parser に混ぜません。
+profile は document の意味から独立しています。font や page size を変えても MDI の解釈は変わりません。Rust が profile を検証して metadata、chapter、typography、page setup、numbering を適用し、PDF host は Chromium の起動と byte の受け取りだけを担当します。
設定付き publication export は `layout.system` を明示します。`japanese-publisher` は strict な和文 book contract です。横書きは `Shirokuban`・10 pt 明朝体・mirror spread・左綴じ 27×26、縦書きは A4 landscape 小説原稿・mirror spread・右綴じ 40×30 です。`word` は意図的に別契約で、A4、四辺 25.4 mm、mirror なし、flowing `typographic` layout を使い、`strict` を reject します。
@@ -19,7 +19,7 @@ EPUB/DOCX adapter は既に parse 済みの Rust IR に metadata、chapter、typ
| TXT | `renderTextFormat` | caller が indent を渡す。 |
| EPUB | `renderEpub(source)` | `await renderEpub(source, { profile, cover })`。 |
| DOCX | `renderDocx(source)` | `await renderDocx(source, profile)`。 |
-| PDF | — | `@illusions-lab/mdi/node` が Rust HTML と profile を Chromium host に渡す。 |
+| PDF | — | `@illusions-lab/mdi/node` が Rust で準備した HTML、page geometry、page-number template を Chromium host に渡す。 |
## diagnostics と HTML
@@ -29,15 +29,23 @@ export UI では先に `parse(source)`/`prepareRender(source)` を呼びます
## EPUB/DOCX
-一引数の synchronous call は Rust baseline export です。設定付き call は async で Rust IR を adapter に構造変換し、JavaScript が MDI source を再 parse することはありません。EPUB は metadata、縦書き、font、indent、chapter level、JPEG/PNG cover を、DOCX は metadata、page size/orientation/margin、font/size/line spacing/indent、page number を受け取ります。
+一引数の synchronous call は Rust baseline export です。設定付き JavaScript call は互換性のため async の形を保ちますが、profile の検証と EPUB/DOCX archive の生成は Rust で行います。JavaScript に別の page-size table や document generator はありません。EPUB は metadata、縦書き、font、indent、chapter level、JPEG/PNG cover を、DOCX は metadata、page size/orientation/margin、font/size/line spacing/indent、page number を受け取ります。
DOCX は pagebreak、書字方向、paragraph と可能な inline/run formatting を OOXML に map します。ruby、tate-chu-yoko、禁則/改行禁止、kern、強制 blank は browser identical ではなく OOXML の近似です。重要な組版は実際の reader で確認してください。
## Chromium/PDF の境界
-`@illusions-lab/mdi/node` の `preparePdfExport(source, profile)` は Rust HTML、profile、front matter の writing direction を返し、Electron は自前 BrowserWindow で print できます。`renderPdfWithChromium` は別 install の `@illusions-lab/mdi-to-pdf` (Playwright) を load するか Electron-compatible `{ renderHtmlToPdf }` adapter を使います。
+`@illusions-lab/mdi/node` の `preparePdfExport(source, profile)` は Rust で準備済みの HTML と print data を返し、Electron は自前 BrowserWindow で print できます。`renderPdfWithChromium` は別 install の `@illusions-lab/mdi-to-pdf` (Playwright) を load するか Electron-compatible `{ renderHtmlToPdf }` を使います。
-PDF adapter は paper、landscape、margin、縦横、font/size/line spacing、文字数/行数、indent、page number を適用します。browser WASM は Chromium を spawn できないため request を Node/Electron/Tauri/CLI host へ送ります。Chromium が受け取るのは完成 HTML/CSS であり `.mdi` ではありません。
+paper、landscape、margin、縦横、font/size/line spacing、文字数/行数、indent、page-number template は Rust が解決して適用します。host は Chromium の実行を担当します。browser WASM は Chromium を spawn できないため request を Node/Electron/Tauri/CLI host へ送ります。Chromium が受け取るのは完成 HTML/CSS と print data であり `.mdi` ではありません。
+
+## Contract test が確認すること
+
+最初に unit test と coverage を通し、その後で publication contract を
+実行します。DOCX は .NET Open XML SDK と LibreOffice、PDF は構造と
+page geometry、EPUB は W3C EPUBCheck、HTML は出力 contract で確認します。
+ZIP として開けるだけでなく、読者が使う application で扱えることまで
+release 前に確かめます。
- [JavaScript / TypeScript](/ja/bindings/javascript/)
- [CLI](/ja/bindings/cli/)
diff --git a/docs/src/content/docs/ja/core/rust-api.md b/docs/src/content/docs/ja/core/rust-api.md
index 41f561a..185ccd0 100644
--- a/docs/src/content/docs/ja/core/rust-api.md
+++ b/docs/src/content/docs/ja/core/rust-api.md
@@ -29,20 +29,32 @@ description: 現在 mdi-core/src/lib.rs に実在する public symbol の一覧
- `render_text` / `render_text_document` → plain `txt`
- `render_text_format(source, format: TextFormat, indent_prefix)` → `txt`、`txt-ruby`、`narou`、`kakuyomu`、`aozora`
- `render_epub` / `render_epub_document` → `Result, String>` EPUB 3
+- `render_epub_with_profile` / `render_epub_document_with_profile` → metadata、typography、chapter split、PNG/JPEG cover 付き EPUB 3
- `render_docx` / `render_docx_document` → `Result, String>` DOCX
+- `render_docx_with_profile` / `render_docx_document_with_profile` → page geometry、typography、grid、mirror margin、page number 付き DOCX
- `render_pdf(source, options: &PdfOptions)` → local Chromium による PDF
- `find_chromium() -> Option` → Chromium の best-effort search
+## Publication profile
+
+- `resolve_export_profile` / `resolve_export_profile_json` — canonical profile を検証し、default を補います。
+- `page_dimensions` / `page_size_catalog_json` — binding と renderer が共用する 67 種の physical paper catalogue。
+- `prepare_chromium_print_profile` と JSON/resolved variants — Chromium host 用の styled HTML、mm 単位 geometry、margin、page-number template。
+- `apply_pdf_profile` / `apply_pdf_profile_json` — browser を起動せず resolved print CSS を適用します。
+
## Public data types
-current-generation API は `ParseOutput`、`ParserCapabilities`、`Diagnostic`、`SourceSpan`、`Document`、`Frontmatter`、`PdfOptions` です。旧 shape は `MdiSyntaxDocument`、`MdiBlock`、`PagebreakVariant`、`Inline`、`RubyReading` です。
+current-generation API は `ParseOutput`、`ParserCapabilities`、`Diagnostic`、`SourceSpan`、`Document`、`Frontmatter`、`PdfOptions`、`EpubCover`、`ResolvedExportProfile` とその nested profile/Chromium print types です。旧 shape は `MdiSyntaxDocument`、`MdiBlock`、`PagebreakVariant`、`Inline`、`RubyReading` です。
## Not yet implemented
- `parse_output` と別個の validation API はありません。
- `serialize_mdi` と別個の normalize API はありません。
-- export-profile-aware EPUB/DOCX(cover、chapter split、page geometry、font)はありません。
-- DOCX の ruby run、boten style、page geometry はありません。現在は typography を plain text に flatten します。
+
+DOCX は ruby、tate-chu-yoko、emphasis、kern、page geometry などを native
+または portable な OOXML で出力します。ただし Word-compatible reader
+ごとに和文の行組みは異なるため、browser と pixel-identical であるという
+意味ではありません。
## 次へ
diff --git a/docs/src/content/docs/ja/ecosystem/export-profiles.md b/docs/src/content/docs/ja/ecosystem/export-profiles.md
index 05003a2..f87f579 100644
--- a/docs/src/content/docs/ja/ecosystem/export-profiles.md
+++ b/docs/src/content/docs/ja/ecosystem/export-profiles.md
@@ -5,7 +5,7 @@ description: 検証される ExportProfile schema、二つの layout system、
**前提:** [Getting Started](/ja/guides/getting-started/)。
-export profile は page、font、margin、indent、page number という**表示**を決めます。MDI syntax や document の意味は変えません。package は `@illusions-lab/mdi-export-profile` です。`resolveExportProfile` は誤った layout data を黙って受け入れず、field 名を含む `Error` を返します。
+export profile は page、font、margin、indent、page number という**表示**を決めます。MDI syntax や document の意味は変えません。package は `@illusions-lab/mdi-export-profile` です。JavaScript API は validation と default resolution を `mdi-core` に渡し、typed result または field 名を含む読みやすい `Error` を返します。
## Schema と必須の system
@@ -33,7 +33,7 @@ export profile は page、font、margin、indent、page number という**表示
| `metadata.*` | optional `string` | 指定時は string。 |
| `typesetting.writingMode` | `"horizontal"` | `"horizontal" \| "vertical"`。 |
| `fontFamily` / `fontSize` / `lineSpacing` | Mincho stack / system default / optional | `lineSpacing` は typographic のみ。 |
-| `pagination.pageSize` | publisher horizontal: `Shirokuban`; vertical: A4 landscape; word: `A4` | `PAGE_SIZES` の key。 |
+| `pagination.pageSize` | publisher horizontal: `Shirokuban`; vertical: A4 landscape; word: `A4` | Rust が持つ 67 種の catalogue key。 |
| `charactersPerLine` / `linesPerPage` | publisher horizontal: 27×26; vertical: 40×30 | 10–400。word の count は情報用で、page contract ではない。 |
| `gridMode` | publisher: `strict`; word: `typographic` | `word` は strict を reject。strict は line spacing を reject。 |
| `margins` | publisher: 16.5/18/18/15.5 mm; word: 四辺 25.4 mm | printable area を残す必要がある。 |
@@ -70,7 +70,10 @@ const resolved = resolveExportProfile(profile);
| full-width-space indent | Yes | Yes(`text.indentCount` で 1–4) | EPUB / DOCX: Yes。 |
| cover image、chapter split | — | — | EPUB: Yes。 |
-adapter は Rust が parse した IR を package し、layout policy は MDI parser の外に保ちます。EPUB は reflowable なので固定の physical page を約束しません。
+default、validation rule、physical paper dimensions、renderer 用 profile は
+`mdi-core` が管理します。JavaScript package は TypeScript type と日本語 UI
+label を加えますが、別の layout table は持ちません。EPUB は reflowable
+なので固定の physical page を約束しません。
## 次へ
diff --git a/docs/src/content/docs/ja/ecosystem/outputs.md b/docs/src/content/docs/ja/ecosystem/outputs.md
index 382aea7..312655b 100644
--- a/docs/src/content/docs/ja/ecosystem/outputs.md
+++ b/docs/src/content/docs/ja/ecosystem/outputs.md
@@ -13,15 +13,20 @@ description: 各出力の意味、renderer、layout の担当範囲。
| DOCX | `@illusions-lab/mdi` (`renderDocx`) | Rust が zip する OOXML(WordprocessingML)document |
| PDF | `@illusions-lab/mdi-to-pdf` | Rust HTML/print CSS を local Chromium が layout |
-PDF 以外の四形式は Rust 内で直接生成されます。CLI と JavaScript package に中間 JavaScript renderer はありません。PDF も native host の `render_pdf` / `find_chromium` を使い、Chromium は `.mdi` source ではなく完成 HTML/CSS だけを受け取ります。
+PDF 以外の四形式は、profile 設定付き EPUB/DOCX も含めて Rust 内で直接生成されます。CLI と JavaScript package は argument を整えるだけで、中間 JavaScript renderer はありません。PDF は Rust が解決した profile と準備済み HTML/print CSS を使い、native または Node host が Chromium を起動します。
## Legacy compatibility packages
-`mdi-to-hast`、`mdi-to-html`、`mdi-to-epub`、`mdi-to-docx` は published のままです。これは current Rust-native renderer より前からある mdast/HAST path 向けです。[remark adapter](/ja/ecosystem/remark/) で既に `mdast` tree を持つ unified consumer は、二度目の parse なしに利用できます。CLI `build` は使用しません。`mdi-to-hast` の CSS は Rust `render_html` embedded CSS より `SYNTAX.md` に近い点があり、詳細は [stylesheet parity](/ja/ecosystem/compatibility/#stylesheet-parity) を参照してください。
+`mdi-to-hast`、`mdi-to-html`、`mdi-to-epub`、`mdi-to-docx` は published のままです。既に `mdast`/HAST tree を持つ unified consumer のための compatibility entry で、EPUB/DOCX は tree を MDI に serialize して最終生成を Rust に委ねます。独立した archive generator は残していません。CLI `build` はこの tree-facing entry を使いません。`mdi-to-hast` の CSS は Rust `render_html` embedded CSS より `SYNTAX.md` に近い点があり、詳細は [stylesheet parity](/ja/ecosystem/compatibility/#stylesheet-parity) を参照してください。
-## 全 renderer でまだ pending のこと
+## Profile 設定付き output
-EPUB/DOCX の cover、configurable chapter split、page geometry/font の export-profile 対応は pending です。現在は front matter の `title`、`author`、`lang`、`writing-mode` のみを読みます。これは静かな欠落ではなく [Rust Core API](/ja/core/rust-api/#not-yet-implemented) に追跡されている後続 API です。
+設定付き EPUB は metadata、writing mode、typography、chapter split、
+PNG/JPEG cover を扱います。設定付き DOCX は metadata、page geometry、
+mirror margin、writing mode、typography、strict/flowing grid、page number
+を扱います。どちらも Rust で同じ [Export profile](/ja/ecosystem/export-profiles/)
+を解決するため、将来 Python、Swift、Android に公開するときも同じ処理を
+書き直す必要はありません。
## 次へ
diff --git a/docs/src/content/docs/ja/index.mdx b/docs/src/content/docs/ja/index.mdx
index d08f300..934bc86 100644
--- a/docs/src/content/docs/ja/index.mdx
+++ b/docs/src/content/docs/ja/index.mdx
@@ -23,13 +23,13 @@ hero:
import { Card, CardGrid, Code } from '@astrojs/starlight/components';
-
+
`mdi-core` が CommonMark、GFM、front matter、すべての MDI 構文を一回で versioned document IR にします。ほかの言語やツールに第二の parser はありません。 [Rust を正とするアーキテクチャ](/ja/core/architecture/)
ルビ、縦中横、傍点、割注、kern、page break、縦書きはすべて第一級の構文です。各構文に実例、解析済み IR、出力があります。 [構文リファレンス](/ja/syntax/reference/) と [ライブショーケース](/ja/syntax/showcase/) を参照してください。
`renderHtml`、`renderTextFormat`(小説家になろう・青空文庫を含む 5 形式)、`renderEpub`、`renderDocx`、Chromium による Rust PDF は、すべて現在実装済みです。 [出力形式](/ja/ecosystem/outputs/)
- Rust、Node.js、Swift、Kotlin、Python はすべて同じ Rust core を基盤にして現在利用できます。環境別の設定と API は [Bindings](/ja/bindings/javascript/) を参照してください。
+ Rust、Node.js、Swift、Python は同じ Rust core を基盤にして現在利用できます。Android / Kotlin binding は開発中で、Maven Central では安定版の公開 package としてまだ配布していません。環境別の設定と API は [Bindings](/ja/bindings/javascript/) を参照してください。
## 30 秒で試す
diff --git a/docs/src/content/docs/zh-tw/bindings/cli.md b/docs/src/content/docs/zh-tw/bindings/cli.md
index 4cd62b5..b03bf90 100644
--- a/docs/src/content/docs/zh-tw/bindings/cli.md
+++ b/docs/src/content/docs/zh-tw/bindings/cli.md
@@ -47,4 +47,4 @@ CLI 以 profile file 為基準讀取 `epub.coverPath`,只接受 PNG/JPEG;cov
明確提供的 `--config` 必須含有 `layout.system`,缺少時 profile 會被拒絕。`"japanese-publisher"` 是書籍 system:橫書預設為 `Shirokuban`/10 pt 明朝體、鏡像左裝訂 27×26 strict grid;直書預設為 A4 landscape 小說原稿、鏡像右裝訂 40×30 strict grid。`"word"` 是另一個流動 system:A4、四邊 25.4 mm、無鏡像、`gridMode: "typographic"`,不能使用 strict grid。
-MDI parse/diagnostic/span 的所有權在 Rust。profile 是 publication policy,而 PDF geometry 與 Chromium layout 是 host policy。PDF 需要 `@illusions-lab/mdi-to-pdf` 與 local Chromium;Chromium 收到的是完成 HTML,不是 `.mdi`。DOCX 支援 page/type/numbering,卻不保證 ruby、tate-chu-yoko、禁則/不換行、kern、blank paragraph 與 browser 排版像素一致,請在目標 reader 驗證。
+MDI parse、diagnostic、span、profile validation、紙張目錄與設定型 EPUB/DOCX generation 都由 Rust 負責。PDF 的 print HTML 與 geometry 也由 Rust 準備,host 只提供 machine-specific Chromium process。Chromium 收到的是完成 HTML,不是 `.mdi`。DOCX 支援 page/type/numbering,卻不保證 ruby、tate-chu-yoko、禁則/不換行、kern、blank paragraph 與 browser 排版像素一致,請在目標 reader 驗證。
diff --git a/docs/src/content/docs/zh-tw/bindings/javascript.md b/docs/src/content/docs/zh-tw/bindings/javascript.md
index da08861..effdc91 100644
--- a/docs/src/content/docs/zh-tw/bindings/javascript.md
+++ b/docs/src/content/docs/zh-tw/bindings/javascript.md
@@ -40,7 +40,7 @@ await writeFile("book.epub", renderEpub(source));
await writeFile("book.docx", renderDocx(source));
```
-需要出版設定時,使用兩個參數的 overload(或明確的 `WithProfile` 名稱)並 `await`。它會把已 parse 的 Rust IR 結構轉換給 Node publication adapters,不會在 JavaScript 重新 parse MDI source。
+需要出版設定時,使用兩個參數的 overload(或明確的 `WithProfile` 名稱)並 `await`。Promise 形式是為了保持相容;profile 驗證與 EPUB/DOCX 生成都在 Rust 內完成。JavaScript 不會重新 parse MDI,也沒有另一套 document generator。
```ts
const epub = await renderEpub(source, {
@@ -66,9 +66,9 @@ EPUB 支援 metadata、直排、font、indent、`h1`/`h2`/`h3`/`none` chapter sp
每個設定型 export 都必須寫出 `layout.system`。`"japanese-publisher"` 用於鏡像的日文書籍:橫書預設為 10 pt 明朝體與 `Shirokuban`、左裝訂 27 字 × 26 行 strict grid;直書預設為 A4 landscape 小說原稿、右裝訂 40 字 × 30 行 strict grid。`"word"` 用於 Word 式流動頁面:預設 A4、四邊 25.4 mm、無鏡像、`gridMode: "typographic"`;`"word"` 會拒絕 `"strict"`。
-## 設定的所有權與 DOCX 限制
+## 各層負責什麼,以及 DOCX 的限制
-Rust 擁有 grammar、diagnostic、span、semantic HTML/baseline export。EPUB/DOCX profile 屬 publication adapter;紙張、Chromium 與 app UI preference 屬 host。DOCX 可將 page break、直排及一般 paragraph/run 對應到 OOXML,但不承諾 ruby、tate-chu-yoko、禁則/不換行、kern、強制 blank paragraph 與 browser HTML 像素一致。這些日文排版很重要時,請在目標 Word-compatible reader 驗證。
+Rust 負責 grammar、diagnostic、span、profile validation、canonical 紙張目錄,以及設定型 EPUB/DOCX generation。PDF 的 styled HTML、page geometry 與 page-number template 也由 Rust 準備;host 只控制 Chromium 與 app UI。DOCX 可將 page break、直排及一般 paragraph/run 對應到 OOXML,但不承諾 ruby、tate-chu-yoko、禁則/不換行、kern、強制 blank paragraph 與 browser HTML 像素一致。這些日文排版很重要時,請用讀者實際使用的 Word-compatible reader 驗證。
## HTML/PDF host
@@ -79,6 +79,6 @@ const request = preparePdfExport(source, profile); // 可交給 Electron print A
const pdf = await renderPdfWithChromium(source, profile);
```
-Node 的 default PDF adapter 要另外 `npm install @illusions-lab/mdi-to-pdf`。Electron 可傳入 `{ renderHtmlToPdf(html, profile, sourceWritingMode) }` adapter。PDF profile 支援紙張、橫向、邊距、直/橫排、font、font size/line spacing、每行字數/每頁行數、indent、page number。browser/WASM 無法啟動 Chromium,應把 `preparePdfExport()` 交給 Node/Electron/Tauri/CLI host。
+Node 的 default PDF host 要另外 `npm install @illusions-lab/mdi-to-pdf`。Electron 可傳入 `{ renderHtmlToPdf(html, profile, sourceWritingMode) }`。PDF 的紙張、橫向、邊距、直/橫排、font、font size/line spacing、每行字數/每頁行數、indent、page number 都由 Rust 解決。browser/WASM 可在本機生成設定型 EPUB/DOCX;只有 PDF 因無法啟動 Chromium,需要把 `preparePdfExport()` 交給 Node/Electron/Tauri/CLI host。
非字串 source 與無效 option 都是 `TypeError`。diagnostic 應作為 document feedback;只有 I/O/archive/host renderer failure 才適合 `try`/`catch`。span 是 UTF-8 **byte** offset,不是 JavaScript string index。
diff --git a/docs/src/content/docs/zh-tw/bindings/python.md b/docs/src/content/docs/zh-tw/bindings/python.md
index 445572c..1785170 100644
--- a/docs/src/content/docs/zh-tw/bindings/python.md
+++ b/docs/src/content/docs/zh-tw/bindings/python.md
@@ -90,7 +90,7 @@ def byte_span_to_str_index(source: str, byte_offset: int) -> int:
- **尚無 PDF function。**Python 可以 spawn subprocess,沒有 WASM 那類根本限制,但 package 今天未 expose `mdi.render_pdf`;Python workflow 的 PDF 暫用 [CLI](/zh-tw/bindings/cli/)。
- **沒有自己的 grammar。**每個 function 都直接呼叫同一個 `mdi-core`;若與 CLI 或 Rust 不一致,是這個約 60 行 wrapper 的 bug,而不是另一套 parser。
-- **不套用 export profile。**`render_epub`/`render_docx` 只收 `source`,仍是 Rust baseline renderer;cover、分章與頁面幾何尚未接入,見[Rust Core API](/zh-tw/core/rust-api/#尚未實作)。
+- **尚未提供 export profile 參數。**`render_epub`/`render_docx` 目前只收 `source`。設定型 EPUB/DOCX 已在 Rust 實作,只是 Python wrapper 尚未公開 profile 與 cover 參數。
## 下一步
diff --git a/docs/src/content/docs/zh-tw/core/architecture.md b/docs/src/content/docs/zh-tw/core/architecture.md
index 373bc1e..4dc6fca 100644
--- a/docs/src/content/docs/zh-tw/core/architecture.md
+++ b/docs/src/content/docs/zh-tw/core/architecture.md
@@ -36,12 +36,13 @@ description: 一個可執行的文法、一個已版本化的 IR,以及薄薄
▼ ▼ ▼
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
```
-以上關係今天都可驗證。Rust、Node.js、Swift、Kotlin 與 Python 都呼叫同一份 Rust 程式碼;請見 [Bindings](/zh-tw/bindings/javascript/) 的各平台細節。
+以上已發布的關係今天都可驗證。Rust、Node.js、Swift 與 Python 都呼叫同一份 Rust 程式碼。Android / Kotlin 仍在開發中,尚未在 Maven Central 提供公開穩定套件;請見 [Bindings](/zh-tw/bindings/javascript/) 的各平台細節。
## 為何是一趟,而非每層各一趟
@@ -73,14 +74,14 @@ MDI 不能在另一棵已解析 Markdown tree 上疊一層獨立解析,因為
| 層 | 狀態 |
| --- | --- |
| `mdi-core`:一次 Rust parse 完整處理 CommonMark + GFM + front matter + MDI | **已實作。**請見 [Rust Core API](/zh-tw/core/rust-api/)。 |
-| Rust 原生 `renderHtml`/`renderText`/`renderEpub`/`renderDocx` | **已實作**(baseline)。媒體、細緻 DOCX typography、完整 export-profile/pagination parity 是既有 API 的後續延伸。 |
+| Rust 原生 `renderHtml`/`renderText`/`renderEpub`/`renderDocx` | **已實作。**Baseline 與設定型 EPUB/DOCX 共用同一組 Rust renderer;cover、分章、typography、page geometry 與頁碼目前都可使用。 |
| Rust 協調 Chromium 的 PDF | **已實作。**請見[轉譯模型](/zh-tw/core/rendering/#chromiumpdf-邊界)。 |
| `@illusions-lab/mdi`(JavaScript/WASM) | **已實作**,每項操作直接呼叫 Rust。 |
| `@illusions-lab/mdi-remark`(mdast adapter) | **已實作**,但僅單向 MDI → mdast。 |
| `@illusions-lab/mdi-cli` | **已實作**,除最後 Chromium print 外每格式皆直接呼叫 Rust。 |
| Python binding(PyO3) | **已實作。**PyPI 套件為 [`illusion-markdown`](https://pypi.org/project/illusion-markdown/)(import 名稱 `mdi`)。 |
| Swift binding | **已實作。**見 [Swift](/zh-tw/bindings/swift/)。 |
-| Android / Kotlin binding | **已實作。**見 [Android / Kotlin](/zh-tw/bindings/android/)。 |
+| Android / Kotlin binding | **開發中。**已有 source 與本機驗證方式,但尚未在 Maven Central 提供公開穩定套件。見 [Android / Kotlin](/zh-tw/bindings/android/)。 |
儲存庫的 [`ARCHITECTURE.md`](https://github.com/illusions-lab/MDI/blob/main/ARCHITECTURE.md) 是此契約的規範版本;本頁負責解釋與核對狀態。
diff --git a/docs/src/content/docs/zh-tw/core/rendering.md b/docs/src/content/docs/zh-tw/core/rendering.md
index 1c16495..3f22c47 100644
--- a/docs/src/content/docs/zh-tw/core/rendering.md
+++ b/docs/src/content/docs/zh-tw/core/rendering.md
@@ -1,15 +1,15 @@
---
title: 轉譯模型與 Chromium/PDF 邊界
-description: Rust-owned MDI 語意如何流向 baseline export、可設定 publication adapter 與 PDF host。
+description: Rust 如何把同一份 MDI 文件轉成 baseline/設定型輸出,以及 PDF host 從哪裡接手。
---
**先備知識:**[Document IR](/zh-tw/core/document-ir/)、[export profiles](/zh-tw/ecosystem/export-profiles/)。
-## 一份語意、兩層設定
+## 一份語意、一份出版契約
-Rust 擁有 `.mdi` parse、diagnostic code、UTF-8 span,以及 semantic HTML/baseline EPUB/DOCX/TXT。它是 ruby、tate-chu-yoko、不換行、kern、blank paragraph、pagebreak 等 MDI 意義的唯一 authority。
+Rust 負責 `.mdi` parse、diagnostic code、UTF-8 span、semantic output 與 publication profile resolution。Ruby、tate-chu-yoko、不換行、kern、blank paragraph、pagebreak,以及紙張尺寸和 EPUB/DOCX layout,都在同一處決定。
-EPUB/DOCX adapter 對已 parse 的 Rust IR 套用 metadata、chapter、typography、page setup、numbering。Chromium path 與 app UI preference 是 host 設定。這樣不把 platform-specific print policy 塞入 parser。
+Profile 與文件語意彼此分開:更換字型或紙張,不會改變 MDI 的解析方式。Rust 會驗證 profile,並套用 metadata、chapter、typography、page setup 與 numbering;PDF host 只負責尋找/啟動 Chromium 並取回 bytes,app UI preference 仍由應用程式決定。
設定型 publication export 必須明示 `layout.system`。`japanese-publisher` 是 strict 的日文書籍契約:橫書為 `Shirokuban`、10 pt 明朝體、鏡像頁、左裝訂 27×26;直書為 A4 landscape 小說原稿、鏡像頁、右裝訂 40×30。`word` 刻意採不相容的契約:A4、四邊 25.4 mm、無鏡像、流動 `typographic` layout,且拒絕 `strict`。
@@ -19,7 +19,7 @@ EPUB/DOCX adapter 對已 parse 的 Rust IR 套用 metadata、chapter、typograph
| TXT | `renderTextFormat` | caller 傳入 indent。 |
| EPUB | `renderEpub(source)` | `await renderEpub(source, { profile, cover })`。 |
| DOCX | `renderDocx(source)` | `await renderDocx(source, profile)`。 |
-| PDF | — | `@illusions-lab/mdi/node` 將 Rust HTML 與 profile 交給 Chromium-capable host。 |
+| PDF | — | `@illusions-lab/mdi/node` 將 Rust 準備好的 HTML、page geometry 與 page-number template 交給 Chromium-capable host。 |
## diagnostics 與 HTML
@@ -29,15 +29,22 @@ EPUB/DOCX adapter 對已 parse 的 Rust IR 套用 metadata、chapter、typograph
## EPUB/DOCX
-單一參數的 synchronous call 是 Rust baseline export。可設定 call 為 async,會把 Rust IR 結構轉換至 Node adapter,JavaScript 不會重新 parse MDI source。EPUB 接受 metadata、直排、font、indent、chapter level、JPEG/PNG cover;DOCX 接受 metadata、page size/orientation/margin、font/size/line spacing/indent、page number。
+單一參數的 synchronous call 是 Rust baseline export。設定型 JavaScript call 為了相容性保留 async 形式,但 profile 驗證與 EPUB/DOCX archive 生成都在 Rust 完成。JavaScript 不會保留另一份 page-size table 或 document generator。EPUB 接受 metadata、直排、font、indent、chapter level、JPEG/PNG cover;DOCX 接受 metadata、page size/orientation/margin、font/size/line spacing/indent、page number。
DOCX 將 pagebreak、書寫方向、paragraph 與可用 inline/run formatting map 到 OOXML。ruby、tate-chu-yoko、禁則/不換行、kern、強制 blank 的結果是 OOXML approximation,不是 browser-identical。重要組版請在實際 reader 驗證。
## Chromium/PDF 邊界
-`@illusions-lab/mdi/node` 的 `preparePdfExport(source, profile)` 回傳 Rust HTML、profile 與 front matter writing direction;Electron 可在自己的 BrowserWindow print。`renderPdfWithChromium` 會 load 另外安裝的 `@illusions-lab/mdi-to-pdf` (Playwright),或使用 Electron-compatible `{ renderHtmlToPdf }` adapter。
+`@illusions-lab/mdi/node` 的 `preparePdfExport(source, profile)` 回傳 Rust 已準備完成的 HTML 與 print data;Electron 可在自己的 BrowserWindow print。`renderPdfWithChromium` 會 load 另外安裝的 `@illusions-lab/mdi-to-pdf` (Playwright),或使用 Electron-compatible `{ renderHtmlToPdf }`。
-PDF adapter 套用紙張、橫向、邊距、直/橫排、font/size/line spacing、每行字數/每頁行數、indent、page number。browser WASM 不能 spawn Chromium,須把 request 交給 Node/Electron/Tauri/CLI host。Chromium 收到的是完成 HTML/CSS,從不接觸 `.mdi`。
+紙張、橫向、邊距、直/橫排、font/size/line spacing、每行字數/每頁行數、indent 與 page-number template 都由 Rust 解決並套用。host 只負責執行 Chromium。browser WASM 不能 spawn Chromium,須把 request 交給 Node/Electron/Tauri/CLI host。Chromium 收到的是完成 HTML/CSS 與 print data,從不接觸 `.mdi`。
+
+## 契約測試會檢查什麼
+
+CI 會先完成 unit test 與 coverage,再執行 publication contracts。DOCX
+會經過 .NET Open XML SDK 驗證,並由 LibreOffice 實際匯入;PDF 會檢查
+結構與 page geometry,EPUB 使用 W3C EPUBCheck,HTML 則核對輸出契約。
+這能抓到「ZIP 可以解開,但讀者使用的應用程式仍無法開啟」這類問題。
- [JavaScript / TypeScript](/zh-tw/bindings/javascript/)
- [CLI](/zh-tw/bindings/cli/)
diff --git a/docs/src/content/docs/zh-tw/core/rust-api.md b/docs/src/content/docs/zh-tw/core/rust-api.md
index b4b58be..ecc2788 100644
--- a/docs/src/content/docs/zh-tw/core/rust-api.md
+++ b/docs/src/content/docs/zh-tw/core/rust-api.md
@@ -31,13 +31,22 @@ description: "`mdi-core/src/lib.rs` 現在實際公開的所有 symbol,包含
- `render_text(source: &str) -> String` / `render_text_document(document: &Document) -> String` — deterministic plain text(硬編碼 `txt` flavor)。
- `render_text_format(source: &str, format: TextFormat, indent_prefix: &str) -> String` — 五種 TXT flavor。`TextFormat` 為 `Plain | Ruby | Narou | Kakuyomu | Aozora`,透過 `TextFormat::parse` 解析 binding-facing 字串 `txt`/`txt-ruby`/`narou`/`kakuyomu`/`aozora`。
- `render_epub(source: &str) -> Result, String>` / `render_epub_document(document: &Document) -> Result, String>` — 完整 EPUB 3 archive。
+- `render_epub_with_profile(source: &str, profile_json: &str, cover: Option<&EpubCover>) -> Result, String>` / `render_epub_document_with_profile(...)` — 支援 metadata、typography、chapter split 與 optional PNG/JPEG cover 的設定型 EPUB。
- `render_docx(source: &str) -> Result, String>` / `render_docx_document(document: &Document) -> Result, String>` — 完整 DOCX archive。
+- `render_docx_with_profile(source: &str, profile_json: &str) -> Result, String>` / `render_docx_document_with_profile(...)` — 支援 OOXML page geometry、typography、grid、mirror margin 與頁碼的設定型 DOCX。
- `render_pdf(source: &str, options: &PdfOptions) -> Result, String>` — Rust-rendered HTML 交給 local Chromium layout。見[轉譯模型](/zh-tw/core/rendering/#chromiumpdf-邊界)。
- `find_chromium() -> Option` — `PdfOptions.chromium_path` 為 `None` 時 best-effort 搜尋本機 Chromium-family executable。
+## Publication profile
+
+- `resolve_export_profile(...)` / `resolve_export_profile_json(...)` — 驗證 canonical profile 並補齊 default,也可繼承文件 writing mode。
+- `page_dimensions(page_size: &str) -> Option<(f64, f64)>` / `page_size_catalog_json() -> Result` — binding 與 renderer 共用的 67 種 physical paper catalogue。
+- `prepare_chromium_print_profile(...)` 及 JSON/resolved variants — 為 Chromium host 產生 styled HTML、mm page geometry、margin 與 page-number template。
+- `apply_pdf_profile(...)` / `apply_pdf_profile_json(...)` — 不啟動 browser,直接套用 resolved print CSS。
+
## Public data types
-`ParseOutput`、`ParserCapabilities`、`Diagnostic`、`DiagnosticSeverity`、`SourceSpan`、`Document`、`Frontmatter`、`FrontmatterEntry`、`PdfOptions`(目前的 `Document`-based API);`MdiSyntaxDocument`、`MdiBlock`、`PagebreakVariant`、`Inline`、`RubyReading`(較舊、只供 `parse_mdi_syntax` 的 shape;`Inline`/`RubyReading` 也在內部用來建立目前 `Document` 的 MDI nodes)。
+`ParseOutput`、`ParserCapabilities`、`Diagnostic`、`DiagnosticSeverity`、`SourceSpan`、`Document`、`Frontmatter`、`FrontmatterEntry`、`PdfOptions`、`EpubCover`、`ResolvedExportProfile` 與其 nested profile/Chromium print types(目前 API);`MdiSyntaxDocument`、`MdiBlock`、`PagebreakVariant`、`Inline`、`RubyReading`(較舊、只供 `parse_mdi_syntax` 的 shape;`Inline`/`RubyReading` 也在內部用來建立目前 `Document` 的 MDI nodes)。
## 尚未實作
@@ -45,8 +54,10 @@ description: "`mdi-core/src/lib.rs` 現在實際公開的所有 symbol,包含
- 獨立於 `parse_output` 的 **standalone validation API**。今日 validation 就是 parsing 時 `parse_output` 回傳的 diagnostics,沒有 `validate(document, options)`。
- 獨立於 `serialize_mdi` 的 **normalize API**。serialization round-trip 時已套用推薦形式 normalization,沒有只 normalize 不 serialize 的 function。
-- **Export-profile-aware EPUB/DOCX rendering**:cover image、可設定 chapter-split level、page geometry、font selection。`render_epub`/`render_docx` 現只讀 front-matter metadata(title/author/lang/writing-mode),不讀 [export profile](/zh-tw/ecosystem/export-profiles/)。
-- **完整 DOCX typography**:OOXML 的 ruby runs、boten character styles、page geometry。現在 `render_docx` 將 MDI typography flatten 為 plain text runs,和 `render_text` 一樣。
+
+DOCX 現在會以 native 或 portable OOXML 表示 ruby、tate-chu-yoko、
+emphasis、kern、page geometry 等支援項目。Word-compatible reader 的日文
+行組仍可能不同,因此這代表格式支援,而非承諾與 browser 像素完全一致。
## 下一步
diff --git a/docs/src/content/docs/zh-tw/ecosystem/export-profiles.md b/docs/src/content/docs/zh-tw/ecosystem/export-profiles.md
index 581e80b..dc045ee 100644
--- a/docs/src/content/docs/zh-tw/ecosystem/export-profiles.md
+++ b/docs/src/content/docs/zh-tw/ecosystem/export-profiles.md
@@ -5,7 +5,7 @@ description: 經驗證的 ExportProfile schema、兩種 layout system,以及
**先備知識:**[快速開始](/zh-tw/guides/getting-started/)。
-export profile 設定的是頁面、字型、邊界、縮排與頁碼等**呈現**;它不改變 MDI syntax 或文件語意。套件為 `@illusions-lab/mdi-export-profile`。`resolveExportProfile` 不會靜默接受錯誤的版面資料,而會回傳含 field 名的 `Error`。
+export profile 設定的是頁面、字型、邊界、縮排與頁碼等**呈現**;它不改變 MDI syntax 或文件語意。套件為 `@illusions-lab/mdi-export-profile`。JavaScript API 會把 validation 與 default resolution 交給 `mdi-core`,再回傳有型別的結果,或包含 field 名且容易理解的 `Error`。
## Schema 與必填 system
@@ -33,7 +33,7 @@ export profile 設定的是頁面、字型、邊界、縮排與頁碼等**呈現
| `metadata.*` | optional `string` | 指定時必須為 string。 |
| `typesetting.writingMode` | `"horizontal"` | `"horizontal" \| "vertical"`。 |
| `fontFamily` / `fontSize` / `lineSpacing` | Mincho stack / system default / optional | `lineSpacing` 僅可用於 typographic。 |
-| `pagination.pageSize` | publisher 橫書:`Shirokuban`;直書:A4 landscape;word:`A4` | `PAGE_SIZES` 的 key。 |
+| `pagination.pageSize` | publisher 橫書:`Shirokuban`;直書:A4 landscape;word:`A4` | Rust 管理的 67 種 catalogue key。 |
| `charactersPerLine` / `linesPerPage` | publisher 橫書 27×26;直書 40×30 | 10–400;word 的 count 僅供資訊,不是頁面契約。 |
| `gridMode` | publisher: `strict`; word: `typographic` | `word` 拒絕 strict;strict 拒絕 line spacing。 |
| `margins` | publisher: 16.5/18/18/15.5 mm;word: 四邊 25.4 mm | 必須留下 printable area。 |
@@ -70,7 +70,10 @@ const resolved = resolveExportProfile(profile);
| 全形空白縮排 | 是 | 是(`text.indentCount` 為 1–4) | EPUB / DOCX:是。 |
| cover image、chapter split | — | — | EPUB:是。 |
-adapter 包裝 Rust 已 parse 的 IR,layout policy 留在 MDI parser 外。EPUB 為 reflowable,不能承諾固定 physical pages。
+Default、validation rule、physical paper dimensions 與 renderer-facing
+profile 都由 `mdi-core` 管理。JavaScript package 補上 TypeScript type 和
+日文 UI label,但不另外保存 layout table。EPUB 為 reflowable,不能承諾
+固定 physical pages。
## 下一步
diff --git a/docs/src/content/docs/zh-tw/ecosystem/outputs.md b/docs/src/content/docs/zh-tw/ecosystem/outputs.md
index 53ff3e6..b419e7c 100644
--- a/docs/src/content/docs/zh-tw/ecosystem/outputs.md
+++ b/docs/src/content/docs/zh-tw/ecosystem/outputs.md
@@ -15,15 +15,19 @@ description: 每個輸出格式的含義、rendering package 與 layout responsi
| DOCX | `@illusions-lab/mdi`(`renderDocx`) | 真正 OOXML(WordprocessingML)文件,由 Rust zip |
| PDF | `@illusions-lab/mdi-to-pdf` | Rust-rendered HTML/print CSS,由本機 Chromium-family browser layout/rasterize |
-五種中四種完全在 Rust 內產生:CLI 與 JavaScript package 直接呼叫 `mdi-core` renderer,沒有中間 JavaScript render step。PDF 是例外,只因它需 launch OS process;native host 的 Rust core 用 `render_pdf`/`find_chromium`,Node CLI 則經由 `@illusions-lab/mdi-to-pdf`。
+五種中四種完全在 Rust 內產生,包含套用 profile 的 EPUB/DOCX。CLI 與 JavaScript package 只整理 arguments,不含中間 JavaScript renderer。PDF 使用 Rust 解決的 profile 與準備完成的 HTML/print CSS,再由 native 或 Node host 啟動 Chromium。
## Legacy compatibility packages
-`@illusions-lab/mdi-to-hast`、`mdi-to-html`、`mdi-to-epub`、`mdi-to-docx` 仍存在並發布。它們早於 Rust-native renderer,操作 `mdast`/HAST tree 而不直接呼叫 Rust render functions,對已有 `mdast`(經由[remark adapter](/zh-tw/ecosystem/remark/))的 unified consumer 仍有用。**CLI 已不再使用它們**。重要差異是 `mdi-to-hast` stylesheet 比 Rust `render_html` embedded CSS 更接近 `SYNTAX.md`,見[stylesheet parity](/zh-tw/ecosystem/compatibility/#stylesheet-parity)。
+`@illusions-lab/mdi-to-hast`、`mdi-to-html`、`mdi-to-epub`、`mdi-to-docx` 仍發布,供已持有 `mdast`/HAST tree 的 unified consumer 使用。EPUB/DOCX compatibility entry 會把 tree serialize 為 MDI,再交給 Rust 完成輸出;JavaScript 不再保留獨立 archive generator。**CLI 不使用這些 tree-facing entry**。重要差異是 `mdi-to-hast` stylesheet 比 Rust `render_html` embedded CSS 更接近 `SYNTAX.md`,見[stylesheet parity](/zh-tw/ecosystem/compatibility/#stylesheet-parity)。
-## 所有 renderer 尚待完成的部分
+## 設定型輸出
-EPUB/DOCX 尚未接上 [export profile](/zh-tw/ecosystem/export-profiles/) 的 cover image、configurable chapter-split level、完整 page-geometry/font control;目前只讀 front matter 的 `title`/`author`/`lang`/`writing-mode`。這在 [Rust Core API](/zh-tw/core/rust-api/#尚未實作) 明確列為 pending,而非靜默缺失。
+設定型 EPUB 支援 metadata、writing mode、typography、chapter split 與
+PNG/JPEG cover。設定型 DOCX 支援 metadata、page geometry、mirror margin、
+writing mode、typography、strict/flowing grid 和頁碼。兩條路徑都在 Rust
+解析同一份 [export profile](/zh-tw/ecosystem/export-profiles/),因此未來
+Python、Swift 或 Android API 可以直接公開同樣的能力,不必重寫邏輯。
## 下一步
diff --git a/docs/src/content/docs/zh-tw/index.mdx b/docs/src/content/docs/zh-tw/index.mdx
index 3fd5c63..12f8127 100644
--- a/docs/src/content/docs/zh-tw/index.mdx
+++ b/docs/src/content/docs/zh-tw/index.mdx
@@ -30,7 +30,7 @@ import { Card, CardGrid } from '@astrojs/starlight/components';
本網站就是用 MDI 自己的解析器渲染 MDI 的。
- Rust、Node.js、Swift、Kotlin 與 Python 現在都已可使用,且都呼叫同一個 Rust core。各平台的安裝與 API 請見 [Bindings](/zh-tw/bindings/javascript/)。
+ Rust、Node.js、Swift 與 Python 現在都已可使用,且都呼叫同一個 Rust core。Android / Kotlin binding 仍在開發中,尚未在 Maven Central 提供公開穩定套件。各平台的安裝與 API 請見 [Bindings](/zh-tw/bindings/javascript/)。
`renderHtml`、五種 `renderTextFormat`、`renderEpub`、`renderDocx`,以及透過 Chromium 的 Rust PDF 都已實作。詳見[輸出格式](/zh-tw/ecosystem/outputs/)。
diff --git a/mdi-core/README.md b/mdi-core/README.md
index bf26f62..6e7cc05 100644
--- a/mdi-core/README.md
+++ b/mdi-core/README.md
@@ -30,6 +30,13 @@ When rendering one parsed document in multiple formats, use the
`*_document` functions, such as `render_html_document`, to avoid parsing it
again.
+Publication profiles are also resolved in Rust. Use
+`render_epub_with_profile` or `render_docx_with_profile` when you need
+metadata, typography, chapter splitting, cover art, page geometry, or page
+numbers. `page_size_catalog_json` exposes the same 67 paper definitions to
+bindings and user interfaces, so other languages do not need to copy the
+dimension table.
+
## Documentation
- [API reference](https://docs.rs/mdi-core)
diff --git a/nodejs/packages/cli/README.md b/nodejs/packages/cli/README.md
index abad447..56f01ec 100644
--- a/nodejs/packages/cli/README.md
+++ b/nodejs/packages/cli/README.md
@@ -17,10 +17,10 @@ mdi build input.mdi --to html|pdf|epub|docx|txt|txt-ruby|narou|kakuyomu|aozora|t
```
HTML, EPUB, DOCX, and text output call `mdi-core` directly through the
-JavaScript binding. PDF passes Rust-rendered HTML to Chromium solely for page
-layout; Chromium never receives or parses MDI source. `--config` currently
-applies PDF and text settings. EPUB and DOCX use Rust's deterministic baseline
-and front matter metadata while their full profile options move into Rust.
+JavaScript binding. With `--config`, Rust also validates and applies EPUB/DOCX
+metadata, typography, chapter splitting, cover art, page geometry, and
+numbering. PDF receives Rust-prepared HTML and print data, then uses Chromium
+solely for page layout; Chromium never receives or parses MDI source.
`txt-all` writes every text variant next to the input and does not accept `-o`.
The `narou`, `kakuyomu`, and `aozora` variants are contract-tested against
@@ -43,14 +43,15 @@ mdi build novel.mdi --to pdf --config print.json
## Architecture
-The CLI does not parse MDI itself. HTML, EPUB, DOCX, and text call the Rust
-engine through `@illusions-lab/mdi`; PDF gives Rust-rendered HTML to Chromium
-solely for print layout. `--config` currently configures PDF and text output.
+The CLI does not parse MDI or implement publication formats itself. HTML,
+EPUB, DOCX, and text call the Rust engine through `@illusions-lab/mdi`.
+Profile defaults, validation, paper dimensions, and print CSS also come from
+Rust. The host-specific step is launching Chromium for PDF.
## Documentation
- [CLI guide](https://mdi.illusions.app/bindings/cli/)
-- [Export profiles](https://mdi.illusions.app/guides/export-profiles/)
+- [Export profiles](https://mdi.illusions.app/ecosystem/export-profiles/)
- [JavaScript documentation](https://mdi.illusions.app/bindings/javascript/)
Part of the [MDI repository](https://github.com/illusions-lab/MDI). MIT licensed.
diff --git a/nodejs/packages/export-profile/README.md b/nodejs/packages/export-profile/README.md
index aabff0f..bf6246b 100644
--- a/nodejs/packages/export-profile/README.md
+++ b/nodejs/packages/export-profile/README.md
@@ -1,8 +1,9 @@
# `@illusions-lab/mdi-export-profile`
-Typed export-profile schema shared by MDI output adapters. A profile controls
-presentation and metadata—page geometry, typography, page numbers, EPUB
-chapters, and publication-text options—without changing MDI syntax.
+Typed JavaScript interface to MDI's Rust-owned export-profile contract. A
+profile controls presentation and metadata—page geometry, typography, page
+numbers, EPUB chapters, and publication-text options—without changing MDI
+syntax.
## Install
@@ -51,9 +52,11 @@ listPageSizes({ locale: "ja" });
// [{ key: "A4", label: "A4判", widthMm: 210, heightMm: 297 }, ...]
```
-Use the resolved profile with the JavaScript PDF/mdast adapters or pass a JSON
-profile to `mdi build --config export.json`. Rust remains responsible for MDI
-syntax and document semantics.
+Use the resolved profile with the JavaScript export APIs, or pass a JSON
+profile to `mdi build --config export.json`. Validation, defaults, physical
+page dimensions, and renderer-facing values come from `mdi-core`; this package
+adds TypeScript types and Japanese UI labels without keeping a second layout
+table.
Configured exports must select `layout.system`. The two systems deliberately
do not blend:
diff --git a/nodejs/packages/mdast-util-mdi/src/source.ts b/nodejs/packages/mdast-util-mdi/src/source.ts
index c9adac2..bedacc5 100644
--- a/nodejs/packages/mdast-util-mdi/src/source.ts
+++ b/nodejs/packages/mdast-util-mdi/src/source.ts
@@ -5,7 +5,7 @@ import { mdiToMarkdown } from "./to-markdown.js";
/**
* Serialize a host mdast root to portable MDI source before passing it to the
- * Rust core. This is the sole publication-adapter serialization path.
+ * Rust core. This is the sole mdast compatibility serialization path.
*/
export function mdastToMdiSource(tree: Root): string {
const yaml = tree.children.find(
diff --git a/nodejs/packages/mdi/README.md b/nodejs/packages/mdi/README.md
index cffa70b..fab4cc0 100644
--- a/nodejs/packages/mdi/README.md
+++ b/nodejs/packages/mdi/README.md
@@ -62,11 +62,12 @@ unsupported version.
## Rendering
-Rendering starts from the same Rust IR. Canonical MDI, plain text, HTML, and
-the one-argument baseline EPUB/DOCX renderers execute in Rust and are exposed
-through this package. PDF uses Rust HTML as its input to a host layout adapter such as
-`@illusions-lab/mdi-to-pdf`; the adapter may control Chromium, but it never
-parses MDI or produces semantic HTML.
+Rendering starts from the same Rust IR. Canonical MDI, plain text, HTML, EPUB,
+and DOCX—including profile-configured EPUB and DOCX—execute in Rust and are
+exposed through this package. PDF uses Rust-prepared HTML, print CSS, page
+geometry, and header/footer data as input to a host such as
+`@illusions-lab/mdi-to-pdf`. The host launches Chromium, but it never parses
+MDI or decides publication settings.
Browser WebAssembly cannot start Chromium. Browser code sends Rust-rendered
HTML to a server or desktop host when it needs PDF output.
@@ -94,25 +95,28 @@ Rust-authoritative parser rather than accepting mutable JavaScript IR. This
keeps the source spans and error codes predictable and prevents JavaScript from
becoming a second syntax implementation.
-Configuration ownership is deliberately split: Rust owns MDI parsing and
-semantic HTML; publication profiles own EPUB/DOCX metadata and typesetting;
-the host owns Chromium/Electron, paper-printer integration, and application UI
-preferences. This keeps platform-specific pagination controls out of the
-parser and lets Electron supply its own PDF adapter.
+Configuration ownership is deliberately clear: Rust validates publication
+profiles and applies EPUB/DOCX metadata, typography, page geometry, and
+numbering. For PDF, Rust also prepares the print HTML and resolved page data;
+the host owns only Chromium/Electron process control, printer integration, and
+application UI preferences. This keeps layout behavior consistent without
+putting application concerns into the parser.
### Configured EPUB and DOCX
For publication output, pass an export profile to the overloads (or use the
-explicit `WithProfile` functions). These Node.js-only async paths map the
-Rust-owned IR through the publication adapters and retain configuration for
-metadata, chapter splitting, vertical writing, font selection, paper size,
-margins, and page numbers. EPUB also accepts in-memory PNG or JPEG cover art.
+explicit `WithProfile` functions). The Promise-shaped API is retained for
+compatibility, while profile validation and archive generation both run in
+Rust. It supports metadata, chapter splitting, vertical writing, font
+selection, paper size, margins, and page numbers. EPUB also accepts in-memory
+PNG or JPEG cover art.
```ts
import { renderDocxWithProfile, renderEpubWithProfile } from "@illusions-lab/mdi";
const epub = await renderEpubWithProfile(source, {
profile: {
+ layout: { system: "japanese-publisher" },
metadata: { title: "Book", author: "Author" },
typesetting: { writingMode: "vertical", fontFamily: "Noto Serif JP" },
epub: { chapterSplitLevel: "h1" },
@@ -121,6 +125,7 @@ const epub = await renderEpubWithProfile(source, {
});
const docx = await renderDocxWithProfile(source, {
+ layout: { system: "word" },
pagination: { pageSize: "A5", margins: { top: 12, bottom: 12, left: 14, right: 14 } },
});
```
diff --git a/nodejs/packages/mdi/src/index.test.ts b/nodejs/packages/mdi/src/index.test.ts
index 6a85100..7c05902 100644
--- a/nodejs/packages/mdi/src/index.test.ts
+++ b/nodejs/packages/mdi/src/index.test.ts
@@ -313,7 +313,7 @@ describe("Rust MDI JavaScript binding", () => {
expect(tree.data?.frontmatter).toMatchObject({ pageProgression: "ltr" });
});
- it("rejects malformed cover shorthands before loading publication adapters", () => {
+ it("rejects malformed cover shorthands before calling Rust", () => {
expect(() => renderEpub("text", { cover: { data: new Uint8Array(), mediaType: "image/gif" as never } })).toThrow("options.cover.mediaType");
expect(() => renderEpub("text", { coverImage: "not-bytes" as never })).toThrow("options.coverImage");
expect(() => renderEpub("text", { coverMediaType: "image/gif" as never })).toThrow("options.coverMediaType");
diff --git a/nodejs/packages/mdi/src/index.ts b/nodejs/packages/mdi/src/index.ts
index 0b82577..75ddfc6 100644
--- a/nodejs/packages/mdi/src/index.ts
+++ b/nodejs/packages/mdi/src/index.ts
@@ -62,9 +62,9 @@ export type MdiDocxExportProfile = ExportProfile & {
date?: string;
verticalWriting?: boolean;
fontFamily?: string;
- /** Point size; requires a publication adapter that supports it. */
+ /** Body type size in typographic points. */
fontSize?: number;
- /** Line-height multiplier; requires a publication adapter that supports it. */
+ /** Baseline multiplier, for example 1.5 for one-and-a-half spacing. */
lineSpacing?: number;
textIndent?: number;
pageSize?: NonNullable["pageSize"];
@@ -133,7 +133,7 @@ export interface MdiDocument {
children: MdiNode[];
}
-/** Resolved front matter attached to mdast publication-adapter roots. */
+/** Resolved front matter attached to mdast compatibility roots. */
export interface MdiPublicationFrontmatter {
mdi: string;
title?: string;
@@ -242,7 +242,7 @@ export function prepareRender(source: string): MdiSyntaxParseResult {
export function renderEpub(source: string): Uint8Array;
/**
* Build a profile-configured EPUB 3 archive. This overload is asynchronous
- * because archive generation is performed by the Node.js publication adapter.
+ * for backward compatibility; validation and archive generation run in Rust.
*/
export function renderEpub(
source: string,
@@ -279,7 +279,7 @@ export function renderEpubWithDiagnostics(
export function renderDocx(source: string): Uint8Array;
/**
* Build a profile-configured DOCX archive. This overload is asynchronous
- * because archive generation is performed by the Node.js publication adapter.
+ * for backward compatibility; validation and OOXML generation run in Rust.
*/
export function renderDocx(
source: string,
@@ -313,10 +313,9 @@ export function renderDocxWithDiagnostics(
}
/**
- * Build a configured EPUB using the same profile schema as the dedicated
- * publication adapters. Unlike the one-argument {@link renderEpub}, this
- * returns a Promise and supports cover art, metadata, chapters and vertical
- * writing.
+ * Build a configured EPUB in Rust. Unlike the one-argument
+ * {@link renderEpub}, this returns a Promise for API compatibility and
+ * supports cover art, metadata, chapters and vertical writing.
*/
export async function renderEpubWithProfile(
source: string,
@@ -336,7 +335,7 @@ export async function renderEpubWithProfile(
}
/**
- * Build a configured DOCX using the shared print profile. The profile
+ * Build a configured DOCX in Rust using the shared print profile. The profile
* supports metadata, writing mode, paper size, margins and page numbers.
*/
export async function renderDocxWithProfile(
@@ -351,13 +350,9 @@ export async function renderDocxWithProfile(
}
/**
- * Map the Rust-owned IR to mdast for the publication adapters. This is a
- * structural conversion only: all grammar decisions have already been made
- * by {@link parse}, so it never reparses MDI source in JavaScript.
- */
-/**
- * Convert a Rust-owned MDI document IR into mdast for publication adapters.
- * This performs no parsing and preserves the adapter node conventions.
+ * Convert a Rust-owned MDI document IR into mdast for unified compatibility
+ * workflows. This performs no parsing and preserves the established mdast
+ * node conventions.
*/
export function toPublicationMdast(document: MdiDocument): MdiPublicationRoot {
const children = document.children.map(toPublicationMdastNode) as unknown as Root["children"];
diff --git a/nodejs/packages/to-docx/README.md b/nodejs/packages/to-docx/README.md
index 6aea7e3..0118c96 100644
--- a/nodejs/packages/to-docx/README.md
+++ b/nodejs/packages/to-docx/README.md
@@ -1,7 +1,8 @@
# @illusions-lab/mdi-to-docx
-Creates a DOCX file from an MDI-flavoured mdast tree. It is a profile-aware
-compatibility adapter for unified applications and does not parse MDI source.
+Creates a DOCX file from an MDI-flavoured mdast tree. It is a compatibility
+entry point for unified applications: the tree is serialized to MDI, then the
+same Rust OOXML renderer used by every binding creates the document.
## Install
@@ -44,12 +45,13 @@ remaining fallbacks:
| blank paragraph / pagebreak | Empty paragraph / native page break |
The source mdast remains available to the host application, so a UI can retain
-source spans and render diagnostics before choosing to export. This converter
-does not invent diagnostics or silently claim pixel-equivalent Japanese layout.
+source spans and render diagnostics before choosing to export. JavaScript does
+not contain a second DOCX generator, and the package does not claim
+pixel-equivalent Japanese layout across Word-compatible readers.
## Documentation
- [Output model](https://mdi.illusions.app/ecosystem/outputs/)
-- [Export-profile guide](https://mdi.illusions.app/guides/export-profiles/)
+- [Export-profile guide](https://mdi.illusions.app/ecosystem/export-profiles/)
- [API reference](https://mdi.illusions.app/api/to-docx/)
- [JavaScript documentation](https://mdi.illusions.app/bindings/javascript/)
diff --git a/nodejs/packages/to-epub/README.md b/nodejs/packages/to-epub/README.md
index 792588b..247319a 100644
--- a/nodejs/packages/to-epub/README.md
+++ b/nodejs/packages/to-epub/README.md
@@ -1,8 +1,9 @@
# @illusions-lab/mdi-to-epub
-Creates an EPUB 3 archive from an MDI-flavoured mdast tree. The adapter keeps
-support for profile metadata, chapter splitting, cover images, and unified
-integration; it does not parse MDI.
+Creates an EPUB 3 archive from an MDI-flavoured mdast tree. The compatibility
+entry point keeps unified integration, while profile validation, chapter
+splitting, cover packaging, XHTML, and ZIP generation all use the shared Rust
+implementation.
## Install
@@ -23,11 +24,11 @@ const archive = await mdiToEpub(mdastTree, {
For complete `.mdi` source, use `renderEpub(source)` from
[`@illusions-lab/mdi`](https://www.npmjs.com/package/@illusions-lab/mdi).
-The Rust-first CLI follows that route.
+The CLI follows that route as well.
## Documentation
- [Output model](https://mdi.illusions.app/ecosystem/outputs/)
-- [Export-profile guide](https://mdi.illusions.app/guides/export-profiles/)
+- [Export-profile guide](https://mdi.illusions.app/ecosystem/export-profiles/)
- [API reference](https://mdi.illusions.app/api/to-epub/)
- [JavaScript documentation](https://mdi.illusions.app/bindings/javascript/)
diff --git a/nodejs/packages/to-pdf/README.md b/nodejs/packages/to-pdf/README.md
index 56d5bba..5933e5d 100644
--- a/nodejs/packages/to-pdf/README.md
+++ b/nodejs/packages/to-pdf/README.md
@@ -48,13 +48,14 @@ for the shared CSS layout; page-number headers and footers remain host-specific.
MDI source → Rust parser + HTML renderer → Chromium layout → PDF bytes
```
-This package owns only the final print-layout step: page size, margins,
-writing mode, page numbers, and the browser process. It never receives MDI
-source and cannot make syntax or semantic-rendering decisions.
+Rust resolves the page size, margins, writing mode, type settings, and page
+number templates. This package owns the final host step: loading that prepared
+HTML in Chromium and requesting PDF bytes. It never receives MDI source and
+cannot make syntax, profile-validation, or semantic-rendering decisions.
## Documentation
- [Rendering and Chromium boundary](https://mdi.illusions.app/core/rendering/)
-- [Export-profile guide](https://mdi.illusions.app/guides/export-profiles/)
+- [Export-profile guide](https://mdi.illusions.app/ecosystem/export-profiles/)
- [API reference](https://mdi.illusions.app/api/to-pdf/)
- [JavaScript documentation](https://mdi.illusions.app/bindings/javascript/)