ill-identified diary

所属組織の見解などとは一切関係なく小難しい話しかしません

Rグラフィックスの文字化け問題中間報告 (2021年版)

この記事は最終更新日から3年以上が経過しています


概要

  • 去年書いた話のその後の更新について整理した.

おまえはもうRのグラフの日本語表示に悩まない (各OS対応) - ill-identified diary

あとこっちのスライドも閲覧数が少しづつ増えてるようなので

Mac でも Windows でも, PNG でも PDF でもRのグラフに好きなフォントで日本語を表示したい (2020年最終版)/Display-CJK-Font-in-Any-Gpraphic-Device-and-Platform-2020 - Speaker Deck

  • 去年の話の直後にいろいろと大きな環境変化あった
  • 改めて変更点と残る問題点をここでまとめる
  • 本当は不具合を潰してから書きたかったが思ったより時間がかかりそうなので「中間報告」
  • 以降の記述のほとんどはこれまでネット上のどこかで断片的に書いていた話で, 今回はそれらを一箇所にまとめただけ
  • tikz についてももう少し掘り下げたかったが時間がなかった
  • plotly とか shiny とか leaflet とかもブラウザ依存の問題なのでここでは扱わない

大まかに言えば, 使用する日本語フォントにもこだわりたい人でないなら後半はほとんど関係ないような内容.

なおこれはグラフの話であり, Windows などのコンソールのメッセージの文字化けの話ではない. その話はこっち.


要約

まず, 前回の記事で書いた (out-of-date になってしまった) 話は以下の6点に要約できる. 前回の話を読んでない人は飛ばして実際にどう操作すればいいのかを読めばいい

  1. Mac は XQuartz のインストールが必要 (初心者のための補足: Rコマンダー/EZR を使っていた人はインストール済みのはず)
  2. Windows および Mac ではデフォルト設定ではラスタ画像 (PNG, JPG) でも PDF でも文字化けする
  3. ラスタ画像は私の作った fontregisterer パッケージを読み込むことで簡単に文字化けを回避できる
  4. フォント名はファイル名やPOSTSCRIPT名ではなく常にフォントファミリ名を正しく書く必要
  5. PDF 画像は cairo_pdfバイス関数を使うと, (日本語に限らず) 多くのフォントを埋め込むことができ文字化けを回避できる
  6. SVG 画像は svglite::svgliteバイス関数で日本語フォントを指定できる (SVG に埋め込み機能はないので実際に表示できるかはクライアント側の環境依存)1
  7. 以上を守れば, 少なくとも各 OS の標準日本語フォントのいずれかを使用できる
  8. 一部のフォントはこの方法でもうまく表示できない

最後の表示できない問題は「残された不具合」に直結するので最後のセクションでまとめて解説する.

これが現在は次のように変わった.

  1. Mac は XQuartz のインストールが必要 (初心者のための補足: Rコマンダー/EZR を使っていた人はインストール済みのはず)
  2. Windows および Mac ではデフォルト設定ではラスタ画像 (PNG, JPG)2 でも PDF でも文字化けする
  3. ragg パッケージを使えば fontregisterer を使わずともラスタ画像は文字化けしない, というか, デフォルトのデバイスでも fontregisterer なしでもできなくはない. ラスタ画像は私の作った fontregisterer パッケージを読み込むことで簡単に文字化けを回避できる
  4. ragg を使用すればラスタ画像ならば指定しなくとも日本語フォントにフォールバックする フォント名はファイル名やPOSTSCRIPT名ではなく常にフォントファミリ名を正しく書く必要
  5. PDF 画像は RStudio のエクスポートオプションで cairo_pdf を使うよう設定するか, cairo_pdfバイス関数を使うことでフォントを埋め込み文字化けを回避できる. PDF 画像は cairo_pdfバイス関数を使うと, (日本語に限らず) 多くのフォントを埋め込むことができ文字化けを回避できる
  6. SVG 画像は svglite::svgliteバイス関数で日本語フォントを指定できる (SVG に埋め込み機能はないので実際に表示できるかはクライアント側の環境依存)
  7. 以上を守れば, 少なくとも各 OS の標準日本語フォントのいずれかを使用できる
  8. ただし, 一部のフォントはこの方法でもうまく表示できない
    • 表示できない場合は, いくつかの回避策で解決できる場合がある. 特に Macquartz を使うほうが良い場合もある.

始めて見る人・初心者はこれでもよくわからないと思うので以降の具体的な手順を参考にしてほしい. 最低限必要なのは, ragg, svglite, systemfonts (前2者が依存しているので自動インストールされるはず) パッケージ, そして v1.4.1717 以降のRStudio である. MacUbuntu の場合は外部ライブラリのインストールも必要になるかもしれない (後で詳述).


現時点での認識共有

以降では, 前回から更新された話を要約する.


OS別フォントファミリ名早見表

調べるのを面倒くさがる人のために, 最近の主要 OS で使われているフォント名の一覧表を書いてみる. 一見同じフォントでも OS によって認識できる名称が変わることがあるので注意. RStudio Cloud は Ubuntu OS 上で動かしているらしいので, Cloud を使用している場合は Ubuntu の欄を見れば良いと思う3. WSL2 や docker 上でも Ubuntu を使っているなら同じ.

