macOSにフォールバックフォントを追加する

## 要旨

Powerlineをはじめ，fishやzshのテーマでアイコンフォントが使われているケースがある．
これは当然ながら標準のフォントには含まれていないものであり，そのままだと文字化けしてしまう．
文字化けを解決するためには，対応するグリフを含むフォントを導入すれば良いが，アイコンフォントを含む自分好みのフォントを手に入れたい場合自分で合成することになるだろう．それはそれで良いのだが，次のようなデメリットがある．

* フォントのファイルサイズが大きくなる
* 異なるフォントを使いたい場合やフォントが更新された場合，フォントを再合成する必要がある

前者は良いとして後者は少し面倒である．
できれば通常のフォントとアイコンフォントは分けて管理したい．
実現する方法で何か良いものはないか探していたところ，フォントフォールバックを使うことで実現できることが分かった．

## フォントフォールバックとは

fallbackという単語がコンピュータの文脈で使用される場合，`縮退` や `代替` という意味合いで使われるのでなんとなく予想がつく通り，表示しようとしている文字のグリフが指定されたフォントにない場合に代替となるフォントを走査して，対応するグリフがあれば表示してくれるというというものだ．

Web関係の仕事をしていれば，意識的か無意識か問わずフォントフォールバックを使用していることだろう．
CSSの `font-family` を複数指定したことがあればそれである．
font-familyはカンマ区切りで複数のフォントを指定でき，前方にあるフォントがシステムに入っていればそれを適用し，なければ次の候補を代替 （フォールバック）として使用する．

OSのGUIでこの機能を意識したことはなかったが，LinuxであればFontconfig，macOSであればCoreTextの設定でフォールバックフォントを制御できることが分かったので，これをアイコンフォントの表示に利用することにした．

## 設定

ここではmacOS 10.14 Mojaveのフォントフォールバック設定を変更する．
フォントフォールバックの設定はSIPによって保護される `/System` 以下のファイルを編集する必要があるため，SIPのファイルシステムに対する制限解除と再設定で，都合2回の再起動を必要とする．

### 使用するフォールバックフォントのインストール

私は[sidebar.el](https://github.com/sebastiencs/sidebar.el)を使用しているので，アイコンフォントには[icons-in-terminal](https://github.com/sebastiencs/icons-in-terminal)を使用する．
これにはFont AwesomeやPowerline Extra Symbols, file-icons, material-design-iconsなどが含まれているので，現在使用しているfishのテーマやemacsの拡張で困ることはない．

sidebar.elを使っている，またはfontconfigでフォールバックフォントを制御できるLinux環境でリポジトリをクローンして適宜インストールを行えば良いが，icons-in-termianlフォントを導入してフォールバックフォントに設定するだけであれば，リポジトリに含まれるビルド済みフォントだけをダウンロードすれば良い．

```
curl https://raw.githubusercontent.com/sebastiencs/icons-in-terminal/master/build/icons-in-terminal.ttf -o ~/Library/Fonts/icons-in-terminal.ttf
```

これでユーザのフォントディレクトリにicons-in-termianlがインストールされる．

### SIPによるFS保護の一時無効化

`/usr` や `/System`など，SIPによってシステムのみが変更できるように保護されたディレクトリは，Disk Full Accessが許可されたターミナルエミュレータからでも編集することができない．

これらのディレクトリを変更するためには，リカバリモードでSIPの制限を解除する必要がある．

リカバリモードに入るには，一旦macをシャットダウンして，`Command (⌘) - R` を押下した状態で起動する．ディスク内のリカバリ領域を削除している場合は，`Command (⌘) – Option (⌥) – R` を押下することでインターネット経由でリカバリモード用のイメージを取得することができる．

リカバリモードが起動したらメニューバーからターミナルを起動し，次のコマンドを実行する．

```
csrutil enable --without fs
```

当然ながら，SIPが完全に無効化された状態というのは，あまり望ましい状態ではない．
上記コマンドはファイルシステムの保護だけを無効化し，そのほかの保護機能は有効な状態を維持できる．

成功の表示が出たらターミナルを閉じて再起動を行う．

### DefaultFontFallbacks.plistを編集する

macのフォールバックフォントは以下のplistで管理されている．

```
 /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/Resources/DefaultFontFallbacks.plist
```

まず編集のために適当な場所へコピーする．

```
sudo cp /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/Resources/DefaultFontFallbacks.plist ~/
```

plistといえばxmlだろうと思っていたのだが，このファイルはバイナリ形式になっているので，編集できるようxml形式に変換する．

```
plutil -convert xml1 ~/DefaultFontFallbacks.plist
```

あとは好みに合うようにplistを編集すればよい．

今回は等倍フォントとサンセリフフォントのフォールバックとしてicons-in-terminalを使うようにしたいので，
以下のkeyのarrayにicons-in-terminalを追加した．

* monospace
* sans-serif

<script src="https://gist.github.com/cariandrum22/822bc1b2e0d8b6dd6b037aae2f5b7f1d.js"></script>

編集が完了したら，編集までの手順を逆に行えばよい．

まず，形式をバイナリに変換する．

```
plutil -convert binary1 ~/DefaultFontFallbacks.plist
```

続いて元の場所にファイルをコピーする．

```
sudo cp ~/DefaultFontFallbacks.plist /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/Resources/DefaultFontFallbacks.plist
```

### SIPによるFS保護の有効化

無効化したFS保護を再度有効化する．

無効化した時と同様，再起動してリカバリモードに入ったらターミナルを開き，以下のコマンドを実行する．

```
csrutil enable
```

成功の表示が出たらターミナルを閉じて再起動を行う．

## 最後に

自分で合成したフォントは細かい微調整も可能でポータビリティも高い．
わざわざフォントフォールバックを設定する必要はないという人が大半だろうが，私は気分や書くものによってフォントを変えたりするので，その全てを合成フォントにするよりはフォールバックを使ったほうが都合が良かった．

Linuxでフォントフォールバックを利用する方法は簡単に見つかったのだが，macOSでやる方法がGitHubのIssueぐらいでしか見つからなかったので，備忘のために残しておく．