Matplotlib が PC で追加のフォントをインストールしなくても日本語を表示できるようになった

遊ゴシック、ヒラギノ等日本語対応フォントの多くは ttc ファイルで提供されていますが、Matplotlib が ver 3.1.0 で ttc ファイルにやっと対応しました。( [Support (first font of) TTC files. #9787](https://github.com/matplotlib/matplotlib/pull/9787) ）

ただし、その PR にもあるように、まだ次のような問題があります。でも、使えるようになっただけでも進歩です。

- TTCファイルには複数のフォントを含めることができるが、そのコレクションの最初のフォントだけをサポートしている。
- pdf や ps のフォント埋め込みには対応していない。
- フォントの weight を正しく取得できていない場合があって、予期していない太さのフォントが表示される場合がある。

動作テストは簡単にできるのでしてみました。まず、matplotlib を最新版に更新します。

```
pip install --upgrade matplotlib
```

なお、Google Colab を使ってテストをすることもできますが、Google Colab は日本語フォントがインストールされていないので先にフォントをインストールします。

```
!apt -qy install fonts-noto-cjk
!pip install -q --upgrade matplotlib
```

そして、何でもいいので matplotlib で、日本語を表示するグラフを描いてみてください。ただし、設定ファイル (matplotlibrc) で日本語を指定していない場合は、以下のように rcParams を使ってコードの最初でフォントの設定を変更をしておきます。

```
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['font.family'] = 'sans-serif'
rcParams['font.sans-serif'] = ['Hiragino Maru Gothic Pro', 'Yu Gothic', 'Meirio', 'Takao', 'IPAexGothic', 'IPAPGothic', 'Noto Sans CJK JP']

plt.plot([1, 2, 3, 4])
plt.xlabel('日本語フォントです。ABCWijG123')
plt.show()
```
![matlib-test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103055/517c4ada-5d3e-6162-e074-378b62a2d5c0.png)

日本語が豆腐にならずにきちんと表示されるはずです。ただし、Colab 等の Jupyter Notebook 使っている場合は、一度パッケージを読み込むとパッケージをアップグレードしても Restart するまで有効にならないので、Restart が必要な場合があります。

なお、フォントのキャッシュファイルの方は `fontlist-v310.json` というように matplotlib のバージョン毎に別ファイルになったので、新しくフォントをインストールしなければキャッシュをクリアーをする必要はないです。

## 具体的な問題点

最初に問題点を書きましたが、使ってみると具体的には次のような問題がありました。

Ubuntu の Noto CJK フォントは、日本語だけでなく、繁体字中国語、簡体字中国語、韓国語をカバーした TTC ファイルになっていますが、幸運なことに日本語がコレクションの最初のフォントになっているので日本語での利用が可能です。

一方で、昨年の秋から Windows10 に標準で搭載されている BIZ UD フォントはコレクションの最初のフォントが BIZ UDGothic で BIZ UDPGothic ではないので、等幅フォントとしては使えても、残念ながらゴシック体のフォントとしては使いづらいです。

Mac のヒラギノ角ゴシックは、weight（太さ）が極細の 0 から極太の 900 まで 10 種類のフォントが標準で提供されていて非常に便利なフォントなのですが、var 3.1.0rc1 ではフォントの weight を正しく取得できずに細いフォントで表示されることがあります。それで次のようなコードを書いて、キャッシュファイル `fontlist-v310.json` の内容を調べてみました。

```
import json
import os
import re
import matplotlib as mpl

m = re.match('(\d)\.(\d)\.', mpl.__version__)
fontlist_path = os.path.join(mpl.get_cachedir(), f'fontlist-v{m.group(1)}{m.group(2)}0.json')
with open(fontlist_path, 'r') as f:
    fontlist = json.load(f)
for font in fontlist['ttflist']:
    if font['name'] == 'Hiragino Sans':
        print(font)
```
次のように結果になり、weight の小さなフォントが誤って 400 でキャッシュされていました。標準の場合には weight が 400 で最初にあるものが選択されるので、場合によっては w0 や w1 にような極細のフォントが選択される場合があることがわかります。

```
{'fname': '/System/Library/Fonts/ヒラギノ角ゴシック W7.ttc', 'name': 'Hiragino Sans', 'style': 'normal', 'variant': 'normal', 'weight': 700, 'stretch': 'normal', 'size': 'scalable', '__class__': 'FontEntry'}
{'fname': '/System/Library/Fonts/ヒラギノ角ゴシック W2.ttc', 'name': 'Hiragino Sans', 'style': 'normal', 'variant': 'normal', 'weight': 400, 'stretch': 'normal', 'size': 'scalable', '__class__': 'FontEntry'}
{'fname': '/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc', 'name': 'Hiragino Sans', 'style': 'normal', 'variant': 'normal', 'weight': 400, 'stretch': 'normal', 'size': 'scalable', '__class__': 'FontEntry'}
{'fname': '/System/Library/Fonts/ヒラギノ角ゴシック W1.ttc', 'name': 'Hiragino Sans', 'style': 'normal', 'variant': 'normal', 'weight': 400, 'stretch': 'normal', 'size': 'scalable', '__class__': 'FontEntry'}
{'fname': '/System/Library/Fonts/ヒラギノ角ゴシック W0.ttc', 'name': 'Hiragino Sans', 'style': 'normal', 'variant': 'normal', 'weight': 400, 'stretch': 'normal', 'size': 'scalable', '__class__': 'FontEntry'}
{'fname': '/System/Library/Fonts/ヒラギノ角ゴシック W9.ttc', 'name': 'Hiragino Sans', 'style': 'normal', 'variant': 'normal', 'weight': 700, 'stretch': 'normal', 'size': 'scalable', '__class__': 'FontEntry'}
{'fname': '/System/Library/Fonts/ヒラギノ角ゴシック W4.ttc', 'name': 'Hiragino Sans', 'style': 'normal', 'variant': 'normal', 'weight': 400, 'stretch': 'normal', 'size': 'scalable', '__class__': 'FontEntry'}
{'fname': '/System/Library/Fonts/ヒラギノ角ゴシック W8.ttc', 'name': 'Hiragino Sans', 'style': 'normal', 'variant': 'normal', 'weight': 700, 'stretch': 'normal', 'size': 'scalable', '__class__': 'FontEntry'}
{'fname': '/System/Library/Fonts/ヒラギノ角ゴシック W5.ttc', 'name': 'Hiragino Sans', 'style': 'normal', 'variant': 'normal', 'weight': 700, 'stretch': 'normal', 'size': 'scalable', '__class__': 'FontEntry'}
{'fname': '/System/Library/Fonts/ヒラギノ角ゴシック W6.ttc', 'name': 'Hiragino Sans', 'style': 'normal', 'variant': 'normal', 'weight': 700, 'stretch': 'normal', 'size': 'scalable', '__class__': 'FontEntry'}
```
Ubuntu の Noto CJK フォントも、`fonts-noto-cjk-extra` をインストールすると、'Noto Sans CJK' フォントファイルは 7個になるのですが、`Thin` が間違って weight 400 になっているので、ヒラギノ角ゴシックと同じ問題が発生します。

## すべての PC で問題なく日本語を表示する設定
以上を考慮して、Windows, Mac, Linux の殆どすべての PC で、フォントを新しくインストールせずに問題なく日本語が表示できる設定を考えてみました。

```
rcParams['font.sans-serif'] = ['Hiragino Maru Gothic Pro', 'Yu Gothic', 'Meirio', 'Takao', 'IPAexGothic', 'IPAPGothic', 'VL PGothic', 'Noto Sans CJK JP']
```

- Windows用  
  遊ゴシック (Yu Gothic)  
  メイリョウ (Meirio)
- Mac 用  
  ヒラギノ丸ゴシック (Hiragino Maru Gothic Pro)
- Linux 用  
  Noto Sans CJK JP (Noto Sans CJK JP)  
  Takao フォント (Takao)  
  IPAexゴシック (IPAexGothic)  
  IPA Pゴシック (IPAPGothic)  
  VL Pゴシック (VL PGothic)  

最後に、今回のテーマとは少し外れるのですが、日本語等幅フォントを使う場合に英数字:かな漢字=1:2では見づらいという場合には、[源ノ角ゴシック Code JP (Source Han Code JP)](http://blogs.adobe.com/CCJKType/2015/06/source-han-code-jp.html) を使うと英数字:かな漢字=2:3 なので英数字も読みやすいです。