和文入力に助かるプラグイン「markdown-it-cjk-breaks」

作成

Markdown で和文文章を改行したときに起こる問題

(執筆現在、)このサイトではマークダウンで記述したものを markdown-it で HTML に変換して表示しています。

レンダラーにも依ると思いますが、マークダウンで和文を入力するとき、文の途中で改行をすると、その箇所に空白が挿入されてしまいます。これは、変換後の HTML においても改行が維持されているためです。

たとえば、次のようなマークダウンを考えます。(引用元:「宮沢賢治ポラーノの広場」。)

あのイーハトーヴォのすきとおった風、
夏でも底に冷たさをもつ青いそら、うつくしい
森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。

これを markdown-it で変換すると次の HTML が得られ、 2箇所の改行がそのまま残っているのが分かります。

<p>あのイーハトーヴォのすきとおった風、
夏でも底に冷たさをもつ青いそら、うつくしい
森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。</p>

ブラウザで表示すると、改行が空白スペースとして扱われ、「あのイーハトーヴォのすきとおった風、_夏でも底に冷たさをもつ青いそら、うつくしい_森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。」という不要な空白(_で表示)を含んだ結果となってしまいます。英語などの改行箇所が単語の区切りである言語の文章であれば、問題はありません。

この問題を解決するのが、プラグイン markdown-it-cjk-breaks です。

プラグイン「markdown-it-cjk-breaks」

GitHub のレポジトリは「markdown-it/markdown-it-cjk-breaks」です。

インストール

npm install --save markdown-it-cjk-breaks

動作例

var md = require('markdown-it')()
    .use(require('markdown-it-cjk-breaks'))

md.render(`
あのイーハトーヴォのすきとおった風、
夏でも底に冷たさをもつ青いそら、うつくしい
森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。
Lorem ipsum dolor sit amet, consectetur adipiscing
elit, sed do eiusmodtempor incididunt ut labore et
dolore magna aliqua.
`)

この例は以下の HTML を出力します。 Lorem ipsum 部分の改行はそのままに、和文の途中のの改行が削除されていることがわかります。

<p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。
Lorem ipsum dolor sit amet, consectetur adipiscing
elit, sed do eiusmodtempor incididunt ut labore et
dolore magna aliqua.</p>

プラグインの /README.md に拠ると、動作のアルゴリズムは W3C Working Draft の「CSS Text Module Level 3」の「4.1.2. Segment Break Transformation Rules」に準拠しているとのことです。

Segment Break Transformation Rules

当該箇所へのリンクは「4.1.2. Segment Break Transformation Rules」です。もし、この仕様が実装されたならば、 markdown-it での変換で改行がそのままになっていても問題ないわけですが、長い間ずっと実装されないままでいるようです。

markdown-it-cjk-breaks/README.md で引用されているのは、 2017年8月22日のバージョンでした。以下に引用します。

  • If the character immediately before or immediately after the segment break is the zero-width space character (U+200B), then the break is removed, leaving behind the zero-width space.
  • Otherwise, if the East Asian Width property [UAX11] of both the character before and after the segment break is F, W, or H (not A), and neither side is Hangul, then the segment break is removed.
  • Otherwise, the segment break is converted to a space (U+0020).

4.1.2. Segment Break Transformation Rules, CSS Text Module Level 3

これを翻訳する代わりに、執筆現在の最新版である2018年12月12日のバージョンの引用と拙訳を書いておきます。

  • If the character immediately before or immediately after the segment break is the zero-width space character (U+200B), then the break is removed, leaving behind the zero-width space.
  • Otherwise, if the East Asian Width property [UAX11] of both the character before and after the segment break is F, W, or H (not A), and neither side is Hangul, then the segment break is removed.
  • Otherwise, if the writing system of the segment break is Chinese, Japanese, or Yi, and the character before or after the segment break is punctuation or a symbol (Unicode general category P* or S*) and has an East Asian Width property of A, and the character on the other side of the segment break is F, W, or H, and not Hangul, then the segment break is removed.
  • Otherwise, the segment break is converted to a space (U+0020).

4.1.2. Segment Break Transformation Rules, CSS Text Module Level 3

以下が拙訳(大意)です。

  • 直前の文字または直後の文字がゼロ幅スペースであったとき、

そのゼロ幅スペースは保持したまま、そのセグメントブレークは削除される。

  • 上記以外で次を満たすとき、そのセグメントブレークは削除される。
    • 直前と直後の文字の両方の East Asian Width Property が F(全角)、 W(広)、H(半角)のいずれか(A(曖昧)でない)である。
    • かつ、どちらの文字もハングルでない。
  • 上記以外で次を満たすとき、そのセグメントブレークは削除される。
    • 記述言語(HTML の lang での指定など)が中国語、日本語、イ語のいずれかである。
    • かつ、前後の文字の一方が句読点または記号で East Asian Width Property が A(曖昧)である。
    • かつ、他方の East Asian Width Property が F(全角)、 W(広)、H(半角)のいずれかある。
  • 上記以外のとき、セグメントブレークは半角スペースに変換される。

4.1.2. Segment Break Transformation Rules, CSS Text Module Level 3