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

[日本語 Web フォントの読込み速度を改善する - その1](https://qiita.com/megurock/items/da484d4ec44577ae7f95) の続きですが、「その 1」は未読でも全く問題ありません！:sweat:

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

ちなみに Google Fonts 同様に、残念ながら IE :shit: は非対応です（いつものことですね）。[^1]

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

**:octocat: ソース一式**
https://github.com/megurock/webfont-optimization

**完成ページ**
https://jp-webfont-subset-example.herokuapp.com/#/
![img_01.PNG](https://qiita-image-store.s3.amazonaws.com/0/37412/4be7bdbc-c364-9a74-31e1-42a70d7f6d0e.png)


## 環境

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

- Windows 7
- Node 10.9.0
- Yarn 1.9.4
- Python 3.7.2

## 準備

### フォントの用意

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

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

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

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

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

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

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

完成したファイルはこのようなものになります。
https://github.com/megurock/webfont-optimization/blob/master/js/ranges.js

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

フォントのサブセット化は、[font-ranger](https://www.npmjs.com/package/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

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

```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 つにまとめます。

```css:/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` の設定をすれば完成です！

```html:index.html
<link rel="stylesheet" href="./css/fonts.css">
```

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

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

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

## Service Worker を使ったキャッシュ（その 3 へ続く）

さらなる速度改善を求め、Service Worker を使ったキャッシュに挑戦してみました。
[日本語 Web フォントの読込み速度を改善する - その3](https://qiita.com/megurock/items/ac9c430d734ff84369eb)


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