ill-identified diary

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

rsvg で「不」自由に日本語フォントを埋め込む


概要

  • 半期の総括のような風の投稿を昨日したばっかりだが, さらに補足.

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

  • rsvg パッケージを使えば, WindowsMac でうまく表示できなかったフォントが使えるかもしれない, と思ったが挙動が謎で, 残念ながら思ったよりうまく行かない.
  • 上記の投稿に書かれている, 現状最も無難な方法が気に入らない人のみ, 今回紹介する方法に手を出してほしい.

rsvg パッケージ

rsvg パッケージは librsvg2 を使ってSVG画像を他の形式に変換する関数を提供している. 昨日の投稿でも触れたように, SVG にはCSSを適用することができ, 実際 svglite を使って出力したSVG画像のテキストはインラインCSSでフォントが指定されている. このパッケージの公式のビネットがここにある.

https://cran.r-project.org/web/packages/rsvg/vignettes/svg-css.html

rsvgの関数には librsvg2 の機能を使って既存のSVG画像にCSSファイルを適用する機能がある.1 これにしたがって css = に簡単な CSS を適用してみた. 例えば Windows でうまく適用できなかった「BIZ UDP明朝」を適用するなら,

text {
  font-family: "BIZ UDPMincho";
}

という CSS ファイルを与えればできるはずだ, ということで試してみた. コードはこんな感じになる.2

require(ggplot2)
ggplot(...)
ggsave("out.svg", device = svglite::svglite)
css <- tempfile()
writeLines('text {font-family: "BIZ UDPMincho";}', css)
rsvg::rsvg_pdf("out.svg", "out.pdf", css = css)
file.remove(css)

rsvg パッケージはグラフィックデバイスを提供するわけではないので, 一旦 svglite で画像ファイルを作成した上で, 変換する必要がある.

しかし, 実際にやってみると, グラフの設定に関係なく全てのテキストが「BIZ UDP明朝」で表示されるようになってしまった. さらにより深刻なのは, 色情報が抜け落ちてしまうということである. CSS を上書きしてしまうのが良くないのかと, デフォルトのスタイルをマージしたCSSを与えてみてもうまく行かなかった. 例えば以下は Windows で BIZ UDP明朝が使えるか試した際の SVG と PDF (再現のためPNG形式のスクリーンショットで掲載).

f:id:ill-identified:20210912171400p:plain
SVG (左)とPDF変換後 (右) の出力 (のスクリーンショット)

上記の公式ドキュメントには「外部CSSの適用」と書いてあったので, <![CDATA] ... ]]>CSS を書くのと同等の結果になるのかと思ったがならなかった (試しに SVGファイルの<![CDATA] ... ]]> に手動で書き込んでみたところ, Chrome や MS Edge の表示では個別のインラインCSSでのフォント指定のほうが優先されていたし, CSSの書き換えによってデザインが崩れることもなかった).

別の方法として, xml2 なんかを使って CSS を書き換え, さらに個別の style 属性の font-family: ...; の部分を消去すれば表示できるはずだが, ここまでやったとしても全てのフォントが置き換わるだけなので労力に見合わない気がする. 個別に指定したフォントを修正するのはさらに難易度が高くなるだろう.

やはり cairo の実装/Rへのバインドから見直すべきなのだろうか?


fontregister の機能追加

このように, ggplot2 で設定したフォント指定が全て上書きされてしまう, 色情報が消失するという問題があるものの, 一応は前回紹介した ragg::agg_*cairo_pdf 関数を使った方法では適用できなかった Windows における BIZ UDP明朝や Mac における游明朝フォントをPNGやPDFに埋め込めるようになった. つまり, 印刷物で色わけをしないような状況でならかろうじて役に立つかもしれない… そこで, どうしてもこれらのフォントをグラフに使いたい表示したい人のために, fontregisterer にもこの操作を手軽にするための関数, rsvg_fallback を追加した. やっつけ仕事なので master ブランチにはまだ反映しておらず, 以下のようにインストールし直すことで使用できる.

remotes::install_github("Gedevan-Aleksizde/fontregisterer@rsvg-fallback")

実際の使用例はこのようになる.

require(ggplot2)
ggplot(...)
ggsave("out.svg", device = svglite::svglite)
fontregisterer::rsvg_fallback("out.svg", "out.pdf", type = "pdf", family = "BIZ UDPMincho")

この関数の引数は rsvg::rsvg_* とほぼ同じで, SVG画像が既に作成されていることが前提になっている. family 引数にフォントファミリ名を与えれば, 上記のような簡単なCSSが内部で作成され適用される. type には変換したいファイル形式を指定する. 現時点では "pdf", "png", "ps", "svg", "webp" が指定できるが, webp 形式のみ, 別途 webp パッケージのインストールが必要になる. また, webp と png は解像度を調整できず, すこし粗い画像になり, svg 画像はデフォルトの svg と同様に文字が画像になってしまうため, 手軽に使えるのは pdf だけだと思う.




  1. librsvg のバージョンが古いと動作しない, と書いてあるが具体的なバージョンが書かれていない.↩︎

  2. 前回同様, svglite パッケージは私の作った修正版https://github.com/Gedevan-Aleksizde/svglite/tree/quote-fontname修正版のほうが無難だと思う.↩︎