環境構築メモ ~ Visual Studio Code + WSL2 + Ubuntu + Docker + LaTeX のコンパイル環境

目的

LaTeX コンパイル環境を構築したい。LaTeX はモジュールや文字フォント等で複雑な依存関係がありそう、ということで、構築失敗による環境汚染を避けるために Ubuntu Docker コンテナで実現したい。LaTeX の文書作成 & PDF 閲覧は Windows 上の Visual Studio Code で行いたい。(あれもこれも、欲張りだなぁ ...)

前提

Windows 10 上には Visual Studio Code と SumatraPDF *1 が、WSL2 上には Ubuntu 20.04 LTS と Docker が既に導入されているものとする。

成果

以下の手順を踏むと Visual Studio Code にて次のことが可能になる。

  • Ctrl + Alt + B (Build) でビルド
  • Ctrl + Alt + V (View PDF) で PDF 閲覧
  • Ctrl + Alt + C (Clear intermediate files) で中間ファイル削除

一度、ビルドが通ると、編集 → ファイル保存、するだけで自動リビルドされる。

TeX 記述サポートにおいても、@a と書くだけで \alpha に補完される、など Visual Studio Code はかなり便利。LaTeX Workshop のスニペットこちら

Ubuntu での LaTeX 実行環境構築

こちらの記事 によると Docker で日本語 LaTeX をやるには paperist/alpine-texlive-ja イメージで uplatex コマンドを使うのがよいらしい。ということで、このイメージを Docker Hub から docker pull しておく。次に ~/.bashrc に以下のように記述しておく。

# latex through docker
function func_tex2pdf() {
  docker run --rm -it -v /mnt/d/[working folder path on raw Ubuntu]/$1:/workdir paperist/alpine-texlive-ja latexmk -pdfdvi -latex=uplatex -e '$dvipdf=q/dvipdfmx %O -o %D %S/;' -synctex=1 -silent $1.tex;
}

alias tex2pdf=func_tex2pdf

