情報系の手考ノート

数学とか情報系の技術とか調べたり勉強したりしてメモしていきます.

自作の TeX 用 Dockerfile

修論を書く時に TeX を使ったのですが,この TeX の環境構築,結構面倒臭いという印象が強いです. なにせバージョンだったり必要なパッケージだったりといろいろ考えないといけないことが多いです. さらに日本語を含むソースコードを扱おうとすると jlisting パッケージを自力でダウンロードしたりしないといけないです. 他にもフォントによってはちゃんと適切な場所に配置しないと,TeX が見つけてくれなかったりします.

ということで修論を書く時には TeX 環境を Docker に構築し,Docker 内の tex を実行することで pdf を生成するということを行っていました. これによって Docker さえあれば自由に環境構築ができるようになり,修論執筆のポータビリティが高くなって QOR (Quality Of Research) が爆上がりしていました.

この TeX 環境構築用の Dockerfile を作って使っていたわけですが,どこにも公開していないのもどうかと思ったので,この記事に書くことで供養しておきたいと思います.

この記事下部に Dockerfile,ビルド用の docker-compose.yml,実行に使える補助用のスクリプトを載せます. 自由に使ってください. 改変とかも好きにしていただいて結構です.

内容

いろいろ入ってます. 基本的には TeXLive を使っていて,パッケージマネージャ tlmgr を使っていろんなパッケージをインストールしています. 日本語を扱う上で必要なパッケージもある程度そろっているはずです. 少なくとも私はこのコンテナだけで修論を書ききりました.

tlmgr が参照する先が各年でアーカイブされたバージョンになっています. そのためコンテナをビルドするタイミングで,できあがる環境が変わるようなこともありません. また TeX英語圏で開発されているため,邦文を書くことを考慮した設計はあまり優先されません. (TeX ガチ勢かつ邦文環境ガチ勢でないと,邦文利用時のエラーを消しきれないため,日本語環境に完全に適用するのが困難) そのため無闇矢鱈にパッケージを更新すると,日本語でうまく動作しなくなる危険があります. しかし本 Dockerfile ではアーカイブされた過去の固定のバージョンをインストールするので,そういった危険性も抑えられています.

また tlmgr では管理できない jlisting パッケージもインストールしてあるので,日本語を含むソースコードも扱えます. それと日本語フォントとして Noto Sans,Noto Serif,源ノ角ゴシック (Source Han Sans),源ノ明朝 (Source Han Serif) の4つをインストールし,TeX が見つけられる場所に配置してあります. そのためこれらのフォントも使えるようになっています. これらは GoogleAdobe が開発した日本語フォントで,非常にきれいだったりするので,フォントに困っていたらとりあえずこれを使えばいいと思います. それと初期設定でフォントを埋め込むようにしているはずなので,生成された PDF には勝手に使ったフォントが埋め込まれるはずです.

とにかく言いたいのは,このコンテナを使っておくと変なところで悩まされずに TeX の環境が構築できるから,使うといいよっていう話です. ただし私が欲しいと思わなかった都合上,GUI の利用が一切想定されていません. 欲しい人は適宜 Dockerfile をいじるか,諦めるかしてください.

使い方

コンテナのビルド

下の方に載せている Dockerfile と docker-compose.yml ファイルを同じ階層に用意し下記コマンドを実行すれば,きれいに TeX の環境が整った Docker コンテナがビルドできます.

docker-compose build

ただし TeX を1から――いいえ,0からインストールするのでアホみたいに時間がかかります. また3GB くらいのクソデカいコンテナになるので,コンテナを作る場所は気をつけるようにしてください. 共用のサーバ上に配置すると(容量によりますが)迷惑になります.

また GUI の利用は想定していないので,基本的にコンソールでの操作になります.

既にインストールされていないパッケージをインストールしたい場合は,Dockerfile の下の方に下記のような行を追記し,ビルドしなおすことでインストールできます.

RUN tlmgr install <インストールしたいパッケージ名>

たぶん1番下の行に追記すれば良いはずです. ダメならちょっとずつ上に上げてみてください. このときのリビルドでは,そんなに時間がかかりません. これは Docker がキャッシュを持っているためです.