Table 1: 主要フォントと認識される名前
OS ファミリ名 実際に認識できる名称 備考
Windows 游明朝 Yu Mincho
Windows 游ゴシック Yu Gothic
Windows BIZ UDP明朝 BIZ UDPMincho Win での表示は工夫が必要/Win ユーザ以外も使用可能(後述)
Windows BIZ UDPゴシック BIZ UDPGothic Win ユーザ以外も使用可能(後述)
Windows メイリオ Meiryo
Windows MS P明朝 MS PMincho
Windows MS Pゴシック MS PGothic
MacOS ヒラギノ明朝 ProN Hiragino Mincho ProN
MacOS ヒラギノ角ゴシック Hiragino Sans EL Capitan 以降の名称
MacOS ヒラギノ丸ゴ ProN Hiragino Maru Gothic ProN
MacOS 游明朝 YuMincho Windows版との違いに注意
MacOS 游明朝+36ポかな YuMincho +36p Kana svglite でうまく認識されないことがある
MacOS 游ゴシック YuGothic Windows版と名前の違いに注意
Ubuntu/Others 原ノ味明朝 原ノ味明朝 Tex Live 2020 以降の標準日本語フォント.Windowsでは “Harano Aji Mincho”
Ubuntu/Others 原ノ味ゴシック 原ノ味ゴシック Tex Live 2020 以降の標準日本語フォント.Windowsでは “Harano Aji Gothic”
Ubuntu/Others IBM Plex Sans JP IBM Plex Sans JP 最近公開された: https://github.com/IBM/plex/releases 日本語グリフは源ノ角ゴシックを元に細部を変更しているようだ
Ubuntu/Others Noto Serif CJK JP Noto Serif CJK JP Ubuntu 18以降の標準フォント
Ubuntu/Others Noto Sans CJK JP Noto Sans CJK JP Ubuntu 18以降の標準フォント
Ubuntu/Others Takao P明朝 Takao P明朝 以前のUbuntu標準フォント
Ubuntu/Others Takao Pゴシック Takao Pゴシック 以前のUbuntu標準フォント
Ubuntu/Others IPAex明朝 IPAex明朝 昔のUbuntu標準フォント.
Ubuntu/Others IPAexゴシック IPAexゴシック 昔のUbuntu標準フォント.

フォントは個別の環境に依存しているので, より確実な方法としては, systemfonts::system_fonts() で帰ってくるデータフレームの family 列にある名前を確認する方法がある. 例えば以下のようにすれば, 名前に「明朝」や「ゴシック」のつくフォントがだいたい全て網羅できるだろう.

require(tidyverse)
unique(filter(systemfonts::system_fonts(), str_detect(family, "明朝|Mincho|ゴシック|Gothic"))$family) 

さらに別の方法として, RStudio Addins でフォントのプレビューをしつつフォント名を取得する機能を, fontregisterer に追加してみた. これは試験的なのでまだ不安定かもしれない.


フォントの指定方法

これらのようなフォントファミリ名をグラフを描く際に指定する. 基本グラフィックス4なら最初に par(family = "...") で一括設定できる.

par(family = "Noto Serif CJK JP")
plot(1, main = "Rで広がる可視化の世界", xlab = "x軸", ylab = "y軸")
f:id:ill-identified:20210910142319p:plain
基本グラフィックでの表示

ggplot2 系のパッケージならばテーマ関数の textbase_family に指定したり, タイトルや軸ラベルなど細かく指定できたりする. theme_set() を使えば以降のデフォルトとして設定することもできる.

theme_set(theme(text = element_text(family = "Noto Serif CJK JP")))
ggplot(mtcars, aes(mpg, disp, color = cyl)) +
  geom_point() + labs(title = "Rで広がる可視化の世界", x = "x軸", y = "y軸", caption = "使用フォント: Noto Serif CJK JP")
f:id:ill-identified:20210910142321p:plain
Noto フォントでの表示

theme_* という名称のテーマ関数の多くは, base_family という引数で指定する.

ggplot(mtcars, aes(mpg, disp, color = cyl)) +
  geom_point() + labs(title = "Rで広がる可視化の世界", x = "x軸", y = "y軸", caption = "使用フォント: IMB Plex Sans JP") +
  theme_minimal(base_family = "IBM Plex Sans JP")
f:id:ill-identified:20210910142323p:plain
IBM Plex Sans JP での表示

ggplot2 のこれらのテーマ関数で指定したフォントは, geom_text などグラフ描画領域内に文字を書く関数には反映されない. geom_text(family = "...") のように毎回指定するか, update_geom_defaults または update_stat_defaults 関数で family 引数のデフォルトの値を設定する.

update_geom_defaults("text", list(family = "..."))
update_geom_defaults("label", list(family = "..."))
# 以下, ggrepel を使う場合
update_geom_defaults("text_repel", list(family = "..."))
update_geom_defaults("label_repel", list(family = "..."))

それ以外の ggplot2 派生パッケージでは, patchwork など変則的なものもあるが, 有名どころはおおむねどこかにフォントファミリ名を設定できる箇所があると思う.


ragg による RStudio 上での文字化け回避

Linux 系ならばおそらく上記のようにフォントファミリ名を指定するだけで表示できるだろう. しかし RStudio をデフォルト設定のまま使っている場合, WindowsMac では文字化けしたりエラーが出たりする. この不具合を回避するために ragg パッケージを使うことができる.

ragg パッケージはグラフの画像出力制御を行うバイス関数を提供するパッケージである. 「高品質なグラフィック描画ライブラリ」である AGG (Anti-Grain Geometry Library) を利用している. 従来の Windows 版のグラフィックデバイスが, 処理が遅い上にアンチエイリアスや文字のヒンティングができていないなどの不満から開発が始まったようだ. 2021年2月にリリースされた v1.1.0 は, フォントのフォールバック機能が追加されたため, 追加の設定なく日本語フォントを表示できる.5 さらに, 従来は RStudio 上での表示に PNG 画像を表示していたため, WindowsMac では, 画像保存時と同様に RStudio 上で文字化けしていたところ, 現在の最新版 (v1.4.1717) では RStudio 上で表示する際のグラフィックデバイスを変更できるようになったため ragg の機能で文字化けを回避できる. Tools -> Global Options -> General -> Graphics で “Backend” に “AGG” を指定することで ragg のグラフフィックデバイスを使って表示できる.

f:id:ill-identified:20210910230437p:plain
バックエンドデバイスの設定画面

ragg を使用した場合, デフォルトで選ばれるフォントは以下のようになる.

Table 2: ragg パッケージのデフォルト日本語フォント
OS font
Windows MS UIゴシック
Mac (El Capitan 以降) ヒラギノ角ゴシック (Hiragino Sans)
Ubuntu (18.04 以降) Noto Sans CJK JP

