Macのターミナルで全角記号を綺麗に表示したかった


はじめに

Macのデフォルトのターミナルや Iterm2 等のコンソール上で一部全角記号を表示すると挙動が怪しくなります。

echo 程度なら文字が重なって読みにくいだけで済みますが、vim等のエディタ上ではカーソルの制御が狂って意図しない挙動になったりと結構不便です。
何とかならないものかと色々と調べてみたのでまとめてみました。

環境

  • Mac OS: Sierra (version 10.12.6)
  • iTerm2: 3.3.7

tl;dr

  • iTerm2の設定は役に立たない
  • /usr/share/locale/{locale値}/LC_CTYPE を書き換えれば解決するが、OS X 10.11 El Capitan 以降のMacの場合リカバリーモードに入らないと書き換え不可
  • 上記以外の方法として、全角記号を半角で表示するフォントを使えば一応ごまかせる (今回はこちらで対応)

なんで文字が重なってしまうのか

コンソール上で文字を表現するときに、その文字の幅が半角英数字何文字分なのかを判断する必要があります。
かつて東アジア圏では1バイト文字に1桁の幅(いわゆる半角)の文字を、2バイト文字に2桁の幅(いわゆる全角)の文字を割り当てることが行われおり、今現在普及されている文字規格Unicodeでもその特性との互換性を残すため East_Asian_Width 特性が定められています。
(詳細は 東アジアの文字幅 - Wikipedia にまとまっているので割愛)

この East_Asian_Width プロパティの中で East Asian Ambiguous (A) 属性の文字は文脈や言語によって文字幅が異なり、コンソールではロケール LC_CTYPE ファイルによって文字幅が制御されます。
この LC_CTYPE で設定されている文字幅とフォントファイルで指定されている文字幅が一致しないと、意図しない余白や文字同士が重なってしまう現象が発生します。
上記例ですと、文字 (U+25ef), (U+2606), (U+25ce) が当てはまります。 文字フォント上は全角(2桁幅)であるにもかかわらず、Unicode 上では半角(1桁幅)として扱われ、重なった表記になる様子です。1

[非推奨] 解決策1: iTerm2の設定で Ambiguous characters are double-width を設定

iTerm2 の機能で Unicode の East Asian Ambiguous (A) 属性の文字を強引に全角(2桁幅)にするオプションです。
...が、shellの履歴を遡ると表示がバグったり2、vimのカーソルの位置はやっぱり狂いまくりなので対応としてはイマイチ。
Preferences > Profile > Text > Unicode の Ambiguous characters are double-width で設定できますが、やめておいたほうが良さそうです。

解決策2: Ambiguous の適応範囲を強引に書き換えて全角表記にする

/usr/share/locale/{locale値}/LC_CTYPE を書き換え不具合のある文字の文字幅を矯正する手法です。
該当文字コードを1つ1つ指定するのはめんどくさい...と思ったのですが、既に同様の問題を対応している mutt-ja というプロジェクトのリポジトリに良さそうなコードがあったのでこちらを利用していきます。

LC_CTYPEを書き換える
# 良さそうなファイルをダウンロード
curl -L "http://sourceforge.jp/projects/mutt-j/scm/svn/blobs/146/mutt-ja-doc/trunk/samples/UTF-9.src?export=raw" > $WORK_DIR/ja_JP.UTF-8.src
# メモ:
#   URLが UTF-9 だしファイルの文頭も "UCS-4 used here as the internal representation, not UTF-8." と書いてあり怪しいが、
#   https://ja.osdn.net/projects/mutt-j/scm/svn/blobs/head/mutt-ja-doc/trunk/UTF-8.txt によると
#   UTF-8 を元に UTF-9 というファイル名で書き換えた、とあるので多分大丈夫なはず。

# LC_CTYPE ファイルを作成
mklocale -o $WORK_DIR/LC_CTYPE $WORK_DIR/ja_JP.UTF-8.src

# 念の為既存の LC_TYPE ファイルをバックアップ
sudo cp /usr/share/locale/UTF-8/LC_CTYPE /usr/share/locale/UTF-8/LC_TYPE.bak

# 作成した LC_TYPE ファイルに上書き
sudo cp $WORK_DIR/LC_CTYPE /usr/share/locale/UTF-8/LC_TYPE

これで文字が重なる問題からオサラバだぜ!よっしゃー今年のAdventCalendarのネタ終了!!
...と思っていましたが sudo cp の部分で Operation not permitted が出て進められないようです。
どうやら OS X 10.11 El Capitanより追加されたセキュリティ機能、SIP(System Integrity Protection)によって /usr 配下の書き込みは root 権限を持ってしても書き込みができないらしい。まじか。

