# この記事は、2015年10月6日にYahoo!ブログへ投稿した記事を、Yahoo!ブログの終了に伴って2019年8月に移行したものです。

MarkdownからPDF作りたいですよね。やりましょう。

この記事では、.mdファイルをpandocで.pdfにする過程を自分用メモします。

環境はUbuntu 14.04 LTSです。

前準備

pandoc

http://pandoc.org/installing.html

https://github.com/jgm/pandoc/releases/1.15.1 <—– バージョンによってURLが変わります

sudo apt-get install pandocで入れても良いのですがバージョンが古いのでdebian packageから入れましょう。上の1つ目のリンクから最新バージョンの.debファイルが置いてあるGitHubまで行けます。

$ sudo dpkg -i pandoc-1.15.1-1-amd64.deb

また、多少時間がかかりますがビルドすることもできます。

pandocはHaskellで書かれているのでHaskellを入れていない人は入れましょう。

# 2019年8月31日追記:以下ではCabalを使ってインストールしていますが、現在ではStack等を使う方がオススメです。

Haskellアプリのパッケージマネージャであるcabalを使ってインストールします。

$ sudo apt-get install haskell-platform
$ cabal update
$ sudo cabal install --global cabal-install
$ sudo cabal install --global pandoc

LaTeX

pandocは内部的にMarkdown ->* LaTeX -> PDFという変換をしているのでLaTeXが必要です。今ならTeX Live 2015が良いです。数年前まで日本語フォント周りが面倒臭かったですが最近は楽です。

TeX Wikiを参考に、install-tl-ubuntuを使うのが良いでしょう。

https://oku.edu.mie-u.ac.jp/~okumura/texwiki/?Linux%2FUbuntu

https://github.com/scottkosty/install-tl-ubuntu

$ git clone https://github.com/scottkosty/install-tl-ubuntu.git
$ cd install-tl-ubuntu
$ sudo ./install-tl-ubuntu

gitが無いならwgetでもいいです

$ wget https://github.com/scottkosty/install-tl-ubuntu/raw/master/install-tl-ubuntu && chmod +x ./install-tl-ubuntu

そのまま運用してると、忘れてsudo apt-get install texliveしたときなんかに/usr/binに同名の(あんまり設定がされていない)TeX Liveが入ることがあるので、それが嫌な人はapt-get/usr/local/texliveにTeX Liveがあることを教えてあげます(dummyパッケージを作ります)。

$ sudo apt-get install equivs
$ wget http://www.tug.org/texlive/files/debian-equivs-2015-ex.txt
$ equivs-build debian-equivs-2015-ex.txt
$ sudo dpkg -i texlive-local_2015-1_all.deb

エディタ

適当なMarkdownファイルを作ります。

# 2019年8月31日追記:以下ではAtomをオススメしていますが、今ならVS Codeをオススメします。

Emacsでもvimでもウェブエディタでも良いですが、Markdownに特化するならAtom.ioが使い勝手良いです。まだUbuntu用だとアップデートが自動ではないですが、そんなには気になりません。

https://atom.io/

https://github.com/atom/atom/blob/master/README.md

Ubuntu用には上のページに書いてあるようにatom-amd64.debをダウンロードして

$ sudo dpkg --install atom-amd64.deb

が良いです。

バージョンによっては日本語の入ったMarkdownのプレビューが豆腐になることがあるので、設定で調整します。これはフォントの問題です。

Atomを起動後Ctrl+,でSettingsを開いて、左のカラムから”Open Config Folder”を開きます。

そして以下のCSSをコピペしてstyles.lessという名前で出てきたフォルダ(デフォルトは~/.atom)に保存します。

* {
    font-family: Source Han Code JP;
}

.markdown-preview {
    h1,h2,h3,h4,h5,h6 {
      font-family: Source Han Code JP;
    }
}

atom.ioの解説記事では無いのでこれ以上説明しませんが、色々面白いパッケージがあるのでググってみてください。

Node.js

単に変換するだけなら今までのだけでできるんですが、使いたいフィルターがあるのでNode.jsを使えるようにします。sudo apt-get install nodeだと違うのが/usr/sbin/に入ってしまうので注意してください。

同時にNode.jsアプリのパッケージマネージャーであるnpmも入れます。

$ sudo apt-get install nodejs
$ sudo apt-get install npm

Node.jsのバージョン問題が気になる人はnvmでバージョン管理する方法があるようです。

http://liginc.co.jp/web/programming/node-js/85318

このままだとnodeというコマンド名でNode.jsが起動できないので、

$ sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10

