chlonolog

web, digital gadgets, and more.

gulp + fonttoolsでフォントのサブセットを作成する

綺麗なwebフォントを使いたい! でも大元のフォントをそのままwebフォントにするとファイルサイズがでかい! それならサブセット化だ! ……ということで、やってみました。
CSSでfont-feature-settings:を使用したかったので、サブセットフォントメーカーは使用していません。その代わりに、pyftsubset(fonttoolsのライブラリ)を使用しています。

fontmin & gulp-fontminならもっとお手軽に使えますが、こちらだとwoff2を作成できないのと、自分の環境では設定していないcssやsvgなども一緒に作成されてしまうのでやめました。

参考にしたのはこちら。


必要なものをインストールする

まず、必要なもろもろをインストールします。

フォントの変換には、fonttoolsが必要です。
fonttoolsはPythonのモジュールなので、Pythonとpipを使えるようにしておく必要もあります。

pip install fonttools

fonttoolsをgulpから実行する際、gulp-execを使います。

npm install gulp-exec --save

gulpの動作を引数で切り替えたいので、minimistも入れます。

npm install minimist --save

woff2を作りたいので、brotliも入れておきます。

git clone https://github.com/google/brotli
cd brotli
python setup.py install

あと、Macでファイルのリネーム方法を考えるのが面倒だったのでrenameを入れてあります。

brew install rename

ディレクトリの構造

┬ node_modules/  (gulpで使用しているモジュール)
├ project-name/  (Hexoのプロジェクト)
├ _fonts/ ┬ src/ (大元のフォントを置く場所)
│     ├ dest/ (作成したサブセットを出力する場所)
│     ├ subset-level1.txt(サブセット変換用のデータを格納しているファイル)
│     ├ subset-level2.txt(〃)
│     └ subset-mini.txt (〃)
└ gulpfile.js

subset-XXX.txtの中身は、こんな感じになっています。
いずれもひらがな・カタカナ・ASCII文字を省いた状態で作成しています。

level1:第一水準までの漢字

subset-level1.txt

level2:第二水準までの漢字

subset-level2.txt

mini:タイトル用に、ぎりぎりまで削った文字列

内容は適当に端折っていますが、基本的にはこんな感じです。
ファイル読み込み時に改行は削除しますので、単純な単語の列挙のままです。

月
検索
技術
更新履歴

gulpfile.js

モジュール読み込み・引数の取得

var gulp = require('gulp');
var fs = require('fs');
var exec = require('gulp-exec');
var minimist = require('minimist');

// gulpでパラメータを取得するにはminimistを使う
// gulp font --type level2
var env = {
  default: {
    type: 'level2'
  }
};
var options = minimist(process.argv.slice(2), env);
var font_path = '_fonts/src';
var font_dest = '_fonts/dest';

minimistを使用し、実行時にパラメータを取得しています。

gulp font --type level2

といった感じに呼び出すことを想定しています。
--typeで指定するパラメータは、サブセット変換用データを格納しているファイルsubset-XXXXX.txtXXXXXの部分です。

サブセット対象の文字データをファイルから取得

指定したパラメータのサブセット文字データをファイルから取得し、ひらがな・カタカナ・ASCII文字のデータを追加して返します。

/**
 * 指定したファイルからサブセット対象文字データを取得し、
 * 基本のテキストを追加した状態で返す
 */
function getSubsetText(filename) {
  const ascii = "\\'\\!\\\"#$%&\\(\\)*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\\`abcdefghijklmnopqrstuvwxyz\\{|\\}~";
  const kana = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろわゐゑをんァアィイゥウェエォオヵカガキギクグヶケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロワヰヱヲンヴ、。、。,. ・:;?!゛゜´`¨^ ̄_ヽヾゝゞ〃仝々〆〇ー―‐/\~∥|…‥‘’“”〔〕〈〉《》「」『』【】+-±×÷=≠<>≦≧∞∴♂♀°′″℃¥$¢£%#&&*@@§☆★○●◎◇◆□■△▲▽▼※〒→←↑↓∀∃∠⊥⌒∂∇≡≒≪≫√∽∝∵∫∬ʼn♪†‡¶―—﹅";
  const data = fs.readFileSync(filename, {
    encoding: 'utf-8'
  });
  const all = ascii + kana + data.toString().split('\n').join('');
//  console.log(all);
  return all;
}

フォントのサブセット作成

_fonts/src/内にあるファイルを読み込み、pyftsubsetを使用してサブセットを作成します。
出力先は--output-fileで指定できるのですが、ファイル名の変換が手間だったので、デフォルトでつく「fontfile-name.subset.woff」の「.subset.」の部分をrenameで置換してからファイル移動しています。

/**
 * pyftsubsetを使ってフォントのサブセットを作成し、woffとwoff2に変換する
 */
gulp.task('font', function() {
  var opt = {
    continueOnError: false,
    pipeStdout: false
  };
  var reportOptions = {
    err: false,
    stderr: true,
    stdout: true
  };
  var txt = getSubsetText('./_fonts/subset-' + options.type + '.txt');
  var cmd_woff = "pyftsubset \
  <%= file.path %> \
  --text=\"" + txt + "\" \
  --layout-features='*' \
  --flavor=woff";
  var cmd_woff2 = "pyftsubset \
  <%= file.path %> \
  --text=\"" + txt + "\" \
  --layout-features='*' \
  --flavor=woff2";

  gulp.src(font_path + '/*.*')
  .pipe(exec(cmd_woff, opt))
  .pipe(exec(cmd_woff2, opt))
  .pipe(exec("rename s/\.subset/-" + options.type + "/ " + font_path + "/*.subset.*", opt))
  .pipe(exec('mv ' + font_path + '/*.woff* ' + font_dest, opt))
  .pipe(exec.reporter(reportOptions))
});

フォントを試す楽しみが増えました

Oradano明朝というフォントがあります。非常に美しいフォントですが、凝っているためにファイルサイズが大きいのが難点です。
それでも、本当に必要なテキストのみを厳選してサブセットを作成すれば、webフォントとして十分使えるようになりました。
手軽に作成できるようになったので、いろいろなフォントを試す楽しみができました。

でもいろいろ試した結果、シンプルな上にカーニングもきちんとかかるNoto Sans/Serifに帰結しちゃったんですけどね。
「これいいな」と思っても、font-feature-settings:が効かないOpenTypeフォントが結構あるんですよね……でも、いい感じに効くフォントを探すのも楽しみのひとつ、でしょうか。

コメント

© 2018 chlono