フリーフォントのサブセットを作り、Webフォント化

フリーフォントのサブセット(部分集合、つまり特定の文字だけを含むことで軽量化したフォント)を作り、Webフォントにする方法を説明します。


FontForgeを使用して、フリーフォントであるM+フォントのサブセットを作るには私は以下の手順を取りました。M+フォント以外のフリーフォントにも、この手順を使うことができます。

  1. 使用したい文字をすべて含んだテキストファイルをUTF-8で保存。
  2. そのテキストファイルを以下のスクリプト listChar.js に通して、収録文字リストを作成。
  3. 収録文字リストを、以下のスクリプト listCharFF.js に通して、FontForgeスクリプトを作成。
  4. FontForgeでフォントを読み込み、FontForgeスクリプトを実行する。
  5. FontForgeで[Font - Save as Font - WebOpenFormatFont]でwoff保存。


f:id:itouhiro:20140511231150p:plain


具体的な方法

1. 使用したい文字をすべて含んだテキストファイルをUTF-8で保存。

Webフォントに含めたい文字の入った文章を用意します。
ここでは「吉川 英治/三国志 02 桃園の巻」
http://www.aozora.gr.jp/cards/001562/card52410.html
を使ってみます。

そのページの 52410_ruby_51060.zip をダウンロードして展開。
文字コードShift_JISなので、UTF-8に変換して再保存。


2. そのテキストファイルを以下のスクリプト listChar.js に通して、収録文字リストを作成。

node.js http://nodejs.org/ をインストールしておいて(ここではWindows版 v0.10.26を使用)、上記の listChar.js を使用。

c:\home> node listChar.js 02toenno_maki.txt.utf8 > char.txt

f:id:itouhiro:20140511221727p:plain

生成されたchar.txtは割と大きなファイルになってしまった。

×―…○ 、。々《》「」【】ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶ・ー一丁七万丈三上下不与丕世丘丞両並中丸丹主乃久乎乏乗九乞乱乳乾予争事二于云互五井些亜亠亡交享京
‥‥
3. 収録文字リストを、以下のスクリプト listCharFF.js に通して、FontForgeスクリプトを作成。

上記のchar.txtをさらにlistCharFF.jsで加工。

c:\home>node listCharFF.js char.txt > charFF.txt

f:id:itouhiro:20140511222533p:plain

生成されたcharFF.txtはこんな感じ。

SelectWorthOutputting();
SelectFewer(0ud7);
SelectFewer(0u2015);
SelectFewer(0u2026);
SelectFewer(0u25cb);
SelectFewer(0u3000);
SelectFewer(0u3001);
SelectFewer(0u3002);
SelectFewer(0u3005);
SelectFewer(0u300a);
‥‥
4.FontForgeでフォントを読み込み、FontForgeスクリプトを実行する。

