Chienomi

フォント帳アプリを作る → Linuxフォントの闇に触れる

spacing

  • TOP
  • Old Archives
  • フォント帳アプリを作る → Linuxフォントの闇に触れる

追記分について

日本ではあまりなじみのない欧文コーディングフォント(プログラミング向けフォント, モノスペースフォント)をなんでこんな苦労してるんだろうとか思うくらいの労力を掛けてカタログ作ったので、気合いれて紹介させていただく

ぜひ見ていただきたい。

はじまりはmonospace

フォント一覧として“monospaceフォントの一覧を作りたい”と考えたのだ。

アプリ自体は全フォント一覧も作れるようにしたが、monospace限定でも出力したい。 どのような方法があるかと考えた。

調べるとspacingの値を見ればわかるということはわかったのだが、spacingMonospaceProportionalのような文字列ではなく、90, 100といった値になっている。

とりあえず90100もおおよそmonospaceだということはわかったが、90を提示するがmonospaceでないフォントもある。

だいたい90100の違いはなんなのか…もしかしてQtのmonospaceフォント一覧に関係があるのか…

と調べていくといろいろわかってきた。

100FC_MONOの定数であり、Monospaceのことであるらしい。 90FC_DUALであり、幅を2種類持っているもののようだ。

確かに等幅和文フォントは “monospace” ではない。 “dualspace” である。

では等幅和文フォントのspacing100にした場合Qtウィジットもmonospaceとして認識してくれるのだろうか?

FontConfigの一覧はいじれない?

で、どうやらfontconfigの設定(fonts.conf)はフォントを “要求されたとき” には反映されるけれど、 “リストするとき” には無視されるようだ。

例えばこんな設定ファイルを作る。

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">

<fontconfig>
    <match target="font">
        <test compare="eq" name="family">
            <string>Migu 1M</string>
        </test>
        <edit name="spacing" mode="assign" binding="strong">
            <int>100</int>
        </edit>
    </match>

    <match target="pattern">
        <test qual="any" name="family">
            <string>Foogothic</string>
        </test>
        <edit name="family" binding="same" mode="prepend">
            <string>Anonymous Pro</string>
            <string>Migu 1M</string>
        </edit>
    </match>
</fontconfig>

Migu 1Mのspacing100(FC_MONO)になる。

% fc-match "Migu 1M" : spacing family
Migu 1M:spacing=100

けど、リストには入らない。

% fc-list :spacing=100 : family | grep -i migu

Foogothicもちゃんと認識はされているけど

% fc-match -a Foogothic : family | head
Anonymous Pro
Anonymous Pro
Anonymous Pro
Anonymous Pro
Migu 1M
Migu 1M
Bitstream Vera Sans
Bitstream Vera Sans
Bitstream Vera Sans
Bitstream Vera Sans

リストにはない。

% fc-list | grep -i foo

90(FC_DUAL)100(FC_MONO)として認識させると、フォントに設定されている幅を無視するアプリケーションが存在するため、リストに入れてくれるわけでもないのならばデメリットしかない。

欧文フォントを和文で使いたいという場合には和文フォントを加える形にしてあげれば良いのだが

<match target="pattern">
    <test qual="any" name="family">
        <string>Envy Code R</string>
    </test>
    <edit name="family" binding="same" mode="prepend">
        <string>Envy Code R</string>
        <string>Migu 1M</string>
    </edit>
    <edit name="spacing" mode="assign" binding="strong">
        <int>90</int>
    </edit>
</match>

問題はQtである。

QtはMonospaceフォントとしてFC_MONOの値よりもspacingが大きいフォントを提示する。

問題は、FC_MONO100であり、FC_DUAL90であるため、FC_DUALなフォントは提示されない、ということだ。

この問題はバグとして報告されているのだが、今以て解決していない。

現状、monospaceを和文フォントにするか、適当な(使わない)欧文monospaceフォントをインストールして、その名前を利用して置き換えるくらいしかなさそうだ。

和文プロポーショナルフォントの話

実は和文フォントは一般的には「ラテン文字はプロポーショナル、全角文字は等幅」となっており、 日本語部分までプロポーショナルになっている「和文プロポーショナルフォント」というのはかなり珍しい。

このためか、ラテン部分がプロポーショナルであるにもかかわらず、spacing=90にしてしまうフォントが結構あるようだ。例えばDynafontとか。

