⛩️

JuliaのPlots.jlでヒラギノ角ゴシックを使おう!(Mac)

に公開

こんにちは。
JuliaのPlots.jlでフォントが正しく表示されない問題にハマったので、その時の解決法を書いておきます。

Plots.jlでヒラギノ角ゴシックを使おう!

環境

  • macOS Sequoia 15.3.2
  • Julia 1.11.3
  • Plots 1.40.11
  • GR 0.73.13

結論

using Plots
using Unicode

gr()

@eval Plots begin
    function gr_set_font(
        f::Font, s; halign = f.halign, valign = f.valign, color = f.color,
        rotation = f.rotation,)
        family = lowercase(f.family)
        GR.setcharheight(gr_point_mult(s) * f.pointsize)
        GR.setcharup(sincosd(-rotation)...)
        if !haskey(gr_font_family, family)
            # この辺を変更
            gr_font_family[family] = begin
                ttf = GR.loadfont(string(f.family, ".ttf"))
                ttf >= 0 ? ttf : GR.loadfont(string(f.family, ".ttc"))
            end
        end
        haskey(gr_font_family, family) && GR.settextfontprec(
            gr_font_family[family],
            gr_font_family[family]  200 ? 3 : GR.TEXT_PRECISION_STRING,
        )
        gr_set_textcolor(plot_color(color))
        GR.settextalign(gr_haligns[halign], gr_valigns[valign])
        nothing
    end
end

fontfamily="ヒラギノ角ゴシック W4"
p = plot(
    sin,
    title="日本語OK",
    label="凡例はもうはみ出さない!",
    fontfamily=Unicode.normalize(fontfamily, :NFD),
    dpi=300
)
display(p)
savefig(p, "plot.png")

参考
https://ujimushisradjp.hatenablog.jp/entry/2023/12/26/010655
https://zenn.dev/hacobell_dev/articles/68ccc92bffd6cc

解説

大きく分けて二つのステップがあります。

  1. TTC形式のフォントファイルを読み込めるようにする
  2. フォントの指定にNFD形式の文字列を使用する

1. TTC形式のフォントファイルを読み込めるようにする

JuliaのGRライブラリではTTC形式のフォントファイルを読み込めないため、ヒラギノ角ゴシック(Hiragino Sans) を使うことができません。
しかし、TTC形式のフォントファイルを読み込めるようにするコードを公開されている方がいらっしゃったので、こちらを引用させていただきます。
(非常に有益なコードを提供してくださった著者の方に感謝いたします。)
https://ujimushisradjp.hatenablog.jp/entry/2023/12/26/010655

@eval Plots begin
   function gr_set_font(
       f::Font, s; halign = f.halign, valign = f.valign, color = f.color,
       rotation = f.rotation,)
       family = lowercase(f.family)
       GR.setcharheight(gr_point_mult(s) * f.pointsize)
       GR.setcharup(sincosd(-rotation)...)
       if !haskey(gr_font_family, family)
           # この辺を変更
           gr_font_family[family] = begin
               ttf = GR.loadfont(string(f.family, ".ttf"))
               ttf >= 0 ? ttf : GR.loadfont(string(f.family, ".ttc"))
           end
       end
       haskey(gr_font_family, family) && GR.settextfontprec(
           gr_font_family[family],
           gr_font_family[family]  200 ? 3 : GR.TEXT_PRECISION_STRING,
       )
       gr_set_textcolor(plot_color(color))
       GR.settextalign(gr_haligns[halign], gr_valigns[valign])
       nothing
   end
end

こちらのコードを最初に実行しておくと、TTC形式のフォントファイルが指定できるようになりました🎉
具体的には、フォントA.ttc を指定する場合は fontfamily="フォントA" とします。

2. フォントの指定にはNFD形式の文字列を使用する

「NFC/NFD問題に気をつけるべし!」

NFCNFDはUnicodeの正規化形式であり、ちゃんと区別しないと文字列を正しく認識できない等の問題が発生します。

NFC/NFD問題については、以下の記事で分かりやすく説明されています。
本記事では、解説の一部を引用させていただきます。
(詳しく解説してくださった著者の方に感謝いたします。)
https://zenn.dev/hacobell_dev/articles/68ccc92bffd6cc

例えば、「パ」はUnicodeでU+30D1と表されますが、こちらの「パ」をNFCとNFDそれぞれの正規化形式で正規化を行うと、以下のようになります。

正規化形式 正規化後のUnicode
NFC (パU+30D1)
NFD (ハU+30CF)+ (゜U+309A)

NFCではそのまま「パ」として表されますが、NFDでは「ハ」(基底文字)と「゜」(結合文字)の組み合わせとしての「パ(UTF-8でe3 83 8f e3 82 9a)」(合成文字)で表されます。試しにNFDで正規化された「パ(e3 83 8f e3 82 9a)」を任意のテキストエリアに貼り付けて削除してみると、半濁音のみが取れて「ハ」のみになると思います。

MacOSのファイルシステムはNFD形式を採用しているため、ファイル名に濁点や半濁点が含まれている場合は注意する必要があります。
文字列をNFD形式で扱っているのはあくまでもファイルシステムであり、大抵のアプリでは普通にキーボードで入力された文字列はNFC形式として処理されます。

さて、今回使いたいフォントは「ヒラギノ角ゴシック」です。
怪しい文字が二つありますね。

普通に入力するとNFC形式として処理されてしまうため、NFD形式に変換する必要があります。

julia> using Unicode

julia> Unicode.normalize("ヒラギノ角ゴシック W4", :NFD)
ヒラギノ角ゴシック W4

これで正しくフォントを指定できるようになりました🎊🎊

失敗例

以下は試して失敗した例です。
画像は savefig() を使って PNG 形式で保存したものです。

失敗例① フォント指定なし

julia> using Plots

julia> gr()
Plots.GRBackend()

julia> plot(sin, title="日本語", label="指定なし")

文字が全部◻︎になってしまいました。
フォントを指定しない場合はOS標準のフォントが使われるはずですが…

失敗例② 英語フォント

julia> using Plots

julia> gr()
Plots.GRBackend()

julia> plot(sin, title="日本語", label="英語フォント", fontfamily="Helvetica")

出力すらされませんでした。まあ英語フォントなので当たり前ですよね。

ところが、試しにSVG形式で保存してみると・・・

あれ!?表示できてる!?
一見、正しく出力できているように見えますが、凡例の名前がはみ出しています。

個人で使う分には問題ないかもしれませんが、正式な場面で使うにはこれで妥協できません。
何よりカッコ悪いです。

失敗例③ 誤ったフォント名

julia> using Plots

julia> gr()
Plots.GRBackend()

julia> plot(sin, title="日本語", label="はみ出る", fontfamily="ヒラギノ角ゴシック W4")

ここの「ヒラギノ角ゴシック W4」はNFC形式なので、誤ったフォント名となっています。

残念。

ただ、SVGで保存してみると

はみ出てしまいますね。

Discussion