日々是好日

プログラミングについてのあれこれ、ムダ知識など

Pandoc+Dockerでドキュメント作成環境を整備する

Pandoc、MarkdownからHTMLでもPDFでも、Wordにだって出力できて便利ですね。

軽い気持ちでPandocを導入しようとしたら地獄を見たのでその備忘録です。

なお、筆者の環境は Windows10 Pro + Docker for Windows で構築しています。 Linux歴3週間程度のため、コマンドの打ち方等は未熟な部分がありますのでご容赦ください。

2020/7/27 17:00 更新 HTMLの見出しの採番用Luaフィルタを追加

github.com

リポジトリを作成しました。

目的

和文MarkdownからPDF及びHTMLを出力することを目的とします。

概要

Docker上にPandocの環境を構築します。

最終的に、次のコマンドでPDFやHTMLの出力ができるようにします。

# ワンライナーで出力する場合
> docker run -rm --volume "$(pwd):/data" mypandoc -d xxx-defaults.yaml

# 一度コンテナ内に入ってから出力する場合
> docker run -it --volume "$(pwd):/data" mypandoc /bin/sh
$ pandoc -d xxx-defaults.yaml

手順

下記の手順にてPandocの環境を整備していきます。

  1. Docker (Docker for Windows)をインストールする
  2. pandoc/latexイメージを導入する
  3. 和文Markdownに対応するため、LuaTeXパッケージ等導入する
  4. 図表対応のため pandoc-crossref を導入する
  5. 簡単にきれいな見た目のHTMLを出力するため、easy-pandoc-templateを導入する
  6. 上記をまとめたDockerfileを作成する
  7. 長いオプションをDefault fileにまとめる
  8. 補足:採番の接頭辞・接尾辞を修正する(PDFとHTMLの2パターン)

Docker (Docker for Windows)をインストールする

最初にDockerをインストールします。

docs.docker.com

> docker -v
Docker version 19.03.8, build afacb8b