としてやってnodeでも起動できるようにするか、すぐ下でダウンロードするimport.jsのnodeをnodejsに書き換える必要があります。特に拘りがないなら上のコマンドを使うのが速いです。

import.js

@azu_re さんのimport.jsが便利なので使ってみたい人はgit cloneします。

pandocでは内部的にMarkdown -> JSON -> filtered JSON -> PDFみたいな感じに、変換途中に自前のフィルタースクリプトを噛ませることができます。

import.jsは、Markdownのコードブロック(プログラムのコードを書く部分)に外部ファイルをインポートできるようにするフィルターです。

後々楽なので~/.pandoc以下にダウンロードするようにしていますが、好みで変えても構いません。

$ cd ~
$ mkdir .pandoc
$ cd .pandoc
$ git clone https://github.com/azu/pandoc_import_code_filter.git
$ cd pandoc_import_code
$ npm install

試してみる

たとえばこんなMarkdownファイルで試してみます。

https://gist.github.com/nekketsuuu/5f49279090b7d1301376

Markdownの文法はググればすぐでてきます。AtomだとCtrl+Shit+MでMarkdown previewをトグルできます。

今回使っている文法は、GitHub Markdownにpandoc拡張のpandoc_title_blockheader-attributeを入れたものです。

LaTeXの数式モードが若干分かりにくいので付記しておくと、tex_math_dollarsでは$ ... $がインライン数式モードとして使えます。tex_math_single_backslashでは\[ ... \]が行立てする数式モード(ディスプレイ数式モード)として使えます。マニュアルによるとこれで\( ... \)も使えるようになるはずなのですが何故か使えませんでした。

このような文法の細かい設定は、pandocの実行時フラグで設定できます。

たとえばこのファイルを以下のコマンドでpdfに変換できます。

$ ls
example.md  hello.c
$ pandoc example.md -s --self-contained -f markdown_github+pandoc_title_block+header_attributes -o example.pdf -V documentclass=ltjarticle --latex-engine=lualatex -V geometry:a4paper -V geometry:margin=2.5cm -V geometry:nohead --filter ~/.pandoc/pandoc_import_code_filter/import.js
$ ls
example.md  example.pdf  hello.c

今回の設定は基本的にGitHub Markdownの指定にしたがっていますが、以下の拡張がなされています。

  • pandoc_title_block : 最初の%で始まる行がタイトル扱いされる。デフォルトだとタイトル・著者名・日付の順番。これを自分好みに変えるためにはYAMLメタデータとテンプレートが必要です。たとえばこのissueや、このissueなんかが参考になります。

  • header_attributes : 各sectionには#section_nameでinternal link(内部リンク)が貼れるのですが、日本語セクション名だと自動生成のidになってしまい直感的なリンクが貼れません。header_attributes拡張を入れると、セクション名の後に {#section_name}と書くとそれがidになります。

  • --filter import.js : コードブロックの中で$importすることで外部ファイルを読み込むことができるようになります

他にも便利な拡張がたくさんあります。implicit_header_referencesとか。Pandocユーザーズガイドの日本語訳を読むと勉強になります。

Makefile

毎回この長いコマンドを打つのは面倒なので、aliasしてあげたり(-t フラグを使うと出力フォーマットを指定でき、-o を省略できます)、Makefileを作ることを思いつきます。

たとえば僕は下のようなMakefileを使っています。

https://gist.github.com/nekketsuuu/5f49279090b7d1301376

PANDOC = pandoc
IMPORT_JS = $(HOME)/.pandoc/pandoc_import_code_filter/import.js
MD = example
TOC =

# 入力はGitHub Markdown + タイトルブロック
# http://sky-y.github.io/site-pandoc-jp/users-guide/#title-block
FLAGS = -f markdown_github+pandoc_title_block

# standalone: 適切なヘッダ・フッタをつける(実は出力がpdfなら勝手にこのモードになる)
# http://sky-y.github.io/site-pandoc-jp/users-guide/#general-writer-options
FLAGS += -s

# 出力ファイルは1つにまとめる
FLAGS += --self-contained

# 日本語でLaTeXを使うための設定
# pandocだとpdfLaTeXで日本語がうまくいかないのでLuaLaTeXを使う
# Tex Live 2015で成功
# デフォルト設定だと余白が広いので調整する
FLAGS += -V documentclass=ltjarticle \
--latex-engine=lualatex \
-V geometry:a4paper \
-V geometry:margin=2.5cm \
-V geometry:nohead

# import.jsをフィルターに入れる
# http://efcl.info/2014/0301/res3692/
# https://github.com/azu/pandoc_import_code_filter
FLAGS += --filter $(IMPORT_JS)

