自作の 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 が見つけられる場所に配置してあります. そのためこれらのフォントも使えるようになっています. これらは Google と Adobe が開発した日本語フォントで,非常にきれいだったりするので,フォントに困っていたらとりあえずこれを使えばいいと思います. それと初期設定でフォントを埋め込むようにしているはずなので,生成された 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