VScode Dev Container でポータブルな Typst の日本語執筆環境を構築する

最近急速に勢いを伸ばしている、Rust製の新興組版システム [Typst](https://typst.app/) を、自分も使ってみようと思いました。Typst 自体の説明は[他の記事](https://qiita.com/tags/typst)が多数あると思いますので、ここではそのローカル執筆環境構築法の一例を説明しましょう。

# 普通の環境構築法

macOS環境の場合、例えば Homebrew や MacPorts を使っている環境ならば、`brew install typst` や `sudo port install typst` とすれば、依存関係を解決して、Rustやcargoなどの必要なソフトウェアを自動的にインストールしてくれます。

また、執筆環境としては、今時ならば VScode を使うのが標準的でしょう。[Tinymist Typst](https://marketplace.visualstudio.com/items?itemName=myriad-dreamin.tinymist) という拡張機能を入れれば、いい感じに執筆をサポートしてくれます。

# ポータブルなコンテナとして Typst 執筆環境を整えたい！

ですが、これで終わってしまっては面白くありません。ここでは、

* 自分の日頃の環境を汚すことなく Typst を試してみたい
* だがオンラインサービスとしてではなく、あくまでローカル環境で Typst を動かしたい
* **他のマシンで環境を再現しやすい、ポータブルな Typst 執筆環境を構築したい**

という願いをかなえるために、VScode Dev Container を使った Typst 環境構築法を述べます。

# 用意するもの

今時のエンジニアの環境ならば、

* VScode
* Docker

はインストールされていることでしょう。この2つさえあれば、他のソフトウェアをインストールすることなく、便利な Typst の執筆環境を持ち運べるようにする、ということを目指します。そのために、VScode の Dev Container という仕組みを使います。

ここでは、

* macOS
* VScode
* Docker Desktop for Mac

が存在する環境を前提に環境構築してゆきますが、macOS以外でも大きくは変わらないはずです。

# Typst Dev Container 環境構築手段
## Docker側の準備

後ほど、Dockerコンテナ内からmacOS標準インストールのフォント類が使えるようにするために、macOSのフォントディレクトリをDockerからバインドマウントするわけですが、Dockerからアクセス可能にするために、Docker Dekstopの設定に追加が必要です。

### Step 1
Docker Desktopのダッシュボードを起動し、設定の歯車アイコンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/201201ea-63e1-5a91-7095-854aa0be8712.png)

### Step 2

`Resources` → `File Sharing` を選び、右側の `Virtual file shares` までスクロールします。
そして、次に、`/path/to/exported/directory` のところの `Browse` ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/ed9f7614-7a0d-0008-3734-d5b9c133c184.png)


### Step 3

`/System/Library/Fonts` を選び、`+` ボタンを押して追加します。同じことを、`/Library/Fonts` に対しても繰り返します。両方が灰色の状態になれば追加成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/a7946c5f-552c-be99-55a3-0a74d0120b18.png)

### Step 4

右下の `Apply & restart` ボタンを押し、Dockerサービスを再起動して、設定変更を反映させます。

## VScode側の準備

VScodeの拡張機能で、`Dev Containers`、あるいはそれを含むパックである `Remote Development` をインストールしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/f1e4915f-a953-3b59-b757-c4ad1422e057.png)

## macOS側の準備

macOSのセキュリティに起因するトラブルを避けるため、VScodeとDockerに対して、フルディスクアクセスの権限を与えておきましょう。システム設定アプリの検索欄に「フルディスク」と打ち込んでフルディスクアクセスの画面を開き、`+` ボタンを押して、`/Applications/Visual Studio Code.app` と `/Applications/Docker.app` を追加しておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/dfe1fabd-fb8d-ea23-bddc-2e946e49651f.png)

そして、VScode や Docker を再起動しましょう。

これで準備は完了です。実際に文書作成を始めてみましょう。

# 執筆開始手順
## Typst 文書執筆フォルダを用意してVScodeで開く

例えば `~/Documents/typst` を Typst の執筆フォルダとしましょう。フォルダを作成し、そこを VScode で開きます。VScodeのCUIである `code` コマンドがインストールされていれば、次のようにして開けます。

```sh
$ mkdir ~/Documents/typst
$ cd ~/Documents/typst
$ code .
```
## Reopen in Container