# TOCフラグが立ってるなら目次も入れる
# usage: make TOC=1
ifdef TOC
	FLAGS += --toc
endif

$(MD).pdf: $(MD).md
	$(PANDOC) $(FLAGS) $< -o $@

.PHONY: clean
clean:
	rm -f $(MD).pdf

atom.io再び

markdown-preview-plusというパッケージとmathjax-wrapperというパッケージを入れて、markdown-previewをdisableし、markdown-preview-plusの設定で色々弄るといちいちpandocでコンパイルしなくてもそれなりの出来上がり見本がCtrl+Shift+Mで見られるようになります。便利。

pandocパーサーで数式表示させるためにはPandoc Options: Markdown Flavorにtex_math_dollarsを入れましょう。

AtomでMarkdown+数式を利用する http://qiita.com/noppefoxwolf/items/335323b98f0400a6f07d

まだ僕にはよくわかっていないこと

Markdownには標準で中央寄せ、右寄せがありません(表のセル内alignならあるのですが、<div align="right">に相当するものがありません。

HTMLやLaTeXを直接打つことで解決することもできるらしいのですが、あまりスマートでないのでスマートなやり方を探しています。それこそfilter作るとか。

このStack OverFlowが参考になりそうです: Right align (an address) in Pandoc : StackOverFlow

テンプレートについてはたとえば: http://pandoc.org/demo/example9/templates.html

さいごに

MarkdownからPDFを作れると、

  • 作ったはいいものの詳細に仕様をまとめるほどではないプログラムの簡単な説明を書いたり

  • 適当に書いた説明書を体裁よくして印刷できたりします。

綺麗なPDFを手早く作って楽しましょう。

追記.1. (2015/11/12)

「動作が遅い」という感想をもらいました。確かにMarkdown -> pdfはちょっと遅いです。これはpandocが重いというよりか、lualatexが重いのではないかと考えています。試しに同じ入力でMarkdown -> latexをしてみると超速いです。

対応策として、pandocでMarkdown -> htmlしてからブラウザでpdfに印刷する、という方法を@dmingnがしていましたが、これする位ならはじめからMarkdown -> pdfした方が速いのではないかと思う熱血です。

追記.2. (2015/11/12)

LaTeXの数式モードの中でボールドイタリック体を出したいという意見をもらったのでやってみました。LaTeXの数式モードでは太字\mathbfとイタリック\mathitがありますが、ボールドイタリックはありません。\mathbf{\mathit{x}}では片方の性質が打ち消されてしまいます。

LaTeXそのものでは、以下のサイトに書かれているように、\newcommandする方法や\usepackageする方法が知られています。

http://d.hatena.ne.jp/yascentur/20111215/1323879218

以下では下のように\newcommandしてやることでボールドイタリックを使えるようにします。上のページに記載されている他の方法も同じようにして使えます。

\newcommand{\mathbi}[1]{\mbox{\boldmath $#1$}}

これをpandocで使える形にするにはいくつか方法がありますが、一番手早いのはYAMLメタデータブロックを使う方法だと思います。

たとえばこのサンプルファイルを見て下さい。このMarkdownをpandocに変換させると、確かにボールドイタリックとして出力されます。

ただしいくつかエクステンションを増やさないと駄目です。YAMLメタデータブロックを使うためのyaml_metadata_block、LaTeXの\newcommand\renewcommandを使えるようにするlatex_macrosが必要です。僕はしばらくlatex_macrosの存在を知らなくて、バックスラッシュが\textbackslashに変換されてしまってエラーが出ていました。

上のMakefileに追加すればそれで大丈夫なのですが、一応最終的なコマンドをコピペしておきます。

pandoc -f markdown_github+tex_math_dollars+yaml_metadata_block+latex_macros -s --self-contained -V documentclass=ltjarticle --latex-engine=lualatex -V geometry:a4paper -V geometry:margin=2.5cm -V geometry:nohead --filter /home/itak/.pandoc/pandoc_import_code_filter/import.js  example.yaml.md -o example.yaml.pdf

YAMLメタデータのheader-includesにはLaTeX以外にも色々書けて便利です。リファレンスこのStackExchangeが参考になります。

この方法は手軽なのですぐできますが、こういうヘッダーが増えてくると同じようなヘッダーを複数ファイルに何回も書くことになって面倒くさいです。そういう状況にはYAMLメタデータではなくて、LaTeXテンプレートファイルの方が適していると思います。

これはあらかじめ~/.pandocなどに置かれたテンプレートファイルを実行時にテンプレートとして指定することでヘッダーファイルとしてインクルードできるという機能です。詳しくはリファレンスをば

参考