ただし, Ubuntu に関しては, おそらく fontconfig 依存なので, 他のフォントが使われる可能性がある. 例えば 17.xx あたりは Takao が標準フォントで Noto はプリインストールされていない可能性が高いのでおそらく 「Takao Pゴシック」が選ばれるだろう. 一方で今回 20.04 で Noto も Takao もインストールした状態でも, 「Takao Pゴシック」がデフォルトのフォントになるようだ. fontconfig 依存の Ubuntu では fonts.conf で優先度を設定することでデフォルトのフォントを変更できるはず (詳しいやり方は検索してほしい)だが, 何も設定しない場合の既定値はこのように私にはよくわからない.

そして, ragg を使用する場合 fontregisterer は不要になる. fontregisterer は, 基本デバイスpngjpeg に対して必要なフォント登録処理を行っているだけであるため.

なお, ragg は各環境でそのままインストールできるわけではない. 特に MacLinux では, 外部ライブラリのインストールが必要なことがある. 必要なものに関しては私自身が過去に WSL 上で R を動かす場合 や, R Markdown の環境構築と両立する方法について書いたので参考になるだろう. ほぼ変わらないが Mac での設定画面は 『日本語プロットの文字化けストレスを低減する - RStudio v1.4とraggパッケージを使う - cucumber flesh』に掲載されている.


画像の保存方法

RStudio の “Export” から保存する場合, 既に紹介したバックエンドで設定したデバイスで保存されるため, AGG を設定して保存すれば RStudio 上の見た目のまま保存され, 文字化けの心配はない. また, PDF でエクスポートする場合, RStudio v1.4 以降では, “Use cairo_pdf device” にチェックを入れると cairo_pdf で保存できるため, こちらも文字化けするケースがかなり減る.

f:id:ill-identified:20210910142751p:plain
PDFエクスポートの設定画面

しかし, SVG に関しては現在もデフォルトの svg 関数で固定されているため, svglite::svglite で保存することができず, 保存する関数を自分で呼び出す必要がある.

スクリプト内で保存するコードを書く場合は, 基本グラフィックであれば, 以下のようにdev.copy() を使うのが簡単だろう.

par(family = "...")
plot(...)
dev.copy(svglite::svglite, filename = "hoge.svg")
dev.off()

PNG ならば既に書いたように ragg を使うと良いので, dev.copy() には代わりに ragg::agg_png を指定する. もし JPEG 形式が必要なら ragg::agg_jpeg が使用できる.

ところで, 普通の教科書ではグラフ描画処理をデバイス関数と dev.off() で挟むサンプルコードが紹介されることが多い.

svglite::svglite(filename = "hoge.png")
par(family = "...")
plot(...)
dev.off()

しかしこれだと保存に成功するまでグラフを見ることができないので, dev.copy のほうが使いやすいのではないかと思う.

グラフ描画に失敗したときのデバイスの閉じ忘れはさらなるグラフ保存の失敗の原因につながる. これは基本グラフィックの弱点の1つである. 一方で ggplot2 の場合は2つも関数を呼び出す必要はなく, ggsave() 関数だけで保存できる. デバイス関数は device = で指定する. PDF の場合は cairo_pdf を指定する.

ggplot(...) + ...
ggsave("....pdf", device = cairo_pdf)

ggsave はデフォルトでは直前に描画された ggplot2 のグラフを保存する. ggplot2 はグラフをオブジェクトとして保存できるため, それを ggsave に与えることもできる.

p <- ggplot(...) + ...
ggsave("....pdf", plot = p, device = cairo_pdf)

数ヶ月前までは ggplot2 パッケージの ggsave()ragg のデバイス関数を指定した場合に, 保存時のサイズが正しく認識されない不具合があった. しかしこれも ggplot2 v3.3.4 以降は修正されている.6 加えて ragg パッケージがインストールされているなら, device のデフォルトに ragg によるデバイス関数が選ばれるようになった.7 よってPNG/JPEGの保存なら最低限ファイル名さえ書けば十分ということでかなり便利になった.


R Markdown での指定方法

R Markdown で文書を生成する際に埋め込むグラフの出力デバイスは, RStudio ではなく knitr に依存している. チャンクオプションの dev がそれに当たる.

```{r, dev="cairo_pdf", fig.cap="XXの散布図"}
ggplot(...) + geom_point()
```

あるいはどこかのチャンク内で knitr::opts_chunk$set(dev = "...") を実行することで以降の使用デバイスを一括設定することもできる.

また, 多くの出力フォーマットでは, YAMLメタデータにも dev という項目があり, こんなふうに全てのチャンクの dev を一括設定できる.8

output:
  bookdown::pdf_document2:
    dev: cairo_pdf

ただしこのオプションには関数は指定できず, 文字列で指定する必要がある. つまり dev = cairo_pdf ではだめで, dev = "cairo_pdf" とする必要がある. また, 上記の ragg 系のデバイスを指定する場合は注意が必要である. dev = "ragg_png", dev = "ragg_jpeg" というように, 名称が関数名とは微妙に異なる.9

残された不具合

これは主なフリーフォントやOSプリインストールフォントを対象に実験した結果に基づいている. 結構ややこしいので, 先に OS 別に残された不具合の対応表を書いてみる.

topic MacOS Ubuntu Windows
svglite で認識されないフォント・スタイルがある X X
ragg で認識されないフォント・スタイルがある X X X
ragg で1文字だけのテキストが文字化けする X X
cairo_pdf で認識されないフォント・スタイルがある X X
cairo_pdf でフォントを指定しないと文字化けする X X
notebook + ragg の組み合わせが機能しない X ? X

現時点では Ubuntu では目立った不具合は発生しない. ほとんどが MacWindows でみられた不具合である. WSL もあることだしこういうのが気になるなら Ubuntu に移行するのも手である.

これらの不具合は私の手元にある実機で再現したので不具合が起こることはほぼ間違えないと思う. しかし関連する処理の実装にはまだ詳しくないため, もしかすると原因に関しては誤解している箇所があるかもしれない.


