コンテンツにスキップ

MDI とは?

前提知識: なし。このページは MDI の知識がないことを前提にしていますが、通常の Markdown(# 見出し、*強調*[リンク](url))は既に知っているものとします。

MDI(illusion Markdown、拡張子 .mdi)は、日本語組版のための拡張構文をいくつか加えた、ふつうの Markdown です。ルビ(振り仮名)、縦中横(縦書き中の数字を横向きに組む)、傍点(圏点による強調)、割注(行内の二行組注記)、明示的な改行、改ページ、ブロック指定、縦書きなどを追加します。

通常の Markdown 文書はすべて、そのまま有効な MDI 文書でもあります。MDI は構文を追加するだけで、CommonMark や GFM の何かを取り除いたり意味を変えたりしません。.mdi ファイルは通常の UTF-8 テキストファイルであり、バイト列としては .md ファイルと何ら変わりません。拡張子は単なる慣習であり、パーサーがそれを検査するわけではありません。

---
mdi: "2.0"
title: 雪女
lang: ja
writing-mode: vertical
---
# 第一章
{雪女|ゆきおんな}が現れたのは、第^12^話のことだった。
[[em:決して]]忘れない、と彼は思った。

この例からフロントマターと {...}^...^/`` の記法を取り除けば、ごく普通の Markdown 文書になります。これが MDI の設計そのものです。MDI は Markdown に、フォールバックする(一致しない場合は無視されるかリテラルになる)組版拡張を足したものです。

日本の小説、特にライトノベルやウェブ小説は、CommonMark にも GFM にも Pandoc Markdown にも記法が存在しない組版上の工夫に依存しています。

  • 漢字だけでは曖昧・不慣れなため、読み仮名を文字の上や脇に添える必要がある(ルビ)―― {東京|とうきょう}
  • 縦書きの中で、数個の算用数字を1文字ずつ縦に積むのではなく横向きに直立させたい(縦中横)―― 第^12^話
  • 日本語の組版はイタリックで強調しないため、文字の脇に圏点を打って強調したい ―― `それ`。
  • 脚注ではなく、行の中に二行組の注記を挟みたい ―― `注記`。
  • 文書全体を横書きではなく縦書き・右から左(縦書き)で組みたい。

既存のツールはこれらを場当たり的に解決してきました。一部の投稿プラットフォーム(カクヨム、なろう)は独自の括弧記法を発明し、一部のエディタは共有された文法のない独自の Markdown 拡張を後付けしています。MDI が提供するのは、単一の仕様SYNTAX.md)と単一の実行可能な実装mdi-core Rust クレート)です。これにより、同じ .mdi ファイルはどのツールで開いても ―― エディタでも、CLI でも、Web アプリでも、出版パイプラインでも ―― 同じように解析されます。

