2004年3月4日(日本時間5日)、MKVでSSA字幕用のフォント埋め込みが可能になったと発表された。
SSAの フォント埋め込み とは、字幕表示用に使うフォントデータを動画内に格納しておき、 再生時にそれを使用することで、再生側のシステムにインストールされていないフォントを使えるようにするもの。 画像の一部として焼き込んでしまうハードサブと変わらない自由な字幕表現が、 オンオフ・切り替え可能なソフトサブで可能になる。
単にスタイリッシュなフォントが自由に使えるようになっただけでなく、 特別なフォントを必要とするマイナーな言語のサポートという点でも意味が大きい。 すぐれた映画作品などを、 英語スペイン語などの主要言語だけでなく、 世界のいろいろな人が母国語で楽しめるようにする、といったことは望ましいことであり、 インターネット配信を使えば、字幕のテキストデータだけを入れ替えることで低コストでこれが実現できる。 そのとき、どんなフォントでも自由に使える統一規格があれば便利だ。 現在のDVDの、不鮮明でまともにリサイズもできない画像字幕は、もはや時代遅れだ。
ファンタジーで「ルーン文字」や「エルフ文字」などを使った特殊字幕効果への応用も考えられる。
下記サンプルでは、SSA にて MT TARE_P(MTたれっぴ(プロポーショナル))が指定され、 かつ、このフォントファイル MT_TARE_P.ttf が埋め込まれている。 システムにこのフォントがなてくも、 新しいフィルターを使えば、自動的に「たれっぴ」で表示される。
画像データではなくフォントデータだから、 優秀なレンダラーを使えば、拡大すれば解像度も上がり、アンチエイリアスまでかかる。 (画像例は減色しているので、実物よりやや見劣りするかもしれません。)
フォント埋め込みは、 Haali の MatroskaSplitter [CRC: B552 21F5] で、 実験的にサポートされた。このフィルターは、 Gabest の MatroskaSplitter 1.0.2.1 Unicode 版(2004年3月4日現在の最新版)に基づく。 Unicode 版であるため、Windows 2000/XP での動作が期待される。 (追記: MatroskaSplitter 1.0.2.2で正式版にマージされた。Windows 98系では未サポート) (追記2: Media Player Classic 6.4.7.9 では内蔵フィルターでも用いられている。 オプションの Player - Output で VMR9 モード、Subtitle - Maximum texture resolution を desktop にして、DirectVobSub の字幕は切ってから、 [Alt] + [Enter] で全画面表示にしてスペースを押して再生スタートしてみよう。 全画面の解像度で描画されるソフトサブの美しさは、なかなかのものだ。)
作成には特別なフィルターなどは要らない。 すぐ後で説明するように非常に簡単で、 MIME Type application/x-truetype-font でフォントファイルを添付するだけだ。 この MIME Type は MMG では自動選択されるので、要するに、作成時に [Browse] をクリックして埋め込みたいフォントファイルを指定してやるだけでいい。
再生には上記の新しいフィルターが必要だが、上記のフィルターがない場合でも、 単にシステムにないフォントは埋め込みフォントの効果が現れないだけで、全般的な再生には問題ない。 後方に何ら影響を及ぼさない、整然とした上位互換だ。 Media Player Classic (MPC) 6.4.7.8以下で再生テストするときは、 内蔵の MatroskaSplitter を無効にする必要がある(内蔵フィルターにはまだこの機能がないので)。 現時点では、まだ実験的段階なので、試したい場合 regsvr32 でフィルターを手動登録する必要がある。 また、MPC内蔵の字幕レンダラーと、VSFilterとから、二重に字幕が描画されると、CPUリソースが無駄なだけでなく、 字幕が二重になって輪郭がぼけるので注意。Media Player Classic 6.4.7.9以降なら、 内蔵フィルタで埋め込みフォントを使える。 一般のDSプレーヤー用のフィルターをとりあえずインストールしたい場合には、 mkvinst の b6 以降を使おう。
フォント埋め込みは、再生側にそのフォントがあるかどうか分からない場合、つまり不特定の相手にデータを配布する場合に、 特に役立つが、実は個人使用でもかなり有益だ。
OSを再インストールしたときなど、どこかで入手したちょっとしたフォントがシステムから消えてしまい、 入手先が思い出せず困ることがある。 そんなときでも、使いたいフォントデータが、自分の動画ファイルのなかにあらかじめ埋め込んであれば、問題ない。
2台以上のマシンや、ハードウェアデバイスなど、いろいろな場所で同じ動画を見る場合にも、 いちいちそれだけのために全部のデバイスに必要な特殊フォントを入れるのは非効率だ。 動画側にフォントが格納されているほうが合理的だ。
一台のマシンだけでも、 その動画だけでちょっと使うだけの目的で、いちいちフォントをシステムにインストールするのは面倒だしリソースの無駄だ。 Windows は基本的に FONT フォルダにある全フォントを起動時に読み込み、フォントが多ければそれだけ動作が遅くなる。
以上のようなことがあるので、 一部の動画でちょっと使うだけの特殊なフォントなどは、動画側に埋め込んでしまうのが、 効率の点でも、保守性の点でも得策であり、 配布を予定しない個人使用の動画でも、フォント埋め込みは使い方次第でかなり役に立つ。
半面、企業などが配布するファイルでフォント埋め込みを使用した場合など、 映像や音声そのものの版権に問題なくても、フォントの利用がEULAによって制限されている場合があるため、 権利関係に注意が必要だ。自由な利用を許可しているフォントも多いかもしれないが、 フォント埋め込みの場合、間接的にフォントファイルそのものを再配布することになるから、 ライセンスに注意を要する。
別記事「フォントデータの書き換えの法的問題」も参照。
一時フォルダにフォントファイルを取り出して読み込むため、 潜在的に、悪意をもって加工されたフォントファイルにより、 再生すると害を及ぼすような動画ファイルが作成される危険性もある。 規格外のデータは完全に無視するなど、再生アプリケーション側での適切な対応が重要だ。
実に簡単。SSAで指定したフォントのうち埋め込みしたいものを、 MUX のとき MMG の Attachments タブで指定してやるだけ。
注意: TTF以外のファイル形式(TTC、OTFなど)は、現在の、 DirectShowフィルターでは対応しないようである。 この場合、TTFに変換して添付することができる。
このとき MIME Type が application/x-truetype-font になっていることを確認しよう。 再生側システムにもともとあると考えられるフォントは埋め込む必要ない。
本家SSAフォーマットには、UuencodeでASCII文字列に変換したフォントデータを埋め込む機能があり、 SSAユーザの間では比較的よく用いられてきたが、この機能は VSFilter によるソフトSSAではサポートされなかった。 Uuencodeで実行ファイルなども埋め込めてしまうので、セキュリティ上の配慮もあったのかもしれない。
タイプセッターが指定したフォントが確実に使われるようにしたい、というのは、当然の要求であり、 タイプセットとはまさにその作業なのだが、このことは(フォントを画像の一部として埋め込む)ハードサブでは当然でも、 (フォントを独立したテキストデータとして埋め込む)ソフトサブではこれまで制約があった。 指定したフォントが再生側のシステムで利用可能かどうかの保証がないからだ。
Matroska ではもともとフォントファイル自体を「添付」(動画内に埋め込み)できたので、 あとは、埋め込まれているフォントを再生時に一時フォルダに取り出して利用するようにすれば、 フォント埋め込みが可能になる。一部のユーザの間で前から要望が高かった機能だ。
これまでにも、ソフトサブで任意のフォント種を使う方法として、 SSA→IDX+SUB方式(Son2Vobsub)があった。この方法では任意のフォントのイメージを確実に埋め込めるが、 同時に使用できる色数が4色程度、エフェクトも使えず、 オンオフはできるものの固定画像なので、SSAの直接レンダリングよりやや品質が劣り、 拡大するとぼけてしまうという問題があった。
クロスプラットフォーム(特に将来的にはハードウェアデバイス)や、WindowsでもVLCを含むより広い環境で安定的に再生できるという点で、 今なお SSA→IDX+SUB方式 も独自の価値を持っている。フォント内蔵方式は理論的には優れているが、 現時点では、Windows 用の MatroskaSplitter.ax に依存しており、IDX+SUBほど普遍的には使えない。 明らかに便利な機能で技術的にも特に困難でないので、 将来的にはサポートが広がるだろうが、今のところは、まだ実験段階だ。
若干のフォントは、環境によって、SSA/ASSのスタイル定義の既定値Encoding=0では機能せず=1を要求する。
タグでは {\fe1}
となる。
ハードサブの場合、 エンコードの時点で問題なければとりあえずは良いが(スクリプトが再利用可能でなくなる恐れはある)、 ソフトサブの場合、間違えると、環境によっては字幕の文字が表示されないので、細心の注意を要する。 ファイルサイズを消費して、フォントをわざわざ埋め込む場合はなおさらだ。
実用的には「フォント指定が利かないときはEncodingを1にしてみるとしばしば解決する」と経験される。 大半のケースは「古いフォントで今のようにEncodingのデータが格納されていないので今と違う」のである。 古いフォントで平凡なグリフなら自然と使われなくなるが、 スタイリッシュでかっこいいフォントは古くても使われる。 この結果、Encoding=1は経験上、スタイリッシュなフォントで発生しやすい。
実際には、二つのケースがある。
EnumFontFamiliesEx
で見ると(必要に応じ、第3変数はFONTENUMPROC
にキャスト)、
elfLogFont.lfCharSet
が1(elfScript
がOther)となり、
Windows のフォントマッパーがこのフォントの CharSet を1であると認識している場合。
このケースの発生条件は、恐らく、
フォントデータの OS/2 テーブルのversionが1以上で、
かつCodePageRange1~2の全ビットがゼロである場合である。
フォント自体が、どの定義済み文字セットでもない、と自己主張している。
本来的には、
「マイナーな言語のフォントなどでWindows的に定義されている文字セットでないので『その他』」というケースと思われる。
理由はともかく、Windows が1を要求していることは、
EnumFontFamiliesEx
にて、プログラム的に簡単に確認できる。
注1: CodePageRangeの全ビットがゼロであることは、
API関数からもFONTSIGNATURE
の ntmFontSig.fsCsb
にて確認できるが、
これは必要条件であって、十分条件ではない。
そこが全ビットゼロとなる場合でも、
OS/2 テーブルのversionがゼロの場合は、フォントマッパーは、
そのフォントの文字セットとして、1ではなくシステムの既定値を返す。
OS/2 テーブルのversionに、API経由でアクセスできるかは不明。自分でファイルを開けば簡単にできる。
特殊な例外として、システムフォントもそのビットフィールドがゼロになっている。
注2: SSA/ASSのフィールド名「エンコーディング」Encoding は、 Windowsプログラミングではしばしば「文字セット」CharSetと呼ばれる。 この場合まったく同じ意味である。
発生条件は、 フォントデータの OS/2 テーブルが、 version 0 (TrueType rev 1.5) の場合、でまず間違いない。 というのも、このバージョンでは、 今で言う CodePageRange1~2 のビットフィールドが存在していない。 第一のケースではフォント自体が「どの文字セットでもない」と積極的に主張していたが、 このケースではフォントデータにそもそも文字セットを主張する場所がないのである。 当時の仕様がそうだったので、古いフォントでは珍しくない。
今で言う UnicodeRange1~4 の128ビットは予約されているが、
割り当てが未定で全ビットをゼロにすることになっていた
(The actual bit assignments are not yet completed; presently, all bits must be set to zero (0)
)。
OS/2 - OS/2 and Windows Metrics (Version 0)
よって、Windows のフォントマッパーは、TrueType rev 1.5 のフォントについては、
どのような文字セットなのかまったく判断することができない。
この場合、
EnumFontFamiliesEx
は(仕方ないので)現在のシステムロケールの文字セットを返す。
第一の場合が積極的に「その他」を意味する1を選択しているのに対し、
第二の場合は選択自体が不可能なので消極的に既定値にしている。(1でなく、
システムの既定値を返す。)例えば、システムが日本語環境の場合、日本語の文字など一つもないフォントでも、
128を返す。しかし128と判定したわけでなく「不明」が真相だから、エンコーディングは1と考えるべきだ
(128が返ったからと128にすると、他の言語環境では動作しない)。
フォントマッパーが積極的に文字セット1だと信じているケースでは、 ユーザーが文字セット1を指定せず例えば0を指定すると、 「そのフォント名で文字セット0のものは登録されていない」となって、 字幕のフォント指定が利かない。
分からないので既定値を返すケースでも、 たとえユーザーがそのフォントの文字セットが ASCII だと知っていて、0 だと「教えて」あげても、 フォントマッパーは「そのフォント名で ASCII 文字セットのフォントは登録されていない」という反応をし、 字幕のフォント指定が利かない。
ただし、フォントマッパーは、システムのデフォルト文字セットと1を内部的に同一視するので、 デフォルト文字セットが0のときは、1の代わりに0と指定しても一見正常動作する。
Encoding=1 とは、DEFAULT_CHARSET である。 もしOSのシステムロケールが西欧語だと、既定の文字セットは ANSI_CHARSET となり、 Encoding=1 は Encoding=0 とみなされるので、 英語版などのOSで作業していると、Encoding=1 のフォントを Encoding=0 としていても、見かけ上、正常動作する。 その書き方だと、他の環境で動作しないので注意。
同様に、例えば日本語環境で、 Encoding=1 のフォントを Encoding=128 と書いても、見かけ上、動作してしまうが、 避けるべきである。
上記のケース2では、フォントマッパー自体が、1ではなく既定の文字セットの番号を返すが、 その文字セットでたまたま合っている場合でも、やはり 1 にするのが良いように思う。
というのも、「エンコーディングが1」のフォントを例えばエンコーディング0なり128なりで呼び出そうとして、 かつ、その環境の既定のエンコーディングが0なり128でない場合には、 フォントマッパーは「そのフォント名でANSIなりSJISなりの文字セット」を探そうとする結果、 「そういうフォントは見つからない」という結果になり、その字幕をフォントをデフォルトで出してしまう。 要するにスタイル指定のフォント部分が利かないことになる(色などは利く)。 「エンコーディング1」のフォントを0や128にしても環境によってはたまたま動作するが、 たまたまに頼ってはいけない。
エンコーディングを1にしておくと、 Windows のフォントマッパーはそのフォント名のフォントがあれば、必ず ―― と言い切れるかは分からないが、 多くの場合 ―― マッチさせてくれる。
がある。
エンコーディング1がワイルドカード的であることは、
EnumFontFamiliesEx
を呼び出すとき、
lfCharSet
を DEFAULT_CHARSET (1) にすることで文字セットによるフィルタリングを無効にできることからも、
分かる。
このように1という値はある意味「安全」であるため、 SSA/ASS関係のソフトの中には、 本来の文字セットが分かっている場合でさえ、 デフォルトでEncodingを1と書くものもある。 考えようによっては良いアイデアだが、 明示的に指定できるときまであいまいな1にするのは変であり、必ず動作する保証があるわけでなく、 マッパーの奇妙な挙動に依存しているので、必要ないのにみだりに1にするのは良くない。 上記のケース1と2では1を指定すべきだが、 ケース3と4まで1にするのは考え物である。 (Sabbu のように Encodingが1だと常にエラーになるものもある。)
多くのレンジにまたがるフォントでは、同じフォントでも、 呼び出したい範囲によって文字セットの指定を変えるのが、Windowsの内部的には正しいようだ。 ところが、 多くのレンジにまたがることがはっきりしているフォントは、 よほど例外的なケースでない限り ASCII のグリフは必ず持っているから、 文字セット 0 で呼び出して、その呼び出しが失敗することはない。
一方、「エンコーディングが1」のフォントは0で呼び出すと環境によって確実に失敗する。 たまたま成功するのは、その環境の既定のエンコーディングが0で、0と1がたまたま同じ意味になっている場合のみだ。
上記の1(「その他の文字セット」)または2(「文字セット不明」)に該当するケースでは、 SSA/ASSでフォントのエンコーディングを1に指定することが望ましい。 特に日本語環境でこの種のASCIIフォントを0指定で使うと、フォント指定が利かないことがある。
結果的にケース1と2を区別しないで良いとすれば、
FONTSIGNATURE
の ntmFontSig.fsCsb
が全部ゼロであるときはエンコーディングを1にする、
という簡単な方法でプログラム的に解決できる。(システムフォントは除く。)
Windows が単純に Unicode ベースでなく、
非 Unicode アプリから進化してきたため、互換性のためにいまだに「文字セット」というパラメータを内部的に使わざるを得ない。
フォントマッパーは、
「文字セット」と関係なくそのフォントで Unicode番号が一致すればグリフを出すという当たり前の動作ができない。
歴史的に仕方ないことながら、結果的に Windows の一つの弱点であり、
他のOSのユーザーから \fe
タグが分からないと言われる理由でもある。
実際、ファイルがUnicodeで、コードポイントが明確なのに、 文字セットを指定しないとフォントマッパーが失敗することがある、というのは、実にばかばかしい。 おまけに、その指定も「不明」を意味する1なら動作し、 文字セットは0だからと正しく0を指定するとかえって失敗することがある。
TTCなど、TTF以外のファイル形式のフォントをTTF形式に変換するには、 FontForgeが便利だが、 多少の注意事項もある。
このメモはもともと「MKVの字幕用フォントとして、 Gabest/HaaliいずれのフィルターもTTC形式に対応していない問題」との関連で、字幕作成者向けに書かれたが、 ほかの目的でTTFに変換する場合も、やり方は同じだ。
TTCファイル(TrueType Collection)は複数のTTF(TrueType Font)形式フォントを、 1ファイルにまとめたもの。多くのフォントを1ファイルにまとめる書庫的な意味合いがあり、 実際に次のような意味で、データ量をかなり圧縮できる場合も多い。
説明上の例として、 CJK(中国語・日本語・韓国語)で、文字のデザイン(グリフ)が同じフォントの、 等幅とプロポーショナルを1ファイルに格納する場合を考えてみよう。 CJKは文字数が多くフォントのファイルも大きい。 もしグリフの実データが同じで単に文字間隔だけが違う場合、 実データの方は共有すればかなりのサイズが節約できる。
実際のTTC内部でのデータ共有はもう少し複雑だが、原理はそういうことだ。
TTCをフォントリソースとして追加した場合、 Windowsを含め、多くの環境では TTFをセットでインストールしたのと同じように、働く。よって変換は必要ない。 グリフデータを共有するという用法の意味から明らかなように、 典型例では、TTCを複数のTTFに分割してもサイズは大して減少しない。 プロポーショナルだけ使うから等幅は消してサイズ節約、といったメリットは通常ない。
例外として、みかちゃんフォントの、mikachanALL.ttc のように、グリフのデータが独立している場合は、 分割すればサイズも分割される。 (みかちゃんフォントの場合、一つ一つのフォントも最初からTTFで提供されているので、自分で分割する必要はない。)
TTCからTTFの変換が必要になるのは、 システムがTTCを認識しないか、 または、システムは認識しても、フォント関係のプログラムがTTCに対応していない場合だ。 前者の例として、BeOSなどがTTCに対応しないらしい(今はするかも)。 後者の例として、MKV動画の字幕用フォントとしてTTCを埋め込んでも、Gabest/Haaliいずれのスプリッターからも無視される。 OTF形式も無視されるようだ。こういう場合、TTFに変えてやる必要が出てくる。
FontForgeで開いて(TTCの場合、開くとき内部の複数フォントの一つを選択できる)、TTFとして再生成するだけ。 つまりはFontForgeが動作すれば(WindowsでもCygwin上で簡単に動作する)、難しくない。OTFも同様に変換できる。
また ASS_Help3r にもコンバーターが付いた。 Tools | Other Font Tools | Convert TTC to TTFs... でGUI的にできる。
FontForgeで書き出したフォントでは、 OS/2テーブルのulCodePageRange1/2フィールドが変わってしまうときがある(ゼロにクリアされたり)。 このフィールドは一般的にはどうでもよく、Macユーザー、Linuxユーザーには無意味なのだが、 Windowsにとっては歴史的に致命的な意味を持っていて、 最新版のWindowsでも「グリフデータはUnicode番号で平坦に」という扱いができない。 例えば、仮にSJISフラグ(CharSet=128)しか立っていないフォントFooJpnがあるとして、CharSet=0(ANSI_CHARSET)を指定すると、 一般には、その実フォントに日本語文字のグリフがあっても、マッパーは論理フォントが作れない。 FooJpn自体はあっても、内部的に( FooJpn, 128 ) だけで ( FooJpn, 0 )がないと、 ( FooJpn, 0 )で FooJpn を呼び出せない。( FooJpn, 128 ) で呼び出す必要がある。
そうなっても、「その他のフォント」(WindowsのAPIでDEFAULT_CHARSET、ASSではEncoding=1)だと思えば、 ちゃんと動作する。 ( FooJpn, 1 ) はワイルドカード ( FooJpn, * )として機能する。しかし、 最初から1指定でない限り、ulCodePageRangeが変わるとWindows上では、 ASSからみてフォントの挙動が非互換になる。
上記のように、何らかの理由でこのデータが変わってしまった場合、 バイナリエディターでも簡単に直せる。
バイナリエディターで開くと、ヘッダにOS/2という文字が読める。 これはテーブルディレクトリーという物体でOS/2テーブルの場所を示している。 2という文字の後ろの4バイトがチェックサム、そのさらに後ろ4バイトがオフセットである。 オフセットが00 00 12 34だとすると、0x1234番地からOS/2テーブルが始まっている。 ulCodePageRangeはOS/2テーブルのバージョンが1以上のときに限り、テーブルの頭から76バイト目(十進数)から8バイトのフィールドである。 上の例では0x1234+76=0x1280番地からの8バイトを元のフォントと同じように復元すればよい。
Windows上ではこれだけでも動作するが、フォントファイル的には、 チェックサムが合わなくなってしまう。 (テーブルディレクトリのOS/2の後ろの4バイト。) チェックサムは、テーブルの頭から4バイトずつ、 ビッグエンディアンのUINT32としてmod 2^32で(=早く言えばオーバーフローしても気にせず)足し算するだけだが、 これは手動でやるのはさすがに億劫なので、ツールを使った方が良いであろう。 「チェックサムを全部再設定する」という小物ツールも簡単に作れるだろうが、 とりあえず、ASS_Help3r 0.0.7.44以降で、[Ctrl]+[Shift]+[F]でフォントを開くと、 各テーブルの正しいサムが見れる。
日本の現行法・判例(2007年現在)では、原則として、フォントは著作物ではなく、著作権法では保護されないが、 作者は、使用許諾契約(EULA)によって改変などを制限することができる。 ただし、一般に、リバースエンジニアリングはEULAで禁止できない。
このメモは、もともと別記事「TTCなどからTTFへの変換」に対する補足説明として書かれた。
Beer(freeware)のttfdasmの項に「リバースエンジニアリング禁止のフォントに注意」と書いてあるが、実は TrueType Font (TTF)はオープン規格であり、 オープン規格の内部構造を調べることはリバースエンジニアリングとは呼ばないし、 たとえリバースエンジニアリングであるとしても、一般にはむしろ「禁止されないこと」が法律によって保障される。 このように、技術者の間では、法律関係について過剰に解釈する傾向が根強いようだ。 個人的に親しい相手から「禁止」された場合の人間関係に注意する、などの私的な意味は別として、 個々のTureTypeフォントの内部構造の研究などには、法律上は、特に問題ない。
ユーザーの立場から言えば、正規に入手したフォントは他のOS用でも、変換して使って良い。 ソフト作者の立場では、 Mac信者、Windows信者などで「このソフトを他のOSで使ったやつは呪い殺す」みたいな考えなら、 その人には最初から何も公開しないことをおすすめします。
法律の特異的な運用などの結果、 2004年くらいから、日本の技術者の間に、 かなりの萎縮的気分が存在することは周知である。 また、フォントの権利関係については、歴史的にもいろいろと問題が多い。 フォントは作るのが面倒なので、簡単にコピーされると腹が立つ、みたいな気分的なことも案外大きいのだろう。 ただし、ここで考えるのはコピー販売とか書体の改変とかの大々的なことではなく、 単に内部のフラグをちょっといじる程度の話だ。
TTFへの変換での「ulCodePageRangeが壊れてしまったので、 元通りに直す」といった、 どう見ても常識的には問題のない行為(改変するのではなく壊れたものを元に戻す)ですら、 バイナリエディターで書き換えるというだけで「やばいのでは」と感じる方もおられるかもしれない。
技術者というのは、一般ユーザーが気にしないような細部(通常では考えられないような異常入力への対策など)に神経を使うもの。 習慣的にナーバスだ。 ソフトウェア特許の問題でもそうだが、「危うきに近寄らず」という防衛的態度に出ることが多い。 ポインターなどでも、ロジック上、どう転んでもNULLにならないはずでも、 NULLでないことを確認してからでないと怖くて使えない、 3で割った余りのcaseに0と1と2の下に一応defaultを作る(笑)…と技術者は、 「ありえないけど念のため」と、物事を冗長なまでに安全側に倒す習性を持っている。
つまり絶対にありえないと断言できない限り、一応ありえるかもと仮定しておく習性がある。
論理的というよりは文学的な一部の裁判官などが特異的な判例を作ると、 「そういう解釈もありうるなら、こういう解釈も…」と半分ネタ、半分本気で、 一応心配してみるのが、ありがちな姿なのである。
通則としては、 フォントは著作物ではないという判例が、完全に確立している。 ヤギ・ボールド事件、タイプフェイス事件(ゴナ・新ゴ事件)など。 よって、フォントについて語るとき、著作権が…ということ自体、既に間違っている。
例外は、カリグラフィー(書道的な文字)など「それ自体が美術鑑賞の対象となり得る美的特性」を持っている場合であり、 アラビア語のフォントにはこれに該当すると思われるものがある。 あるいは書道の神様級の師範のような人が書いた文字をフォントにしたら、当てはまるかもしれない。 ともかく一般のフリーフォント作者が作ったものは、あまり当てはまらない。
フォントは著作権法によってではなく、EULAによる一般の契約によって保護される。 フォント作者は、何でもかんでも著作権、著作権といわず、 もしグリフをパンフレットなどで使われたくなければ、 「このフォントは次の条件で使用許諾する。…商用印刷物で使用しない。…はいけない。べからず。べからず」と、 EULAを書いておくべきである。 著作権法ではフォントはほとんど保護されないが、 EULAで禁止すれば、その禁止は民法上、かなりの程度まで効力をもつ。
ところで、以下で問題にするのは、 EULAすなわちシュリンクラップ契約の成立そのものに関する外形的な有効性ではなく、 その内容である。
EULAに書けば何でも成立するわけではなく、 「このフォントを使う条件として妹を差し出すこと」などとEULAに書いても、 公序良俗に反する契約は無効である(民90)。ここでは、 EULAにおけるリバースエンジニアリング禁止条項などの有効性を考える。 微妙で興味深い話題ではあるが、ここでは単に通説的なことを、 教科書的に、「電子商取引及び情報財取引等に関する準則」などを中心に、説明する。 この準則なるもの、行政の見解のお知らせにすぎず司法的な拘束力はないが、 おおむねは法学上の通説を反映していると考えられる。
準則では、2007年3月の改訂版でも、その前でも一貫して、 「独占禁止法の上で不法となる禁止は、EULAに書いても無効で、ユーザーは拘束されない」という理論展開を取っている。
分かりやすく言えば契約は当事者双方の合意に基づくことであり、 社会的な常識にも合致しなければならないわけで、 一方の当事者の勝手な考えで何でもかんでも禁止しまくるな、ということだ。
そして、 公正取引委員会の「ソフトウェアライセンス契約等に関する独占禁止法上の考え方―ソフトウェアと独占禁止法に関する研究会中間報告書」を参照している。
注意: 「禁止」「違反」などの言葉が入り乱れ、 ナーバスな技術者は尻込みしてしまうだろうが、 以下で議論するのは「ユーザーは~してはいけない」と禁止することが違法で、禁止が無効なので禁止されない=やってもOKという話、という全体像をまずとらえてほしい。
公正取引委員会の見解をまとめると、
例えば、仮にMicrosoft製のTTCフォントがWindowsでしか動作しないとして(実際はそんなことはないが)、 TTCフォントをBeOSでも使える互換性のあるフォーマットに変換することを禁止することは、 使えるソフトウェアを無意味に制限して、OSの間の公正な競争を阻害する形になる。 そのような場合のリバースエンジニアリングや、それに基づく互換性製品の開発は、禁止できない。 (これは説明のための例で、実際にはもともと禁止されていない。)
フォントにも著作権があるので、勝手に改変できない、という誤解もありがちだが、 一般にフォントには著作権はないうえ、公取委の通達のように、この場合は著作権法ではなく独占禁止法で判断される。
同様に、Linux上でもRV40形式やVP62形式をデコードできるようにリバースエンジニアリングを行うことは、 少なくとも日本では、まったく問題ない。RealPlayerと互換性のあるプロトコルを使って、 ストリーミングを受信する例えばVLCのようなプレーヤーも、 まったく問題なく、それを禁止することのほうがかえって違法、なのである。 もっとも今は、Real自身がAppleに対するリバースエンジニアリングを仕掛けたり、 率先してHelixPlayerを出すなど、時代の変化を感じさせる。
以上のように、ドキュメント上の見解は、技術者の一般常識に照らして、まあまあ普通というか、 まともなのである。ただし、これらは行政の見解であって、 裁判官の珍解釈に対する抑止力はない。 一方、余談だが、2007年3月の準則改訂ではP2Pソフトの開発者が罪に問われることもありうるなどと、 係争中の事案について行政が口出ししており、三権分立の立場から極めて不適切である。
フォントが著作物にならないことは、 最高裁でも確定しているので、その点は安心して良いが、 逆に、著作物性が否定されたからといって何をやってもいいわけではなく、 EULAには民法上の契約の効力がある。まとめると、最初に書いたように、
念のために繰り返すが、ここで話題にしているのはTTF以外の形式をTTFに変換すること、 それに付随するわずかなデータの整合性の調整で、 「フォントは著作物でないから勝手にコピーして売ろうが、何をしようが勝手だ」などの、 めちゃくちゃな主張をしているわけではない。「技術者は細かいことまでナーバスで、やっていいことでも、 ついつい不安に思ってしまいがちだが、このTTF変換は問題ありません」という趣旨であり、 要は「技術的にも法的にも相互運用可能なのに、 単に技術者が心配性なせいでWindowsでしか使えない」ような状態は好ましくない、という話だ。
ソフトウェアライセンス契約等に関する独占禁止法上の考え方―ソフトウェアと独占禁止法に関する研究会中間報告書
http://www.jftc.go.jp/pressrelease/02.march/020320.pdf
電子商取引及び情報財取引等に関する準則(2007年3月版)
http://www.meti.go.jp/press/20070330011/denshishoutori3.pdf
TTFフォントのテーブルディレクトリーのチェックサムのうち、 'head'テーブルだけは、checkSumAdjustmentを考慮した特別な計算が必要となる。
流通している ttc2ttf のうち、 ASS_Help3r、 Beerはこれに従っているが、 K. Takeshi および、 それから派生した TrueType Collection を True Type Font にバラす は、checkSumAdjustmentを考慮せず、厳密に言うと壊れたTTFを生成してしまう。
FontForgeでTTCからTTFを生成した場合、 checkSumAdjustmentは正しく設定される。
TTFのcheckSumAdjustmentは明快だが、 数カ所あるTTCのcheckSumAdjustmentたちは仕様がはっきりしないようだ。
TTFの場合は仕様書に書いてあるとおりにやれば良い。
分かりやすく言うと、
「オフセットテーブル、テーブルディレクトリーの全部、および全テーブル」…(★)
のチェックサムが 0xB1B0AFBA になるように逆算した値を書けば良い。
具体的には、任意の(最終的な値と必ずしも一致しない)値 x が記入された状態で(★)のチェックサムを取り、
結果を 0xB1B0AFBA + x から引き算したもので x を上書きすれば良い。
仕様書では x=0 としている。
ところで、この計算をするためには、headテーブルのテーブルディレクトリーが完成していないといけない。
それにはheadテーブルのチェックサムをまず計算する必要があるが、
そのときは checkSumAdjustmentの4バイト(この段階では任意の x )はスキップする(チェックサムに加えない)。
後からサムを検証するとき、checkSumAdjustmentをスキップしないと計算が合わないのはそのためだ。
注意事項として、(★)は、「ファイルの頭から末尾まで」とは必ずしも一致しない。 テーブルとテーブルの間に4バイトの位置合わせ以上の隙間があるかもしれないし、 ファイル内の最後のテーブルの末尾の後にゴミデータがあるかもしれない。 特に、TTCでは、「ファイルの頭から末尾まで」と(★)が異なることは明白だ。
仕様書には the entire font とあるが、 フォント全体とはフォントファイル全体ではなく(★)の意味と解釈しないと、 観測値と合わない。 早い話が、オフセットテーブルとテーブルディレクトリーのチェックサムだけ計算したら、 後は各テーブルディレクトリーに格納済みのチェックサムを全部足せば良い。
TTCのcheckSumAdjustment(一般に複数ある)について、 (★)の考え方で計算が合うのは、 確認したなかでは、mingliu.ttc と msgothic.ttc のみだった。 ほかは一致しなかった。なかにはheadテーブルのチェックサム自体がやはり間違っているものもあった。 TTCのcheckSumAdjustmentについては、作成ツールがいい加減ということもあるだろうが、 もっとさかのぼって、仕様書自体にあいまいさをなくすための補足説明があるべきだろう。
この点について、1997年に、 TrueType Q&Aに 「checkSumAdjustment value in ttc font」として、そのままの質問が寄せられている。 残念ながら、回答者も問題の意味を理解しておらず、参考にならない。 「仕様書があいまいなため、現在、次の○とおりの実装が存在しています。 わたしの解釈で最も正しいのは…」のような答をしてほしいところだが、 なぜheadテーブルが複数必要なのですか、と問い返すだけで終わっている。 名前がheadだからヘッダーに一つだけあるとでも思ったのだろう。
上述のように、 mingliu.ttc と msgothic.ttc については、(★)のようにアルゴリズムを説明でき、 それが「正しい」解釈だと考えられるが、 現時点では、そのほかのTTCについて「どう間違っているか」までは解明できなかった。 simsun.ttc は、理論値4E5ACF9B、実際の値4E5AB6ABなど (★)とわずかに違う方法を取っていると推定される。 Windows標準のTTCでもよく分からない状態なので、フリーフォントなどは推して知るべしだ。
可能性としては「計算にTTCのヘッダまで含める」「フォント全体という仕様書の言葉をファイル全体と解釈する」などが考えられる。 後者の場合、数カ所あるcheckSumAdjustmentがデータ生成中にだんだん埋まっていくため、 どのタイミングで和を計算するかが問題になる。直感的に言って、その方法のはずがない。 最もありそうなシナリオは、変換元のTTFファイルのheadテーブルをそのままコピーして整合しなくなっている、ということだが…。
この値が違っていても事実上何も害はなく(チェックサムにすら関係しない)、 逆にどうでもいい値だからこそ、適当なままなのだろう。