🐈

ユーザーエージェントのフォント自動合成には気をつけよう

2024/11/07に公開

事象

cssのアットルールfont-faceでフォントを指定/使用していたところ文字が合成されたような見た目で表示されてしまいました

結論

下記コードのように使用したfont-weight(700)はfont-faceで指定しておらず、ユーザーエージェントが無理やり太く見せようと自動合成してしまったことが原因でした
あえて、同じフォントを重ねることで擬似的にfont-weight700を再現しようと頑張っているようでした
基本的にはfont-faceには明示的にfont-weightを指定する方が良さそうです

@font-face {
  font-family: 'RobotoLight';
  src: url('./Roboto-Light.ttf') format("truetype");
}

.font-light-bold {
  font-family: 'RobotoLight';
  font-weight: 700;
  font-size: 180px;
}

この辺の挙動が気になってみたので下記で詳細を調査してみました

ケース1 font-faceでfont-weight300のフォントファイルを指定して、使用時は700として使用した場合はどうなるか

こちらは上記で説明したケースです

コード

@font-face {
    font-family: "RobotoLight";
    src: url("/Roboto-light.woff") format("woff");
    font-weight: 300;
}

// BAD🙅 上記font-faceではlightを含んだフォントファイルに対してfont weight300を設定しているが、下記では700として使用している
.test {
    font-family: "RobotoLight";
    font-weight: 700;
}

挙動

挙動としてはブラウザによって異なり、下記2パターンが考えられます

  1. font-faceの指定通り表示される表示される(300のフォントとして表示)
    2. font-faceではfont weight 300のファイルが使用されており、700用のフォントファイルは設定されていないので、700を指定しても太くならずに、細いフォントとして使用されるパターン
    3. また、逆にfont-faceにはfont weight700のファイルが使用されており、300を使用するパターンは自動補正で細くすることは難しいためこちらが採用されることがある
  2. フォントの自動補正/自動合成で太く見せる(今回のケース)
    3. ブラウザによってはこちらが採用されるそうです

2の挙動についてですが、結論で述べたように書いてあり、ユーザーエージェントが気を遣って太さを調整してくれているようでした

Although the practice is not well-loved by typographers, bold faces are often synthesized by user agents for families that lack actual bold faces.
https://drafts.csswg.org/css-fonts/#missing-weights
https://www.w3.org/TR/2018/REC-css-fonts-3-20180920/#font-matching-algorithm

ケース2 1つのフォントファイルに複数のfont weightが含まれており、フォントウェイトを指定しない場合

コード

@font-face {
    font-family: "Roboto"
    /* 複数のfont weightが含まれたフォントファイル */
    src: url("/Roboto.woff") format("woff");
}

// 上記font-faceではfont weightを明示的に設定せずに、700として使用している
.test {
    font-family: "Roboto";
    font-weight: 700;
}

挙動

  1. ブラウザが自動的に設定にあった、または似たfont weightを使用して表示
    2. フォントファイルに100から500の太さが存在している場合に700を設定すると、500が使用される
  2. フォントの自動補正/自動合成で太く見せる(今回のケース)

1の挙動についてですが、近いweightのフォントが使用されるそうです

When a weight is specified for which no face exists, a face with a nearby weight is used. In general, bold weights map to faces with heavier weights and light weights map to faces with lighter weights.
https://drafts.csswg.org/css-fonts/#missing-weights
https://www.w3.org/TR/2018/REC-css-fonts-3-20180920/#font-matching-algorithm

対策1 各font weightごとにfont-faceでフォント設定を追加

使用する各font weightごとにfont-faceでフォント設定を追加する

例えば300, 500を使用したい場合

/* 下記のように設定すれば太さごとに適切なフォントが設定され、使用できるようになる */
@font-face {
    font-family: "Roboto"
    src: url("/Roboto-light.woff") format("woff");
    font-weight: 300;
}
@font-face {
    font-family: "Robot"
    src: url("/Roboto-Medium.woff") format("woff");
    font-weight: 500;
}

/* 複数のfont weightが含まれたフォントファイルの場合 */
@font-face {
    font-family: "Roboto"
    src: url("/Roboto.woff") format("woff");
    font-weight: 300;
}

@font-face {
    font-family: "Roboto"
    src: url("/Roboto.woff") format("woff");
    font-weight: 500;
}

100 500と範囲で設定もできます

/* 下記のように設定すれば太さごとに適切なフォントが設定され、使用できるようになる */
@font-face {
    font-family: "Roboto"
    src: url("/Roboto.woff") format("woff");
    font-weight: 100 500;
}

対策2 ユーザーエージェントに自動で調整をさせたくない場合(根本的ではない)

1 font-synthesis: noneで上記動作を回避する
下記のように設定すれば自動合成は回避できます
font weightを700にしていますが、下記画像のように細いままとなります

.test {
    font-family: "RobotoLight";
    font-weight: 700;
    font-synthesis: none;
}

参考
https://drafts.csswg.org/css-fonts/#font-synthesis
https://developer.mozilla.org/en-US/docs/Web/CSS/font-synthesis

番外編 フォントファイルの実態を確認するようにしよう

ファイル名とファイルの中身の実態がバラバラとなっている可能性も拭えないので下記のようにどの太さに対応したフォントファイルか確認して設定した方が良さそう

macの場合はフォントファイルをダブルクリックすると、font bookアプリが立ち上がります
下記画像のようにセレクトボックスの選択肢として対応しているフォントの種類が見れます

既にインストールされているファイルはfont bookアプリに表示されたフォントを選択して、「情報」を開けばfont weightの情報を見ることができます

まとめ

font-faceの設定と使用部分を照らし合わせて適切に設定を追加する
そのためには、下記3点を気をつける必要がありそうです

  1. どのフォントファイル、font weightを使用するのか
  2. 使用するフォントファイルは使用したいfont weightを含んでいるか
  3. 1で確認したfont weightが明示的にfont-faceで設定できているか

Discussion