50
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Nerd fontとpowerlineとambiguous width

Last updated at Posted at 2019-12-25

はじめに

こんにちは。まとまった休日を使ってターミナルの設定をいじるのが最近の趣味です。
そんなことをしていたらフォント周りで2日くらい溶けた。失われた時間への供養として調べたことのメモを置いておく。

簡単にこの記事をまとめると、

  • Nerd Fontsの一部であるPowerline Extra Symbolsがかっちょいいので和文フォントでも使ってみたい
  • なんか表示がずれるのでフォントずれの原因であるUnicodeのambiguous widthについて調べてみた
  • 自分の環境では色々試したけど結局上手くいかないので自分でフォントを作るか?

2019/12/27 追記
以下で区切り文字がピッタリ合わないとひたすら文句を言っている部分があるが、powerline用フォントの表示が「微妙に」ずれるのはambiguous widthかどうかは関係なくターミナルとフォントの相性によるようだ。フォントサイズや解像度、フォント合成に使ったpatcher等で容易にずれる。そもそもpowerlineの区切り文字を合わせる際に無理をしているので高さが合わないことがある。

Powerline Extra Symbolsとの出会い

私はterminal上にtmuxを起動し、その中でvimを起動するスタイルで普段作業している。
今までの設定でも特に不満はなかったのだが、気持ちを入れ替えるという意味でも設定を見直してみようと思った。年末の大掃除みたいな。

最近、powerlineのいかにもpowerlineな区切り文字(>とか<)にどうも飽き飽きしてしまった。
導入した当初は最高にクールなターミナルだと感じていたのだが、慣れというものは恐ろしい。
なにか目新しいものはないかと探していたところ、𝑺𝒆𝒙𝒚 & 𝑷𝒐𝒘𝒆𝒓𝒇𝒖𝒍 𝑪𝒐𝒏𝒇𝒊𝒈𝒖𝒓𝒂𝒕𝒊𝒐𝒏 𝒇𝒐𝒓 𝑳𝒊𝒈𝒉𝒕𝒍𝒊𝒏𝒆 𝒂𝒏𝒅 𝑻𝒎𝒖𝒙を発見した。

𝑺𝒆𝒙𝒚 & 𝑷𝒐𝒘𝒆𝒓𝒇𝒖𝒍 𝑪𝒐𝒏𝒇𝒊𝒈𝒖𝒓𝒂𝒕𝒊𝒐𝒏 𝒇𝒐𝒓 𝑳𝒊𝒈𝒉𝒕𝒍𝒊𝒏𝒆 𝒂𝒏𝒅 𝑻𝒎𝒖𝒙
画像元

なんだこの斜め区切りは。見たことがない。しかもセクシーである。

そしてこの区切り文字はNerd Fontsの一部であると知った。これは何としても使ってみなくてはならない。

Nerd Fontsは相当な量のアイコン用フォントをunicodeのprivate領域に割り当てるもので、その中に例の斜め区切りが含まれているようだ。
Powerline Extra SymbolsはNerd Fontの一部としてインストール可能であり、この中に斜め区切りのフォントが仕込まれている。Powerline Extra symbolsにはよく見る>みたいな形だけではなくスタイリッシュなものから厨二的なものまで揃っている。

ぜひこれをRicty等の和文フォントでも使ってやりたいというのがこの記事。
先に断っておくと、自分の環境では微妙にズレたり何だりで上手く和文フォントと組み合わせることができなかった。

Unicodeにまつわる背景

Nerd Fontsをターミナルで適切に表示させるためには、Unicodeの仕様がどのように定義されていて、ターミナルやshell、vimなどの実装がどうなっているかを知る必要がある。

(もっとも、そんなことを考えずにfontを使えたら一番であるが、、)

そもそもPowerlineはどのように実現されているか

ターミナルの表示は全て文字である。vimの区切り線もtmuxの区切り線も、powerlineの区切り文字も全て文字である。
Unicodeではbox drawingが文字として定義されており、tmux等ではこれを用いて境界を引いている。
powerlineの区切り文字(>とか<)はunicodeの仕様で定義されたものではなく、unicodeの好き勝手にしていい領域(private)に区切り文字を用意し、それを表示することで実現している。
つまり、フォントに新たな区切り文字を追加し、その文字コードをshellやvim、tmuxに埋め込むのだ。