svglite で認識されないフォント・スタイルがある

この問題は, いくつかパターンがあるため, 少しややこしい. svglite パッケージは, 簡単にグラフを SVG 形式で変換してくれる. SVG は, XML 形式で画像を記述するフォーマットでありフォントも画像として埋め込まない限りは, フォントファミリを指定する属性が HTML のインライン CSS のように <text style='font-family: "..."'> という形式でファイルに書かれている. しかし現時点 (v2.0.0) ではこの出力が不正な場合がある.

  1. WindowsMac の場合, 一部のフォントファミリが認識されず, 別のフォントに置き換えられてしまう
  2. 名前にスペースを含むフォントファミリを指定した場合, うまく認識されない (これはブラウザ側の問題であるかもしれない)

1番目は systemfonts パッケージに由来する問題ではないかと疑っている. だとすればこれは後述する Windows で BIZ UD 系フォントを ragg で使用できない不具合や, スタイル変更がうまくいかない場合と同じである. これは次の ragg の問題でまとめて解説する.

2番目は 例えば最近の Mac にプリインストールされている「游明朝 +36ポかな」のような名前にスペースを含むフォント名が指定されていると, ブラウザ側が正しく認識してくれないことがある.10 svglite パッケージのこの挙動について, 既にプルリクエストを投稿しているが今の所反応はない. 私の改造版なら, 少なくとも「游明朝 +36ポかな」を認識させることができた.


ragg で認識されないフォント・スタイルがある

ragg を使えばとりあえず日本語の文字化けは回避できる. しかし Windows の場合デフォルトでは MS UIゴシックが使用されてしまう. MS UI ゴシックを体裁の整ったレポートや論文に使いたいという人はかなり少ないだろう.11 現在サポートされている Windows OS には游書体やメイリオが最初から入っており, Windows 10 ならばさらに, 可読性に優れるとされるユニバーサルデザインフォントの一種である UDデジタル教科書体フォントや, その派生フォントである BIZ UD フォントが用意されている12. そのため UDフォント系を積極的に使いたい人は潜在的に多そうであるが, 残念ながら Windows で, かつ ragg を使用する場合 BIZ UD(P)明朝フォントを指定しても認識されず, フォールバックフォントである MS UIゴシックが使用されるという不具合がある.

この不具合の回避方法は, Windows では現時点であまり有効なものがない. systemfonts::register_font() を使うという手もあるが, これでもうまくいかないことがある. また, あえて 基本関数の png/jpeg を使うという手もあるが, これも確実ではない (実際, BIZ UD明朝はこれらでも認識されない). あるいは, 一旦 svglite::svglite で保存してから rsvg パッケージで PNG に変換することでもしかしたらうまくいくかもしれない.

一方で, Mac では BIZ UD明朝は認識されるが, すべてのフォントについて斜体・太字斜体は全てヒラギノ角ゴシックに置き換えられてしまう. これもおそらく標準スタイルでないフォントが認識されずフォールバックフォントが適用されるということだろう. Mac の場合, quartzバイスを使うことで, 太字や斜体は適用できないもののフォントファミリは同一にすることができた.

quartz を指定する場合 以下のように書くことができる.

plot(...)
dev.copy(quartz, file = "...png")
dev.off()

しかし, 引数名がなぜか他のデバイスと違うため, ggsave にはうまく適用できない. 私も Mac はあまり使わないのだが, たぶんこんな風ににラッパ関数を作れば使えると思う.

quartz_ <- function(filename, type = NULL, hei...){
  quartz(file = filename, type = type, ...)
}

ggsave("...png", device = quartz_)

quartz を使用する場合, quartzFonts() でフォントの登録が必要になるので, 再び fontregisterer が有用となる. 画像サイズの既定値も他のデバイス関数と異なることに注意.


この不具合の原因

これらの ragg の不具合, および前節の svglite の不具合の原因には, 両パッケージがフォント検出に使用している systemfonts パッケージが関係していると考えられる. まず, すでに書いたように, systemfonts::system_fonts() で調べると BIZ UD フォントは含まれている. しかしフォント検出には c++ で実装されている別の関数が使われており, これはフォントファミリ名と, 太字かどうか, イタリック (斜体) かどうか, という引数をもとにフォントをマッチングする. その処理は OS ごとに分かれ, Linux では fontconfig が使われており, これは fc-list の出力ともおおむね一致し, system_fonts() とも一致する. 一方で Mac は coretext というライブラリが使用され, Windows で独自実装のマッチング処理が実装されている. Windows の場合は, 標準フォントが Regular ウエイトのものであると定義している. しかし Windows 10 にプリインストールされているBIZ UD(P)明朝フォントは, “Medium” ウエイトのみなので, マッチングの条件に一致せず名前を正しく指定しているのにフォントが認識されないという問題がある.13 14 また, Mac の場合に使われる coretext は, 標準のフォントはおおむね適切に認識してくれるが, 斜体や太字などスタイルを変えた場合は別のフォントに置き換わってしまう. 私の場合は, 例えば游明朝の斜体は “PT Serif” という欧文フォントにマッチングされてしまった. そのため ragg のフォールバック機能が作動しヒラギノ角ゴシックに置き換えられてしまうようだ.

もともと日本語フォントの多く (特にフリーフォント) は, 欧文フォントと比べ太字や斜体といったスタイルやウエイトの異なるフォントがあまり用意されていないことが多いため, 別のフォントで置き換えられてしまうのだと思われる. ライブラリによっては標準フォントを変形して太字や斜体を生成されることもある (Word とかにもおそらくこの機能がある) が ragg や quartz にはこの機能がないようだ.

この不具合は既にいちおう systemfons のリポジトリに投稿したが, 投稿時点では問題の全容を把握しておらず, 提案する解決方法が的外れだったと思う (他のフォントが認識しなくなる).

