Markdown の HTML 変換 - Ruby, PHP, Groovy, Scala, Node.js

Markdown 形式の文字列を HTML 変換する処理を複数のプログラム言語で試してみました。

処理としては、標準入力から UTF-8 の Markdown 形式の文字列を取得し HTML 変換した結果を標準出力へ UTF-8 で出力しています。

ちなみに、Markdown 文字列は LOGGiX プロジェクトの日本語版サンプル markdown-sample.text を使用しました。

サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20120809/

Ruby の場合

Pure Ruby な kramdown モジュールを JRuby で実行してみました。

tohtml.rb
require 'kramdown'

Encoding.default_external = 'utf-8'

puts Kramdown::Document.new($stdin.read).to_html
実行例
 jruby tohtml.rb < markdown-sample.text > ruby_result.txt
結果

特に問題なく変換されました。

更に kramdown は以下のような表組みにも対応していました。

プログラム言語 | モジュール名
---------------|-------------
PHP            |   Markdown
Ruby           |   kramdown
Groovy         |   MarkdownJ
Scala          |   knockoff
Node.js        |   markdown

HTML 変換結果は以下の通りです。

<table>
  <thead>
    <tr>
      <th>プログラム言語</th>
      <th>モジュール名</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>PHP</td>
      <td>Markdown</td>
    </tr>
    ・・・
  </tbody>
</table>

PHP の場合

Markdown Extra をダウンロードし、markdown.php をカレントディレクトリに配置して、実行しました。

tohtml.php
<?php
include_once "markdown.php";

$mkStr = stream_get_contents(STDIN);

echo Markdown($mkStr);
?>
実行例
 php tohtml.php < markdown-sample.text > php_result.txt
結果

kramdown と同様、特に問題なく変換され表組みにも対応していました。
Extra の付いていない Markdown 1.0.1o の方は表組みに対応していないのでご注意ください。

Groovy の場合

Java 用のモジュール MarkdownJ を Groovy で使いました。

tohtml.groovy
@Grab('com.madgag:markdownj-core:0.4.1')
import com.petebevin.markdown.*

def mk = new MarkdownProcessor()
def enc = 'UTF-8'

System.out.withWriter enc, {w ->
    w.print mk.markdown(System.in.getText(enc))
}
実行例
 groovy tohtml.groovy < markdown-sample.text > groovy_result.txt
結果

以下のような問題が発生しました。

  • 画像の「リファレンス」スタイル箇所が a タグになった *1
・・・
<p>
「リファレンス」スタイル: !
<a title="Loggix" href="../../../theme/images/loggix-logo.png">Loggix</a>
</p>

Scala の場合

Scala は 2種類のモジュールを試してみました。Scala 2.9.2 用のモジュールが見当たらなかったため、2.9.1 用のモジュールを 2.9.2 でスクリプト実行しました。

tohtml.scala (Knockoff 版)
import scala.io._
import com.tristanhunt.knockoff.DefaultDiscounter._

val mkStr = new BufferedSource(System.in)(Codec.UTF8).mkString

val ps = new java.io.PrintStream(System.out, false, "UTF-8")

ps.println(toXHTML(knockoff(mkStr)))
実行例1 (Knockoff 版)
 scala -nc -cp knockoff_2.9.1-0.8.0-16.jar tohtml.scala < markdown-sample.text > scala_result.txt

ここで -nc オプションを指定している点にご注意ください。-nc オプションを付けないとコンパイラのプロセスがリダイレクト先のファイルを開いたままになり不都合が生じます。

結果

以下のような問題が発生しました。

  • '=' と '-' の見出しが正しく変換されなかった *2
  • 画像の「リファレンス」スタイル箇所が a タグになった
<p>Markdown Sample
=======================
</p><p>サンプルドキュメント日本語版
-----------------------
</p>
tohtml2.scala (Actuarius 版)
import scala.io._
import eu.henkelmann.actuarius.ActuariusApp

val mkStr = new BufferedSource(System.in)(Codec.UTF8).mkString

val ps = new java.io.PrintStream(System.out, false, "UTF-8")

ps.println(ActuariusApp(mkStr))
実行例2 (Actuarius 版)
 scala -nc -cp actuarius_2.9.1-0.2.3.jar tohtml2.scala < markdown-sample.text > scala_result2.txt
結果2 (Actuarius 版)

Knockoff とは別の問題が発生しました。

  • '+' と '-' のリストが正しく変換されなかった
<p>(プラス記号で記述)</p>
<p>+   モツァレラチーズ
+   パスタ
+   ワイン</p>
<p>(ハイフン(マイナス記号)で記述)</p>
<p>-   モツァレラチーズ
-   パスタ
-   ワイン</p>

なお、Scala IO を使うと標準入出力まわりの処理が以下のようになります。

ToHtml.scala
package fits.sample

import scalax.io.JavaConverters._
import com.tristanhunt.knockoff.DefaultDiscounter._

object ToHtml extends App {
    val mkStr = System.in.asUnmanagedInput.slurpString

    val html = toXHTML(knockoff(mkStr)).mkString

    System.out.asUnmanagedOutput.write(html)
}

Node.js (CoffeeScript) の場合

Markdown-js を npm install markdown でインストールし、CoffeeScript で実行しました。

tohtml.coffee
mk = require 'markdown'

process.stdin.resume()

process.stdin.on 'data', (data) ->
    console.log mk.markdown.toHTML data.toString()
実行例
 coffee tohtml.coffee < markdown-sample.text > coffee_result.txt
結果

以下のような問題が発生しました。

  • '=' と '-' の見出しが正しく変換されなかった
  • '<' '>' '&' などの不要なエスケープが実施されてしまった
<pre><code>&lt;blockquote&gt;</code></pre>

<p>
        &lt;p&gt;For example.&lt;/p&gt;
    &lt;/blockquote&gt;
</p>

<h3>HTMLとの共存</h3>
・・・
<p>例:April 1&lt;sup&gt;st&lt;/sup&gt;

まとめ

今回の結果をまとめると以下のようになります。

言語 モジュール 見出し = - リファレンススタイル(画像) リスト + - エスケープ 表組み
Ruby kramdown 0.13.7
PHP Markdown Extra 1.2.5
Groovy MarkdownJ Core 0.4.1 × ×
Scala Knockoff 0.8.0 × × ×
Scala Actuarius 0.2.3 × ×
Node.js (CoffeeScript) Markdown-js 0.4.0 × × ×

*1:img タグになるのが正しい

*2:それぞれ h1 と h2 タグに変換されるのが正しい