これで WSL2 Ubuntuコマンドラインから tex2pdf Sample と打つことで、Docker コンテナをワンタイム起動し、コンテナ内の作業フォルダ /workdir にある tex ファイルから同フォルダに pdf が生成できるようになる。ここでのポイントは以下のとおり。

  • コンテナの /workdir-v オプションでホスト Ubuntu の作業フォルダにマウントしている
  • さらに当該 Ubuntu フォルダの実体は Windows 側の NTFS ドライブ (/mnt/d/ すなわち d:/) にある
  • 環境変数 $1 にはコマンドの第1引数 (上述の例では Sample) が入り、共通フォルダの下のサブフォルダ Sample を作業フォルダとしてその中の Sample.tex を対象ファイルとしている
  • 環境変数$1alias 文内で2度使えないため、関数定義にする
  • オプション -synctec は後述 (Ubuntu 環境では利用する予定なし)
  • uplatex や dvipdf による一連の作業を latexmk を用いたワンライナーで実行する (texwiki 参照)
  • 当該イメージ内に dvipdf がないため、代わりに dvipdfmx を用いることを Perl 表記の引数で指示している *2
  • マウント対象パスのスペース文字はエスケープシーケンス \ ("\" + Space) になる

Visual Studio CodeLaTeX 作業環境構築

こちらの記事 に従い、Visual Studio CodeLaTeX Workshop 拡張機能をインストールする。

Visual Studio CodeJSON 設定

次に Power Shell から WSL を通じて上述の Docker イメージのワンタイム起動を行うように Visual Studio Code に設定する。ただし先ほど定義した .bashrc の tex2pdf エイリアスをそのまま利用することはできない。Visual Studio Code の設定 (JSON ファイル *3 ) には以下のように記述する。

    // -- LaTeX Workshop --
        // 使用パッケージのコマンドや環境の補完を有効にする
        "latex-workshop.intellisense.package.enabled": true,

        // 生成ファイルを削除するときに対象とするファイル
        // デフォルト値に "*.synctex.gz" を追加
        "latex-workshop.latex.clean.fileTypes": [
            "*.aux", "*.bbl", "*.blg", "*.idx", "*.ind", "*.lof",
            "*.lot", "*.out", "*.toc", "*.acn", "*.acr", "*.alg",
            "*.glg", "*.glo", "*.gls", "*.ist", "*.fls", "*.log",
            "*.fdb_latexmk", "*.snm", "*.nav", "*.dvi",
            // "*.synctex.gz", // 中間ファイル消去後も SyncTeX 利用の場合はコメントアウト
        ],
    
        // ビルドのレシピ
        "latex-workshop.latex.recipes": [
            {
                "name": "tex2pdf",
                "tools": [
                    "tex2pdf",
                ]
            },
        ],
    
        // ビルドのレシピに使われるパーツ
        "latex-workshop.latex.tools": [
            {
                "name": "tex2pdf",
                "command": "wsl",
                "args": [
                    "docker",
                    "run",
                    "--rm",
                    "-v",
                    "/mnt/d/[working folder path on raw Ubuntu]/%DOCFILE%:/workdir",
                    "paperist/alpine-texlive-ja",
                    "sh",
                    "-c",
                    "latexmk -latex=uplatex -synctex=1 -silent %DOCFILE%.tex && dvipdfmx %DOCFILE%.dvi"
                ],
            },
        ],

        // PDF Viewer の表示方法
        "latex-workshop.view.pdf.viewer": "tab",      // Internal PDF Viewer
     // "latex-workshop.view.pdf.viewer": "external", // External PDF Viewer

        // SyncTeX
        "latex-workshop.view.pdf.internal.synctex.keybinding": "double-click", // or "ctrl-click" 
        "latex-workshop.synctex.afterBuild.enabled": true,
        "latex-workshop.synctex.path": "synctex",
        "latex-workshop.synctex.synctexjs.enabled": true,

        // Internal PDF Viewer Settings
        "latex-workshop.view.pdf.zoom": "page-width",

        // External PDF Viewer
        "latex-workshop.view.pdf.external.viewer.command": "c:/Users/[user name]/AppData/Local/SumatraPDF/SumatraPDF.exe",
        "latex-workshop.view.pdf.external.viewer.args": [
          "-reuse-instance",
          "%PDF%",
        ],
        "latex-workshop.view.pdf.external.synctex.command": "c:/Users/[user name]/AppData/Local/SumatraPDF/SumatraPDF.exe",
        "latex-workshop.view.pdf.external.synctex.args": [
            "-reuse-instance",
            "%PDF%",
            "-forward-search",
            "%TEX%", // Sumatra PDF がなぜかこのパスを正しく認識しないため、順方向検索が無効
            "%LINE%",
            "-inverse-search",
            "\"c:\\Users\\[user name]\\AppData\\Local\\Programs\\Microsoft VS Code\\bin\\code.cmd\" -r -g \"%f:%l\"",
        ],

ここまでの作業により、Visual Studio Code のビルドコマンドを実行すると、WSL と Docker を通じたワンライナーTeX 文書をコンパイルするようになる。ここまでのポイントは以下のとおり。

  • Power Shell から起動する WSLbash は対話シェルではないため、.bashrc が反映されない
  • したがって、Power Shell から WSL コマンドでワンライナー起動する
  • 対話シェルではないため、docker コマンドの引数 -it は付けない
  • JSON 設定の latex-workshop.latex.tools セクションに記述するワンライナー.bashrc と若干違うのは、dvipdf 設定の引数部分である Perl 記述が (どうエスケープシーケンスを書いても) 失敗してうまく起動しないため
  • 環境変数 %DOC% は用いず %DOCFILE% のみを用いるのが吉 (%DOC% の挙動が不安定で何度かハマっている。 参考記事 参照)
  • オプション -synctex によりコンパイル時に .synctex.gz ファイルを生成し、これにより tex と pdf 両ファイルでの項目位置を紐づけ、クリックによる相互ジャンプを可能にする (後述)
  • マウント対象パスのスペース文字はエスケープシーケンス不要になる
PDF Viewer の設定

この記事この記事 を参考に synctex 関連を設定する。

Sumatra PDF を起動し、メニュー > 三 > 設定 > 詳細設定 を開き、設定ファイルの下記部分を修正する *4

EnableTeXEnhancements = true
InverseSearchCmdLine = "c:\Users\[user name]\AppData\Local\Programs\Microsoft VS Code\bin\code.cmd" -r -g "%f:%l"

これにより、Sumatra PDF をダブルクリックしたときに、逆順検索 (ダブルクリック箇所に対応する Visual Studio Code 上の LaTeX ファイル該当箇所へジャンプ) する。

課題

Visual Studio Code 内タブの PDF ビューアも外部 PDF ビューア Sumatra PDF も起動・表示・ビルド時再読み込みはうまく動作するが、いまのところ synctex は外部 PDF ビューアからの逆順検索しか成功しない ...
内部ビューアの場合は、おそらく Visual Studio Code 内の Ctrl 押しながら系の操作がキャンセルかインタラプトされている。
外部ビューアの場合は、Sumatra PDF が -forward-search の次の引数のファイルパスを正しく読み込めていない。不明なソースファイル (C:\ ..... \Sample.tex) と表示される。そのパスにファイルがあるにも関わらず。
継続調査。

*1:PDF をファイルロックせず、更新時に即時再読み込みをしてくれる PDF ビューア。Windows 上で Visual Studio Code + LaTeX Workshop 利用する時の推奨ビューア。

*2:クォーテーションがネストするため、Perl 部分は q// で表現する。

*3:%UserProfile%\AppData\Roaming\Code\User\settings.json

*4:ここでは、環境変数 %UserProfile% は無効。