VScode で `Command + Shift + P` でコマンドパレットを開き、`reopen`と打ち込めば、`Dev Containers: Reopen in Container` という項目が現れるはずですので、それを選びます。（新規作成時でも `Reopen` を選ぶのは、「このフォルダをコンテナで**開き直す**」という意味だからです。`Dev Containers: New Dev Container` を選ぶと、新しいフォルダを作り直すことになってしまいます。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/4fe6ee3a-2bea-bb04-ff00-f422f2d54734.png)

## 構成の追加場所の選択

コンテナ構成の設定を追加する場所を選ばされます。このフォルダをポータブルなものとしたい（他のマシンにコピーしたときに設定が持ち運べるようにしたい）ので、ここでは `ワークスペースに構成を追加する` を選びます。（`ユーザーデータフォルダーに構成を追加する` の方だと、端末のユーザワイド設定のところに追加されることになります。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/edeb0e85-8854-e482-37e1-040986611b8a.png)


## Typst Dev Container を作成する

コンテナのテンプレートとして、既に用意されている、`Typst Dev Container` というテンプレート（[ソースレポジトリ](https://github.com/jmuchovej/devcontainers/tree/main/templates/src/typst)）を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/6ee72ac8-3856-5c84-f653-e1392a37561f.png)

開発者を信頼するか聞かれたら、「信頼する」を選びましょう。

バージョン選択は、普通は `latest` でよいでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/588bfa84-b0cb-73fe-fd41-2507477f9a4c.png)

追加機能は、とりあえず特に何も選ばずに `OK` を押してよいでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/34c22903-33a3-65bb-0ab9-e2bfc5604766.png)

すると、Docker コンテナの生成が始まります。

## 動作確認

コンテナが起動したら、早速動作確認してみましょう。このコンテナと接続されているVScodeは、[Tinymist Typst](https://marketplace.visualstudio.com/items?itemName=myriad-dreamin.tinymist) など、Typst文書作成に必要な拡張機能一式が、既にインストールされて機能しています。

`test.typ` という文書ファイルを作ってみて、中身に `Hello, world!` を書き、`Preview` ボタンを押してみましょう。また、VScodeの統合ターミナルを起動し、`typst --version` を実行して、コンパイラの動作確認をしてみましょう。

![プレビュー 2024-11-13 00.43.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/220b2252-311b-84ec-fbb2-813050b0733b.png)

## macOS標準添付のフォントを使えるようにする

このままでは、macOSに標準インストールされているヒラギノフォントなどの日本語フォントなどがコンテナ側から見えていないので、和文文字を打ち込むと文字化けしてしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/d1a60fee-d576-c6e9-73ed-2b193d28ab71.png)


現在コンテナ内の Typst が認識しているフォントの一覧は、コンテナ内の統合ターミナルで `typst fonts` を実行すれば確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/dad99d15-9530-33c6-619b-616df76fc031.png)

このように、たった4個のフォントしか認識していません。これでは困ります。そこで、OS標準のフォントをコンテナ内の Typst に認識させるべく、コンテナにマウントします。

### `.devcontainer.json` を編集

#### OS標準のフォントフォルダのバインドマウント設定

`.devcontainer.json` を編集し、末尾に次のマウント設定を追記します。

```json:.devcontainer.json
  "mounts": [
    "source=/System/Library/Fonts,target=/usr/local/share/fonts/SystemLibraryFonts,type=bind,consistency=cached",
    "source=/Library/Fonts,target=/usr/local/share/fonts/LibraryFonts,type=bind,consistency=cached",
    "source=${localEnv:HOME}/Library/Fonts,target=/usr/local/share/fonts/homeLibraryFonts,type=bind,consistency=cached",
  ],
```

![Code 2024-11-13 01.11.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/3b71a64c-3769-7a54-73a9-3dade732de53.png)

これらは、ホスト → コンテナ で、

* `/System/Library/Fonts` → `/usr/local/share/fonts/SystemLibraryFonts`
* `/Library/Fonts` → `/usr/local/share/fonts/LibraryFonts`
* `~/Library/Fonts` → `/usr/local/share/fonts/homeLibraryFonts`

というバインドマウントを設定しています。