原理はとても単純である。なんか簡単にできそうな気もする。
しかし、ある特定の文字が全角か、半角か、それが「曖昧」として「定義」されていることによってターミナルの世界に混沌がもたらされる。

Ambiguous-width Characters

Nerd FontsではVimのVマークがU+E7C5に割り当てられる。

早速これをターミナルで表示してみると
nerdfont.png
VimのVがbに重なってしまっている。いかん。

これはシェル、ターミナルがこれを半角文字であるとして取り扱っているのに対し、フォントが全角幅を持っていることが原因である。

これはまだ単純な例だが、ここにtmuxやらvimやらが絡んできて、それぞれが違う文字幅を仮定してしまえばてんやわんやである。

なぜこんなことになっているのかといえば、平たくいってしまうとunicodeの仕様が悪い。

unicodeにはambiguous widthという概念があり、この属性を持った文字は仕様上半角でも全角でもオーケーなのだ。そんなの不整合が生じるに決まっている。

と、仕様に文句を言うのは簡単だが、仕様を決めるのは難しいということを仕様を読んで理解するところまでが1セットである。
(Ambiguous widthについて: Unicode® Standard Annex #11 East Asian Width

では各ソフトウェアはどのように半角か全角かを決めているかという話である。
例えば、gnome-terminalでは設定でAmbiguous-width CharactersNarrowにするかWideにするかを選択することができる。

では先程の文字の重なりを修正するにはambiguous width charactersの幅をwideにすればいいじゃないか。

しかしそれだと今度はtmuxで表示が崩れる。
(issue: Seeing tmux splits in other panes

何故こうなるかといえば、tmuxがpaneの区切りに使っている文字がambiguous widthだからである。

https://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt より

2500..254B;A     # So    [76] BOX DRAWINGS LIGHT HORIZONTAL..BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL

と、box drawing charactersはambiguous width (A)に分類されるため、unicodeの仕様上は半角か全角かはどちらでもいいことになる(文脈依存)。

tmuxはターミナルの設定を知る由もなく、box drawing charactersが幅1であるとしてpaneを埋めるだけの数の文字を発行するが、ターミナルはそれを幅2であると解釈して表示してしまう。

結局、どちらをデフォルトにすべきかどうかは明確ではない。

Unicode® Standard Annex #11 East Asian Widthより

If the context cannot be established reliably, they should be treated as narrow characters by default.

と書いてあったりもするのでやはり、「基本は半角で、特別な場合は全角を使ってね」というスタンスのように感じる。
歴史的経緯でambiguous-width charactersが発生してしまったが、ゆくゆくはhalf widthに統一していきたいということであろうか(であってほしい)。

private領域におけるAmbiguous-width Characters

Ambiguous-width charactersは、日本語環境において等の文字の表示が崩れる、という文脈で触れられることが多いのだが、今回問題になるのは追加で勝手に定義したprivate領域の文字である。

Unicode® Standard Annex #11 East Asian Widthより

Private-use character codes and the replacement character have ambiguous width, because they may stand in for characters of any width.

つまりprivate領域の文字、つまりここではNerd Fontによって定義されるアイコンその他諸々は全てambiguous widthを持つことになる。なんてこった。

前項で触れたようにターミナルにおいてambiguous-width charactersの幅はhalfに指定しておいたほうが何かと都合が良い。
しかしprivateな領域に実際に使われるのはアイコンなどの文字であり、それらは大抵の場合full widthの方が正方形に近くなり見栄えが良い。

ここに来て、「ambiguous-widthがhalfであると仮定している既存ソフトウェア」と「ambiguous-widthをfullで使いたいicon fonts」の間の折り合いを付けなくてはならないのである。

(せめて全角と半角でprivate領域を分けてくれていればこんなことには、、、)

ちなみにunicodeで定義されているemojiはもはやambiguous widthではない。
2016年に出たUnicode® Standard Annex #11 East Asian Width Revision 31で初めてemojiに関する記述が追加された。

Emoji style standardized variation sequences behave as though they were East Asian Wide, regardless of their assigned East_Asian_Width property value.

emojiがambiguous widthではないというのは少々不正確かもしれない。emojiがどんなwidth属性を持っていようと関係なくwide幅で表示するべき、ということになっている。

とにかく、今回問題になるのはprivate領域内の、仕様で定義されないemoji(に類するもの)の表示幅がambiguousである、ということになる。

wcwidthによる解決の可能性

wcwidth関数がPOSIXの仕様に含まれている。
これは文字を引数に取り、その文字の幅(大抵の場合1 or 2)を返す関数である。

shellも、vimも、tmuxも、terminalも、全員が全員wcwidthを使っていれば、wcwidthが返す値を変更することで統一的にambiguous widthの表示幅を決め打ちすることができる。
例えばprivate領域にあるフォントを追加して、それらの文字コードに対してwcwidthが返す幅を自分で設定することができたりすると嬉しい。
(これはLD_PRELOADで動的にwcwidthを上書きすることで可能。wcwidth-cjkとかwcwidth-icons

しかし、shell、vim、tmux、terminalのうち1つでもwcwidthを使用せずに勝手な判断をすると全てが台無しになる。

少なくとも、gnome-shellはwcwidthを使っていないようだしtmuxもなんかあやしい

この解決が最も一貫性があって好きなのだが、現状ではあまり上手くいかない。

自分が思う現時点でのベストプラクティス

ambiguous-widthはとりあえずhalfで固定してしまいたい。
実はtmuxにパッチを当てることでtmux罫線問題は解決するのであるが、自分でパッチを当てるのはあまり気が進まない。
また、このような問題がtmuxだけで生じるとも限らない(例えばこんなやつ)。

そうなるとNerd Fonts等のicon fontも半角で表示されることになる訳だが、これは後ろに空白を入れておくことで解決したりする。
nerdfont2.png
(これもいかにもworkaroundという感じで好きではないが)

以下ではとにかく、ambiguous-widthは半角であるとして扱い、全角のフォントを持つambiguous-width charactersに関しては後ろに空白を入れることで対応する。

動作環境

本記事は多分に環境依存であると思われる。

  • OS: Ubuntu MATE 18.04
  • kernel: 5.0.0-37-generic
  • terminal: MATE terminal v1.20.0
    • (一応gnome-terminal v3.28.2も試してみたが大差なし)
  • shell: bash v4.4.20(1)-release
  • neovim: v0.5.0-dev
  • tmuxは噛ませずにterminalのbashでneovimを起動して動作確認

Nerd Fontsを使ってみる

長くなってしまったが、ようやくここでNerd Fontsを使っていい感じのpowerlineにすることを試みていく。

Powerline Extra Symbolsのフォントも例に漏れず全角なので、後ろにスペースを入れるなどして対処する。

RictyDiminished-Nerd-Fonts

RictyDiminished-Nerd-Fontsは、Ricty DiminishedにNerd Fontsを当てたもの。そのまま配布されていてありがたい。

どうやらNerd Fontsの本家のpatcherではなく、fontmergerという和文フォントでも綺麗に表示されるように改良したものを使ってpatchを当てている。
(参考: ターミナルでアイコンフォントを使う理由

powerline5.png
左右ズレが若干ある。

次に斜め区切りを試してみる。
powerline6.png
やはり少しずれるようだ。

Cica

Cicaはとてもいい感じの日本語フォントセット。Nerd Fontsを含んだ形で配布されているのでインストールがとても楽ちん。
これもNerd Fontsを適用後にズレの修正を行ったりしているらしい。

powerline3.png
これも大きなずれはないものの、左の隙間が気になってしまう。
また、フォントの構造としては>(空白), (空白)<なので、<の左側に追加の空白が生じてしまうのが残念。

斜め区切りを試してみる。
powerline4.png
だいたいOKだがやはり惜しい、、、。

Rictyに直接Nerd Fontのパッチを当てる

ダメ元で。

⟩ fontforge -script /path/to/nerd-fonts/font-patcher Ricty-Regular.ttf --powerline --powerlineextra

(boldも同様)

powerline7.png
なんか似たような感じ。

powerline8.png
右側が余計にひどくなった。

待たれよ

𝑺𝒆𝒙𝒚 & 𝑷𝒐𝒘𝒆𝒓𝒇𝒖𝒍 𝑪𝒐𝒏𝒇𝒊𝒈𝒖𝒓𝒂𝒕𝒊𝒐𝒏 𝒇𝒐𝒓 𝑳𝒊𝒈𝒉𝒕𝒍𝒊𝒏𝒆 𝒂𝒏𝒅 𝑻𝒎𝒖𝒙を見返してみると斜め区切りは全角じゃなくないか?そもそも半角のほうがセクシーである。

どうやらNerd Fontのpatch用スクリプトには以下のオプションがあり、これを指定することで半角のフォントが生成できるらしい。

-s, --mono, --use-single-width-glyphs
                        Whether to generate the glyphs as single-width not double-width (default is double-width)

やってみたが、フォントが分かりやすく"終わって"しまった。日本語フォントはmonoとは相性が悪いみたいだ。

Powerline Fontのpatchを当てる

そもそもNerd Fontsを使うと普通のpowerline(>とか<)でさえズレるじゃないか。どうなってるんだ。

ここで気になったのは、本家のpowerlineではなぜ上手くいっていたか、ということだ。
そこで本来のpowerlineのパッチ(もはやnerd fontは関係ない)を試してみる。

本家のfontpatcherを使う

powerline本家powerline/powerlineのリポジトリにfontpatcherが置かれていたこともあったが、今はpowerline/fontpatcherにスクリプトが移動している。

mapping:

  • ▶ : U+E0B0
  • : U+E0B1

  • ◀ : U+E0B2
  • < : U+E0B3
fontforge -script /path/to/fontpatcher/scripts/powerline-fontpatcher Ricty-Regular.ttf

(boldも同様)

表示してみるとこうなる。
powerline2.png

右側の微妙なズレが気になる。
個人的には三角形が横長過ぎて気に食わない。区切りの<もなんか太い。本家ってこんな感じだったっけ。

vim-powerlineのパッチを使う

vim-powerlineは既にdeplicated。

UnicodeのU+2B60-U+2BFFあたりの文字を上書きして潰してしまうのであまりお行儀の良いパッチとは言えない。これが作られた当初はこの辺の文字が未割り当てだったりしたんだろうか(知らない)。

mapping:

  • ▶ : U+2B80
  • : U+2B81

  • ◀ : U+2B82
  • < : U+2B83

どうも自分が数年前にRictyにパッチを当てた際はこの方法でやって上手くいっていたらしい。

fontforge -script /path/to/vim-powerline/fontpatcher/fontpatcher Ricty-Regular.ttf

(boldも同様)

powerline1.png
1ピクセルの乱れもない美しいpowerlineである。

vim-powerlineでは全角ではなく半角のフォントが生成されるのでシンプルで良い。
本家powerlineのフォントは全角を使いたがっているが、どうもそのせいで表示がずれやすくなっている気がする(気がする)。半角でいいじゃん。

Powerlineフォント内蔵ターミナル

kittyというターミナルはターミナル自体にpowerline的なフォントを備えているのでfontにパッチを当てずともイケてる見た目のpowerlineが得られるらしい。すごい。
しかし、現状ではこんなかんやでRicty等の日本語フォントを含むフォントが無視される。無念。

おわりに

いかがでしたか?

ターミナルの区切り文字をかっこよく表示する方法について調べてみましたが、よく分かりませんでした!

という典型的なアレになってしまった。

まとめると

  • 全角の追加アイコンフォントにはunicode仕様上の困難が伴う
    • private領域のunicodeは全てambiguous width
  • Nerd Fonts(というよりPowerline Extra Symbols)を使うと微妙にズレて気持ち悪い
    • Powerline Extra Symbolsのフォントが全角なのが良くない?
  • 通常版powerlineのvim-powerline(deprecated)は良いpatchを提供する
    • 半角なのでズレにくい?

で、結局、件のセクシーな区切り文字のpowerlineを和文フォントで実現するにはどうしたら良いかといえば、あまりいい解決策が現状ない。

しかしまだ諦めてはいない。

少々手間はかかるが、vim-powerlineのパッチを解読し、fontを親交を深め、自分が思うままの半角の区切り文字のフォントを作り出すしかないだろうか。

svgか何かを渡せば自動でpowerlineの区切り文字フォントを作ってくれるスクリプトも頑張れば作れるのかもしれない(可能であれば)。それは結構ほしいかも。

また、当方にわかフォント人間です。何か間違いだったり、記事に書かれていない知見がありましたらご指摘いただけると幸いです。

参考

50
39
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?