webfont
Googlefonts

日本語 Web フォントの読込み速度を改善する - その2

日本語 Web フォントの読込み速度を改善する - その1 の続きですが、「その 1」は未読でも全く問題ありません!:sweat:

今回は Google Fonts では提供されていない日本語フォントを、自分で 120 ファイルに分割してサブセット化してサイトで使う方法について紹介します。

ちなみに Google Fonts 同様に、残念ながら IE :poop: は非対応です(いつものことですね)。1


サンプルコード & サンプルページ

:octocat: ソース一式

https://github.com/megurock/webfont-optimization

完成ページ

https://jp-webfont-subset-example.herokuapp.com/#/

img_01.PNG


環境

以下の環境で動作を確認しています。


  • Windows 7

  • Node 10.9.0

  • Yarn 1.9.4

  • Python 3.7.2


準備


フォントの用意

今回はサンプルの日本語フォントとして、こちらのサイト で公開されている「しろくまフォント」を利用させていただいております。上記 Git リポジトリにこのフォントは含まれておりませんので、サンプルコードを動かしてみたい人は、予めご自分で「しろくまフォント(001Shirokuma-Regular.otf)」をダウンロードし、プロジェクトのルートへ格納してください。


モジュールのインストール

Node と Python のモジュールをインストールします。

# Node モジュールのインストール

yarn

# Python モジュールのインストール
pip install fonttools brotli


1. unicode-range の定義ファイルを作成する

日本語フォントを 120 分割するにあたり、それぞれのフォントセットにどういう文字列を格納するかを定義します。これには、Google Fonts が提供する Noto Sans JP を利用するのに必要な CSS ファイル に、unicode-range の記述がありますので、それをコピーして JavaScript で扱えるフォーマットに変更します。

完成したファイルはこのようなものになります。

https://github.com/megurock/webfont-optimization/blob/master/js/ranges.js


2. font-ranger によるサブセット化

フォントのサブセット化は、font-ranger という Node モジュールを使用しました。このモジュールは、ranges というパラメータに unicode-range を指定することにより、任意の TrueType/OpenType フォントをwoff2 フォーマットでサブセット化してくれます。

また unicode-range を含む諸々の @font-face 定義がされた CSS ファイルも作成してくれます。


font-ranger 実行用スクリプト

font-ranger のウェブサイトには、CLI による使用方法が記載されていますが、今回は 120 ファイル分のサブセット化が必要なので別途プログラムを用意しました。

[完成版]

https://github.com/megurock/webfont-optimization/blob/master/subset.js

カスタマイズを行う場合は、下記を参考に適宜修正してください。


subset.js

const defaultParams = {

fontFile: '001Shirokuma-Regular.otf', // サブセット化をする元フォントファイル
outputFolder: './fonts', // サブセット化されたフォントファイルと CSS の生成先
urlPrefix: '/fonts/', // @fontface の src の url に付ける接頭辞
fontFamily: 'Shirokuma', // @fontface の font-family
fontWeight: 400, // @fontface の font-weight
fontStyle: 'normal', // @fontface の font-style
fontDisplay: 'swap', // @fontface の font-display
keepFormat: false, // オリジナルのフォントフォーマットを維持するか(用途不明)
addWoff: false, // フォールバック用に woff ファイルを作成するか
skipCss: false, // css 書き出しが不要な場合は true を設定
copyOriginal: false, // 元フォントファイルを出力先にコピーする場合は true
locals: ['Shirokuma', 'Shirokuma-Regular'], // @font-face の local
}

customRanges.forEach((ranges, index) => {
const params = Object.assign({}, defaultParams, {
ranges,
// サブセット化されたフォントファイルの名前
// 結果的には、shirokuma.0.custom-subset.woff2 のようなファイル名になります。
fontName: `shirokuma.${index}`
})
try {
fontRanger(params);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
}
})


このプログラムを実行すると、/fonts ディレクトリ配下に、サブセット化されたフォントデータと、@font-face が定義された CSS ファイルが、それぞれ 120 個生成されます。


3. CSS のマージ

120 個生成される CSS ファイルの 1 例です。これらを 1 つにまとめます。


/fonts/shirokuma.0.css

/* custom-subset */