今私は会社から貸与されたPCでこの記事を書いているのですが、このPCで参考記事「初心者向け MacでOperation not permittedの解決方法」にある通りリカバリーモードでセーフティー外してまで試すのは気が引ける...
別の手段を考えるしか無いのか...ぐぬぬ

解決策3: Ambiguous 文字を半角で表示するフォントを使う

フォント側を修正し、LC_CTYPE 側に文字幅を合わせる手法です。

https://github.com/tomonic-x/Illusion

上記リポジトリのフォントを使うことで解消できそうです。(画像はリポジトリのものをお借りしました)
おおー、なかなか良さそうじゃないですか!!
Illusionのフォントファミリーは3種類あり (詳細は上記リポジトリ参照)、その中の Illusion N (Narrow) が求めていたフォントになります。

全角記号文字を半角サイズで表示するので小さくなって見にくいですが、vimのカーソルがずれて編集できなくなる方が死活問題なので及第点といったところでしょうか。

Tips1: Nerd font と併用 (fontforgeでフォントを合成する)

特にフォントにこだわりのない方は Illusion N (Narrow) をそのまま使うことで解決できるかと思います。
ただ私は Nerd Fonts というリッチなアイコンが詰まったフォントを使用しており、これらによってtmuxやvimをデコレーションしているのです。Nerd Fonts以外のフォントをそのまま利用すると豆腐だらけの画面になってしまい、生産効率が当社比半分以下になってしまいます。
c(`Д´と⌒c)つ彡ヤダヤダ

そこで fontforge を使い Illusion N (Narrow) と Nerd font を合成し、両取りを目指してみます。

# fontforgeを取得 (既にインストール済みの場合は不要)
brew install fontforge
# 以下のコマンドを実行してAplicationにFontForgeを紐付け (Path通す)
brew cask install fontforge

# https://github.com/tomonic-x/Illusion/releases から良さげなフォントファイルをDL
curl -L -O "https://github.com/tomonic-x/Illusion/releases/download/v0.2.5/Illusion-0.2.5.zip"
# 必要あるファイルだけ解凍 (結構ファイル多くて邪魔になるので)
unzip Illusion-0.2.5.zip hinted/Illusion-Regular.ttc  # このttcファイルに3種類のフォントファミリーが入ってる

# Nerd Fonts を clone
#   リポジトリのサイズが大きいので shallow clone する。 通常の clone は公式非推奨
git clone --depth 1 [email protected]:ryanoasis/nerd-fonts.git

# Nerd Fonts リポジトリ上の font-patcher でフォントを合成
# オプション -s: Nerd Fonts のアイコン (glaph) を半角として定義
fontforge -script nerd-fonts/font-patcher hinted/Illusion-Regular.ttc -s --careful --complete -ext ttf
  • メモ: 試したバージョン
    • fontforge: Version 20190801
    • Illusion: v0.2.5
    • Nerd Fonts: v2.0.0

ここまでを実行すると、作業ディレクトリ直下に Illusion N Nerd Font Complete Mono.ttf なるフォントファイルが生成されます。

デモ用にフォントサイズを24pxとしていますが、やっぱりアイコン部分が小さい...
nerd-fonts/font-patcher-s オプションを外すことで大きくできるけど、彼方立てれば此方が立たぬ...

Tips2: フォント変えたけどvim上での文字がやっぱりずれる場合

vimrc に以下を追記すれば良くなるはず。

set ambiwidth=single

今回、 East Asian Ambiguous (A) 属性の文字を半角として扱うようにしたのですが、vim上の設定も同様に設定しておきましょう。

最後に

普段の業務で扱うvimの気になったことをAdventCalendarに取り上げて5行程度の記事で済ませようかなーと思ってたのですが、蓋を開けたらvimとはほとんど関係のない話でかつ想像以上に沼で文量多くなってしまいました (ヽ´ω`)
タイトルの「綺麗に表示したかった」という、当初思い描いていた完成形に着地できていないので「表示した」と言い切れない、モヤモヤする記事となってしまいました。力不足で申し訳ないです。
もっといい方法があるよ!という方がいらっしゃいましたらコメント欄にて連絡いただければ幸いです。
ターミナル上のvimでテキスト編集する人は大勢いるかと思いますが、全角記号を使いまくるケースはあまり想像できないのでちょっとニッチな記事なのかなと思ったりもしてます。

どこかの誰かさまにお役に立てれば光栄です。

参考になった文献


  1. そもそも Unicode® Standard Annex #11 EAST ASIAN WIDTH に記載の通り、 East_Asian_Width プロパティ自体が端末エミュレータでの動作を保証していないらしい。悲しい。 

  2. 参考: East Asian WidthとiTerm2 (そもそもiTerm2公式的にも非推奨らしい)