#### PDFプレビュー拡張機能を追加

ついでに、PDFをVScode内でプレビューするVScodeの拡張を導入しておきましょう。`.devcontainer.json` の `extensions` の中に `"tomoki1207.pdf"` を追加しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/43bd8497-b4eb-5397-2e5e-5b49ccd94094.png)

### Rebuild Container

`.devcontainer.json` を編集すると、コンテナのリビルドが提案されると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/0d25e43a-cdbe-6e88-c1d1-30aedcdcf68b.png)


そのボタンが出なかった場合は、`Command + Shift + P` でコマンドパレットを開き、`Rebuild Container` を選べば、コンテナのリビルドが始まります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/be7eaba5-aa7e-1a60-c427-6725490e9a37.png)

### フォント認識の確認

コンテナのリビルドが終わったら、統合ターミナル上で `typst fonts` としてフォント認識を確認しましょう。`Hiragino Mincho ProN` などのフォントが表示されれば、TypstからOS標準フォントが無事に見えていることになります。


### 和文文書確認

`#set text(font: "Hiragino Mincho ProN")` とすれば、本文書体がヒラギノ明朝体となります。その他、和文書体を適切に設定した Typst 文書を作り、プレビューしてみましょう。

```typst:test.typ
#set text(font: "Hiragino Mincho ProN")
#show strong: set text(font: "Hiragino Sans", weight: "medium", fill: red)
#show emph: set text(font: "Hiragino Maru Gothic ProN", fill: blue)

Hello, world!

こんにちは。ここは*強調*します。

_丸ゴシック_です。
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/4e624cc8-9b3f-5e69-d6d7-06852ed4a51f.png)

正しくヒラギノフォントが埋め込まれています！後は、`Export PDF` を押せば、最終生成物であるPDFも生成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/5a5a1c70-53b8-cbc3-28f6-d113eff7457f.png)

これは、ホスト側の `~/Documents/typst` 内に生成されていますので、

# ポータブルな Typst 執筆環境が実現できた！

この `~/Documents/typst` を持ち運べば、他のmacOS環境でも、VScodeとDockerさえあれば、どこでも Typst 執筆環境が実現できたことになります。ただし、`.devcontainer.json` は、ドット始まりのファイルであるため、デフォルトではFinder上で不可視となります。ファイル単位でコピーするときに抜け落ちやすいですから、気をつけましょう。

# 応用編①：この Typst 用コンテナ内でのみ有効なショートカットキーを設定する

このコンテナ内でよく使う機能として、上の `Export PDF` ボタンを押して最終生成PDFを得る、という機能があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/5a5a1c70-53b8-cbc3-28f6-d113eff7457f.png)

これにショートカットキーを設定してみましょう。

VScodeには、「ワークスペース内でのみ有効な設定(`setting.json`)」を設定する機能はありますが、「ワークスペース内でのみ有効なショートカットキー(`keybindings.json`)」を設定する機能はないようです。

そこで、

* このコンテナ内でのみ有効になる設定項目のフラグを立てる
* そのフラグが立っているときにのみ、特定のショートカットキーが有効になるように、ユーザーワイドの `keybindings.json` を設定する

という手順で設定しましょう。これは、

https://qiita.com/kei-g/items/43c38f918f4cc26d9c01

上記記事のアイデアに基づきます。

## Step 1:  `.devcontainer.json` においてフラグを立てる

`.devcontainer.json` において `"customizations"` → `"vscode"` → `"settings"` の中に、 

```json
        "containerKeybindings.myTypst.enabled": true
```

という設定項目を付け加えます。この `"containerKeybindings.myTypst.enabled"` 
 という名前は何でもよく、`"XXX.YYY.ZZZ"` の名前が一意であれば何でも構いません。

```json:.devcontainer.json
  "customizations": {
    "vscode": {
      // 中略
      "settings": {
        "files.eol": "\n",
        "editor.formatOnSave": true,
        "[typst]": {
          "editor.formatOnSave": true
        },
        "tinymist.formatterMode": "typstyle",
        "files.associations": {
          "*.typ": "typst"
        },
        "containerKeybindings.myTypst.enabled": true // 追加設定
      },
    }
  },
