pandoc & wkhtmltopdf のDockerイメージを作成し、複数マークダウンファイルを1つのpdfにする

この記事は、 JSL(日本システム技研) Advent Calendar 2020 - Qiita 12/8の記事です。

以前、markdownからpdfを作成する機会がありました。
GitLab CI + docker-reviewを使って、Markdownをtextlintしてからpdf化するCI環境を作ってみた - メモ的な思考的な

他の方法がないかを見たところ、 pandoc & wkhtmltopdfでも作成できそうでした。
pandoc + markdownでいい感じの執筆環境を作る - Qiita

そこで、pandoc & wkhtmltopdf のDockerイメージを作成し、複数マークダウンファイルを1つのpdfにしてみました。  
 

目次

 

環境

  • Docker
    • 以下のライブラリを入れる
      • pandoc 2.11.2
      • wkhtmltopdf 0.12.5
      • フォントは Google Noto

 

最終的なディレクトリ構成はこちら。

$ tree
.
├── Dockerfile
├── manuscript
│   ├── りんご.md
│   └── さつまいも.md
├── output
│   └── (merge.pdf)
└── settings
    ├── defaults.yaml
    └── style.css

 
マークダウンファイルは2つ用意しました。

なお、マークダウンファイル内で \ の後にトリプルバッククォートしている部分ですが、はてなブログに貼るために書いているため、本来 \ は不要です。

りんご.md

# りんごの種類

- シナノゴールド
- フジ

