日本語フォントのOpenType機能を維持するサブセット化

_**結論: `pyftsubset --layout-features='*'` を使え**_

#経緯

はじめ、Noto Sans CJK JPをWebフォントとして使うためにサブセット化したくていろいろやってたのですが、全然うまく行きませんでした。というのも、CSSで`font-feature-settings: 'palt';`したいのに、武蔵システムのサブセットフォントメーカーはOpenType機能を削除してしまいますし、かといってFont Squirre Webfont Generatorを初めとするサブセット化Webサービスには、そもそもフォントサイズが大きすぎてだめって言われるか、502 Bad Gatewayか、無かったことにされるかします。

フォントサイズ？ そういえばNoto Sans CJK JPと同じグリフでできているAdobe Source Han Sans JPは、たしか前者よりサイズが小さかった気がします。Regularウェイトを比べてみると、前者は約16.4MBあるのに対して後者は約4.5MBです。期待しながら後者をWebサービスに噛ませてみたのですが、なんとCJKイデオグラフをまるまる削ぎ落とされてしまったので途方に暮れていました。

詳しく調べていくと、FontForgeだとかfonttoolsだとか出てきます。FontForge でフォントを出力するとなぜかまったく使い物にならないフォントが出てきました（CIDサブフォントを平坦化したり出力オプションをいろいろ弄ったりしてもだめでした。スキル不足ですね）。fonttoolsというのはこれです。

- https://github.com/fonttools/fonttools

あとでWOFF2形式にしようと思っていたので、それにはbrotliとかいうライブラリがあるといいみたいです。あなた[^1]はArch Linuxを使っているので`yaourt -S python-fonttools python-brotlipy`しました。`pip install fonttools brotli`とかでもいけるはずです。

#解説

`$ pyftsubset --help`してみます。長いので必要なところだけ抜き出します。

```bash
pyftsubset -- OpenType font subsetter and optimizer

...

Usage:
  pyftsubset font-file [glyph...] [--option=value]...

  At least one glyph or one of --gids, --gids-file, --glyphs, --glyphs-file,
  --text, --text-file, --unicodes, or --unicodes-file, must be specified.

Arguments:
  font-file
    The input font file.

...

Initial glyph set specification:
  These options populate the initial glyph set. Same option can appear
  multiple times, and the results are accummulated.

...

  --text=<text>
      Specify characters to include in the subset, as UTF-8 string.
  --text-file=<path>
      Like --text but reads from a file. Newline character are not added to
      the subset.
  --unicodes=<XXXX>[,<XXXX>...]
      Specify comma/whitespace-separated list of Unicode codepoints or
      ranges as hex numbers, optionally prefixed with 'U+', 'u', etc.
      For example, --unicodes=41-5a,61-7a adds ASCII letters, so does
      the more verbose --unicodes=U+0041-005A,U+0061-007A.
      The special strings '*' will choose all Unicode characters mapped
      by the font.
  --unicodes-file=<path>
      Like --unicodes, but reads from a file. Anything after a '#' on any
      line in the file is ignored as comments.

...

Output options:
  --output-file=<path>
      The output font file. If not specified, the subsetted font
      will be saved in as font-file.subset.
  --flavor=<type>
      Specify flavor of output font file. May be 'woff' or 'woff2'.
      Note that WOFF2 requires the Brotli Python extension, available
      at https://github.com/google/brotli

...

  --layout-features[+|-]=<feature>[,<feature>...]
      Specify (=), add to (+=) or exclude from (-=) the comma-separated
      set of OpenType layout feature tags that will be preserved.
      Glyph variants used by the preserved features are added to the
      specified subset glyph set. By default, 'calt', 'ccmp', 'clig', 'curs',
      'kern', 'liga', 'locl', 'mark', 'mkmk', 'rclt', 'rlig' and all features
      required for script shaping are preserved. To see the full list, try
      '--layout-features=?'. Use '*' to keep all features.
      Multiple --layout-features options can be provided if necessary.
      Examples:
        --layout-features+=onum,pnum,ss01
            * Keep the default set of features and 'onum', 'pnum', 'ss01'.
        --layout-features-='mark','mkmk'
            * Keep the default set of features but drop 'mark' and 'mkmk'.
        --layout-features='kern'
            * Only keep the 'kern' feature, drop all others.
        --layout-features=''
            * Drop all features.
        --layout-features='*'
            * Keep all features.
        --layout-features+=aalt --layout-features-=vrt2
            * Keep default set of features plus 'aalt', but drop 'vrt2'.

...

Example:
  Produce a subset containing the characters ' !"#$%' without performing
  size-reducing optimizations:
  $ pyftsubset font.ttf --unicodes="U+0020-0025" \
    --layout-features='*' --glyph-names --symbol-cmap --legacy-cmap \
    --notdef-glyph --notdef-outline --recommended-glyphs \
    --name-IDs='*' --name-legacy --name-languages='*'
```

`--layout-features`のところに'kern'だとか'liga'だとかお馴染みの文字列が並んでいますね。'palt'もたぶんこのあたりでしょう。あなたは必要な文字をまとめたテキストファイルを用意しておいたので、`--text-file=chars.txt`みたいにしました。どうやらCSSの`unicode-range`みたいな指定方法もできるようですね。

```bash
$ ls -lh
total 4.4M
-rw-r--r-- 1 yuhr users 4.3M Jun 16  2015 SourceHanSansJP-Regular.otf
-rw-r--r-- 1 yuhr users  58K Jan  2 21:19 chars.txt
$ pyftsubset SourceHanSansJP-Regular.otf --text-file=chars.txt --layout-features='*' --flavor=woff2 --output-file=SourceHanSansJP-Regular.min.woff2
$ ls -lh
total 5.2M
-rw-r--r-- 1 yuhr users 883K Jan  2 23:24 SourceHanSansJP-Regular.min.woff2
-rw-r--r-- 1 yuhr users 4.3M Jun 16  2015 SourceHanSansJP-Regular.otf
-rw-r--r-- 1 yuhr users  58K Jan  2 21:19 chars.txt
```

1/5くらいになりました。

#ところで`font-feature-settings: 'palt';`って？

> ![Screenshot from 2017-01-03 00-01-02.png](https://qiita-image-store.s3.amazonaws.com/0/112098/bcb3084d-ac2d-a1a4-a2c8-e0fb8ec5deb5.png)

これが

> ![Screenshot from 2017-01-02 23-56-48.png](https://qiita-image-store.s3.amazonaws.com/0/112098/dddb6f61-8661-4622-abcc-93b30c205783.png)

こうなります。どうですか？ 美しいですよね！



[^1]: 筆者のことです。