またビルドを繰り返していると,使っていない無名のコンテナが PC 上に乱立することになります. 適宜掃除してやって下さい. ストレージが大変なことになります. 特に共用のサーバ上に置いているような人は(ry

TeX のバージョンですが docker-compose.yml 内の下記の行における 2021 の部分を変更することで,別のバージョンを指定可能です.

        TEXLIVE_VERSION: 2021

記事の下に載せている物では 2021 になっているはずですが,この場合 2021 年にアーカイブされたパッケージ群がインストールされます. 動作確認ができているのは 2019 と 2020,それと 2021 の3年分です. 2022 年の分はファイルの配置が他の年と異なっていたので対応していません. 特に古いパッケージを使う理由が無いのであれば 2021 を使うことを推奨します. 特に jlreq というパッケージを使う場合は 2021 の利用を強く推奨します. 2020 以前だと jlreq の仕様が古く使いづらいです.

TeX を使う

下の方に載せている実行用のスクリプト (command.sh と命名すると仮定) を用意し,実行権限を与えて実行することでいろいろできます. ここで実行用のスクリプトの場所は任意です.

このスクリプトはカレントディレクトリをコンテナにマウントし,与えられたコマンドライン引数をそのまま実行するようになっています.

例えば作成した TeX ファイル (master.tex) と実行用のスクリプト (command.sh) が同じ階層にあるとします. この場合は下記コマンドを実行することで pdf を生成できます.

./command.sh uplatex master
./command.sh dvipdfmx master

つまり実行したいコマンドの前に ./command.sh をつけるだけです. TeX をコンソールで使う方法は適宜調べてください. 余裕があればブログ内にそれようの記事が生えるかもしれません.

実行用のスクリプト (command.sh)

#!/bin/sh

docker run --rm -u $(id -u):$(id -g) --volume "$(pwd)"://workdir tex_latex:latest $@

Dockerfile

FROM frolvlad/alpine-glibc:latest AS latex

ARG TEXLIVE_VERSION="2020"
ARG TEXDIR="/opt/texlive"
ARG TEXMFLOCAL="/opt/texlive/texmf-local"
ARG TEXMFSYSCONFIG="/opt/texlive/texmf-config"
ARG TEXMFSYSVAR="/opt/texlive/texmf-var"
ARG TEXMFHOME="~/texmf"
ARG TEXMFCONFIG="~/.texlive/texmf-config"
ARG TEXMFVAR="~/.texlive/texmf-var"
      # "TEXDIR /opt/texlive" \
      # "TEXMFLOCAL /opt/texlive/texmf-local" \
      # "TEXMFSYSCONFIG /opt/texlive/texmf-config" \
      # "TEXMFSYSVAR /opt/texlive/texmf-var" \
      # "TEXMFHOME ~/texmf" \
      # "TEXMFCONFIG ~/.texlive/texmf-config" \
      # "TEXMFVAR ~/.texlive/texmf-var" \
#ARG ARCHIVE="ftp://tug.org/historic/systems/texlive/"
ARG ARCHIVE="ftp://ftp.math.utah.edu/pub/tex/historic/systems/texlive/"

ENV PATH /opt/texlive/bin/x86_64-linuxmusl:$PATH

# texlive のインストール
RUN apk add --no-cache curl perl fontconfig-dev freetype-dev && \
    apk add --no-cache --virtual .fetch-deps xz tar && \
    mkdir /tmp/install-tl-unx && \
    curl -L ${ARCHIVE}${TEXLIVE_VERSION}/install-tl-unx.tar.gz | \
      tar -xz -C /tmp/install-tl-unx --strip-components=1 && \
    printf "%s\n" \
      "selected_scheme scheme-basic" \
      "" \
      "TEXDIR ${TEXDIR}" \
      "TEXMFLOCAL ${TEXMFLOCAL}" \
      "TEXMFSYSCONFIG ${TEXMFSYSCONFIG}" \
      "TEXMFSYSVAR ${TEXMFSYSVAR}" \
      "TEXMFHOME ${TEXMFHOME}" \
      "TEXMFCONFIG ${TEXMFCONFIG}" \
      "TEXMFVAR ${TEXMFVAR}" \
      "" \
      "binary_x86_64-darwin 0" \
      "binary_x86_64-linux 1" \
      "binary_win32 0" \
      "" \
      "option_doc 0" \
      "option_src 0" \
      "" \
      "in_place 0" \
      "option_adjustrepo 0" \
      "option_autobackup 0" \
      "option_backupdir tlpkg/backups" \
      "option_desktop_integration 0" \
      "option_file_assocs 0" \
      "option_fmt 1" \
      "option_letter 0" \
      "option_menu_integration 0" \
      "option_path 0" \
      "option_post_code 1" \
      "option_sys_bin /usr/local/bin" \
      "option_sys_info /usr/local/share/info" \
      "option_sys_man /usr/local/share/man" \
      "option_w32_multi_user 0" \
      "option_write18_restricted 1" \
      "portable 0" \
      "" \
      "tlpdbopt_install_docfiles 0" \
      "tlpdbopt_install_srcfiles 0" \
      > /tmp/install-tl-unx/texlive.profile && \
    /tmp/install-tl-unx/install-tl \
      --profile=/tmp/install-tl-unx/texlive.profile \
      --repository ${ARCHIVE}${TEXLIVE_VERSION}/tlnet-final/ && \
    rm -fr /tmp/install-tl-unx && \
    apk del .fetch-deps
RUN tlmgr install \
        collection-fontsextra \
        collection-fontsrecommended \
        collection-latexextra \
        collection-langjapanese \
        collection-luatex \
        collection-mathscience \
        collection-xetex \
        pxchfon plautopatch \
        platex platex-tools uplatex \
        pxjahyper pxpgfmark \
        biblatex biber \
        latexmk

RUN tlmgr install \
        mylatexformat
RUN tlmgr install \
        biblatex-chem \
        biblatex-phys \
        biblatex-nature \
        biblatex-science \
        biblatex-ieee

RUN apk --update add --no-cache netcat-openbsd unzip

# jlisting をダウンロードする
RUN mkdir -p ${TEXMFLOCAL}/tex/latex/jlisting && \
    curl -o ${TEXMFLOCAL}/tex/latex/jlisting/jlisting.sty.bz2 -O -L "https://osdn.net/frs/redir.php?m=nchc&f=mytexpert%2F26068%2Fjlisting.sty.bz2" && \
    bzip2 -d ${TEXMFLOCAL}/tex/latex/jlisting/jlisting.sty.bz2

# Noto Sans CJK jp
RUN curl -o /tmp/NotoSansCJKjp-hinted.zip -O -L https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip && \
    mkdir -p ${TEXMFLOCAL}/fonts/opentype/google/notosanscjk/ && \
    unzip -d ${TEXMFLOCAL}/fonts/opentype/google/notosanscjk/ /tmp/NotoSansCJKjp-hinted && \
    chmod 644 ${TEXMFLOCAL}/fonts/opentype/google/notosanscjk/*.otf
# Noto Serif CJK jp
RUN curl -o /tmp/NotoSerifCJKjp-hinted.zip -O -L https://noto-website-2.storage.googleapis.com/pkgs/NotoSerifCJKjp-hinted.zip && \
    mkdir -p ${TEXMFLOCAL}/fonts/opentype/google/notoserifcjk/ && \
    unzip -d ${TEXMFLOCAL}/fonts/opentype/google/notoserifcjk/ /tmp/NotoSerifCJKjp-hinted && \
    chmod 644 ${TEXMFLOCAL}/fonts/opentype/google/notoserifcjk/*.otf
# Source Han Sans JP
RUN curl -o /tmp/SourceHanSansJP.zip -O -L https://github.com/adobe-fonts/source-han-sans/releases/download/2.004R/SourceHanSansJP.zip && \
    mkdir -p ${TEXMFLOCAL}/fonts/opentype/adobe/sourcehansans && \
    unzip -d ${TEXMFLOCAL}/fonts/opentype/adobe/sourcehansans/ /tmp/SourceHanSansJP.zip
# Source Han Serif JP
RUN curl -o /tmp/SourceHanSerifJP.zip -O -L https://github.com/adobe-fonts/source-han-serif/releases/download/2.001R/12_SourceHanSerifJP.zip && \
    mkdir -p ${TEXMFLOCAL}/fonts/opentype/adobe/sourcehanserif && \
    unzip -d ${TEXMFLOCAL}/fonts/opentype/adobe/sourcehanserif/ /tmp/SourceHanSerifJP.zip

RUN mktexlsr

RUN kpsewhich NotoSerifCJKjp-Regular.otf
RUN kpsewhich NotoSansCJKjp-Black.otf
RUN kpsewhich SourceHanSerifJP-Regular.otf
RUN kpsewhich SourceHanSansJP-Regular.otf

WORKDIR /workdir

CMD ["sh"]

docker-compose.yml

version: "3"

services:
  latex:
    build:
      context: .
      target: latex
      args:
        TEXLIVE_VERSION: 2021
    tty: true
    image: tex_latex:latest
    container_name: latex