なので、今回のスクリプトは-s 90とすればdualspaceフォントを含めてリストしてくれるけれども、必ずしも等幅フォントであるとは限らない。

今回のスクリプト

GitHubにアップした。

基本的にはFontConfigから値をひらってeRubyで埋め込んだHTMLを作るプログラムである。

プログラム自体は至ってシンプルなものだ。 どちらかといえばFontConfigに関する知識のほうが求められる。

READMEにある通り、うまく動作しない部分がある。 family名を指定しても正しく表示されないもの、fullnameがBoldなどになっているもの、 spacing=90といいながら等幅フォントではないもの1kanahaniが実態と合っていないもの、 などなど「フォントの情報って結構ガバガバだなぁ」と感じてしまう。 もちろん、フォントの情報から取っているわけで、がフォントにウソ情報を書かれたりするとスクリプト側では処理しがたい。

ウェイトの違うフォントが別ファイルになっているものと同一ファイルのもの、PlainやNormalなどRegularではない標準フォントウェイト名であるためにBoldが標準になってしまうもの、 ビットマップとスケーラブルがスタイルの違いになっているもの(例えばTelegramaはRawがビットマップ、Renderがスケーラブル)と厄介なものは色々ある。

欧文モノスペースフォント紹介

ボリュームがすごーく増えてしまったので独立させました

今回の件で気づいたこと

欧文フォントフェイスはかなり似ている

「読みやすいアルファベット」という条件がついていて、あまり標準的でない形は読みづらさにつながる。

結局、こうした条件のためにだいたいのフォントが似ていて、「ターミナル」「タイプライター」「セリフ」「サンセリフ」の4種類で分類できるような形状しか生まれてこない。

だが、微妙な違いが読みやすさにつながる部分もある。 現在人気のフォントはどれも似たようなサンセリフまたはターミナル体なのだけど、探せばちょっと変わったフォントもあるにはある。 人気のあるフォントの中ではMonacoやUbuntu Monoがちょっと変わったグリフをしている。逆にHackやDroid Sans Monoはものすごく普通。

商用フォントはいいぞ

Dark Mono, Operator, Native, Range Mono, Vivala Codeなど見比べて「おっ、いいな」と思うフォントは軒並み商用フォントになっている。

ただし、プログラマ、コーダーなど「日がな一日プログラムを書いている」人に関しては、これぞと気に入ったフォントがあれば買ってみるのも手ではないだろうか。 一日中向き合っているものだし。

欧文フォントも売られているものは高いものもある

Operatorなんて$199もする。 1書体のお値段としてはモリサワフォント並。 Pragmata Proもデスクトップ用 * レギュラー/ボールド/イタリックで€199

日本語フォントとは制作労力が雲泥の差なので、ちょっとえげつない値段だと言っていい。

Dark Monoも40ポンドとかなりお高め。

Atomはレンダリングが汚い

フォントのウェイトやサイズがばらつきやすく、autohintのようなガタツキも発生する。

うまく表示してくれるフォントを探すのにはずいぶん苦労した。

Atomでキレイに表示してくれるのはCPMonoなのだけど、CPMonoはコーディングフォントとして決して快適ではない。

日本語と混ぜるなら、agave, Fantasque, Fira Code, Luculent, Mesloあたりが快適。 私は結局Fira Code+Migu 1Mにした。

この組み合わせは非常に違和感がないが、後述のようにもう少し違和感があったほうが読みやすい。 その点、LuculentやMesloのほうが有力だった。

フォントの情報はいい加減

「漢字グリフはなくてひらがなだけなんだけど、haniは書いてあってkanaは書いてない」みたいなフォントもある。

商用フォントでも結構ザルである。 プログラムとしてはフォントの情報に基づいて処理するしかないわけで、「なんでここでそのフォントが?」と思うようなフォントが提示されたり、偽薬に提示されなかったりというのはここらへんに理由があるのだろう。

日本では紹介されていないモノスペースフォントはいろいろ

日本人としては日本語Glyphのないフォントは使えない、という感覚があるのだろう。 あまり紹介されることもない。

だが、実際にはプログラム中には日本語なんてほぼ使わないわけで、モノスペースフォントは日本語Glyphがなくてもあまり困らないし、 今はglyphがなければそれなりに処理してくれるのが普通なので別に好きに組み合わせても良いのではないだろうか。

日本で紹介されるものは人気があるというよりも無難なものが多く、実際にはもっと使ってみる価値のあるフォントは色々ある。

フォントレンダリングの微妙な差

