PandocでMarkdownをHTMLへ変換
ドキュメント変換ツール Pandoc は様々な入出力形式に対応したコマンドラインツールです。多くの派生版 Markdown への対応や Pandoc による拡張 Markdown などの機能があり、柔軟な Markdown 変換ツールとして興味深いツールです。また、LaTeX を使った PDF 出力機能もそのひとつです。
この投稿では Pandoc を使って Markdown テキストを HTML へ変換する方法を簡単に紹介しています。
使用環境:Windows7 64bit, pandoc 1.13.2, Firefox 37.0.2
Pandocをインストール
Windows 用をダウンロード
最新版 pandoc-1.13.2-windows.msi をインストール。
Pandocを使ってみる
インストールが完了したら手始めに Pandoc の動作確認を兼ねて、Markdown テキストを HTML に変換してみましょう。ここではファイルを使わずに標準入出力を使って試します。
- コマンドプロンプトで
pandoc
と入力 #Hello Pandoc
を入力Crtl + Z
キーを押して EOF を入力HTML に変換された結果が画面に表示されます。
>pandoc #Hello Pandoc ^Z <h1 id="hello-pandoc">Hello Pandoc</h1>
このように入出力ファイルを指定しない場合 Pandoc は入力に標準入力を、出力に標準出力を使用します。また、入出力フォーマットを明示しない場合は入力フォーマットに Markdown を、出力フォーマットに HTML が選ばれます。
実際の Markdown テキストを HTML へ変換する場合は入出力ファイルやフォーマット、テンプレートなどを指定して使います。
pandoc -f markdown input.md -t html5 -s -o output.html
コマンドラインの-s
オプションで標準テンプレートを指定すると、出力フォーマットに合わせた標準テンプレートが選択されます。この例の場合、適用されるのは HTML 出力用のhtml5
テンプレートです。
html5
テンプレートの内容は次のコマンドで確認できます。
pandoc -D html5
また css や MathJax の設定(後述)を含んだカスタムテンプレート(後述)を使用することも可能です。
MarkdownファイルをHTMLファイルへ変換する例
Pandoc では標準の Markdown のほか "GitHub flavored Markdown" もサポートされています。このフォーマットを使用する場合は入力フォーマットにmarkdown_github
を指定します。
markdown_github
オプションを使って HTML に変換pandoc -f markdown_github input.md -t html5 -s -o output.html
markdown_github
オプションで CSS を含んだ HTML に変換。pandoc -f markdown_github input.md -c github.css -t html5 -s -o output.html
この例ではカレントフォルダの
github.css
が HTML 内にリンクされます。このファイルは、はじめから Pandoc に同梱されているわけではないので自分で用意する必要があります。例えば"Github Markdown CSS - for Markdown Editor Preview"などを参考にして作成します。
「段落内の強制改行」を無効化する
Pandoc の拡張 Markdown では、フォーマット名と拡張文字列を使って機能をコントロールします。フォーマット名に拡張文字列を+
で結合するとその機能を有効に、-
で結合するとその機能が無効化されます。
この例では-hard_line_breaks
を結合して「段落内の強制改行」を無効化しています。
pandoc -f markdown_github-hard_line_breaks ^ input.md -c github.css -t html5 -s -o output.html
Markdownテキストに生HTMLを記述
標準の Markdown では、テキスト内に記述した生の HTML はそのまま出力されます。Pandoc でこの機能を有効にするには+raw_html
オプションを使います。これで Markdown テキスト内に生の HTML を記述できるようになります。
pandoc -f markdown_github+raw_html ^ input.md -c github.css -t html5 -s -o output.html
コードブロックの行番号表示とシンタックスハイライト
GFM の"Fenced code blocks"は、コードブロックを貼り付ける時のインデントが不要なのでとても便利です。
さらに Pandoc は行番号表示やシンタックスハイライトまでサポートしています。この機能を有効にするには+fenced_code_attributes
オプションを使います。
pandoc -f markdown_github+fenced_code_attributes ^ input.md -c github.css -t html5 -s -o output.html
例えば、言語に"Ruby"を指定、行番号を表示、開始行を 100 に指定する場合は、コードブロックの属性値をこのように記述します。開始行のstartFrom="100"
を省略すると 1 から始まります。
```{.ruby .numberLines startFrom="100"} puts 'Hello Pandoc!' ```
生成される HTML はこのようになります。.ruby
や.numberLines
の記述からわかるように、この値はクラスに設定されます。もちろん#foo
なら ID に設定されます。
- <table class="sourceCode ruby numberLines" startFrom="100">
- <tr class="sourceCode">
- <td class="lineNumbers">
- <pre>100</pre>
- </td>
- <td class="sourceCode">
- <pre>
- <code class="sourceCode ruby">
- puts <span class="st">'Hello Pandoc!'</span>
- </code>
- </pre>
- </td>
- </tr>
- </table>
コードブロックのシンプルな記述方法は言語名だけを記述します。この場合はシンタックスハイライトのみ行われます。
```ruby puts 'Hello Pandoc!' ```
生成される HTML では言語名がクラスに設定されています。
- <pre class="sourceCode ruby">
- <code class="sourceCode ruby">
- puts <span class="st">'Hello Pandoc!'</span>
- </code>
- </pre>
数式を含んだHTMLを出力する
HTML で数式を扱う場合 MathJax を使うと便利です。MathJax は、数式や LaTeX コマンドを解釈してブラウザで表示できるように変換してくれます。
Markdown の記述に GFM の書式を使い css を追加、MathJax で数式を表示する場合はこのようなコマンドを実行します。
pandoc -f markdown_github-hard_line_breaks ^ input.md -c github.css -t html5 -s ^ --mathjax=https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML ^ -o output.html
カスタムテンプレートを使う
カスタムテンプレートを作っておくと、あらかじめ MathJax のオプション設定や css ファイル等を追加することができます。カスタムテンプレートを簡単に作るには、標準のテンプレート雛形として利用します。
はじめに、次のコマンドを実行してカスタムテンプレートmytemplate.html
を作成します。
pandoc -D html5 > mytemplate.html
このテンプレートを使用する場合は、コマンドに--mytemplate=mytemplate.html
オプションを追加します。
pandoc -f markdown input.md -t html5 ^ --template=mytemplate.html -o output.html
カスタムテンプレートの例
次のコードをカスタムテンプレートの</head>
の直前に追加すれば、css ファイルと MathJax オプションの設定ができます。
この例ではカレントフォルダの github.css をリンクしています。また、MathJax のオプション設定では日本語の数式メニューやエラーメッセージの表示、数式のズーム設定。インライン数式のデリミタに$...$
を使用できるようにしています。
- <link rel="stylesheet" href="github.css">
- <script type="text/x-mathjax-config">
- MathJax.Hub.Config({
- menuSettings: {locale: "ja"},
- extensions: ["tex2jax.js", "MathMenu.js", "MathZoom.js"],
- TeX: {
- extensions: ["AMSmath.js", "AMSsymbols.js", "noErrors.js", "noUndefined.js"],
- noErrors: {disabled: true},
- noUndefined: {disabled: true}
- },
- jax: ["input/TeX", "output/HTML-CSS"],
- tex2jax: {
- inlineMath: [["$$","$$"], ["\\(","\\)"]],
- displayMath: [["$$$$","$$$$"], ["\\[","\\]"]],
- processEscapes: true
- }
- });
- </script>
- <script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" type="text/javascript"></script>
インライン数式のデリミタ$...$
について
インライン数式のデリミタは、他にもう一つの設定方法があります。それは、Pandoc の Markdown 拡張書式に、tex_math_dollars
を追加して--mathjax
オプションを指定します。この場合入力テキスト中の$...$
は\(...\)
に、$$...$$
は\[...\]
に変換して出力されます。
2つの設定方法の違い
MathJaxのオプションで設定する場合
テンプレートファイルに
tex2jax: { ... }
を記述してインライン数式の$...$
を有効にする場合は、単独の$
記号を表示するときにエスケープしなければなりません。例えばPrice: $100
を表示するにはPrice: \$100
と記述します。Pandocのオプションで設定する場合
Pandoc の Markdown 拡張書式
tex_math_dollars
の追加と--mathjax
オプションを指定する場合は、Pandoc が$
で囲まれた範囲を認識して、数式か数式でないかを判断してくれます。そのためPrice: $100
の表示では$
記号のエスケープが不要になります。
記述の容易さでは後者の Pandoc オプションで設定するほうが便利かもしれません。その場合は上記コードのtex2jax: { ... }
の部分を削除することで、デリミタ以外の MathJax オプションをテンプレートファイルで設定することができます。
後者の Pandoc オプションで設定する場合は次のようなコマンドを実行します。
pandoc -f markdown_github-hard_line_breaks+tex_math_dollars ^ input.md -t html5 --mathjax ^ --template=mytemplate.html ^ -o output.html
テンプレート内に MathJax スクリプトを記述している場合コマンドラインの--mathjax
オプションの指定は不要に思えますが、MathJax が受け取る TeX 形式で数式を出力するためにこのオプションが必要です。なぜなら Pandoc のデフォルトの数式出力では可能な限り Unicode 文字で数式を出力しようとします。
YAMLメタデータブロック
テンプレート内のプレースホルダ($
記号で囲まれた変数)を使うと、テンプレートにメタデータを流し込むことができます。Pandoc ではメタデータの流しこみ用にいくつかの方法が用意されていますが、 YAML メタデータブロックを使うと汎用性が高まります。
Pandoc のテンプレートに YAML メタデータブロックを流し込む場合はあらかじめメタデータブロックを別ファイルまたは Markdown テキストの中に作成しておきます。 YAML メタデータブロックは---
で始まり、---
または...
で終ります。
テキスト中に記述する場合の注意点は、YAML メタデータブロックの直前に空行を入れることです。ただし、テキストの先頭に記述する場合に限り、この空行は不要です。
別ファイルに作成した YAML メタデータブロックを使用する場合はコマンドラインでファイル名を指定します。例えば YAML メタデータブロックを記述したファイル
test.yaml
を指定する場合、次のようなコマンドを実行します。
pandoc -f markdown input.md test.yaml ^ -s -c github.css -t html5 ^ -o output.html
GFM の場合は+yaml_metadata_block
オプションを使います。
pandoc -f markdown_github+yaml_metadata_block ^ input.md test.yaml -s -c github.css -t html5 ^ -o output.html
複数の入力ファイルを指定した場合 Pandoc は、処理を開始する前にファイルとファイルの間に空行を追加してそれぞれのファイルを結合します。前述の YAML メタデータブロックの直前に空行を入れる理由はこのためです。
YAMLを使ったテンプレートの使用例
デフォルトテンプレートには、いくつかのプレースホルダが既に用意されています。実際に YAML メタデータの流し込みを試してみましょう。次の例では入力ファイルinput.md
のテキストの先頭に YAML メタデータブロックを記述しています。
input.md
--- pagetitle: YAMLテスト title: YAML メタデータテスト subtitle: サブタイトル author: - foo - bar date: May 5, 2015 --- #こんにちはPandoc
デフォルトテンプレートを使用するため-s
オプションでコマンドを実行します。
pandoc -f markdown input.md ^ -s -c github.css -t html5 -o output.html
GFM の場合は+yaml_metadata_block
オプションを使います。
pandoc -f markdown_github+yaml_metadata_block ^ input.md -s -c github.css -t html5 ^ -o output.html
実行すると YAML メタデータが流し込まれた HTML ファイルが出力されます。プレースホルダの記述方法は、テンプレートと出力されたHTMLファイルを見比べると分かりやすいです。テンプレートの内容はpandoc -D html5
で確認できます。
Pandoc変数
テンプレート内のプレースホルダは Pandoc 変数を使って制御することができます。それではテンプレートと出力されたHTMLファイルを見比べてみましょう。
条件分岐
例えば変数$subtitle$
では条件分岐が使われています。
この場合、変数$subtitle$
に値がセットされていれば
<h1 class="subtitle">$subtitle$</h1>
を適用します。
$if(subtitle)$ <h1 class="subtitle">$subtitle$</h1> $endif$
出力結果を見ると、このようになっています。
<h1 class="subtitle">これはサブタイトル</h1>
同様に変数$title$
では、変数の値がセットされていない場合
<header>...</header>
の行全体が適用されません。
$if(title)$ <header> <h1 class="title">$title$</h1> $if(subtitle)$ <h1 class="subtitle">$subtitle$</h1> $endif$ $for(author)$ <h2 class="author">$author$</h2> $endfor$ $if(date)$ <h3 class="date">$date$</h3> $endif$ </header> $endif$
繰り返し
例えば変数$author$
ではリスト形式で複数の値がセットされています。このように変数の値が複数ある場合は$for$
キーワードを使って値を取得できます。
この場合変数$author$
に値がセットされていれば、
値毎に<h2 class="author">$author$</h2>
を適用します。
$for(author)$ <h2 class="author">$author$</h2> $endfor$
出力結果を見ると、このようになっています。
<h2 class="author">foo</h2> <h2 class="author">bar</h2>
このようにテンプレートと YAML メタデータブロックを使えば、柔軟なメタデータの流しこみが可能になります。
まとめ
簡単ですが Pandoc の HTML 変換を紹介しました。Pandoc には他にも多くの機能があります、興味を持った方はユーザーズガイドを読んでみてください。
本家ユーザーズガイド
日本語ユーザーズガイド
参考までに、今回使用したオプションを全てコマンドラインに適用すると こうなります。
pandoc -f markdown_github-hard_line_breaks^ +raw_html+fenced_code_attributes^ +tex_math_dollars+yaml_metadata_block ^ input.md -t html5 ^ --mathjax --template=mytemplate.html ^ -o output.html
付録
この投稿で使用したカスタムテンプレートと css を載せておきます。
mytemplate.html
<!DOCTYPE html> | |
<html$if(lang)$ lang="$lang$"$endif$> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="generator" content="pandoc"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> | |
$for(author-meta)$ | |
<meta name="author" content="$author-meta$"> | |
$endfor$ | |
$if(date-meta)$ | |
<meta name="dcterms.date" content="$date-meta$"> | |
$endif$ | |
<title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title> | |
<!--[if lt IE 9]> | |
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> | |
<![endif]--> | |
$if(quotes)$ | |
<style type="text/css">q { quotes: "“" "”" "‘" "’"; }</style> | |
$endif$ | |
$if(highlighting-css)$ | |
<style type="text/css"> | |
$highlighting-css$ | |
</style> | |
$endif$ | |
$for(css)$ | |
<link rel="stylesheet" href="$css$"> | |
$endfor$ | |
$if(math)$ | |
$math$ | |
$endif$ | |
$for(header-includes)$ | |
$header-includes$ | |
$endfor$ | |
<link rel="stylesheet" href="github.css"> | |
<script type="text/x-mathjax-config"> | |
MathJax.Hub.Config({ | |
menuSettings: {locale: "ja"}, | |
extensions: ["tex2jax.js", "MathMenu.js", "MathZoom.js"], | |
TeX: { | |
extensions: ["AMSmath.js", "AMSsymbols.js", "noErrors.js", "noUndefined.js"], | |
noErrors: {disabled: true}, | |
noUndefined: {disabled: true} | |
}, | |
jax: ["input/TeX", "output/HTML-CSS"], | |
}); | |
</script> | |
<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" type="text/javascript"></script> | |
</head> | |
<body> | |
$for(include-before)$ | |
$include-before$ | |
$endfor$ | |
$if(title)$ | |
<header> | |
<h1 class="title">$title$</h1> | |
$if(subtitle)$ | |
<h1 class="subtitle">$subtitle$</h1> | |
$endif$ | |
$for(author)$ | |
<h2 class="author">$author$</h2> | |
$endfor$ | |
$if(date)$ | |
<h3 class="date">$date$</h3> | |
$endif$ | |
</header> | |
$endif$ | |
$if(toc)$ | |
<nav id="$idprefix$TOC"> | |
$toc$ | |
</nav> | |
$endif$ | |
$body$ | |
$for(include-after)$ | |
$include-after$ | |
$endfor$ | |
</body> | |
</html> |
github.css
body { | |
font-family: "Source Han Sans JP","メイリオ",Consolas,verdana,"DejaVu Sans Mono", Helvetica, arial, sans-serif; | |
font-size: 0.9em; | |
line-height: 1.4em; | |
background-color: rgb(251, 251, 251); | |
border: 1px solid rgb(200, 200, 200); | |
margin: 15px; | |
padding: 15px; | |
} | |
h1, h2, h3, h4, h5, h6 { | |
line-height: 1em; | |
font-weight: bold; | |
color: rgb(20, 20, 20); | |
margin: 2em 0em 1em 0em; | |
} | |
h1, h2, h3 { | |
border-bottom:1px solid rgb(190, 190, 190); | |
} | |
h1 { font-size: 2.5em; } | |
h2 { font-size: 2.0em; } | |
h3 { font-size: 1.5em; } | |
h4 { font-size: 1.2em; } | |
h5 { font-size: 1.0em; } | |
h6 { font-size: 0.9em; } | |
hr { | |
border: 1px solid; | |
color: #cccccc; | |
height: 1px; | |
padding: 0; | |
} | |
ul, ol { | |
margin-left: 0.5em; | |
padding-left: 1.5em; | |
} | |
blockquote { | |
border-left: 4px solid #dddddd; | |
line-height: 1.5em; | |
color: #777777; | |
padding-left: 0.8em; | |
margin-left: 2.5em; | |
} | |
table { | |
padding: 0; | |
border-spacing: 0px; | |
} | |
table tr { | |
border-top: 1px solid #cccccc; | |
background-color: white; | |
margin: 0; | |
padding: 0; | |
} | |
table tr:nth-child(2n) { | |
background-color: #f8f8f8; | |
} | |
table tr th { | |
font-weight: bold; | |
border: 1px solid #cccccc; | |
text-align: left; | |
margin: 0; | |
padding: 0.1em 1em; | |
background-color: #f8f8f8; | |
} | |
table tr td { | |
border: 1px solid #cccccc; | |
text-align: left; | |
margin: 0; | |
padding: 0.1em 1em; | |
} | |
img { | |
max-width: 100%; | |
} | |
pre { | |
border-radius: 3px; | |
border: 1px solid rgb(180, 180, 180); | |
background-color: rgb(240, 240, 240); | |
font-size: 1em; | |
line-height: 1.4em; | |
overflow: auto; | |
display: block; | |
padding: 5px; | |
margin: 10px; | |
} | |
code { | |
border-radius: 3px; | |
border: 1px solid rgb(180, 180, 180); | |
background-color: rgb(240, 240, 240); | |
margin: 0px 2px; | |
padding: 0px 5px; | |
color: rgb(10, 10, 10); | |
font-size: 1em; | |
line-height: 1.4em; | |
display: inline-block; | |
} | |
pre code { | |
border-radius: 0px; | |
border: none; | |
display: block; | |
margin: 0px 0px 0px 0px; | |
padding: 0px 5px 0px 5px; | |
font-family: Consolas, "DejaVu Sans Mono", "メイリオ", verdana, monospace; | |
font-size: 1em; | |
line-height: 1.4em; | |
white-space: pre-wrap; | |
word-wrap: normal; | |
overflow: auto; | |
} | |
td.lineNumbers { | |
border: none; | |
padding: 0px; | |
width: 50px; | |
} | |
td.lineNumbers pre { | |
line-height: 1.4em; | |
border: none; | |
margin: 0px; | |
background: transparent; | |
} | |
table.sourceCode, table.sourceCode tr { | |
background-color: transparent; | |
} | |
td.sourceCode { | |
padding-left: 0px; | |
} |
0 件のコメント :
コメントを投稿