追記: どうやら R グラフィックスの定義で, フォントファミリとはノーマルフォントと太字が定義されているもの, という想定になっているらしい. よって BIZ UD はフォントファミリの定義として不正ということなのになる (マイクロソフトは新フォント搭載とうたっていたが, これらのフォントは開発元であるモリサワのサイトではBIZ+ フォントの無料お試し版という体で配布されているので実態は「お試し版フォントがタダだったのでインストールしておいたよ」というようなものであり, まあしかたないのかもしれない.). よって, ragg での使用を諦めるか, fontrorge などのソフトを使って太字フォントを登録し直したフォントファミリを自作するなどして対処することになりそうだ (ただしこの方法をとるなら著作権に注意しなければならない).


ragg で一文字だけのテキストが文字化けする

2021/10/14 追記: この不具合は textshaping パッケージの更新で修正された. v0.36 以降なら問題は発生しないと思われる.

これは twitter で以前投稿したもので, 元は R-wakalang で投稿された質問から発見されたもの. タイトルや軸ラベル等のテキストが1文字だけの場合, 文字化けが発生することがある. 例えば名簿のデータをグラフにしたいとき,「林」とか1文字の名前が含まれていると起こりうる. これは上記の不具合とは別のところに原因があるようだ. おそらくはフォールバック機能がうまく動作してないためなので, 日本語に限らずキリル文字ヘブライ文字やハングルといった非ASCII文字全般で起こるのだと思う. この問題は私も修正方法を思いついてないが, 投稿した issuesに対して「問題の再現を確認した. これは ragg というより systemfonts に由来する不具合である」と開発者からコメントをもらったので先方も問題を認識しているようだ. 現時点での回避方法は単純にフォントファミリを必ず指定することである. やや行儀が悪いがテキストの末尾にスペースをつけて2文字にすることで回避できることもある.


PDF 保存時に認識されないフォント・スタイルがある

実際に作成したファイルを見てもらえばわかりやすいと思うが, Mac の場合は「筑紫A/B丸ゴシック」「游ゴシック」「游明朝」「游明朝+36ポかな」が明らかに異なる字形になっている. 埋め込みフォントを調べてみるとこれらは游書体ではなく “Arial Unicode MS” が適用されているようだ (テーマフォントにも游ゴシックを指定したので, 軸ラベルも Arial Unicode MS になってしまっている).

f:id:ill-identified:20210910211802p:plain
Mac でうまく認識されないフォント

Windows の場合も, ragg のときのように「BIZ UD(P)明朝」が「MS Pゴシック」に置き換えられてしまい, 標準フォント以外も文字化けしてしまう.

cairo_pdf でうまく変換されないフォントの斜体や太字をどうしても使いたい場合は, 一旦 SVG 形式で保存してから rsvg::rsvg_pdf を使って PDF へ変換するという方法がある. SVG画像の保存は既に書いたように svglite::svglite を使うことができる. ただし RStudio の GUI からはまだ svglite を選択できないため, SVG画像の保存にはコードを書く必要がある. rsvg パッケージは librsvg2 というライブラリを使用して変換する. このライブラリもおそらく OS ごとに異なるテキスト管理を行っているようだが, 他に比べれば比較的素直に, 指定したフォントに変換してくれるように見える. なお, PNG など他の形式へ変換する関数も用意されている.

ggplot(...)
ggsave("tmp.svg", device = svglite::svglite)
rsvg::rsvg_pdf("tmp.svg", "out.pdf")

しかし既に書いたように svglite の出力時のフォント指定にも不具合があるため, 一長一短である. つまり, Mac では SVG ファイルにした段階で「游明朝」が “PT Serif” の指定に置き換わってしまうため, そのまま rsvg::rsvg_pdf で変換しても文字化けしてしまう. 現状では SVG ファイルを開いて font-family: 属性を手動で書き換えることで対処することになる. さらに rsvg が認識できるフォントファミリの名称は systemfonts のものと異なる可能性もあり複雑である.15 (SVGXML テキストなので, 間に合せでフォント指定を書き換えるスクリプトを作ることは比較的簡単?) 2021/9/12 追記: rsvg でできないか試してみたが以外と難しかった: https://ill-identified.hatenablog.com/entry/2021/09/12/172045

Mac ユーザならば, quartz を使うこともできる. quartz を指定する場合 type = "pdf" という引数を追加することで PDF として保存される. (type = "png") とすれば PNG 形式で保存できる.

plot(...)
dev.copy(quartz, file = "...pdf", type = "pdf")
dev.off()

しかし, 引数名がなぜか他のデバイスと違うため, ggsave にはうまく適用できない. 私も Mac はあまり使わないのだが, たぶんこんなかんじにラッパ関数を作れば使えると思う.

quartz_ <- function(filename, type, ...){
  quartz(file = filename, type = type, ...)
}

quartz を使用する場合, quartzFonts() でフォントの登録が必要になるので, 再び fontregisterer が有用となる. また, 文字化けや誤ったフォント指定はだいぶ減るものの, 太字や斜体は反映されないことが多い.

PDF 画像生成のさらに別の代替案として, tikzDevice を使い画像を tex ソースコードに変換するというものが考えられる. lualatex や xelatex はUnicode文字を扱い, ほとんどのフォントを埋め込むことができるので, 使用する OS にかかわらず, 希望のフォントを使用できる可能性が高い. しかし, これは TeX 環境の構築や変換に際して TeX の知識を要するため, TeX にあまり詳しくない人や, PDF で文書作成したいのではなく単に画像が欲しいだけの人などには向いていない.

まだ読み込みが足りないが, cairo_pdf で PDF として保存した際にフォントが正しく適用されない問題もまた, OS ごとに異なるフォント管理ライブラリを使う実装になっているようだ.16 しかしその分岐ルールは systemfonts と異なるため, フォントが認識しない条件も raggsvglite と比べ変わってくるようだ.


PDF 保存時のデフォルトフォント

ragg パッケージを導入すれば, (既に紹介した1文字のみの場合を除き) ラスタ画像に関しては何もしなくても全ての OS で文字化けを防ぐことができる. よってフォントにこだわらない人ならば, 上記のような指定したフォントと表示されるフォントが一致しない不具合は問題とならないだろう. 一方で Windows
Mac を使っていると, フォントを一切指定せずに cairo_pdf で保存すると文字化けしてしまう.

