ウェブで等幅なコードブロックが乱れる原因と対策

[ ウェブで等幅なコードブロックが乱れる原因と対策 ]

ウェブに掲載されているソースコードなどは、通例、等幅フォントが用いられています。ところが残念な事に、等幅フォントが指定されていながら、全体として桁が揃っていないことが、技術系サービスの大手でも珍しくなかったりします。

少しでもクールな書体にしたい気持ちは分かるのですが、そのために等幅としての機能的性質を損ねてしまうのは、デメリットの方が大きいように感じられます。

原因

太字やイタリック体の装飾

シンタックスハイライトで闇雲に装飾すると、MS ゴシック などのフォントでずれます。そもそも MS ゴシックBoldItalic の字形データを持っていません。自動生成の太字は幅広、ビットマップ表示でのイタリック体は劣悪、泣きたくなります。

CUI / CLI の用途としては、ウェイトやスタイルによって文字の送り幅が変わっては都合が悪いのですが、現状それでもやはり「等幅フォント」です。

日本語を含むソースコードなど、こうしたフォントになる可能性が高い部分では、太字やイタリック体の装飾を施さないのが無難かもしれません。

欧文等幅フォントと和文等幅フォントの混在

メトリクスの異なるフォントを font-family に羅列して混ぜると、全角と半角が噛み合いません。別々の基準の等幅と等幅の掛け合わせが、結果として等幅にならないのは、少し考えれば分かると思います。

半角と全角が揃っているように見えるページでも、画面の拡大(ズーム)などで文字の大きさを変えると、サイズによってマチマチで安定しません。

加えて、Unicode の影響で、日本語では全角として用いられてきた記号類が欧文フォント側にも含まれて、意図せず欧文フォントとなるケースが常態化しています。これは font-family では制御不能な現象で、textarea 要素の中でさえ発生しがちな問題です。

対策

ブラウザ設定の monospace を使う

他のフォントと混ぜずに、総称フォントファミリ monospace からブラウザの設定を経由して解決される仕組みに委ねましょう。

ブラウザの既定のフォントがダサいと言われようと、閲覧者にとっては、いつもの馴染みのフォント。少なくともそれ未満にはなりません。

ブラウザの設定を好みのフォントに変更している人にとっては言わずもがな、好みのテイストと異なる上に不揃いというダブルパンチは、かなりのストレスです。

font-family: monospace, monospace; だけで、通常は十分なはずです。

lang 属性を使う

コードブロックに日本語を含めていなくても、日本語ページでは和文等幅フォントになってしまう、という悩みには lang 属性が有効です。

ブラウザの monospace に紐付くフォントは、lang 属性毎に異なるフォントを設定できるので、内容相応の lang 属性であれば、適切に表示されるでしょう。

言語毎のフォント設定は、Internet Explorer / Microsoft Edge でも [コントロール パネル] – [インターネット オプション] – [全般] – [デザイン] – [フォント] から、Firefox では [オプション] – [言語と概観] – [フォントと配色] – [詳細設定] から。Google Chrome では拡張機能に追いやられていますが [設定] – [デザイン] – [フォントをカスタマイズ] – [フォントの詳細設定] で確認できます。lang="en" がラテン (Latin) での設定になるのがポイントですね。

lang 属性を使うのはトリッキーに感じられるかも知れませんが、シンプルながら強力で安定しています。

更に対策

更に対策すると、他のウェブサイトでも、上記の対策済みの状態に近付けて閲覧することが出来ます。

ユーザースタイルシートでブラウザ設定を優先

ブラウザネイティブのユーザースタイルシート機能を使うか、アドオンや拡張機能を使うかは、好み次第かもしれません。

pre, div > code:only-child, .blob-code-inner, textarea[cols] {
  font-family: monospace, monospace !important;
}
  • 1つ目は、通常の pre 要素以下。
  • 2つ目は、コードブロックを div に入れるシンタックスハイライト向け。
  • 3つ目は、GitHub 向け。
  • 4つ目は、桁数指定でのテキストエリア向け。

あまり厳密ではないのですが、カバー率は高いと思います。

ユーザースクリプトで lang 属性を付与

Greasemonkey や Tampermonkey などで仕込みましょう。(現在の Firefox Quantum では Greasemonkey は上手く動いてくれませんでした)

// ==UserScript==
// @name         Japanese monospace helper
// @namespace    https://number-shot.net/
// @version      1.0
// @description  Japanese monospace helper
// @include      *
// @grant        none
// ==/UserScript==
(function() {
  'use strict';
  if (/^(en|ja)($|-)/.test(document.documentElement.lang || window.navigator.language)) {
    var r = /[\u3000-\u30ff\u4e00-\u9fff\uff00-\uff90]/;
    var list = document.querySelectorAll('pre, div > code:only-child, .blob-wrapper > .highlight');
    var lang = Array.prototype.some.call(list, e => r.test(e.textContent)) ? 'ja' : 'en';
    Array.prototype.forEach.call(list, e => {
      e.lang || e.querySelector('[lang]') || (e.lang = lang)
    });
  }
})();

コードブロックっぽい部分に日本語が含まれているかを、大雑把に判断しています。

半年くらい使っていますが、上手く行かなかったのは、某掲示板風のアスキーアートを pre 要素に入れて MS Pゴシック が指定された時だけでした。(´・ω・`)

終わりに

Unicode と等幅の相性は、正直かなり悪いと思います。日本語だと East Asian Width の曖昧文字の問題など、リモートホストが期待する文字幅と端末で描画される文字幅が異なることに、悩まされていると思います。

そうした中で厳選された至極のフォント設定がある方には、この方法はイチオシの対処法です。お試しあれ!

コメント

タイトルとURLをコピーしました