TeX Live 2019 + 原ノ味フォント + llmk の Docker コンテナを作る

Docker を使えば，普段使いの TeX Live 環境とは別に古い TeX 環境を保管しておいたり，あるいは逆に先端の開発中ブランチを試しに導入できたりと，色々便利です。

というわけで今回は，TeX Live 2019 + 原ノ味フォント + llmk でコンパイルできる Docker コンテナを作ってみました。

# 目標

* モダンな TeX Live 環境を一発で用意できる Docker のコンテナイメージを作りたい。
* pdfLaTeX / LuaLaTeX / XeLaTeX / (u)pLaTeX, dvips / dvipdfmx の全てを使えるようにする。
* Adobe の源ノ明朝／源ノ角ゴシックを Adobe-Japan1 準拠に組み直して作られた高品質なオープンソース和文フォントである[**原ノ味フォント**](https://github.com/trueroad/HaranoAjiFonts)を，和文標準フォントとして使えるように準備・設定しておく。  
  **【参考】**
  * [TeXConf 2019 一般講演「原ノ味フォントと ToUnicode CMap」関連資料](https://github.com/trueroad/tr-TeXConf2019)
  * [LuaTeXで原ノ味フォントを使う - TeX & LaTeX Advent Calendar 2019](https://qiita.com/trueroad/items/5030c9460da71065a721)
  * [商業出版にも用いられる高品質フォント](https://twitter.com/trueroad_jp/status/1208262010464395264)
* ビルドシステムとしては latexmk に代わる新世代ビルドシステム [**llmk**](https://github.com/wtsnjp/llmk) を標準で使えるようにし，デフォルトとする。  
  **【参考】**
  * [llmk プロジェクトが始動しました - ラング・ラグー](https://blog.wtsnjp.com/2018/08/13/llmk-launch/)
  * [llmk: 新時代の LaTeX 文書ビルドツール - Speaker Deck](https://speakerdeck.com/wtsnjp/llmk) 

# とりあえず完成品を使うには

## イメージの pull

完成したイメージは Docker Hub に [doratex/texlive2019ja-haranoaji](https://hub.docker.com/repository/docker/doratex/texlive2019ja-haranoaji) としてアップロードしてあるので，

```sh
$ docker pull doratex/texlive2019ja-haranoaji
```

とすれば pull できます。（TeX Live 2019 がフルに入っているのでサイズが大きいです🙇）

## 使い方

コンテナ内では `/workdir` が作業ディレクトリとなるので，それをカレントディレクトリと bind マウントして使うのを想定しています。

```bash
alias dockertl="docker run --rm \
  --mount type=bind,src=\"\$(pwd)\",dst=/workdir \
  --mount type=volume,src=ltcache,dst=/usr/local/texlive/2019/texmf-var/luatex-cache \
  doratex/texlive2019ja-haranoaji"
```

などとエイリアスを設定しておくと楽でしょう。（`--mount type=volume,...` の部分は，LuaTeX のフォントキャッシュを `ltcache` というボリューム（名前は任意です）に保存し永続化させるための設定です。詳細は[後述](#luatex-の初回フォントキャッシュ作成が遅い問題)。）このエイリアスが設定されている下で

```sh
$ dockertl
```

とすれば，デフォルトで llmk が起動し，カレントディレクトリの `llmk.toml` に基づきビルドしようとします。

```sh
$ dockertl llmk hoge.tex
```

のようにして特定のファイルをターゲットにすることもできます。従来の latexmk もインストールされているので

```sh
$ dockertl latexmk hoge.tex
```

も使えます。

またもちろん，llmk や latexmk のようなビルドシステムを用いずに，

```sh
$ dockertl lualatex fuga.tex
$ dockertl ptex2pdf -l piyo.tex
```

などと普通にコンパイルすることも可能です。`pLaTeX` + `dvips` + `ps2pdf` のワークフローを使う場合は，

```sh
$ dockertl /bin/bash -c 'platex hoge.tex && dvips hoge.dvi && ps2pdf hoge.ps'
```

とすれば OK です。

(u)pLaTeX + dvips/dvipdfmx においては，**デフォルトで原ノ味フォントを埋め込む**設定になっています。

## テスト用サンプルコード

[GitHub レポジトリ](https://github.com/doraTeX/docker-haranoaji-test)にテスト用サンプルコード群を置いてあります。

* (u)pLaTeX + dvips
* (u)pLaTeX + dvipdfmx
* LuaLaTeX
* XeLaTeX

のそれぞれについて，原ノ味フォントが埋め込まれるサンプルコードとなっています。`test_all.sh` を起動すれば llmk による各テストファイルのビルドが走ります。また，`docker-compose` を用いるサンプルも入れてあります。

# Dockerfile の作り方

以下，この Docker イメージのビルドに用いる Dockerfile の作り方を述べます。

## 参考文献

@yyu さんが[LaTeXで年賀状を作る](https://qiita.com/yyu/items/defa9eb6d4cf5e797270)際に製作された [Dockerfile](https://github.com/y-yu/new-year-letter/blob/master/docker/Dockerfile) を大いに参考にさせていただきました。

# 迷ったこと

## Alpine ベースか Ubuntu ベースか？

イメージのサイズ削減のためには，Alpine ベースでいきたいところです。が，実際にやってみたところ，大半はうまくいったのですが，XeTeX に動的リンクされる fontconfig が（`apk add fontconfig-dev` してあっても）うまく呼び出せず，XeTeX を起動することができませんでした。

割り切って「XeTeX は諦めて Alpine ベースにする」という手も考えられます。実際にイメージを作って比較してみたところ，

* Ubuntu ベース（XeTeX 使用可）：3.63 GB
* Alpine ベース（XeTeX 使用不可）：3.52 GB

となりました。TeX Live の scheme-full の容量が圧倒的で，もはや Ubuntu と Alpine のサイズの違いは誤差みたいなものとなっていました。それなら XeTeX が使えた方がいいだろうと考え，今回は Ubuntu ベースでイメージ構築することにしました。

## TeX Live は scheme-full でいくか scheme-basic + collection-langjapanese でいくか？

イメージのサイズ削減のためには `scheme-basic` + `collection-langjapanese` の方がよいでしょう。が，LuaLaTeX / XeLaTeX / (u)pLaTeX で一通りの組版をできるようにするためにどのパッケージを追加すべきかを考えるのが面倒だし，「モダンな TeX Live 環境が丸ごと整う」ことが目標なので，今回はサイズは気にせずに `scheme-full` でいっちゃうことにしました。（ただしサイズ削減のために `option_doc` と `option_src` は外す。）

## 原ノ味フォントへの対応方法

### LuaLaTeX

TeX Live 2019 の最新レポジトリでは，luatexja-preset パッケージが `haranoaji` プリセットに対応しているのでそのままで大丈夫です。

### (u)pLaTeX, dvips, dvipdfmx

#### pxchfon パッケージ

TeX Live 2019 の最新レポジトリで `haranoaji` プリセットに対応済みです。

#### ptex-fontmaps

TeX Live 2019 の最新レポジトリで `haranoaji` プリセットに対応済みです。

#### cjk-gs-integrate

TeX Live 2019 の最新レポジトリで原ノ味フォントがデータベースに登録済みです。

### XeLaTeX

TeX Live 2019 の最新レポジトリで `haranoaji` プリセットに対応済みです。

# Dockerfile でやること

Dockerfile によるイメージのビルドにおいては，大まかに次のことを行います。

1. TeX Live 2019 の最新版をネットワークインストール。
2. ~~原ノ味フォントを GitHub レポジトリから取得して `TEXMFLOCAL` 内にインストール。~~ **（→ TeX Live に収録されたので不要になりました。）**
3. ~~原ノ味フォントに対応した最新版 `ptex-fontmaps` を GitHub レポジトリから取得して `TEXMFDIST` 内に配置。~~ **（→ TeX Live に反映されたので不要になりました。）**
4. ~~原ノ味フォントに対応した最新版 `cjk-gs-integrate` を GitHub レポジトリから取得して `TEXMFDIST` 内に配置。~~ **（→ TeX Live に反映されたので不要になりました。）**
5. ~~原ノ味フォントに対応した最新版の `zxjafont` を GitHub レポジトリから取得して `TEXMFDIST` 内に配置。~~ **（→ TeX Live に反映されたので不要になりました。）**
6. ~~`mktexlsr` して原ノ味フォントを TeX システムに認識させる。~~
7. `cjk-gs-integrate` を走らせて Ghostscript の原ノ味フォント対応。
8. `kanji-config-updmap-sys` で (u)pLaTeX の既定の和文フォントを原ノ味フォントにする。
9. 最新版の llmk を GitHub レポジトリから取得して `/usr/local/bin` に配置。

# 課題の解決

## XeTeX に TEXMF ツリー内のフォントを認識させる（現在では必須ではない）

（ここは原ノ味フォントを別途インストールしていた時代の記述で，現在では必須ではなくなりました。）

Linux の場合，XeTeX は fontconfig を利用してフォントを見つけていますが，上記手順では原ノ味フォントを`TEXMFLOCAL` 内に配置しましたので，fontconfig がそれを見つけられません。そこで，次のような内容を記載した XML ファイルを `/etc/fonts/local.conf` として保存しておきます。こうすることで `TEXMFDIST`，`TEXMFLOCAL` 内のフォントを XeTeX が見つけられるようになります。

```xml:local.conf
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <dir>/usr/local/texlive/texmf-dist/fonts/opentype</dir>
  <dir>/usr/local/texlive/texmf-dist/fonts/truetype</dir>
  <dir>/usr/local/texlive/texmf-local/fonts/opentype</dir>
  <dir>/usr/local/texlive/texmf-local/fonts/truetype</dir>
</fontconfig>
```

その上で `fc-cache -r` することで fontconfig のキャッシュを更新しておきます。

### 参考： fontspec が呼び出すフォント検索エンジン

| エンジン | Windows    | Mac        | Linux/UNIX |
|----------|------------|------------|------------|
| XeTeX    | fontconfig | AAT        | fontconfig |
| LuaTeX   | luaotfload | luaotfload | luaotfload |

⬆ [Mac 上の TeXLive の luaotfload で「クレー」などのフォントを使えるようにする - ルギア君の戯言](https://lugia.hatenablog.com/entry/2017/06/17/190233)より。

## LuaTeX の初回フォントキャッシュ作成が遅い問題

LuaTeX の使用においては，フォントを初使用したときのコンパイルが遅いという点が問題となります。

`luaotfload-tool -u -f` しておくことで，`TEXMFLOCAL/texmf-var/luatex-cache/generic[-dev]/names` 内のフォントのデータベースは事前に作成しておくできます。ただし，それとは別に，フォントの初使用時に `TEXMFLOCAL/texmf-var/luat
ex-cache/generic[-dev]/fonts/otl` にフォントキャッシュが作成されるため，各フォントの初回使用時のコンパイルが遅くなります。2回目からは速くなりますが，Docker イメージからのコンテナ生成ではこれが問題になります。イメージから毎回使い捨てのコンテナを生成して使う場合，毎回が「初回起動」になるため，起動のたびに毎回フォントキャッシュ作成が入って無用に時間がかかってしまいます。

これを防ぐ方法の一案としては，次のようにして「Docker イメージ作成時に全てのフォントを1回ずつ使って全フォントのキャッシュを作成しておく」方法があります。

```bash
for x in $(luaotfload-tool --list=* --fields=fullpath); do \
  /bin/echo "\\relax\\input luaotfload.sty \\font\\x[$x]\\bye" | luatex > /dev/null; \
done 
```

しかし，このようにしてキャッシュを事前に全作成をすると，Docker イメージのサイズが 1GB 以上も増えてしまいます。そこで，キャッシュが保存されるコンテナ内のディレクトリ `TEXMFLOCAL/texmf-var/luatex-cache` を，Docker コンテナ起動時に名前付きボリュームとしてマウントし，ホスト内に保存することで永続化させましょう。[先ほどのエイリアス](#使い方)にはその設定を仕込んであります。

# 完成品

こうしてできあがった Dockerfile が[こちら](https://github.com/doraTeX/Docker-TeXLive-haranoaji/)です。

## イメージのビルド 

```sh
$ docker build --no-cache=true -t doratex/texlive2019ja-haranoaji .
```