Noto Sans の Web Font 対応とサブセットによる最適化
Intro
このサイトのフォントに Web Font を適用することにした。
フォントには Google と Adobe が協同で開発した Noto Sans CJK JP を採用した。
また、このサイトでは使用しないだろう文字を削除したサブセットを作ることで、フォントサイズを最適化した。
フォントサイズの最適化
Noto font は、そもそも豆腐(フォントがなかった場合に代替表示される四角)が出ないように(No-豆腐)することをコンセプトにしているため、フォントの網羅率は非常に高い。
そのため Web Font として利用する場合は、全体だとサイズが大きすぎるため、言語毎に提供されるフォントセットの中から、必要なフォントのみを適用することになる。
本サイトでは、ASCII 、記号、日本語のフォントを用いる。
しかし、特に網羅された漢字の中には、日常では使わない文字が多々ある。
加えて、このサイトはあくまで 技術ブログ なので、さらに使用する文字は限定される。
そこで、提供されているフォントセットの中から、不要な文字を取り除いたサブセットを作成することで、フォントサイズを最適化が可能である。
Noto Sans は OFL ライセンス であるため、非常に自由度が高く、こうした変更が可能である。
Noto Sans Fonts
まず配布ページから、Noto Sans CJK JP をダウンロードしてくる。
ここに収録されている文字については、google fonts の方に書かれていた。
Note: Noto Sans JP has been subsetted to 6,992 characters and contains most of the characters defined by JIS X 0208 and some additional characters from Shift_JIS. The set includes 6,390 Kanji as well as 602 non Kanji characters such as Hiragana, Katakana (including half-width variants), ASCII and full-width and punctuations.
このサブセットが含む JIS X 0208 とは、いわゆる JIS 漢字コード のことであり、7000 字近い漢字がきっちり収録されている。
そして font-weight (太さ) が 100~900 まで 7 段階提供されており、それぞれサイズは以下となっている。(やはり太いとデカい)
- 100 NotoSansJP-Thin.otf (4.0M)
- 300 NotoSansJP-Light.otf (4.2M)
- 350 NotoSansJP-DemiLight.otf (4.3M)
- 400 NotoSansJP-Regular.otf (4.3M)
- 500 NotoSansJP-Medium.otf (4.3M)
- 700 NotoSansJP-Bold.otf (4.4M)
- 900 NotoSansJP-Black.otf (4.6M)
ここまでがデフォルトの状態である。ここから削って行く。
Font Weight
本サイトでは、Markdown から生成していることもあり、「通常の文字」と「太文字」しか基本的に使わない。
(CSS を別途当てて、調整するといったことをしていない)
したがって、Regular と Bold のみ使用する。
この時点で 8.7MB ある。
- 400 NotoSansJP-Regular.otf (4.3M)
- 700 NotoSansJP-Bold.otf (4.4M)
ここからさらに絞っていく。
JISX0208 第一水準
収録されている、JIS X 0208 は以下のようになっている。
JIS (日本工業規格)で定められた漢字の規格で、JISX0208 に含まれる。日常で使用頻度の高い漢字を集め、コンピューターで利用するためにコード化したもの。JIS 第 1 水準は 2965 字で、常用漢字 1945 字とその他の人名用漢字が含まれており、通常の文書であれば JIS 第 1 水準の文字だけで記述できるとされている。JIS 第 2 水準は 3390 字で、DTP で使用するフォントの多くは、JIS 第 1 水準と JIS 第 2 水準を網羅している。JIS 第 3 水準および JIS 第 4 水準は、業種によって必要になる特殊な記号などを集めたもので、JISX0208 を拡張する別の規格である。
しかし、このブログは 技術ブログ であり、使われる漢字は非常に限定的だ。
そこで、必要な漢字のみを収録したサブセットを作成する。
基本ラテン文字 (0000-0070)
いわゆる ASCII 文字。
(ちなみに、pre > code
内のサンプルコードは別のフォントを使うので、コード用という訳ではない)
Unicode 基本ラテン文字 - Basic Latin -
制御文字などを除いて、以下の範囲を全て収録。
! " # $ % & ' ( ) * + , - . /
0 1 2 3 4 5 6 7 8 9 : ; < = > ?
@ A B C D E F G H I J K L M N O
P Q R S T U V W X Y Z [ \ ] ^ _
` a b c d e f g h i j k l m n o
p q r s t u v w x y z { | } ~
CJK 記号と句読点(3000-303F)
句読点やカッコなど。基本的に [](){}
など半角のカッコを使うが、全角のカッコも必要になる。
Unicode CJK 記号と句読点 - CJK Symbols and Punctuation -
以下が候補
、 。 〃 〄 々 〆 〇 〈 〉 《 》 「 」 『 』
【 】 〒 〓 〔 〕 〖 〗 〘 〙 〚 〛 〜 〝 〞 〟
〠 〡 〢 〣 〤 〥 〦 〧 〨 〩 〪 〫 〬 〭 〮 〯
〰 〱 〲 〳 〴 〵 〶 〷 〸 〹 〺 〻 〼 〽 〾 〿
この中から、使わないものを除き、以下のみを収録。
、 。 々 〆 〇 「 」 『 』 〜
ひらがな(3040-3090)
以下が候補
ぁ あ ぃ い ぅ う ぇ え ぉ お か が き ぎ く
ぐ け げ こ ご さ ざ し じ す ず せ ぜ そ ぞ た
だ ち ぢ っ つ づ て で と ど な に ぬ ね の は
ば ぱ ひ び ぴ ふ ぶ ぷ へ べ ぺ ほ ぼ ぽ ま み
む め も ゃ や ゅ ゆ ょ よ ら り る れ ろ ゎ わ
ゐ ゑ を ん ゔ ゕ ゖ ゙ ゚ ゛ ゜ ゝ ゞ ゟ
以下を収録
ぁ あ ぃ い ぅ う ぇ え ぉ お か が き ぎ く
ぐ け げ こ ご さ ざ し じ す ず せ ぜ そ ぞ た
だ ち ぢ っ つ づ て で と ど な に ぬ ね の は
ば ぱ ひ び ぴ ふ ぶ ぷ へ べ ぺ ほ ぼ ぽ ま み
む め も ゃ や ゅ ゆ ょ よ ら り る れ ろ ゎ わ
を ん
カタカナ(30A0-30F0)
以下が候補
゠ ァ ア ィ イ ゥ ウ ェ エ ォ オ カ ガ キ ギ ク
グ ケ ゲ コ ゴ サ ザ シ ジ ス ズ セ ゼ ソ ゾ タ
ダ チ ヂ ッ ツ ヅ テ デ ト ド ナ ニ ヌ ネ ノ ハ
バ パ ヒ ビ ピ フ ブ プ ヘ ベ ペ ホ ボ ポ マ ミ
ム メ モ ャ ヤ ュ ユ ョ ヨ ラ リ ル レ ロ ヮ ワ
ヰ ヱ ヲ ン ヴ ヵ ヶ ヷ ヸ ヹ ヺ ・ ー ヽ ヾ ヿ
以下を収録
ァ ア ィ イ ゥ ウ ェ エ ォ オ カ ガ キ ギ ク
グ ケ ゲ コ ゴ サ ザ シ ジ ス ズ セ ゼ ソ ゾ タ
ダ チ ヂ ッ ツ ヅ テ デ ト ド ナ ニ ヌ ネ ノ ハ
バ パ ヒ ビ ピ フ ブ プ ヘ ベ ペ ホ ボ ポ マ ミ
ム メ モ ャ ヤ ュ ユ ョ ヨ ラ リ ル レ ロ ワ
ヲ ン ヴ ヵ ヶ
半角形と全角形(FF00-FFE0)
全角の英数や半角カタカナなど。しかし、このブログで これらは使わない。
Unicode 半角形と全角形 - Halfwidth and Fullwidth Forms -
以下が候補
0 1 2 3 4 5 6 7 8 9 A B C D E F
! " # $ % & ' ( ) * + , - . /
0 1 2 3 4 5 6 7 8 9 : ; < = > ?
@ A B C D E F G H I J K L M N O
P Q R S T U V W X Y Z [ \ ] ^ _
` a b c d e f g h i j k l m n o
p q r s t u v w x y z { | } 〜 ⦅
⦆ 。 「 」 、 ・ ヲ ァ ィ ゥ ェ ォ ャ ュ ョ ッ
ー ア イ ウ エ オ カ キ ク ケ コ サ シ ス セ ソ
タ チ ツ テ ト ナ ニ ヌ ネ ノ ハ ヒ フ ヘ ホ マ
ミ ム メ モ ヤ ユ ヨ ラ リ ル レ ロ ワ ン ゙ ゚
ᄀ ᄁ ᆪ ᄂ ᆬ ᆭ ᄃ ᄄ ᄅ ᆰ ᆱ ᆲ ᆳ ᆴ ᆵ
ᄚ ᄆ ᄇ ᄈ ᄡ ᄉ ᄊ ᄋ ᄌ ᄍ ᄎ ᄏ ᄐ ᄑ ᄒ
ᅡ ᅢ ᅣ ᅤ ᅥ ᅦ ᅧ ᅨ ᅩ ᅪ ᅫ ᅬ
ᅭ ᅮ ᅯ ᅰ ᅱ ᅲ ᅳ ᅴ ᅵ
¢ £ ¬  ̄ ¦ ¥ ₩ │ ← ↑ → ↓ ■ ○
収録はしない。
漢字
さていよいよ漢字である。単純に Unicode の範囲で指定するわけにはいかない。
過去に書いた記事から、使用する漢字を抽出しようかとも思ったが、ブログを続けていけば新しい漢字が出る可能性は非常に高い。
そのたびにフォントセットを作り直すこともできるが、フォントには長時間のキャッシュを効かせたいため、なるべく一発でこのブログに適したサブセットを作りたいと考える。
他のサイトでは「第一水準漢字」に絞るという方法も紹介されている。
以下に一覧がある。
このサブセットを適用しているサイトが多いということは、経験上第二水準にはブログなどの Web サイト使う漢字が少ないということなのだろう。
これでもだいぶ減っているが、ざっと見ても使わなそうな漢字はかなり多い。
先に引用した定義では、第一水準はざっくり「常用漢字 + 人名用漢字」となっている。
このサイトでは、人名の引用は基本的に Twitter ID などになるだろう。というか書くのは自分なのでそう縛ればいい。
そこで、常用漢字のみに絞ってみた。
常用漢字は文化庁が告示している。2010 年に改定があった。
一般の社会生活において現代の国語を書き表すための漢字使用の目安を、次の表のように定める。
以下に一覧がある。
気になって、二つのセットの diff を取ってみたら、「 JIS 第一水準にはないが、常用漢字にある漢字 」があることがわかった。
「JIS 第一水準でだいたい OK」と言われている候補に無い文字が、常用漢字一覧にはある。
つまり、常用漢字一覧のなかでも、使われ無い可能性が高い漢字が含まれるということだ。
以下の 30 文字である。
ざっと見て、以下 6 文字はいらないだろうと判断した。
他にも、常用漢字一覧には、まだまだ「 技術ブログではおおよそ出てこなさそうな漢字 」がいくつか見られる。
もちろん使う場面はあるかもしれないが、書くのは自分なので把握しておけば良い。
どうしても必要ならフォントセットに追加すれば良いが、その頻度もそう高くはないだろう。
よって、いらない漢字を 1 つ 1 つ丁寧に手動で取り除いた。
先の 6 文字に 17 文字加えて、以下 23 文字が削除対象である。
これらを反映した結果が以下である。
しばらく運用して、省かれていた以下の文字が記事中に必要になったため、文字セットに追加した。
(後述する結果にも反映していある)
第一水準漢字
常用漢字
常用漢字と第一水準漢字の差分
丼 傲 刹 哺 喩 嗅 嘲 彙 恣 惧 慄 憬 拉 摯 曖 楷 毀 璧 瘍 箋 籠 緻 羞 訃 諧 貪 踪 辣 錮 鬱
哺 惧 彙 楷 憬 錮
不要な文字
畝 租 朕 逓 哺 曹 惧 梗 痘 虞 嗣 塑 彙 楷 窟 嘱 畿 憬 璃 錮 嚇 濯 璽
更新(2016/08/28)
✔
ー (伸ばし棒)
叩
套
嬉
揃
殆
溜
筈
紐
綺
繋
脆
閾
騙
結果
ここまでの結果を合わせる。
範囲
文字数
基本ラテン文字
94
CJK 記号と句読点
11
ひらがな
81
カタカナ
83
半角形と全角形
0
常用漢字
2136
記号
1
不要文字
- 23
追加更新
13
合計
2396
全て合わせたのが以下である。
フォント生成
ここまでに選択した文字のみで Web Font を作成する。
まず、以下のツールを用い、この対象文字だけを抜き出したサブセットを作成する。
この OTF ファイルを、以下のツールを用い、Woff 形式に変換する。
メタデータは一切付与しない。
また IE はどうでもいいので、IE に対応させるための EOT ファイルも作成しない。
サブセット化
Woff 変換
効果
簡単にサイズの比較を行ってみる。
比較は Woff で行い、フォントは Regular を使う。
まず対象とする漢字による、サイズの変化はこうなる。
もしここまでに見てきた各項目にあげた候補を、一切削除せず、漢字を JISX0208 とした場合はこうなる。
漢字
範囲
サイズ
JISX0208
505.0K
常用漢字
356.1K
削除後
351.6K
結果
サイズ
before
547.5K
after
378.4K
フォントの設定
font-face の指定
まず woff ファイルを配布し、それを読み込ませる。
ただし、Android Marshmallow では Noto Sans がビルドインになったため、Regular のみが全フォント入っている。
他にも、このブログを見に来る人は、自分でローカルインストールしている人がいるだろう。
そうした場合、local()
を用いてシステムフォントを優先すれば、ダウンロードが不要になる。
現在 NotoSans は "NotoSansCJKjp-Bold.otf" という名前で配布されている。
Android は "NotoSansJP-Regular.otf" という名前で入っているらしい ため、それも含めて以下のように設定する。
@font-face {
font-family: "NotoSansCJKjp-Jxck";
font-style: normal;
font-weight: 400;
src: local("NotoSansCJKjp-Bold.otf"),
local("NotoSansJP-Bold.otf"),
url("https://path-to-font/NotoSansCJKjp-Jxck-Regular.woff") format("woff");
}
@font-face {
font-family: "NotoSansCJKjp-Jxck";
font-style: normal;
font-weight: 700;
src: local("NotoSansCJKjp-Bold.otf"),
local("NotoSansJP-Bold.otf"),
url("https://path-to-font/NotoSansCJKjp-Jxck-Bold.woff") format("woff");
}
font-family の指定
使用したフォントを CSS に設定する。
Google はガイドラインを公開している。
システムにある場合は正式名称で、こちらで提供したフォントがダウンロードされた場合は、それが使われるようにする。
基本的には、サイト全体に適用するため body
へ指定し、pre > code
内は別にしたいため、そこだけ上書きしている。
body {
font-family: "Noto Sans", "Noto Sans CJK JP", "NotoSansCJKjp-Jxck", sans-serif;
}
キャッシュ設定
フォントは、基本的には変更が非常に少ないファイルであるため、積極的にキャッシュをしていきたい。
ただし、今回のような作り方の場合には、フォントの追加によるファイルの更新が皆無ではないため、URL にバージョンを忘れずに入れる。(font-face の指定参照)
url("/path-to-font/NotoSansCJKjp-Jxck.Bold.woff?ver=201603014") format("woff");
ここでは max-age を一年とし、フォントを作り直した場合はバージョンを変える
Cache-Control: max-age=31536000
Preload
Preload を用いたリソースプリローディングの最適化 で解説した <link rel=preload>
でフォントを適用した。
ただし、影響が大きい Regular のみにした。
<link rel=preload as=font type=font/woff href="https://path-to-font/NotoSansCJKjp-Jxck-Regular.woff?ver=201603014" crossorigin>
また、AMP 対応ページでは rel=preload
は許可されてないため指定するとエラーになったため、AMP の方には指定していない。
Outro
Web Font もだいぶ一般的になってきたため、今後の Web 作成では、Web Font の存在は前提となっていくだろうと感じている。
このサイトではパフォーマンスに関わる最適化手法を色々と試しているが、Web Font も無しに表示の速さを語っても片手落ちだろうと思い、いわゆる鉄下駄として入れてみた。
こうした使いやすく優れたフォントが無料で提供されているのは、非常にありがたい。
その他 WebFont に関連する検証は web font タグにまとまっている。