TL;DR
$\LaTeX$ でレポート書くの面倒くさい!
↓
markdownで書いてPandocで$\LaTeX$経由でPDFにすれば $\LaTeX$ の数式も組み込めて神では?
↓
ソースコード含めたいけどPandocの装飾クソダサイな?!
↓
minted使えばきれいにソースコード出力できるんじゃね?
↓
え、ちょっと待って、設定ダルすぎません...?
↓
フィルター書いてああしてこうして...
はじめに
これを書いてる人は典型的な情報系大学生なので、 $\LaTeX$ + ソースコードなんてレポートを書くこともあるのです(実際はないですけど)。でも $\LaTeX$ だらけのレポートは読みにくさの塊で、正直毎回簡単な記述ですら $\LaTeX$ で書くというのは避けたいものです。
そんなあなたにPandoc。しかしPandocの出力するソースコード部分のハイライトはQiitaの記事と比べると圧倒的に醜く、そのままにしておくのは許せないものがありました。
そこでPandocのほうの設定を色々いじり、ソースコードをいい感じにしてくれるmintedを導入したのですが、かなり苦戦したのでその記録です。
環境
- Ubuntu 18.04.3 LTS (Bionic Beaver)
- TeXlive はなんだかんだで結局フルインストールしました。 古いパッケージの更新に苦戦するので
apt
でのインストールは非推奨です。install-tl
を使用しましょう。
TeX Live を Ubuntu に(APT を使わずに)導入する — しっぽのさきっちょ | text.Baldanders.info
Pandocの導入
Pandocはある形式の文書を別な形式の文書に変えてくれるパワフルなツールです。今回はmarkdownを $\LaTeX$ に変換するために使います。(PDFではないんです)
Pandocは普通に apt
で入れましょう。
$ sudo apt install pandoc
...
$ pandoc --version
pandoc 1.19.2.4
...
ここで躓くことはないかと思います。
ちなみにこれからたくさんPandocについて触れていくことになるのですが、親切丁寧なドキュメントがPandocにはあります。悩んだら読みましょう。
Pandoc ユーザーズガイド 日本語版 - Japanese Pandoc User's Association
mintedの導入
mintedを使用するためにはPython3とパッケージ pygments
が必要です。次のページがとても参考になりました。
mintedでLaTeXドキュメントにソースコードを載せる - Qiita
以降、mintedの設定もこの記事様に従っていきます。ただ、 pip3
でインストールすると必要なコマンド pygmentize
がないと言われるので、 sudo pip3
でインストールしましょう1
さて、悪魔融合です...
Pandocとmintedは実はちょっと相性が悪いのです。というのも、mintedは中間ディレクトリに移動されたりすると死んでしまう一方、Pandocは容赦なく中間ディレクトリを生成し移動するためです。
このことに注意しつつ、自前でシェルスクリプトを用意する必要が出てきそうです。
また、mintedを差し込むために Pandocフィルター なるものをこれから作成していきます。
Pandocフィルターって何さ
Pandocって変換前に一度構文解析的なことをしているそうなんですね...で、そこにフィルターを挟むとJSONファイルを介してちょこちょこっと内容をいじることができます。次のページが詳しかったです。
例えば今僕はこの記事を pandoc_article.md
という名前にして書いてるんですけど(どうでもいい)、それを次のコマンドで出力して
$ pandoc pandoc_article.md -f markdown -t json > p.json
一部抜粋するとこんなふうになってます。
{
"t": "CodeBlock",
"c": [
[
"",
[
"bash:bash"
],
[]
],
"$ pandoc -s pandoc_article.md -f markdown -t json > p.json"
]
},
"t": "CodeBlock",
は解析の結果これがコードブロックであることを示しています。
"bash:bash"
というのはQiitaの記事を書いたことがある人ならわかるかもしれませんが、
` ` `bash:bash
$ pandoc -s pandoc_article.md -f markdown -t json > p.json
` ` `
というコードブロックのシンタックスハイライト用の言語と左上につく表示名です。Pandoc的にはまとめて言語名として扱われているようです。
"c"
の持つ2つめの要素に実際のコードが含まれていることがわかります。
で、このJSONをフィルターでいじるといい感じになるわけです。
Python3でいじりたいので、 pip3
で pandocfilters
パッケージを取ってきましょう。
$ pip3 install pandocfilters
では早速フィルターを書いていきましょう。
実は先に挙げた pandoc-minted · PyPI はこれから書こうとしてるフィルターと似たものなのですが、こちらではminted環境しか導入しておらずいい感じの表現になってくれないです。
自前で用意したほうがその後色々変更できて便利でしょうからがんばりましょう。
# !/usr/bin/python3
import sys
assert sys.version_info[0] == 3
from pandocfilters import toJSONFilter, RawBlock
import re
def minted_filter(key, value, frmat, meta):
""" Modifing codeblock
Args:
key => type of pandoc object. In json its key is "t"
value => contents of pandoc object. In json its key is "c"
frmat => target output format
meta => document metadata
"""
# if frmat != "latex" or key != "CodeBlock":
if key != "CodeBlock":
return
[[_, classes, _], contents] = value
if len(classes) > 0:
splt_cls = classes[0].split(":")
title = splt_cls[1] if len(splt_cls) >= 2 else ""
lang = splt_cls[0]
else:
title = ""
lang = "text"
title = re.sub(r"_", "\\_", title)
lang = re.sub(r"_", "\\_", lang)
replaced = """\
\\begin{{spacing}}{{1.0}} {title}\n\
\\begin{{mdframed}}\n\
\\begin{{minted}}[breaklines, linenos]{{{lang}}}\n\
{contents}\n\
\\end{{minted}}\n\
\\end{{mdframed}}\n\
\\end{{spacing}}""".format(title=title, lang=lang, contents=contents)
return [RawBlock(frmat, replaced)]
if __name__ == "__main__":
toJSONFilter(minted_filter)
pandoc-minted · PyPI をもとに見様見真似で書きました。装飾を増やし、Qiita用に若干アレンジを加え、 "Code"
に関しては置換しない設定としました。
また markdown -> latex
以外の変換をする方は frmat
による条件分岐に気をつけてください。。。まぁこのフィルターを markdown -> latex
以外で使うこともないでしょうし、デバッグの容易さより自分は条件分岐していません。
最後に、フィルターに実行権限を与えるのを忘れないでください。(忘れるとShebangが無視されてしまいPython2が実行される可能性があります。)
$ chmod u+x minted_filter.py
フィルターは次のように使います。
$ pandoc --filter minted_filter.py pandoc_article.md -f markdown -t json > q.json
2020/7/30 追記: 面倒を避けるため minted_filter.py
は予め $PATH
変数の通った場所に置いておいてください。
$HOME/bin/
以下が一般的です。詳しくはググって()
$ mkdir -p ~/bin
$ mv minted_filter.py ~/bin/
フィルターを挟むと先程の出力が変わっていることが確認できます。2
{
"t": "RawBlock",
"c": [
"json",
"\\begin{spacing}{1.0} bash\n\\begin{mdframed}\n\\begin{minted}[breaklines, linenos]{bash}\n$ pandoc -s pandoc_article.md -f markdown -t json > p.json\n\\ end{minted}\n\\ end{mdframed}\n\\ end{spacing}"
]
},
メタデータの付加
勘の鋭い読者(もとい $\LaTeX$ が書ける読者)ならこのままだとある問題に気づくでしょう。
minted環境導入してなくないか?!?!
仰るとおりです。その他にも色々と入れてないですね。このまま行くとコンパイルに掛ければもちろんエラーになります。
そこで、Markdownファイルの方にちょこちょこっと細工をしましょう3。Markdownの最後に次のテキストを挿入してください。
---
urlcolor: cyan
header-includes:
- \usepackage{minted}
- \usepackage{setspace}
- \usepackage{mdframed}
---
注意点として、markdown本文との間に空白行を入れておいてください。
これはPandoc用のメタデータで、Pandocで変換する際に様々な注文をつけることができます。メタデータはJSONからも最後のほうで確認できます。
今回はリンクの色を水色にする指定と、 $\LaTeX$ で変換する際に必要な各種パッケージをプリアンブル部に含めてもらうようにする指定を加えました。
以降、順番が前後して申し訳ありませんが、このメタデータを付加したファイルがPandocで変換されていたものとします。(もちろん毎回この処理を行うのは面倒ですから、後ほど自動化します。)
LaTeXファイルのコンパイル
フィルターを通してできたJSONファイルから $\LaTeX$ ファイルを作成し、いよいよ文書をPDF化します。
Pandocの出力する $\LaTeX$ は少々特殊で、うまいことlualatexを使用する必要があります。次の記事がとても参考になりました。
メモ: Pandoc+LaTeXで気軽に日本語PDFを出力する - Qiita
先程作成した q.json
をまたPandocで今度は $\LaTeX$ に変換します。なぜ一度にPDFに変換しないかは悪魔融合の冒頭で話したとおり、一時ディレクトリを作成しないようにするためです。
ここで記事の注意どおり変数を指定するオプションをつけましょう。
$ pandoc -s q.json -o pandoc_article.tex -f json -t latex -V documentclass=bxjsarticle -V classoption=pandoc
ここで注意してほしいのは -s
( --standalone
) オプションです。これは出力されるtexファイルを独立にコンパイル可能にするものです。ないとプリアンブル部が出力されずエラーになります。
最後にlualatexでコンパイルして完成です。ここで最後の注意ですが、mintedを使用するために -shell-escape
オプションを使用する必要があります。
$ lualatex -shell-escape pandoc_article.tex
markdown -> PDFしてくれるコマンドの作成
ここまでのコマンドを一連のシェルスクリプトにしましょう。解説のためにJSONファイルを出力していましたが、今回は必要ありません。
2020/7/30 追記: 注意点ですが、先程も書いたようにminted_filter.py
は予め $PATH
変数の通った場所に置いておいてください。(マークダウンファイルがあるディレクトリにいちいちコピーするのも面倒ですよね?)
# !/bin/bash
mkdir -p .markdown_preview_tmp
set -e
cd .markdown_preview_tmp
fname=`basename $1`
cp ../$fname .
cat <<EOF >> $fname
---
urlcolor: cyan
header-includes:
- \usepackage{minted}
- \usepackage{setspace}
- \usepackage{mdframed}
---
EOF
pandoc -s --filter minted_filter.py $fname -o ${fname%%.md}.tex -f markdown -t latex -V documentclass=bxjsarticle -V classoption=pandoc
lualatex -shell-escape ${fname%%.md}.tex
cp ${fname%%.md}.pdf ../${fname%%.md}.pdf
cd ../
rm -r .markdown_preview_tmp
カレントディレクトリに一時ディレクトリを作成しmarkdownファイルをコピーしてきて、その中で
- メタデータの付加
- md -> latex
- latex -> PDF
という作業をおこなっています。最後に一時デイレクトリを消せばmarkdownとPDFだけが残りハッピーですね!(デバッグ時は rm
をコメントアウトしましょう。)
このコマンドも実行権限を与えパスの通ったところに置いておきましょう。
chmod u+x md2pdf_by_pandoc
各種ツールがインストールされ、フィルターとこのコマンドが適切な位置4にあれば、最終的に次のコマンドで素敵なPDFが出来上がるはずです。
$ mv md2pdf_by_pandoc ~/bin/
$ md2pdf_by_pandoc pandoc_article.md
で、この記事をPDF化したものがこちらになります。
ここまで読んでいただき、ありがとうございました。至らない点があればコメントくださると幸いです。m(_ _)m