この不具合への対処法の1つは, ggasve()family 引数に適当なフォント名を追加することである (日本語グリフのないフォントさえ回避していれば, たぶんでたらめな文字列でも可).

ggplot(...)
ggsave("...pdf", device = cairo_pdf, family = "hogeee")

しかし, Windows の場合は MS Pゴシック, Mac の場合は Arial Unicode MS にフォールバックされてしまうので, あまり見た目が良くない.

よって, ファイル形式ごとに設定を細かく変えるのが面倒だとか, OS ごとの挙動の違いを覚えるの面倒だと感じるなら, 常に par()ggplot2 のテーマ関数にデフォルトのフォントを指定するように習慣づける必要があるだろう.


notebook と ragg の組み合わせが動作しない (2021/9/28追記)

これはさっき R-wakalang で質問があったので気づいた不具合. Windows または Mac の場合, R Markdown notebook のチャンクで dev="ragg_png" など ragg のグラフィックデバイスを指定しても反映されない (よっておそらくはエラーや警告が表示されるか, 文字化けしたりする).

これは RStudio の不具合らしく (issue #7313), 少なくとも現在の最新安定版 2021.09.0 Build 351 (v1.4.1717 の次のバージョン, 最近バージョン表記が日付になった) 時点ではまだ修正されていない. 報告は Mac の例だが, 試したところ Windows でも同様の不具合が発生するようだ. Ubuntu も, もしかすると機能してないのかもしれないが, デフォルトのデバイスでも文字化けしないのであまり気にならない. issue スレッドによれば開発側はもう修正に着手していて daily build で改善されつつあるらしいので, 次の安定版リリースあたりで直ってそうな気がする.

それまでの対処法としては, 以下の2つのいずれかが考えられる.

  1. 最新の daily build をインストールして試してみる
  2. fontregisterer パッケージをロードし, かつフォントファミリを明示的に指定するようにする

どうやら WindowsMac だけでなく, Ubuntu でも起こりうるが, Ubuntu は固有のフォールバック機能があるため気にならないようだ. issue #7313 でも ragg 使用時に R Markdown 上でインライン実行したときにグラフ描画でエラーが出ることは報告されていて, 既に最新版の RStudio では修正されエラーがでなくなったものの, フォールバック機能がうまく動作してないようである. これは issue を作成している https://github.com/rstudio/rstudio/issues/9931


結論

ragg や RStudio の更新によって, 日本語の文字化けはかなり回避できるようになった. しかし, 任意の日本語フォントを自由に表示させるようになりたい, となると必ずしも整備された方法があるわけではない. 前回はプラットフォーム間で統一した方法を重視したためあまり大きく取り上げなかったが, Mac の場合は quartz を使ったほうが良い場合が多い.

改めて現状, OS に依存せず共通し, かつ簡単な方法を改めてまとめると, 以下のようになる.

  • Rstudio 上での表示, およびラスタ画像の保存には ragg パッケージをインストールし, バックエンドで使用するよう設定する.
  • PDF での保存には, cairo_pdf 関数を使用する.
  • PDF で保存する場合, グラフ描画時に日本語フォントファミリを必ず指定するようにする. もしどれを指定すべきか迷うなら以下のフォントファミリを指定する.
  • Mac のみ, Cairo と Xquartz のインストールに注意.
  • Mac のみ, quartzバイスを使用したほうがフォントが認識されやすいかもしれない (quartz 使用時は fontregisterer パッケージの読み込みが必要)
  • 上記の方法で, 希望の日本語フォントがうまく認識されない場合は, 以下の方法で解決できることがある. しかしあまり便利ではない.
    • svglite::svglite で保存し, ファイル内で指定されているフォントファミリ名を書き換えて rsvg パッケージで各フォーマットに変換する.
    • tikzdeviceを使い, LaTeXソースコードに変換してPDFを生成する.

残されたフォントが一貫しない不具合の多くは, OS ごとに異なるフォント管理ライブラリを利用しているのが原因のようだ. Mac でも Windows (Msys2 を元にした Rtools をインストールしている前提) でも fontconfig は使えるはずなので, Linux 以外の環境でも fontconfig をインストールさせた上で現在のソースコードを少し修正すれば, 各主要プラットフォームで fontconfig でテキストを扱うようにリビルドさせて問題を解決できそうな気がするのだが, coretext など他の類似ライブラリと比較して何が違うのか, フォントの規格, 各種デバイスの実装といったことにもそこまで詳しくないため, 現時点では具体的なやり方は私にはわからない.

フォントとかテキスト描画ライブラリに詳しい人だれか助けてください.


補足: 動作環境について

以上の話の検証で使用したパッケージ等のバージョンは, 以下の通り.

  • ggplot2 v3.3.5
  • svglite v2.0.0 を元にした私の fork (remotes::install_github("Gedevan-Aleksizde/svglite.git@quote-fontname") でインストール可)
  • systemfonts v1.0.2
  • ragg v1.1.3
  • rsvg v2.1.2
  • rstudio v1.4.1717
  • R 本体 v4.1.117
  • OS: Windows 10, Mac OS Catalina, Ubuntu 20.04
  • Mac のみ:
  • なおこれら2つが正常にインストールされているかどうかは, capabilities()[c("cairo", "X11")] の結果がそれぞれ TRUE になっているかどうかで判定できる.


補足: 確認用のコードと結果の抜粋

上記の環境で, ragg::agg_png および cairo_pdf, そして Mac のみ quartz で出力した例をgistに置いた.

https://gist.github.com/Gedevan-Aleksizde/5be2ab8577f91a4cf0efe3e8ba7e04b9

いくつか注意点

  • <OS名>-gg-<デバイス名>というファイル名で保存してある.
  • Ubuntu のみ「嵯峨本フォントプロトタイプ」を変わり種のフォントの使用例として表示している. これはひらがなのグリフのみなので, アルファベットや漢字は Noto にフォールバックする結果となった. 他のOSでもフォールバックされるか文字化けすると思われる.


補足: 他の保存方法がなぜ良くないのか

ここはほぼ前回と変わらないので前回読んだ人は読まなくてもいい.


デフォルトの pngjpeg

Linux 系ならばおそらくほとんど影響ないが, これらも OS ごとにバックエンドが異なるため WindowsMac では文字化けしやすい. fontregisterer はこのあたりの問題を解消するために作成したのだが, 直後に ragg や RStudio の追加機能で代替できるようになったのであまり有意義ではなくなった.

実はデフォルトの pngjpeg であっても, type = "cairo" を指定するか, グローバルオプションとして options(bitmapType = "cairo") を設定すれば fontregisterer によるフォント登録なしでもほとんどのフォントは文字化けしない. (RStudio 側での設定は “Cairo” という名前の項目がこれに対応する). ただし, WindowsMac では, 日本語フォントは斜体や太字指定にすると認識できず文字化けするケースが多い. これも svglitecairo_pdf を使用した場合と似たような原因ではないかと思う. Cairo のフォント管理のバックエンドが適応的に選択されていることと関係している. 基本グラフィックスだとデフォルトでタイトル部分が太字指定されるため, 総合的に見ると ragg パッケージをインストールしたほうが簡単である.


SVG 形式

svg 関数は文字を画像にしてしまうため. アクセシビリティを考えると文字は文字情報として埋め込むべきであり, 文字情報を与えたくないことに関して強い理由がない限り必要ないと思われる. そして svg 関数ではフォントも自由に指定できない.


PDF 形式

  • pdf() 関数: このフォントはPCが使用可能なフォントを表示させるものではないし, PDFにフォントを埋め込んでくれるわけでもない. 日本語対応のため「平成角ゴシック」「小塚明朝Pro Acro」などを指定する機能があるが, 現在の多くの環境ではこのフォントはプリインストールされていない. たぶんこれらのフォントは昔からDTP業界 (というか Adobe?) で使われていたから指定可能なリストに含まれているのだろうが, いずれもフリーウェアではなく市販のフォントであり, 現在ほとんどのPC製品ではプリインストールされていない. PDF にフォントが埋め込まれない場合, 別のフォントが代用されるが, どのフォントが代用されるかはビューアしだいであり, 適切な表示になる保証はない..18 体感では Web上で公開されているPDFの文字化けの大半はフォント埋め込みをしていないのが原因のように思える. これらのフォントを持っていてどうしても pdf で出力し埋め込みたい場合はRでのフォントの扱い にあるように ghostscript が必要だが導入方法を教えるのが手間なので推奨しない. cairo_pdf のほうがおそらく簡単だろう.
  • extrafont パッケージ: 上記の pdf を拡張していろいろなフォントを指定できるようにしてくれるが, 結局埋め込み処理はしてくれない. 追加でフォントを使用可能にするにも登録処理が必要であり, 登録が不要でフォント埋め込みもしてくれる cairo_pdfrsvg::rsvg_pdf のほうが使い勝手が良い. 特に後者は, 他のデバイスでうまくフォントが認識できない場合でも, 最悪でも手間はかかるが手動修正で解決できることも多い.
  • showtext パッケージ: svg 関数の項目と同じ理由. あと意外とうまく指定できるフォントが少ない (あまり詳しく検証していない). 以前はロードしておけばPDFだけでなくラスタ画像やRStudio上の表示でもとりあえず文字化けは回避できるという利点があったが, RStudio で AGG (ragg) デバイスをバックエンドに使えるようになった現在ではその意義も薄れた.

補足: 日本語表示にはどのフォントを使うべきか

これは本題から少し外れているが, ついでに書いておく. 文字の形状は好みで左右されることがあるので, ここでは判断材料として日本語グリフをどれだけカバーしているかを主に書く.

  • 源ノフォント (Source Han Sans/Serif) は古典的? な CJK フォントである. ライセンスもゆるく自由にダウンロードできる. 後発の多くの日本語フリーフォントの基になっている. Adobe-Japan1-6 に準拠しているので JIS漢字は一通りカバーしている.
  • Noto Sans/Serif CJK JP は収録グリフ数が 6万5536文字, つまり OpenType や TrueType など現在主流のフォントファイルの規格の上限まである. これは文字化けを防ぐため欧文や和文以外のいろいろなグリフも含んでいることを意味する. 実際は一部のウエイト以外は源ノフォントと同じらしいので, これもJIS漢字は一通りカバーしていることになる. 最近の Ubuntu の標準日本語用フォントでもある.
  • 原ノ味フォントは LaTeX の日本語環境で使いやすいよう源ノフォントを基に作成され, 字形は同じだが CID に対応していることとが特徴である. Adobe-Japan1-6 の漢字グリフは全て収録, 一部の非漢字グリフには抜けがあるとのこと.19 よって LaTeX 文書の本文と文字を統一したい場合は原ノ味を使用すると良いかもしれない.
  • 花園明朝 は JIS 漢字だけでなくCJK統合漢字をカバーし, 10万7518字が収録されている. 以前紹介したように, 中国語でもめったに使われないような文字も収録されている. しかしグリフ数がファイルの上限を超えているため花園明朝A/B という2つのフォントファミリに分割されているのでやや使いづらい.
  • IBM Plex Sans JP は最近公開されたフォントである. 欧文フォントである IBM Plex Sans は数年前から存在したが, これに日本語グリフを追加したのがこのフォントらしい. サンセリフでありながら一部のアルファベットにヒゲがついていたりとどことなく UD フォント的な可読性を意識したような字形であるので個人的に注目している. しかし, 詳しいスペックが掲載されている箇所を見つけられかったものの, issues で Adobe-Japan1-3 準拠と書かれているのを見つけた20 (実際グリフ数はそれくらいだった). よって JIS第1, 第2水準までのカバーということになる.
  • ヒラギノフォントはPr6N系ならば JIS X 0213:2012 に対応しているとのことなので, JIS漢字をカバーしているようだ. ただし製品版と Mac にプリインストールされているものは少し異なる.21
  • 游書体は最近の MacWindows にプリインストールされている. OS にプリインストールされているものと製品版とで差異があるらしいが, MacWindowsAdobe-Japan1-7 準拠らしい.22 よってこれもJIS漢字をカバーしているようだ.
  • IPA MJ明朝を除く IPAフォントは, 基本的にJIS漢字をカバーしている. 正確には IPA (P)/IPAex でそれぞれ異なり, 後者なら v004.01 時点では JIS X 0213:2012 に準拠したグリフを収録している23が, 前者はそれより古い規格のままである. また, エンドユーザにはあまり関係ないが, 再配布に関するライセンス制約が微妙に厄介である.
  • IPA MJ明朝フォントは戸籍システムや住民基本台帳での使用のために制定されたMJ文字集合に準拠している. 人名を文字化けせず表示できるよう網羅しているらしい. 詳しくはしらないがたぶんJIS漢字をカバーしているだろう. ゴシック体はない.

典型的なRの用途 (技術文書, レポート, 学術論文に掲載するグラフの作成) を考えると, 奇をてらったフォントを使う場面はあまりないと思うので LaTeX との連携を考えて原ノ味か, 他言語の文字も文字化けしづらい Noto や源ノフォントということになり, 特殊な人名漢字の表記が必要な場合のみ花園明朝とかIPA MJ明朝とかを使うことになるだろう (ただしこの2つはゴシックに対応するフォントがない). 私の場合は現在の Ubuntu の標準日本語フォントに採用されている Noto のほうを使っている. 一方で Noto も源ノフォントも, MacWindows ではプリインストールされていないので, 新たにインストールするのが面倒だと思うのなら游書体やヒラギノでも十分だと思う. どちらも LuaLaTeX/XeLaTeX で作成するPDFに埋め込むのは比較的簡単である. ただし, 既に書いたように Mac では游明朝がうまく認識されない問題がある.




  1. SVG形式での保存は次のようなページで言及されている https://oku.edu.mie-u.ac.jp/~okumura/stat/svg.html しかし「文字幅の取得にたいへん時間がかかり,しかもあまり美しくありません」など, 私の体感とはかなり異なったことが書かれている (そもそもSVGなら表示はブラウザ依存のような…). おそらくRStudio ではなく XQuartz 上での出力であること, また使用しているパッケージのバージョンがかなり古いため現状と食い違っているのではないかと思う. rvg パッケージの記述も現在のものと異なる.↩︎

  2. PNGやJPGのようなドットで表される画像形式のこと. 「ビットマップ画像」とも呼ばれるが, WindowsBMP 形式のことかと勘違いされそうなので, ここではラスタ画像と呼ぶ.↩︎

  3. 私は使ってないので動作確認したわけではない. なかったらなかったでターミナルで sudo apt install fonts-noto-cjk fonts-noto-cjk-extra を実行すればインストールできると思う.↩︎

  4. base またはR本体と一緒にロードされる graphcis パッケージのグラフ描画関数のこと↩︎

  5. https://www.tidyverse.org/blog/2021/02/modern-text-features/, https://cran.r-project.org/web/packages/ragg/news/news.html↩︎

  6. https://github.com/tidyverse/ggplot2/issues/4347↩︎

  7. https://ggplot2.tidyverse.org/news/↩︎

  8. rmdja パッケージではPDF出力時のデフォルトを "cairo_pdf" としている.↩︎

  9. 指定可能なデバイス名一覧はなぜか公式ドキュメントに記載がなく, 今のところは R Markdown クックブックの SS 11.15 が一番網羅的だと思われる.↩︎

  10. ブラウザにも依存するのだろうが, 他にも条件があるのかもしれない.↩︎

  11. ただし, 実際にどうフォントがレンダリングされるかはエンジン依存なので, 例えば LaTeX で書いた文書の本文と, PDF内の文字に同じフォントを指定したとしても, 完全に同一の見た目になるとは限らない.↩︎

  12. BIZ UD フォント4種はモリサワのサイトでユーザ登録すれば他のOSユーザでも使用できる.↩︎

  13. BIZ UD(P)ゴシックは Regular と Bold があるため表示される. Windows にプリインストールされているものとモリサワのサイトで配布されている無償版はおそらく同じ.↩︎

  14. よって, Windows 上では BIZ UD明朝でなくともRegularウエイトを持たないフォントを使用した場合同様の現象が起こるかもしれない. 同じフォントでも OS によって認識される名称が異なるのもこのあたりが関係していると思われる.↩︎

  15. これも正確な挙動を把握できていないが, フォントファミリのエイリアスを認識できないのかもしれない? https://r-wakalang.slack.com/archives/C06QP6NJ0/p1620480454202100?thread_ts=1620467264.200700&cid=C06QP6NJ0↩︎

  16. たぶんこの辺: https://github.com/wch/r-source/tree/79298c499218846d14500255efd622b5021c10ec/src/library/grDevices/src/cairo↩︎

  17. まだ 4.1 台に更新してない人は結構いそうだが, 3種類のOSでR本体のバージョンを変えて検証するのは大変なので今回はなし. しかしネット上の古い記事やリリースノートを見る限り, v2.x 台とか極端に古いものでなければだいたい同じだと思う.↩︎

  18. Word 等他のワープロソフトでも設定しないと同様の失敗をする可能性がある. 例えばこういう注意喚起も見かけた: https://www.sunrisep.co.jp/00_infomation/info10.html↩︎

  19. https://github.com/trueroad/HaranoAjiFonts↩︎

  20. https://github.com/IBM/plex/issues/334#issuecomment-707345771↩︎

  21. https://www.screen.co.jp/ga_product/sento/support/otf_osx_El_Capitan.html, https://www.screen.co.jp/ga_product/sento/support/otf_ver_hiragino.html↩︎

  22. http://www.jiyu-kobo.co.jp/os-installed-y/↩︎

  23. 「令和」の記号とかごく一部の例外はある: https://moji.or.jp/ipafont/fontspec00401/↩︎