FontForge (ここでは http://www.geocities.jp/meir000/fontforge/fontforge-cygwin_2014_01_04.zip を使用)を起動し、フォントファイル(ここでは http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/download/ のM+ TESTFLIGHT 058 に含まれる mplus-1m-regular.ttf)を読み込みます。
f:id:itouhiro:20140511224201p:plain


[ファイル - スクリプトを実行]で、スクリプト入力画面を出します。[Python]ではなくて[FF]を選択してください。

そして、charFF.txtの中身をコピーして、[右クリック - 貼り付け]します。もし貼り付けできなかったら、もう一度charFF.txtの中身をコピーして[右クリック - 貼り付け]してください。何回かすると貼り付けできます。できたら、[OK]をクリック。30秒ほど待たされました。
f:id:itouhiro:20140511224319p:plain


5. FontForgeで[Font - Save as Font - WebOpenFont]でwoff保存。

[ファイル - フォントを出力 - Web Open Font - 生成] でwoff保存します。
f:id:itouhiro:20140511224827p:plain
f:id:itouhiro:20140511224905p:plain

何か質問されたら以下のように答えます。
「フォントをこのまま出力しますか?」 → はい
「review the errors or save the font anyway?」 → 生成

これでwoff出力はOK。

ちなみに354KBのファイルになりました。漢字が多いからなー。漢字の少ないテキストであれば、woffファイルのサイズが100KB以下になることもあります。画像を用意するよりサイズ小さくてすみますね。


サイトデザインに差をつける Webフォントコレクション (ijデジタルBOOK)

サイトデザインに差をつける Webフォントコレクション (ijデジタルBOOK)


スクリプト


listChar.js (半角英数字を全角英数字に変換する処理が入っていますので、必要ない場合は「text = changeHankakuZenkaku(text);」の行を削除しましょう)

// usage: $ node listChar.js scenario.txt > char.txt
// テキストファイルに含まれる文字のリストを作る。Webフォントに含める文字をリストアップできる。
// caution: scenario.txt must be 'UTF-8'

//フォントには含めない(除外する)文字
var exclude = '※①②③④⑤⑥⑦⑧⑩⑳●■\uFEFF';


var fs = require('fs');
var text = fs.readFileSync(process.argv[2])+''.replace(new RegExp("\uFEFF",'g'),'');

var changeHankakuZenkaku = function(str){
  var hankaku = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
  var zenkaku = ' !″#$%&′()*+、-。/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_‘abcdefghijklmnopqrstuvwxyz{|}~';
  var problemregexp = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~';
  for (var i=0; i<hankaku.length; i++){
    var s = hankaku.charAt(i);
    for(var j=0; j<problemregexp.length; j++){
      if (problemregexp[j] === s){
        s = '\\' + s; break;
      }
    }
    //console.log('replace:'+s+' -> '+zenkaku.charAt(i));
    str = str.replace(new RegExp(s,'g'), zenkaku.charAt(i));
  }
  return str;
}

var unique = function(array){
  var arr = array.sort();
  if (arr.length <= 0) return [];
  var uniq = [arr[0]];
  for(var i=1; i<arr.length; i++){
    if (arr[i] !== arr[i-1]){
      uniq.push(arr[i]);
    }
  }
  return uniq;
};

//全角のひらがな・カタカナ・英数字を全部含める
text += ' ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

text = changeHankakuZenkaku(text);
//console.log(str+"\n"+"\n"+"\n");


var chars = [];
for (var i=0; i<text.length; i++){
  var c = text.charAt(i);
  chars.push(c);
}

uniqchars = unique(chars);

var result = '';
for (var i=0; i<uniqchars.length; i++){
  if (uniqchars[i].charCodeAt(0) < 128) continue; //改行コードと半角英数字は含めない
  var hasExclude = false;
  for (var j=0; j<exclude.length; j++){
    if (exclude.charAt(j) === uniqchars[i]) hasExclude = true;
  }
  if ( ! hasExclude) result += uniqchars[i];
}

console.log(result);

listCharFF.js

// usage: $ node listCharFF.js char.txt > charFF.txt
// テキストファイルに含まれる文字だけ残したWebフォントを作るためのFontForgeスクリプトを生成する
// caution: char.txt = output of listChar.js

var fs = require('fs');
var text = fs.readFileSync(process.argv[2])+''.replace(new RegExp("\uFEFF",'g'),'');

var unique = function(array){
  var arr = array.sort();
  if (arr.length <= 0) return [];
  var uniq = [arr[0]];
  for(var i=1; i<arr.length; i++){
    if (arr[i] !== arr[i-1]){
      uniq.push(arr[i]);
    }
  }
  return uniq;
};

var chars = [];
for (var i=0; i<text.length; i++){
  var c = text.charAt(i);
  chars.push(c);
}

uniqchars = unique(chars);

var result = 'SelectWorthOutputting();\n';
for (var i=0; i<uniqchars.length; i++){
  if (uniqchars[i].charCodeAt(0) < 128) continue; //改行コードと半角英数字は含めない
  result += 'SelectFewer(0u'+uniqchars[i].charCodeAt(0).toString(16)+');\n';
}
result += 'DetachAndRemoveGlyphs();';
console.log(result);