Docker で Pygments 環境を用意して minted パッケージを試す

様々な言語のソースコードをいい感じにシンタックスハイライトして，LaTeX 文書内に貼り込んでくれる [minted パッケージ](https://www.ctan.org/pkg/minted)を，Docker を使って試してみたときのメモです。手元の Python 環境に手を加えずに Docker コンテナを利用して minted パッケージを動かすことを目指します。この手法は，minted に限らず，別途インストールする必要のある外部ツールを呼び出す系の LaTeX パッケージを，手元の環境を汚さずに試す方法の一例として参考になるのではないでしょうか。

#### minted パッケージの使用例

* [mintedでLaTeXドキュメントにソースコードを載せる](https://qiita.com/la_float/items/2884a4d80a54ffa89a34)
* [ソースコードの貼り付けができるmintedをpLaTeXで使ってみた](http://muscle-keisuke.hatenablog.com/entry/2015/11/06/182214)
* [[LaTeX] minted --- プログラムソースコードのハイライト表示](http://konoyonohana.blog.fc2.com/blog-entry-106.html)

# minted パッケージの動作要件

* `minted.sty`（TeX Live ならば初めからインストールされている）
* Pygments がインストールされた Python 環境
* `minted.sty` から `pygmentize` コマンドを呼び出すために LaTeX エンジンに `-shell-escape` を与えることが必須

# Pygments の準備

Pygments のインストールは，`pip` が使える Python 環境であれば

```sh
$ pip install pygments
```

とすればOKです。

ただし，今回はあくまでお試しということで，手元の Python 環境を汚さずに minted パッケージを試したいと思います。Python の`venv` や `virtualenv` を使ってもよいのですが，ここでは，(Python に限らず使える手法という意味で）より一般的で，より環境の分離がしっかりできる，Docker によるコンテナ化の方針でゆくことにします。つまり，Pygments を搭載した Docker コンテナを用意します。

## Pygments 入り Docker コンテナイメージのビルド

Alpine Linux + Python のベースイメージに対し，Pygments を追加インストールして，自動で `pygmentize` コマンドが起動するように設定した Docker コンテナです。

```dockerfile:Dockerfile
FROM python:alpine
WORKDIR /workdir
RUN pip install pygments
ENTRYPOINT ["pygmentize"]
```

この Dockerfile をビルドし，ここでは仮に `pygmentize-wrapper` という名前のイメージとして保存します。

```sh
$ docker build -t pygmentize-wrapper .
```

## pygmentize コマンドのラッパースクリプトの準備

次に，テスト用ディレクトリ（たとえば `/tmp/minted-test` ）に次のスクリプトを用意し，`pygmentize` という名前で保存して，実行権限を与えて(`chmod +x`)おきます。

```sh:pygmentize
#!/bin/sh
docker run --rm \
  --mount type=bind,src="$(pwd)",dst=/workdir \
  pygmentize-wrapper "$@"
```

発想としては，**`minted.sty` から `\ShellEscape` で呼び出される `pygmentize` コマンドを，本物の `pygmentize` ではなく，Docker コンテナ内の `pygmentize` を呼び出すこのラッパースクリプトに差し替えて偽装しよう**というわけです。

# サンプル文書の用意

次のようなサンプル文書を用意します。minted パッケージが提供する `minted` 環境のみならず，tcolorbox パッケージが提供する `tcblisting` 環境のシンタックスハイライトエンジンを minted に設定したものも試します。（このサンプル文書は upLaTeX 文書となっていますが，そこは特に深い意味はありません。pLaTeX や pdfLaTeX 等でもOKです。）

```latex:sample.tex
%#!uplatex
\documentclass[uplatex,dvipdfmx,papersize]{jsarticle}
\usepackage{graphicx,xcolor}
\usepackage[cache=false]{minted}
\usepackage{tcolorbox}
\tcbuselibrary{minted}
\tcbset{listing engine=minted}
\pagestyle{empty}

\begin{document}

\begin{minted}{c}
#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("Hello, world!\n");
    return 0;
}
\end{minted}

\begin{tcblisting}
    {colback=red!5!white,
    colframe=red!75!black,
    left=1cm,
    minted options={fontsize=\small,linenos,firstnumber=1100,numbersep=3mm}}
This is a \LaTeX\ example which displays the text as source code
and in compiled form.
\end{tcblisting}

\begin{tcblisting}
    {listing only,
    title=another language listing only example,
    fonttitle=\bfseries,
    colback=red!5!white,
    colframe=red!75!black,
    minted language=xml}
   <?xml version="1.0"?>
   <project name="Package tcolorbox" default="documentation" basedir=".">
     <description>
       Sample
     </description>
   </project>
\end{tcblisting}

\end{document}
```

# サンプル文書のコンパイル

## コンパイル実行の仕方

現状，次のようなファイル構成になっています。

```
/tmp/minted-test （実験用ディレクトリ）
        ├── pygmentize （ラッパースクリプト）
        └── sample.tex （サンプル文書）
```

LaTeX コンパイルのコマンド実行に前置して，`PATH=.:$PATH` を加えておきます（sh系シェルの場合，コマンド実行に前置してスペース区切りで環境変数の定義を書くことで，[そのコマンド呼び出し時のみ一時的に環境変数の値を変更して実行できます](https://shinkufencer.hateblo.jp/entry/2018/10/19/235900)）。これにより，`PATH` の設定の先頭に一時的にカレントディレクトリが加わった状態で LaTeX エンジンを呼び出せます。その結果，`minted.sty` から `\ShellEscape` で呼び出される `pygmentize` コマンドを，カレントディレクトリのラッパースクリプトに差し替えることができます。

```sh
$ PATH=.:$PATH ptex2pdf -u -l -ot -shell-escape sample.tex
```

`-ot` オプション（options for TeX の意）で TeX エンジンに対して `-shell-escape` を渡しています。

## コンパイル結果

無事にホスト側の LaTeX エンジンから Docker コンテナ内の `pygmentize` コマンドが呼び出され，綺麗に色分けされました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/a78488b5-5ad2-d147-ae99-63b9ed6c8074.png)