Linux環境のmatplotlibにおける日本語の文字化け（通称：豆腐）の治し方

# はじめに
普段業務ではWindows PCを用いていますが、演算はLinuxサーバーで実行することが多く、その際に日本語が入ったグラフをmatplotlibで可視化しようとしたときに文字化けしてしまいました。
通常、Windowsのように日本語のフォントが環境にインストールされているのであれば `plt.rcParams["font.family"] = "日本語のフォント名称"` とすることでこの文字化けは解消できるのですが、日本語のフォントがインストールされていない（or むやみにインストールしてはいけない）環境では、この方法では解決できません。
よって当該記事では、日本語フォントがインストールされていないLinux環境のmatplotlibにおける日本語の文字化けの治し方について、備忘として残します。

# 結論
端的に言うと、次の3点を実施することで日本語の文字化けが解消できます。
1. フォントファイルをダウンロードする
2. フォントファイルのパスを`fontManager.addfont()`する
3. `plt.rcParams["font.family"]`に`FontProperties().get_name()`でフォントの名称を渡す

以下、手順など詳細を記載します。

# 手順
## 1. フォントファイルをダウンロードする
今回は、フリーのフォントが色々提供されている Google Fonts (https://fonts.google.com/) から好きなフォントをダウンロードします。
例えばWindows環境で用いるような"Meiryo"相当であれば、フォントは王道の"Noto Sans Japanese"あたりが良いかと思います（今回の例では個人的な好みにより、等幅フォントの"BIZ UDGothic"を用います）。
ダウンロード手順は以下です。
1. https://fonts.google.com/ にアクセス
2. 検索窓にフォント名称を記載（今回は"BIZ UDGothic"）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631516/cfe55ecc-5ba7-4ba2-9f0c-1ea138eb9a68.png)
3. "Get font" をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631516/94c8aa49-e763-414b-95b9-538fecb59ccc.png)
4. "Download all" をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631516/1505b5ef-52cd-4007-b458-02babef2e839.png)
5. .zipファイルがダウンロードされるので展開しておく

ダウンロードが完了したら、.zipから展開したフォルダを文字化けを解消したい環境に移動させましょう。
例えば、あるLinuxサーバーを複数人で使用することを想定すると、homeの階層(`~/`)に`fonts/`といったフォルダを作り、そこに展開したフォルダを入れておくと他の人も使えるので良いでしょう。

## 2. フォントファイルのパスを`fontManager.addfont()`する
## 3. `plt.rcParams["font.family"]`に`FontProperties().get_name()`でフォントの名称を渡す
ここからはPythonのコードの説明に移りますが、コード量少ないので、2つの章をここで消化します。
今回の検証環境は、
- Windows 11 Home 64 bit(10.0,ビルド26100)・・・・PC自体
- WSL2 (Ubuntu-24.04)・・・・Linuxサーバーに見立てて使用
- miniconda3・・・・condaの仮想環境を使用
- python3.12.9・・・・conda create時に新しめのバージョン指定

としています。
また、下記コードは.ipynbで実行されることを想定しています。

まずは使用するライブラリをインポートします。
`fontManager`, `FontProperties`はフォント設定で使います。
```python
import datetime
import gc
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.font_manager import fontManager, FontProperties
```
次に、テキトーにデータを作って、フォントが適用されているかどうかを確認するための可視化の関数を作っておきます。
```python
def plot_example():
    df: pd.DataFrame = (
        pd.date_range(
            start=datetime.datetime(2025,1,1,0,0,0),
            end=datetime.datetime(2025,1,8,0,0,0),
            freq="1s",
            inclusive="left",
        )
        .to_frame(index=False, name="date_time")
        .assign(
            time_id=lambda d: d.index,
            x_axis=lambda d: 0.2*np.sin(2*np.pi*d["time_id"]*1e-5),
            y_axis=lambda d: 0.3*np.cos(2*np.pi*d["time_id"]*2e-5),
        )
    )

    fig = plt.figure(figsize=(8, 8*9/16), facecolor="white", dpi=100)
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(df["time_id"], df["x_axis"], label="X軸")
    ax.plot(df["time_id"], df["y_axis"], label="Y軸")
    ax.grid(color="gray", linestyle="--")
    ax.legend(loc="upper right")
    ax.set_ylabel("振幅[m]")
    plt.show()
    plt.clf()
    plt.close()
    gc.collect()
```
まだフォント設定とかは何もしていませんが、現状どうなるかグラフ描画して確認してみましょう。
```python
print(f'font.family = {plt.rcParams["font.family"]}')
plot_example()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631516/bd61289b-541f-4357-b22f-3156f858a786.png)
font.familyは["sans-serif"]となっており、赤丸で強調したところのように、"軸"と"振幅"がどちらも文字化けし、通称：豆腐（"□"）になっていますね。
画像では見切れていますが、エラー文の右側を見ると、`UserWarning: Glyph 25391 (\N{CJK UNIFIED IDEOGRAPH-632F}) missing from font(s) DejaVu Sans.`と記載されており、規定値の"DejaVu Sans"が用いられているようです。
そこで、先ほどダウンロードして環境に持ってきたフォントファイルを参照し、matplotlibの設定値を以下のように変更します。
```python
path_font = Path("../../.fonts/BIZ_UDGothic/BIZUDGothic-Regular.ttf")
assert path_font.exists(), f"{path_font.resolve()} のファイルは見つかりません。"
fontManager.addfont(path_font)
plt.rcParams["font.family"] = (
    FontProperties(fname=str(path_font))
    .get_name()
)
```
それでは、もう一度描画してみましょう。
```python
print(f'font.family = {plt.rcParams["font.family"]}')
plot_example()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631516/1814d033-4b31-4c84-baea-c11337d66d1d.png)
先ほど豆腐だった日本語が文字化けせず表示されるようになりました。

# 補足
もし上記手順で上手くいかなかった場合は、以下をお試しください。
- `rm ~/.cache/matplotlib/`でキャッシュを削除
- .ipynbであればカーネルを再起動して実行