```

## Step 2: ユーザーワイドの `keybindings.json` に設定を加える

先程の設定項目名の頭に `config.` を加えて、`"config.containerKeybindings.myTypst.enabled"` が有効の場合にのみ、特定のショートカットキーで `"tinymist.showPdf"` というコマンドが発動するよう、ユーザーワイドの `keybindings.json` に設定を加えます。ここでは `Command+T` としましょう。

```json:keybindings.json
    // Typstのワークスペースで "containerKeybindings.myTypst.enabled": enabled になっている場合にのみ、`Command+T` でPDF生成とする
    {
        "key": "cmd+t",
        "command": "tinymist.showPdf",
        "when": "config.containerKeybindings.myTypst.enabled"
    }
```

これで、コンテナ内で `Command+T` を押すと、PDF生成が発動します。

# 応用編②：Typst のバージョンを最新版に差し替える

本記事執筆時点で、上記の方法でインストールされる Typst のバージョンは、コンテナイメージの更新日時の関係上、 `0.11.1` となりました。ですが、本記事執筆時点で、Typst の最新版は `0.13.0` です。Typst は変化が早いので、常に最新版に追従することが大切です。そこで、最新版の Typst を使うようにコンテナをビルドし直しましょう。

[`docker-bake.hcl`](https://github.com/jmuchovej/devcontainers/blob/main/images/src/typst/docker-bake.hcl) の記述に従い、次のように最新版を確認してゆきます。

## Typst の最新版確認

[Typst の latest リリース](https://github.com/typst/typst/releases/latest) にアクセスすると、本記事執筆時点では `v0.13.0` というタグに飛ばされました。そこで、`v0.13.0` というタグ名をメモしておきます。

## Typstyle の最新版確認

[Typstyle の latest リリース](https://github.com/Enter-tainer/typstyle/releases/latest) にアクセスすると、本記事執筆時点では `v0.12.15` というタグに飛ばされました。そこで、`v0.12.15` というタグ名をメモしておきます。

## Rust の最新版確認

[Microsoft のページ](https://mcr.microsoft.com/en-us/artifact/mar/devcontainers/rust/tags)でRustのバージョンを確認できますが、ここはおそらく `latest` を指定しておけばよいでしょう。

## `.devcontainer.json` の修正

`.devcontainer.json` に

```json:.devcontainer.json
"image": "ghcr.io/jmuchovej/devcontainers/typst:latest",
```

という記述がありますが、このイメージを pull してしまうと、Typst のバージョンが少し古いものになってしまいます。そこで、この行をコメントアウトし、代わりに次のように記載します。

```json:.devcontainer.json
  //  "image": "ghcr.io/jmuchovej/devcontainers/typst:latest",
  "build": {
    "dockerfile": "Dockerfile",
    "context": ".",
    "args": {
      "TYPST_VERSION": "v0.13.0",
      "TYPSTYLE_VERSION": "v0.12.15",
      "VARIANT": "latest", // Rustのバージョン
    }
  },
```

これで、コンテナをリビルドします。キャッシュを使わずに新規ビルドしましょう。

![Code 2024-11-13 12.08.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/910407e4-b144-ef91-310b-8cecdc717b23.png)

ビルドに少し時間がかかりますが、最新の Typst がコンテナにセットアップされるはずです。

## 確認

最新版 Typst が設定されたことを確認してみましょう。

```typst:test.typ
#set text(font: "Hiragino Mincho ProN")
#show strong: set text(font: "Hiragino Sans", weight: "medium", fill: red)
#show emph: set text(font: "Hiragino Maru Gothic ProN", fill: blue)

#let AppleEmoji(body) = {
    set text(font: ("Apple Color Emoji"))
    body
}

現在のTypstバージョンは #sys.version です。

ここは*強調*します。この部分は_丸ゴシック_です。

なお、☃は、#emoji.snowman;や#AppleEmoji("☃")などと出力されます。

```

カラー絵文字部分は、プレビュー画面では欠落してしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/87b7de5e-32c8-1f64-14c2-6fbfc2a95689.png)

ですが、`Export PDF` (`"tinymist.showPdf"`コマンド)を押して最終版PDFを生成させれば、きちんとカラー絵文字グリフがPDFに埋め込まれていることが確認できます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41739/78abf08c-875c-21b9-be36-c3051492344a.png)