@font-face {
font-family: 'Shirokuma';
font-style: normal;
font-weight: 400;
font-display: swap;
src:
local('Shirokuma'),
local('Shirokuma-Regular'),
url('/fonts/shirokuma.0.custom-subset.woff2') format('woff2');
unicode-range: U+25E56, U+25E62, U+25E65, U+25EC2, U+25ED8, U+25EE8, U+25F23, U+25F5C, U+25FD4, U+25FE0, U+25FFB, U+2600C, U+26017, U+26060, U+260ED, U+26222, U+2626A, U+26270, U+26286, U+2634C, U+26402, U+2667E, U+266B0, U+2671D, U+268DD, U+268EA, U+26951, U+2696F, U+26999, U+269DD, U+26A1E, U+26A58, U+26A8C, U+26AB7, U+26AFF, U+26C29, U+26C73, U+26C9E, U+26CDD, U+26E40, U+26E65, U+26F94, U+26FF6-26FF8, U+270F4, U+2710D, U+27139, U+273DA-273DB, U+273FE, U+27410, U+27449, U+27614-27615, U+27631, U+27684, U+27693, U+2770E, U+27723, U+27752, U+278B2, U+27985, U+279B4, U+27A84, U+27BB3, U+27BBE, U+27BC7, U+27C3C, U+27CB8, U+27D73, U+27DA0, U+27E10, U+27FB7, U+2808A, U+280BB, U+28277, U+28282, U+282F3, U+283CD, U+2840C, U+28455, U+2856B, U+285C8-285C9, U+286D7, U+286FA, U+28946, U+28949, U+2896B, U+28987-28988, U+289BA-289BB, U+28A1E, U+28A29, U+28A43, U+28A71, U+28A99, U+28ACD, U+28ADD, U+28AE4, U+28BC1, U+28BEF, U+28CDD, U+28D10, U+28D71, U+28DFB, U+28E0F, U+28E17, U+28E1F, U+28E36, U+28E89, U+28EEB, U+28EF6, U+28F32, U+28FF8, U+292A0, U+292B1, U+29490, U+295CF, U+2967F, U+296F0, U+29719, U+29750, U+29810, U+298C6, U+29A72, U+29D4B, U+29DDB, U+29E15, U+29E3D, U+29E49, U+29E8A, U+29EC4, U+29EDB, U+29EE9, U+29FCE, U+29FD7, U+2A01A, U+2A02F, U+2A082, U+2A0F9, U+2A190, U+2A2B2, U+2A38C, U+2A437, U+2A5F1, U+2A602, U+2A61A, U+2A6B2, U+2A9E6, U+2B746, U+2B751, U+2B753, U+2B75A, U+2B75C, U+2B765, U+2B776-2B777, U+2B77C, U+2B782, U+2B789, U+2B78B, U+2B78E, U+2B794, U+2B7AC, U+2B7AF, U+2B7BD, U+2B7C9, U+2B7CF, U+2B7D2, U+2B7D8, U+2B7F0, U+2B80D, U+2B817, U+2B81A, U+2F804, U+2F80F, U+2F815, U+2F818, U+2F81A, U+2F822, U+2F828, U+2F82C, U+2F833, U+2F83F, U+2F846, U+2F852, U+2F862, U+2F86D, U+2F873, U+2F877, U+2F884, U+2F899-2F89A, U+2F8A6, U+2F8AC, U+2F8B2, U+2F8B6, U+2F8D3, U+2F8DB-2F8DC, U+2F8E1, U+2F8E5, U+2F8EA, U+2F8ED, U+2F8FC, U+2F903, U+2F90B, U+2F90F, U+2F91A, U+2F920-2F921, U+2F945, U+2F947, U+2F96C, U+2F995, U+2F9D0, U+2F9DE-2F9DF, U+2F9F4;
}

マージはどんなやり方でもよいのですが、今回は専用の Node スクリプトを作成しました。

https://github.com/megurock/webfont-optimization/blob/master/merge.js

実行すると /css ディレクトリ配下に、マージ結果である fonts.css ファイルが生成されます。

https://github.com/megurock/webfont-optimization/blob/master/css/fonts.css


4. Done! :sunglasses:

/css/fonts.css を HTML に読み込み、font-family の設定をすれば完成です!


index.html

<link rel="stylesheet" href="./css/fonts.css">


body { font-family: 'Shirokuma', sans-serif; }

これで Google Fonts を利用した場合と同様に、ページで使われている文字列に対応したサブセットフォントファイルを、ブラウザが自動的に読みこんでくれます。セルフホスティングの場合は、CDN の恩恵を受けることができませんが、サブセット化をしないすべての日本語フォントデータを読み込む場合よりも、サイトの読込みは高速化されると思います。

日本語フォントのサブセット化には、有料のダイナミックサブセットサービスを利用するという選択肢もありますが、無料でできるこのやり方ももうひとつの選択肢としてみても良いのではないでしょうか?


Service Worker を使ったキャッシュ(その 3 へ続く)

さらなる速度改善を求め、Service Worker を使ったキャッシュに挑戦してみました。

日本語 Web フォントの読込み速度を改善する - その3





  1. Google Fonts の場合は、Windows 10 に限っては IE11 でも、サブセット化されたフォントファイルを非同期読込みしてくれるようです。そして、それ以外の OS の IE11 ではサブセット化されていない日本語全部入りのフォントファイルを読み込むようです。