(以下の \ は不要)
\```python
print('りんごです!')
\```


<div class="hidden">
# コメント

シナノゴールドはイタリアをはじめとした海外に進出してる

</div>

<div class="page-break"></div>

 

さつまいも.md

# さつまいもの種類

- 紅はるか
- 安納芋

(以下の \ は不要)
\```python
print('さつまいもです!')
\```

<div class="hidden">
# コメント

紅優甘は、紅はるかの商標登録名

</div>


<div class="page-break"></div>

実装

Dockerfile

同じようなDockerfileがないかを探したところ、以下のリポジトリがありました。
https://github.com/slurdge/docker-pandoc-wkhtmltopdf

 
ただ、docker buildしてみると

E: Package 'libssl1.0-dev' has no installation candidate

というエラーでビルドできませんでした。

 

他のDockerfileがないかを探したところ、 wkhtmltopdf を使っているDockerfileがありました。
Docker コンテナ上で wkhtmltopdf を動かす - Qiita

そこで、これらを組み合わせて作ってみることにしました。

 

まずはpandocのリポジトリを見たところ、 pandoc-2.11.2-1-amd64.deb がありました。
https://github.com/jgm/pandoc/releases/

次にwkhtmltopdfのリポジトリを見たところ、 wkhtmltox_0.12.5-1.buster_amd64.deb 等のdebファイルがありました。
https://github.com/wkhtmltopdf/wkhtmltopdf/releases

そこで、今回はDebianベースで作ることにしました。

 
ただ、上記だけでは日本語を含んだマークダウンが文字化けしてしまいました。

そこで「Docker コンテナ上で wkhtmltopdf を動かす」の記事に合わせてフォントを入れることにしました。

DebianでNotoフォントを入れる方法を探したところ、 fonts-noto-cjkfonts-noto-cjk-extra を使えば良さそうでした。
Linuxだって、綺麗にフォントが表示できるんだからねッ!

そのため、pandocとwkhtmltopdfはGithubから、それ以外はdebファイルからインストールすることにしました。

 
ただ、debファイルからインストールする際にいくつか依存関係が発生することから、 gdebi もインストールしておきます。

 
他に、 --no-install-recommends でインストールすると

ERROR: The certificate of 'github.com' is not trusted.
ERROR: The certificate of 'github.com' doesn't have a known issuer.

が発生することから、 ca-certificates も追加しています。
Ubuntu on Docker で SSL/TLS 通信するとエラーになる問題の対処 - Qiita

 

最終的なDockerfileはこちら

FROM debian:buster-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
  xorg \  
  libssl-dev \
  libxrender-dev \
  wget \
  gdebi \
  fonts-noto-cjk \
  fonts-noto-cjk-extra \
  ca-certificates \
  && rm -rf /var/lib/apt/lists/* \
  && apt-get autoremove \
  && apt-get clean

RUN wget https://github.com/jgm/pandoc/releases/download/2.11.2/pandoc-2.11.2-1-amd64.deb -O pandoc.deb \
    && dpkg -i ./pandoc.deb \
    && rm pandoc.deb

RUN wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.buster_amd64.deb -O wkhtmltox.deb \
    && dpkg -i ./wkhtmltox.deb \
    && rm wkhtmltox.deb

RUN mkdir /var/tmp/output

 

docker build

Dockerfileができたのでビルドします。

$ docker build ./ -t pandoc_wkhtmltopdf:1.0
...
Successfully built ae80e0763df1
Successfully tagged mydoc:1.0

 

ビルド後のサイズはこんな感じです。

$ docker image list pandoc*

REPOSITORY            TAG    IMAGE ID        CREATED          SIZE
pandoc_wkhtmltopdf    1.0    ae80e0763df1    2 minutes ago    998MB

 

docker run

Dockerイメージができたので、docker run します。

オプションとして以下を指定します。

オプション 内容
--mount type=bind,src=...,dst=... ホストとDockerでファイルを共有するため。生成したpdfのコピーを不要にする
-w /var/tmp/output 作業ディレクトリをホストと共有したディレクトリにすることで、cdとか不要に

 
docker run後、Dockerに入って作業ディレクトリにいればOKです。

$ docker run -it --mount type=bind,src="$(pwd)"/,dst=/var/tmp/output -w /var/tmp/output --name mypandoc pandoc_wkhtmltopdf:1.0
...
root@f190aac6d170:/var/tmp/output# 

 

pandocとwkhtmltopdfによる変換
Default filesファイルを作成

pandoc実行時にオプションを渡してpdfファイルへと変換します。

ただ、pandocにはオプションが多くあるため、実行時に指定漏れが発生しそうでした。

そこで、Default filesを使って、コマンド時のオプションは必要最低限とすることにしました。
https://pandoc.org/MANUAL.html#default-files

なお、今回複数マークダウンファイルを1つのpdfにまとめますが、 input-files ではワイルドカード指定ができなかったため、Default filesには記載しませんでした。
Wildcard for multiple input files in the defaults file variable "input-files" - Google グループ

from: markdown
to: html5

# 入力ファイルはコマンドラインから指定
# manuscript/*.md が指定できないため

# 出力ファイル (単一で指定)
output-file: output/merge.pdf

# コードブロックの背景色
highlight-style: tango

# 独自CSS
css:
- settings/style.css

 

独自CSSファイルの用意

今回は改ページと非表示のclassを用意しました。

.page-break {
    page-break-before: always;
}

.hidden {
    display: none;
}

 

pandocコマンドの実行

pandocコマンドで変換を行います。

# pandoc ./manuscript/*.md -d settings/defaults.yaml

[WARNING] This document format requires a nonempty <title> element.
  Defaulting to 'さつまいも' as the title.
  To specify a title, use 'title' in metadata or --metadata title="...".
Loading pages (1/6)
Counting pages (2/6)                                               
Resolving links (4/6)                                                       
Loading headers and footers (5/6)                                           
Printing pages (6/6)
Done 

 

pdfの確認

改ページされていること、不要な部分が表示されていないことが確認できました。

f:id:thinkAmi:20201207231228p:plain:w400

 

ソースコード

Githubに上げました。
https://github.com/thinkAmi-sandbox/pandoc_wkhtmltopdf_docker

 

その他参考

aptまわり