通常の Markdown(CommonMark/GFM)MDI
見出し・リスト・リンク・表・コードフェンス・強調ありあり、変更なし
フロントマター(--- ... ---CommonMark の一部ではなく、ツール依存あり。MDI 固有のキー(writing-modepage-progression)を追加
ルビ・縦中横・傍点・割注・字間調整記法なしあり
明示的な改行 vs 段落改行末尾2スペースの壊れやすいハードブレークのみ[[br]](曖昧さなし) vs 空行
縦書き概念がないフロントマターの属性。複数の構文の描画方法に影響するため
どの構文が有効かを決めるのは誰か使用しているライブラリ次第単一の仕様(SYNTAX.md)、単一の実装(mdi-core

MDI 固有の構文を含まない .md ファイルと .mdi ファイルは、まったく同じように描画されます。違いが現れるのは MDI 固有の記法を使ったときだけです ―― しかもそのときでさえ、無効・曖昧な MDI 記法は文書を壊さずリテラルテキストへフォールバックするよう設計されています(各構文ページの「よくある間違い」節に正確なフォールバック規則があります)。

ソースから出力までの全体パイプライン

Section titled “ソースから出力までの全体パイプライン”

言語に関わらず、すべての MDI ツールが従う唯一の流れです。

.mdi ソース(UTF-8 テキスト)
mdi-core(Rust)── CommonMark + GFM + フロントマター + MDI を一度に解析
バージョン付きドキュメント IR ── タグ付きの木構造。すべてのノードが UTF-8 バイト span を持つ
├──▶ renderHtml() → HTML 文字列
├──▶ renderText(format) → TXT / なろう / カクヨム / 青空文庫 文字列
├──▶ renderEpub() → EPUB 3 バイト列
├──▶ renderDocx() → DOCX バイト列
└──▶ renderHtml() + Chromium printToPDF → PDF バイト列

2点、注目してください。

  1. 解析は Rust で一度だけ行われます。 どのレンダラーも元のテキストを読み直したり、ルビの範囲をどこから始めるか再判定したりしません。すべてのレンダラーは mdi-core が既に構築した同じ木を消費します。
  2. PDF は「HTML+印刷ステップ」です。 Rust はブラウザに渡すのと同じ HTML/CSS を描画し、ローカルにインストールされた Chromium 系ブラウザにそのレイアウトと printToPDF の呼び出しを依頼します。Chromium は .mdi ソースを読むことも、構文上の判断をすることも一切ありません ―― 渡された HTML をレイアウトするだけです。Chromium の責務がどこで始まりどこで終わるかは レンダリングモデルと Chromium/PDF の境界 を参照してください。

以下は実際に今日動く @illusions-lab/mdi の JavaScript API です。

import { parse, renderHtml } from "@illusions-lab/mdi";
const source = "{雪女|ゆきおんな}が第^12^話に現れた。";
const { document, diagnostics } = parse(source);
console.log(diagnostics); // [] ―― このソースには問題なし
console.log(document.children[0]); // 解析済みの段落ノード。MDI のルビ/縦中横を含む
console.log(renderHtml(source));
<!DOCTYPE html><html lang="ja"><head><meta charset="utf-8"><style>/* Rust がマークアップと一緒に出力する .mdi-* スタイルシート */</style></head><body>
<p><ruby class="mdi-ruby">雪女<rp>(</rp><rt>ゆきおんな</rt><rp>)</rp></ruby>が第<span class="mdi-tcy">12</span>話に現れた。</p>
</body></html>

自分で CLI と JavaScript パッケージをインストールして実行する手順は はじめに にあります。

  • ルビ・縦書き・圏点強調が必要な日本語の小説(ライトノベル、ウェブ小説、脚本)をプレーンテキストで、バージョン管理しやすいソース形式で書きたいとき。
  • 1つのソースファイルから、HTML(Web リーダー向け)、EPUB(電子書籍リーダー向け)、DOCX(出版社向け)、プレーンテキスト(なろうやカクヨムなど特定プラットフォームへの投稿用)を、各出力を手作業で編集せずに得たいとき。
  • 同じソースがエディタでも、ビルドパイプラインでも、CLI でも同じように解析される保証がほしいとき ―― 3つの異なる再実装ではなく、3つとも同じ Rust コードを呼び出しているからです。
  • ワープロや WYSIWYG 形式ではありません。 MDI はソース記法であり、DOCX/PDF 出力はその描画結果であって、逆ではありません。
  • 汎用の組版言語ではありません。 SYNTAX.md に列挙された、名前の付いた日本語組版の仕組みをカバーするだけで、構文自体に「任意の CSS プロパティ」という汎用エスケープハッチはありません。
  • 対応プラットフォームで利用できます。 Rust、Node.js、Swift、Kotlin、Python はすべて同じ Rust-authoritative grammar と Document IR を利用します。各プラットフォームは Bindings を参照してください。
  • コア概念 ―― IR・診断・span・capabilities など、他のすべてのページで使う語彙。
  • はじめに ―― CLI と JavaScript パッケージをインストールし、最初の変換を実行します。
  • 完全構文リファレンス ―― すべての MDI 構文を1つずつ解説します。