バージョンが返ってくればOKです。 Dockerの操作方法が分からない方には、わかばちゃんシリーズのDocker本をおすすめします。 私はほとんどこれしか読んでません(ぇ

llminatoll.booth.pm

なお、本ブログではコマンドの実行を>$で区別します。

> docker ... ← PowerShell上のコマンド
$ pandoc ... ← Dockerコンテナ上のコマンド

pandoc/latexイメージを導入する

pandocとlatexが構築されている神Dockerイメージがあるので、これを利用します。

hub.docker.com

latestだと随時更新されてしまうため、現時点の最新版である2.9.2.1を使用します。 ちなみにディストリビューションはalpineっぽいです。

> docker run -it --volume "$(pwd):/data" pandoc/latex:2.9.2.1 /bin/sh
$ ls
contents.md etc...
$ pandoc -v
pandoc 2.9.2.1 etc...
$ cat /etc/alpine-release
3.11.5

ここで、次のcontents.mdをカレントディレクトリに作成し、pandocでpdfにコンパイルしてやります。

# contents
contents.
本文だよ
$ pandoc contents.md -o conetnts.pdf
Error producing PDF.
! Package inputenc Error: Unicode character 本 (U+672C) 
...
Try running pandoc with --pdf-engine=xelatex.

pandoc/latexはそのままでは日本語フォントを読むことができません。 そこで、次にLuaTeXやIPAフォントパッケージ等を導入します。

LuaTeXパッケージ等導入する

次のパッケージをまとめてインストールします。 tlmgrというTeXLiveのパッケージマネージャが既に入ってるので、これを利用します。

$ tlmgr install ipaex luatexja
$ tlmgr update latex

# 以下必要に応じて
$ tlmgr install bxjscls bxwareki everyhook svn-prov type1cm

参照:pandoc/latexをいじって日本語LaTeXできるようにしたやつ · GitHub

ipaex luatexjaパッケージをインストールすれば、和文コンパイルはできます。 次のコマンドでカレントディレクトリにPDFが出力されることを確認してください。

$ pandoc contents.md -o contents.pdf --pdf-engine=lualatex -V documentclass=ltjsarticle

図表対応のため pandoc-crossref を導入する

図や表のキャプション(図1, 表1など)を簡単に付けてくれるpandoc-crossrefを導入します。

github.com

$ wget -O - https://github.com/lierdakil/pandoc-crossref/releases/download/v0.3.6.4/pandoc-crossref-Linux-2.9.2.1.tar.xz | \
  tar Jxf - \
  && mv pandoc-crossref /usr/lib/
  && rm -rf pandoc-crossref.1

いちいち保存せずに、tarにパイプして処理できるの楽ちんですね。 contents.mdに次のように記述することで、キャプションの相互参照が可能になります。

# contents
contents.
本文だよ

![すごい図](./images/test.png){#fig:sugoi}

すごい[@fig:sugoi]を貼り付けたよ。
$ pandoc contents.md -o contents.pdf --pdf-engine=lualatex -V documentclass=ltjsarticle --filter pandoc-crossref

だんだんオプションが長くなってきました。後ほどオプションをyamlファイルに切り出します。 出力されたPDFはこうなります。

f:id:kcpoipoi:20200727120937p:plain:w600

Fig.1等の記述は、contents.mdの最初にyaml形式でメタデータを記述することにより修正できます。

---
figureTitle: "図 "
tableTitle: "表 "
listingTitle: "コード "
figPrefix: "図."
eqnPrefix: "式."
tblPrefix: "表."
lstPrefix: "コード."
---

# contents
...

f:id:kcpoipoi:20200727121056p:plain:w600

PDF形式への出力に必要な操作は以上となります。 次にHTML出力を行います。

easy-pandoc-templatesを導入する

pandocでHTMLに出力する場合、ただ出力するだけなら次のワンライナーでOKです。 ただしものすごく味気ないスタイルになります。

$ pandoc contents.md -o contents.html --filter /usr/lib/pandoc-crossref

比較的簡単に、きれいなHTMLを出力するため、easy-pandoc-templatesを導入します。

github.com

dev.classmethod.jp

$ wget -O - https://github.com/ryangrose/easy-pandoc-templates/archive/master.tar.gz | \
  tar zxvf - -C /usr/lib/

easy-pandoc-templates-master/html/elegant_bootstrap_menu.htmlが使用するHTMLのテンプレートです。 --tocオプション(table-of-contents: 目次)を付与することで、左側に見出しの一覧が表示されるようになります。

また、--metadata titleを指定していないと警告が表示されるので、オプションで指定します。

$ pandoc contents.md -o contents.html --toc --template=/usr/lib/easy-pandoc-templates/html/elegant_bootstrap_menu.html --filter /usr/lib/pandoc-crossref --metadata title=すごい記事

f:id:kcpoipoi:20200727121251p:plain:w500

Dockerfileを作成する

ここまでいろいろツールをインストールしてきましたが、Dockerイメージを作る段階で全て導入してしまいましょう。 pandoc/latex:2.9.2.1をベースにDockerfileを作成します。

# Dockerfile

FROM pandoc/latex:2.9.2.1
RUN tlmgr install \
      bxjscls \
      bxwareki \
      everyhook \
      ipaex \
      luatexja \
      svn-prov \
      type1cm && \
    tlmgr update latex
RUN wget -O - https://github.com/lierdakil/pandoc-crossref/releases/download/v0.3.6.4/pandoc-crossref-Linux-2.9.2.1.tar.xz | \
  tar Jxf - \
  && mv pandoc-crossref /usr/lib/ \
  && rm -rf pandoc-crossref.1
RUN wget -O - https://github.com/ryangrose/easy-pandoc-templates/archive/master.tar.gz | \
  tar zxvf - -C /tmp/ \
  && mv /tmp/easy-pandoc-templates* /usr/lib/easy-pandoc-templates \
  && rm -rf /tmp/*

このDockerfileを基にイメージをコンパイルします。 イメージ名(mypandoc)は適当に指定してください。

> docker build -t mypandoc .

使い方は次のとおりです。

> docker run -rm --volume "$(pwd):/data" mypandoc ...どちゃくそ長いオプション

オプションが長いので、Pandoc 2.8で解禁されたDefault fileを定義します。

長いオプションをDefault fileにまとめる

オプションを次のとおりyaml形式でまとめます。

# xxx-defaults.yaml
# 入力ファイル(複数の入力も可)
input-files:
  - content.md
  - content2.md
  - content3.md
  
# 出力ファイル (単一で指定)
output-file: content.pdf

# --pdf-engine オプション
pdf-engine: lualatex

# テンプレート変数
variables:
  documentclass: ltjsarticle
  classoption: pandoc
  # 字下げ
  indent: true
  # 余白
  geometry:
    - top=20mm
    - right=24mm
    - left=24mm
    - bottom=20mm
    - heightrounded
  # 採番接頭辞・接尾辞の追加(HTML用のyamlでは削除)
  header-includes:
    - \renewcommand{\thesection}{第\arabic{section}章}
    - \renewcommand{\thesubsection}{第\arabic{subsection}節}
    - \renewcommand{\thesubsubsection}{第\arabic{subsubsection}項}

# HTML用のテンプレートファイル(PDF用のyamlでは削除)
template: /usr/lib/easy-pandoc-templates-master/html/elegant_bootstrap_menu.html

# メタデータ
metadata:
  title: すごい記事
  figureTitle: "図 "
  tableTitle: "表 "
  listingTitle: "コード "
  figPrefix: "図."
  eqnPrefix: "式."
  tblPrefix: "表."
  lstPrefix: "コード."

# 目次
table-of-contents: true
# 目次出しの深さ
toc-depth: 2

# 見出しの採番
number-sections: true

# 各種フィルタ
filters:
  - /usr/lib/pandoc-crossref

使用するときは-dオプションでyamlを指定します。だいぶすっきりしました。

> docker run -rm --volume "$(pwd):/data" mypandoc -d xxx-defaults.yaml
# またはコンテナを起動して
$ pandoc -d xxx-defaults.yaml

以上で和文MarkdownからPDF及びHTMLの出力は完了です。

補足:採番の接頭辞・接尾辞の修正

デフォルトでは、見出しの採番は次のようなフォーマットになります。

# 見出し1
## サブ見出し
# 見出し2
## サブ見出し
↓
1. 見出し1
  1.1 サブ見出し
2. 見出し2
  2.1 サブ見出し

ここに接頭辞・接尾辞を追加する場合、PDFとHTMLとで次のように処理を分けます。

PDF出力の場合

テンプレート変数のheader-includeに次のように記述します。

variables:
  header-includes:
    - \renewcommand{\thesection}{第\arabic{section}章}
    - \renewcommand{\thesubsection}{第\arabic{subsection}節}
    - \renewcommand{\thesubsubsection}{第\arabic{subsubsection}項}
# 見出し1
## サブ見出し
# 見出し2
## サブ見出し
↓
第1章 見出し1
  第1節 サブ見出し
第2章 見出し2
  第1節 サブ見出し

HTML出力の場合(easy-pandoc-templates使用を前提)

easy-pandoc-templates-master/css/elegant_bootstrap.cssに次の記述を追加します。 疑似要素(::before, ::after)を使って見た目だけ変えてやる形です。

forkして修正するなら、rawgitがHTMLテンプレート中に記述されているのでjsdelivrに書き換えればより安全です。

h1 > span.header-section-number::before{
  content: "第";
}

h1 > span.header-section-number::after{
  content: "章";
}

h2 > span.header-section-number::after{
  content: "節";
}
# 見出し1
## サブ見出し
# 見出し2
## サブ見出し
↓
第1章 見出し1
  1.1節 サブ見出し
第2章 見出し2
  2.1節 サブ見出し

HTMLではサブ見出しの採番の修正がうまくいってません。 「第1節」のように「1.」だけを消す方法があればお教えください。

zr_tex8r氏にとんでもねぇ速度で解決していただきました。 numberring.luaを作成し、filters: - numberring.luaで追加すればOK。

参考にさせていただいたサイト

他にもまだまだありますが、ネットの海に沈んでしまいました(´・ω・`)

llminatoll.booth.pm

ipafont.ipa.go.jp

qiita.com

laclefyoshi.hatenablog.com

dev.classmethod.jp

www.jsdelivr.com

Pandoc:節番号のスタイルを変えるやつ · GitHub