Carpe Diem

備忘録。https://github.com/jun06t

Webフォントのファイルサイズを小さくする

概要

Webフォントを当てると途端にサイトが良く見えるようになりますが、一方でフォントファイルに入っている文字数が多いため、必然的にファイルサイズが大きくなってしまいます。
そこでフォントのサブセット化と呼ばれる、使用する文字だけをフォントから抽出して独自のフォントとして用意する方法があります。

5〜10MBほどあるフォントが大体数十KB程度になるので初期ロード時間が劇的に改善されます。

サブセットフォント生成ツール

github.com

このfontminというツールを使用します。

インストール

gulp用のパッケージがあるのでそれを使います。

$ npm install -g gulp
$ npm install --save-dev gulp gulp-fontmin

基本的な使い方

例として日本語以外の全ASCII文字列を対象にしてみましょう。
gulp.srcに元々のフォントファイルを、gulp.destに生成先ディレクトリを指定します。

gulp.task('font', () => {
  return gulp.src('./fonts/src/*.ttf')
    .pipe(fontmin({
      text: '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
    }))
    .pipe(gulp.dest('./fonts/dest'));
});

実行

$ gulp font

Mgen+ (ムゲンプラス) | 自家製フォント工房mgenplus-1c-thin.ttfに対して実行すると、以下のようなファイルが生成されます。

mgenplus-1c-thin.css
mgenplus-1c-thin.eot
mgenplus-1c-thin.svg
mgenplus-1c-thin.ttf
mgenplus-1c-thin.woff

自動で生成されたmgenplus-1c-thin.css

@font-face {
    font-family: "Mgen+ 1c thin";
    src: url("mgenplus-1c-thin.eot"); /* IE9 */
    src: url("mgenplus-1c-thin.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
    url("mgenplus-1c-thin.woff") format("woff"), /* chrome, firefox */
    url("mgenplus-1c-thin.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
    url("mgenplus-1c-thin.svg#Mgen+ 1c thin") format("svg"); /* iOS 4.1- */
    font-style: normal;
    font-weight: normal;
}

font-faceが定義されているので、これを読み込んで使用すればOKです。

ソース中の全日本語を対象にしたい場合

コマンドラインで日本語の抽出をすることができます。

$ find ./src/app/ | xargs grep -dskip -o -h -e "[亜-熙ぁ-んァ-ヶ]" | sort | uniq > ./subset.txt

これをgulp task化します。

gulp.task('japanese', (cb) => {
  exec('find ./src/app/ | xargs grep -dskip -o -h -e "[亜-熙ぁ-んァ-ヶ]" | sort | uniq > ./subset.txt', (err) => {
    cb(err);
  });
});

あとは先程のコードと結合すればOKです。

gulp.task('font', () => {
  return gulp.src('./fonts/src/*.ttf')
    .pipe(fontmin({
      text: getSubsetText(),
    }))
    .pipe(gulp.dest('./fonts/dest'));
});

function getSubsetText() {
  const ascii = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
  const data = fs.readFileSync('./subset.txt', {
    encoding: 'utf-8'
  });
  const all = ascii + data.toString().split('\n').join('');
  console.log(all);
  return all;
}

ソース