KDEアプリケーションは割とにじみ強く太めに描画するため、キレイに見える。

Gnomeアプリケーションはもっとくっきり描画する。

どちらもFontConfigを使っているのだけど、描画ライブラリの微妙な差なのか、見え方が割と違う。 FirefoxとChromiumの描画差は気づきやすい。

そして前述のようにAtomはあまりよくない。

autohinterは諸悪の根源

以前は有効にするのが一般的だったように思うautohinterだけども、有効にするとガタガタになる。

フォントが寄る、ガタつく、崩れるなどのことがあったらautohinterを切るといいかも。

FontConfigはデフォルト設定がだいたいいい感じ

FontConfigの設定はディストリビューションによって作り込まれていることが多く、違いがあるけれども、 Arch設定だと色々いじってみても基本的にはデフォルトが良いようだ。

nobitmapと、サブピクセルレンダリングの配置くいらはいじったほうがいいけども。

日本語とアルファベットは差があるほうが見やすい

コーディング向きのglyphを持つ日本語フォント2の場合、統一感があるように調整されている。 「日本語とアルファベットの統一的デザインってなんだ」と思うかもしれないが、主にはグリフのボックスサイズと、線の太さである。

普通は「半角は全角の半分」となるように設計する。Source Code JPフォントの場合は全角は半角の1.5倍幅になっている。

だが、Linux環境ではこのような和文フォントのスペーシングがうまく適用されないことが多い。 Atomの場合和文フォントを指定すると縦には揃ってくれない。このことから、このようにメリットは少ない。

また、日本語にアルファベットを混ぜ書きするケース(スペースを持たないケース)においては、アルファベットは「検索(eyeboll-search)の対象」であることがあり、 区別しやすいほうが良いと考えられる。

このことを踏まえると、「日本語とアルファベットの統一感はなくても困らないし、むしろないほうが便利」だったりする。

おまけ: 今回のフォント画像に使ったコード

JavaScript組み込みHTMLのPandocテンプレートである。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="$lang$" xml:lang="$lang$"$if(dir)$ dir="$dir$"$endif$>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
$for(author-meta)$
  <meta name="author" content="$author-meta$" />
$endfor$
$if(date-meta)$
  <meta name="dcterms.date" content="$date-meta$" />
$endif$
$if(keywords)$
  <meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
$endif$
  <title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>
  <style type="text/css">
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      body { padding: 50px; font-size: 25pt; }
$if(quotes)$
      q { quotes: "“" "”" "‘" "’"; }
$endif$
  </style>
$if(highlighting-css)$
  <style type="text/css">
$highlighting-css$
  </style>
$endif$
$for(css)$
  <link rel="stylesheet" href="$css$" />
$endfor$
$if(math)$
  $math$
$endif$
$for(header-includes)$
  $header-includes$
$endfor$
</head>
<body>

$for(include-before)$
$include-before$
$endfor$
$if(title)$
<header>
<h1 class="title">$title$</h1>
$if(subtitle)$
<p class="subtitle">$subtitle$</p>
$endif$
$for(author)$
<p class="author">$author$</p>
$endfor$
$if(date)$
<p class="date">$date$</p>
$endif$
<input type="text" size="20" id="FontForm" /><input type="button" value="Change" id="FS" />
</header>
$endif$
$if(toc)$
<nav id="$idprefix$TOC">
$table-of-contents$
</nav>
$endif$
<input type="text" size="40" id="FontName" style="border: none 0px; font-size: 26pt; background-color: #fff; padding: 5px; border: #39f double 3px; color: #333; margin-top: 2em; margin-bottom: 0.8em" />
$body$
$for(include-after)$
$include-after$
$endfor$
<script type="application/javascript">
  var fm = document.getElementById("FontForm")
  var nameBox = document.getElementById("FontName")
  document.getElementById("FS").addEventListener("click", function(e) {
    for (var code of document.getElementsByTagName("code")) {
      code.style.fontFamily = fm.value
      nameBox.value = fm.value
      nameBox.style.fontFamily = fm.value
    }
  }, false)
</script>
</body>
</html>

  1. Iosevkaはdualspace扱いになっている。↩︎

  2. そもそも合成でないフォントでそのようなフォントはかなり少ない。M+(VLゴシックとMiguもこのglyph)くらいではないだろうか。Source Han Code JPとOsaka等幅は専用に調整されてリリースされているものなので含めてもいいかもしれないが。↩︎