OpenTypeフォントの続き(12)・・・PostScriptアウトラインの更に続き

前回は位置が固定であるHeaderからGlobal Subr INDEXまで見たので残りのいくつかのデータをとっとと見て行こう。

残りのデータはオフセット経由でアクセスされる。

まずは、CharStrings INDEX。

Cff3_1_2

肝となるデータである。CharStrings INDEXにはフォントに含まれる各グリフのアウトラインを記述するプログラムが格納される。前回も述べたがこのプログラムはCharstringと呼ばれ、そのフォーマットはTop DICTのCharstringTypeキーによって指定される。MinionPro-Itの場合、Top DICTにCharstringTypeキーが含まれていないので、デフォルト値2のType2 Charstringとなる。

CharStrings INDEXのcountフィールドはフォントに含まれるグリフの数を表す。上の画像より1851個のグリフが含まれている事が分かる。また、CFFにおいて各グリフはGID(Glyph Identifier)によって識別されるが、CharStrings INDEX内の先頭のグリフから順にGIDが0、1、2・・となる。上の画像ではGIDが265のグリフが表示されている。厳密にはCFFのGIDとOpenTypeフォントの文字コードからcmapテーブルによって変換されるグリフインデックスは別物であるが、PostScriptアウトラインのOpenTypeフォントの場合、OpenTypeフォントのグリフインデックス=CFFのGIDとなる。ちなみに、CharStrings INDEXへのオフセットはフォントのTop DICTのcharstringキーの値として指定される(このオフセットはCFFフォーマットの先頭からのオフセット)。

Charset。

Cff3_2

Charsetにはグリフ(GID)と名前(SID)のマッピング情報が格納される。詳細は省略するが、例えば、先ほどのCharStrings INDEXの画像でGIDが256のグリフが表示されていたが、このグリフのSIDは上の画像より1465であることがわかる(GID 0のグリフの名前は省略されているので、[Index]列が256-1=255のエントリ)。また、String INDEXよりSIDが1465の文字列はc_kであるから、GIDが256のグリフの名前はc_kであるdashdash

Encoding。

Encodingを持ったフォントが見つからんdash。たぶん、文字コードとSIDかGIDのマッピング情報が格納される(SIDかGIDのどっちかは仕様書読んでも理解できませんsad)。

Private DICT。

Cff3_3

そのまんまであるが、Private DICTにはフォントにプライベートな情報が格納されるsweat02sweat02。Subrキーはこのフォントだけからアクセスできるサブルーチンが格納されたLocal Subr INDEXへのオフセットを表す。

Local Subr INDEX。

Cff3_4

はやくも燃え尽き気味sweat02sweat01

とりあえず、バージョンを1.2として最新版をアップロードしておきました。Type2 Charstringの解析はまだですが、とりあえず、グリフの形状が分かるようになったのでこれでいいかなと・・・

| | コメント (0) | トラックバック (0)

OpenTypeフォントの続き(11)・・・PostScriptアウトラインの続き

準備ができたので今回は実際にPostScriptアウトライン形式のOpenTypeフォントを解析してみるのだが、PostScriptアウトライン形式のOpenTypeフォントってどこで無料で手に入るんだdashdash??と思ってたら、いいものが既にインストールされていた。

たいていの人が既にインストールしてあるであろう無償のPDFビューアのAdobe ReaderをインストールするといくつかPostScriptアウトライン形式のOpenTypeフォントがインストールされるのでこれを解析してみる。ということでここでは、<Adobe Readerのインストールフォルダ>\Resource\Fontフォルダにある欧文用のMinonPro-It(ファイル名MinionPro-It.otf)というOpenTypeフォントを使って話を進める(別のフォルダCIDFontに小塚明朝、小塚ゴシックという日本語用のPostScriptアウトライン形式のOpenTypeフォントがあるのだが、これらのフォントは脇においておく)。

実際に解析結果を示しながら話を進めるが、最初にAdobeというかPostScript系のフォントはある程度、歴史を知らないとなぜこういう構造や仕組みになっているかなど理解に苦しむかも知れないbearing。実際、CFFフォーマットを理解するために、その前身?のType1やCID-Keyedフォントの仕様書や更にはPostScriptの言語リファレンスまで自分はかいつまんで読むはめになったsweat02sweat01

ということで、まずは前回示したCFFフォーマットのレイアウトにおける位置が固定であるHeaderからGlobal Subr INDEXまでの解析結果を順に見ていく。

まずは、Header。

Cff2_1

文字通りヘッダ情報が格納される。Major、Minorは順にCFFフォマーットのメジャーバジョーン番号、マイナーバージョン番号を表す。

Name INDEX。

Cff2_2

Name INDEXには含まれるフォントの名前が前回説明したINDEXデータとして格納される。含まれるフォントの数はこのName INDEXのcountフィールドによって決定される。上の画像より1個のフォントが含まれていることがわかる。ちなみに、他のデータはそのままにしてこのフォントの名前の先頭文字をヌル文字(0x00)にすることで、フォントを論理削除できる。

Top DICT INDEX。

Cff2_3_2

Top DICT INDEXには、含まれるフォントのトップレベルの情報が前回説明したDICTデータとして格納される。もちろん、フォントの各情報はキー・値のペアとして表されるが、定義済みのTop DICTキーには例えば次のようなものがある。

Top DICTキー
キー
version(0) SID
Notice(1) SID
Copyright(12 0) SID
FullName(2) SID
FamilyName(3) SID
省略
FontBBox(5) 配列
省略
charset(15) 数値
Encoding(15) 数値
CharStrings(15) 数値
省略

1列目はキーの名前で、括弧内はキー自身の値つまりキー値である。例えば、フォントの完全名を指定するFullNameキーの値はSIDを表すので、このSIDを使い次のString INDEXの対応するエントリを見れば、フォントの完全名がわかる。また、フォントのバウンディングボックス(境界矩形)を指定するFontBBoxキーの値は4つの要素を持つ配列(順に矩形の左端のx座標、下端のy座標、右端のx座標、上端のy座標)である。charset、Encoding、CharStringsへのCFFフォーマットの先頭からのオフセットをそれぞれ指定するcharset、Encoding、CharStringsキーの値は単純な数値である。定義されているすべてのキーとその詳細は仕様書を参照ということでdashdash・・・・ちなみに、上の画像では、計10個のキーが定義されてるが、デフォルト値を持つキーは省略できる。

String INDEX。

Cff2_4

String INDEXには含まれるフォント間で共通のフォント名以外の文字列が格納される。ちなみに、これらの文字列はSID(String Identifier)と呼ばれる2バイトの符号無し整数によって参照される。また、一般的な文字列は標準文字列として計391個(SID::0-390まで)定義済みであるので、String INDEXに含まれる各文字列のSIDは先頭の文字列から順に、391、392、393・・・となる。上の画像より1616個の文字列が格納されていることが分かる。

Global Subr INDEX。

Cff2_5

Global Subr INDEXには各フォント間で共有されるサブルーチンが格納される。CFFフォーマットでは各グリフのアウトラインが単純なデータというより一種のプログラム(Type2 Charstringなどと呼ばれる)によって定義されるが、サブルーチンとはこのプログラムから呼び出されるサブプログラムの事である(通常のコンピュータプログラム言語におけるサブルーチンと同一の概念)。上の画像より1024個のグローバルなサブルーチンが格納されている事が分かる。各サブルーチンの中身についてはまだ解析してないので表示してない・・・

と長くなったので今回はここまでdash。残りの解析に手間取ってるsweat02ので、とりあえず、ここまでの解析に対応したT2FAnalyzerの最新版をアップロードしておきました。ダウンロードは脇のいつものリンクから。

| | コメント (5) | トラックバック (0)

OpenTypeフォントの続き(10)・・・PostScriptアウトライン

一連のOpenTypeフォントのシリーズでOpenTypeフォントは、グリフデータとしてTrueType形式のアウトラインまたはPostScript形式のアウトラインを含むことができると書いたが、今回はPostScriptアウトラインについて。

PostScriptアウトラインに関するグリフデータは第2回で述べたようにCFFテーブルに格納され、このテーブルはCFF(Compact Font Format)と呼ばれる形式のフォーマットになっているが、このCFFフォーマットについて書いてみる。

CFFフォーマットは複数のフォントを格納でき、Type1、CID-Keyedフォントよりコンパクトなバイナリ表現のフォーマットである。CFFフォーマットがコンパクトな理由は、バイナリ表現を使用したり、フォント間で共通のデータを共有したり、また、頻繁に出現するデータに対してデフォルト値を与えたりしているためである。(Type1やCID-Keyedフォントって何?って話はここでは触れない。)

ということで、まずは、CFFフォーマットで使われるデータ型から。

CFFデータ型
名前 範囲 説明
Card8 0-255 1バイト符号無し整数
Card16 0-65536 2バイト符号無し整数
OffSize 1-4 オフセットのサイズを指定する1バイト符号無し整数
Offset 可変 1,2,3または4バイトオフセット
SID 0-64999 2バイトストリングID

Card8、Card16はそれぞれ1、2バイトの符号無し整数を表すデータ型である。OffSizeは対応するOffsetで表されるデータ型のフィールドのサイズを表し、1-4の範囲の値を取る。例えば、この値が1なら対応するOffset型のフィールドのサイズは1バイトになる。順に2なら2バイト、3なら3バイト、4なら4バイトである。

CFFフォーマットのレイアウトの概要は次のようになる。

CFFレイアウト
名前 説明
Header -
Name INDEX -
Top DICT INDEX -
String INDEX -
Global Subr INDEX -
Encodings -
Charsets -
FDSelect -
CharStrings INDEX -
Font DICT INDEX -
Private DICT -
Local Subr INDEX -
Copyright and Trademark Notices -

上の表のようにCFFフォーマットはいくつかのデータの集合として定義されている。また、上の表を見てわかるようにINDEXやDICTと共通の名前を持ったデータがあるが、最初にこれらについて説明する。

ちなみに、HeaderからGlobal Subr INDEXまでの最初の5つの位置は固定であり、残りはオフセット経由でアクセスされる。

まずはINDEXデータについて。

INDEXデータは可変サイズのオブジェクトの配列であり、次のような構造になる。

INDEXデータ
名前 説明
Card16 count INDEXデータ内のオブジェクトの数
OffSize offSize オフセット配列の要素のサイズ
Offset offsets[count+1] オフセット配列(オフセット配列の直前のバイトからのオフセット)
Card8 data[varies] オブジェクトデータ

countフィールドはINDEXデータ内に含まれるオブジェクトの数を表す。offSizeフィールドは続くオフセット配列の要素のサイズを表す。offsetsフィールドは各オブジェクトへのオフセットの配列であり、INDEXデータ内の各オブジェクトへはこのオフセットを使ってアクセスする。また、このオフセット配列の要素数はINDEXデータ内に含まれるオブジェクトの数+1つまりcount+1であり、i番目のオブジェクトのサイズは

i番目のオブジェクトのサイズ=offsets[i+1] - offsets[i]

で一様に求まる。このオフセットはオフセット配列の直前のバイトからのオフセットつまりoffSizeフィールドからのオフセットを表すので、オフセット配列の最初の要素offsets[0]は常に1になる。ちなみに、countフィールドが0の空のINDEXの場合、残りのフィールドは存在しない。

例えば、各フォント間で共有される文字列はString INDEXに上記の構造においてオブジェクトとして格納されている。同様に、フォント毎の情報は次に説明するDICTデータとして表現されるが、これらの情報はTop DICT INDEXに上記の構造においてオブジェクトとして格納されている(ややこしいが、Top DICT INDEX全体は上記のINDEXデータでINDEXデータ内に含まれる各オブジェクトが次に説明するDICTデータである)。

次はDICTデータについて。

DICT(DICTionary)データはキー・値のペアのコレクションであり、キーは1または2バイトのデータ、値は可変長の整数または実数を表すデータである(ちなみに、値には複数個の整数や実数の配列としても格納できる)。

DICTデータの各キー・値のペアは特別なルールでエンコードされていて、ここでは、詳細については省略するが、簡単に説明すると

値を表すバイトデータ、キーを表すバイトデータ、値を表すバイトデータ、キーを表すバイトデータ・・・・・

のようにエンコードされる。キー・値のペアの値のデータが先にくる。また、(先頭の)バイトデータによって値とキーが区別できるようになっているので、DICTデータの先頭から1バイトずつ処理すればデコードできるようになっている。例えば、DICTデータをデコードするプログラムは次のようになる。

具体的には先頭バイトが0-21ならキー、28、29、247-254なら整数の値、30なら実数の値という具合になっているdash。上記のプログラムは1つのキー・値ペアをデコードするプログラムであるが、通常、DICTデータへのオフセットと共にDICTデータ全体のサイズが与えられるので、与えられたサイズに達するまでデコードすれば、DICTデータに含まれるすべてのキー・値のペアをデコードできる。

と、抽象的なことばかりだとイメージが掴みにくいかもしれないので、次回以降具体的にこれらの中身を見ていこうと思う。また、次回でCFFの解析に対応したT2FAnalzyerの最新版も同時にアップロードしようと思うhappy02

| | コメント (0) | トラックバック (0)

CFF/Type2 Charstring

データベース絡みの事をやっていたのであるが、行き詰まったdashので、気分転換にちょっと本格的にT2FAnalyzerで要望のあったCFF(Compact Font Format)/Type2 Charstringの解析に向けて、仕様書を読み始めた。

Type2 Charstringというものがグリフのアウトラインを記述してるようであるが、TrueTypeアウトラインのglyfテーブルと違って、CFFアウトラインは一種のプログラムじゃんbearing。このプログラムの実行環境であるType2 Charstringインタプリタもどきみたいなの作らんとアウトラインの形状をラスタライズできそうにもないな(TrueTypeインストラクションほど複雑そうではないが・・)wobbly

うーん。

漠然とCFF/Type2の全体のイメージが掴めたので、CFF/Type2の解析部分のプログラミングを開始しましたhappy02

Cfftype2_2

| | コメント (1) | トラックバック (0)

OpenTypeフォントの続き(9)・・・インストラクション

久しぶりにOpenTypeフォントの続き。今回はインストラクション

インストラクションは特定のグリフに、または、フォント全体に関連付けることができる。特定のグリフに関連付けられたインストラクションはグリフプログラム(Glyph Program)と呼ばれ、glyfテーブルに格納され、そのグリフが要求される度に実行される。フォントプログラム(Font Program)はfpgmテーブルに格納されるインストラクションで、フォントにアクセスされる最初に一度だけ実行される。コントロール値プログラム(Control Value Program)はprepテーブルに格納されるインストラクションで、フォントのポイントサイズが変わるたびに実行される。

ところで、インストラクションを実行するプログラムはTrueTypeインタプリタと呼ばれるが、TrueTypeインタプリタは主に

  • インストラクションストリーム
  • インタプリタスタック
  • グラフィックスステート

で構成される。インストラクションのオプコードとデータは順序付けれた一連のバイト値として格納されるが、この一連のバイト値はインストラクションストリームと呼ばれ、インストラクションポインタ(IP)が次に実行するインストラクションを指し示す。

Opentype9_ip_2

また、TrueTypeインタプリタはインストラクションに必要なデータとインストラクションによって生成された結果をスタックに格納するが、このスタックはインタプリタスタックと呼ばれる。ほとんどのインストラクションはこのスタックに対して操作を行うが、PUSH命令と呼ばれるインストラクションはインストラクションストリームからその引数を取り、それをスタックに転送する。

PUSH命令
インストラクション 説明
NPUSHB[] PUSH N Bytes
NPUSHW[] PUSH N Words
PUSHB[abc] PUSH Bytes
PUSHW[abc] PUSH Words

ところで、インタプリタスタックの各要素は32ビットの固定長であるので、PUSH命令でインストラクションストリームから取り出したデータはゼロ拡張や符号拡張を行い32ビット長にしてからスタックにPOPする必要がある。

と、いろいろ省略してるが、まぁ、TrueTypeインタプリタを作る人以外は、これ以上、深入りしてもしょうがないような気がするので、実際にインストラクションを解析してみる。まずは、Tahomaフォントのフォントプログラムから。

Opentype9_1

フォントプログラムでは主に他で使用される関数やインストラクションが定義されるのだが、そのためのFDEF(Function DEFinition)命令が見てとれる。次は、コントロール値プログラム。

Opentype9_2

PUSH命令だらけである。先ほども書いたが、PUSH命令は引数がインストラクションストリームから取られるので、PUSH命令の次の命令のオフセットが引数のサイズだけ飛んでいるのである。最後はグリフプログラム。

Opentype9_3

とこんな感じ。

ちなみに、TrueTypeインタプリタの実行環境のメモリ要件など関する情報はmaxpテーブルに格納されている。

Opentype9_4_2

上の画像はTahomaフォントのmaxpテーブルの内容であるが、例えば、定義されている関数の数(MaxFunctionDefs)は57個、定義されているインストラクションの数は(MaxInstructionDefs)は0個、グリフプログラムの最大バイト数(MaxSizeOfInstructions)は1406バイトであるので、グリフプログラムの実行のために1406バイト以上のメモリを確保しておけばよい・・・・

というかT2FAnalyzerのプログラムが巨大になり手に負えなくなってきたwobbly・・・リファクタリングしないとだめだなぁ・・・

| | コメント (0) | トラックバック (0)

アイコン。

TrueType/OpenTypeフォント解析ツールのT2FAnalyzerのアイコンを変更しました。

Icon

paradlx様より頂いたものです。ありがとうございますwink

そういえば、もう今年も終わりが近づいてきた。振り返ると英語三昧だったような・・・ODBC(Open DataBase Connectivity)のマニュアルから始まって、TrueType・OpenTypeフォントの仕様書、Unsicribe関連の文書、規格化が進んでいる?10進浮動小数IEEE754rの文書、LL(∞)のパーサージェネレータCocoRのマニュアルとDelphiのソースを出力するようにソースの改変・・更には、Windowsで使われてる実行形式のファイルフォーマットPE/COFFの仕様書・・すべて英語だし・・

ODBCは気合入れたので理解がすごい深まった。今更ODBCなんだけど、これを理解することで、JDBCやADO.NETとのつながりも見えてきた。特に、ODBCとJDBCを比較するとおもしろいように似ている。ODBC絡みで、OLEDB(ADOではなくて)もやってみようとしたが、根本的な概念はODBCと変わらず、ただ、インターフェースがWindowsのCOM(Component Object Model)なだけであり、あんま、面白み味がなかったのでキャンセル。TrueType・OpenTypeフォントもT2FAnalyzerの作成を通して、理解が深まった。といってもODBCに比べると範囲が広いので、まだ色々調べていない部分がたくさん残っているがwobbly。10進浮動小数というよりむしろ、多倍長演算についてもアセンブラで組んでみたり。パーサージェネレータというよりむしろ、コンパイラ全般についても適当なスクリプト言語を作ってみたり(作りかけだがdespair)。作りかけのプログラムたくさんあるな・・・

いや、頑張った。けど、新しい事をやるにつれ、前やってた事を忘れていくような・・

| | コメント (4) | トラックバック (0)

T2FAnalyzer1.1.0リリース

TrueType/OpenTypeフォント解析ツールT2FAnalyzerV1.1.0をリリースしました。

ちょくちょくアップデートしてたのですが、次の3つのテーブルの解析をサポートしました。

  • PCLTテーブル
  • EBLCテーブル
  • EBSCテーブル

他にもメニュー、ウィンドウとグリッドのレイアウトの状態がアプリケーション起動・終了時に復元・退避されます。レイアウトをリセットしたい場合は該当のIniファイルを削除して下さい。メインウィンドウにステータスバーも追加しておきました。

いい加減フォントの事に飽きてきたので他の事やろうとしてるのだが、モチベーションが・・・despair

追記:そういえば、TrueType/OpenTypeフォントを解析する部分のソースコードも添付してあります。前にも書きましたが、CやC++言語ではなく、Delphiですのであしからず・・

最初のバージョンに比べると随分アプリケーションらしくなったので、誰かT2FAnalyzerのかっこいいアイコンを作ってくださいgood

| | コメント (8) | トラックバック (0)

OpenTypeレイアウトへの道(5)・・・GSUBテーブル

ということで、やっとGSUBテーブルに突入。

GSUBテーブルはフォントでサポートされる様々なスクリプトや言語システムを表すためのグリフ置換(Glyph Substitution)の情報を定義するテーブルであり、様々な言語システムでグリフ置換が必須である。

GSUBテーブルはScriptListFeatureListLookupListへのオフセットを定義するGSUBヘッダーで始まり、GSUBヘッダーの構造は次のようになる。

GSUBヘッダー
名前 説明
Fixed Version バージョン番号
Offset(USHORT) ScriptList ScriptListへのGSUBテーブルの先頭からのオフセット
Offset(USHORT) FeatureList FeatureListへのGSUBテーブルの先頭からのオフセット
Offset(USHORT) LookupList LookupListへのGSUBテーブルの先頭からのオフセット

また、前回説明したが、Lookupテーブルは1つ以上のLookupサブテーブルによって定義され、ルックアップタイプによってそのLookupサブテーブルの構造が違うが、グリフ置換でサポートされているルックアップタイプは次のようになる。

ルックアップタイプ
タイプ 説明
1 単独置換(Single Substitution) 1つのグリフを他の1つのグリフで置き換える
2 複数置換(Multiple Substitution) 1つのグリフを複数のグリフで置き換える
3 代替置換(Alternate Substitution) 1つのグリフを複数のグリフの内の1つのグリフで置き換える
4 合字置換(Ligature Substitution) 複数のグリフを1つのグリフで置き換える
5 文脈依存置換(Contexual Substitution) 1つ以上のグリフを文脈に応じて置き換える
6 連鎖型文脈依存置換(Chaining Contexual Substitution) 1つ以上のグリフを連鎖文脈に応じて置き換える
7 拡張置換(Extension Substitution) 他の置換のための拡張メカニズム
8 逆順連鎖型文脈依存単独置換(Reverse Chaining Contexual Single Substitution)
9以上 予約済み 将来の使用

ルックアップタイプ1の単独置換は1つのグリフを他の1つのグリフに置き換える置換で、例えば、縦書きために横書きのグリフを縦書きのグリフを置き換えたりするために使われる。ルックアップタイプ2の複数置換は、1つのグリフを複数のグリフに置き換える置換で、合字分解などで使われる(例えば、合字のグリフ"fi"を"f"と"i"の2つのグリフに置き換える等)。ルックアップタイプ4の合字置換は、複数のグリフを1つのグリフに置き換える置換である(例えば、"f"と"i"の二つのグリフを"fi"という単一のグリフに置き換える等)。ルックアップタイプ7の拡張置換は、他の置換のための拡張メカニズムで、通常のLookupサブテーブルは16ビット(USHORT)のオフセット位置に格納されるが、32ビットのオフセット位置に格納するために使われる。

各ルックアップタイプのLookupサブテーブルの構造だが、ここでは、ルックアップタイプ1の単独置換のLookupサブテーブルの構造だけを見てみる。ルックアップタイプ1の単独置換のLookupサブテーブルのフォーマットは2つあり、その構造はそれぞれ次のようになる。

ルックアップタイプ1(単独置換) フォーマット1

名前 説明
USHORT SubstFormat フォーマット識別子=1
Offset(USHORT) Coverage CoverageテーブルへのLookupサブテーブルの先頭からのオフセット
USHORT DeltaGlyphID グリフインデックスのデルタ値

単独置換フォーマット1は出力グリフのグリフインデックスを計算して求めるのだが、出力グリフのグリフインデックスを求めるには、定数のデルタ値(DeltaGlyphID)を入力グリフのグリフインデックスに加算する。Coverageはグリフ置換を適用するグリフを定義するCoverageテーブルへのオフセットである。

ルックアップタイプ1(単独置換) フォーマット2
名前 説明
USHORT SubstFormat フォーマット識別子=2
Offset(USHORT) Coverage CoverageテーブルへのLookupサブテーブルの先頭からのオフセット
USHORT GlyphCount Subsitutes配列内のグリフインデックスの数
GlyphID(USHORT) Substitutes[GlyphCount] Substitutes配列(カバレッジインデックス順)

単独置換フォーマット2はフォーマット1より融通の利くフォーマットであるが、より多くのスペースを必要とする。

ということで、実際にフォントを解析してみるのだが、まずは、前回のMSゴシックのGSUBテーブルのLookupListの解析結果を再掲載する。

Lookupmsgothic

このインデックス0のルックアップはkana->JAN言語システム内で定義されている縦書きのためのvertフィーチャーで使われているルックアップであるが、今回の説明を踏まえて、そのルックアップタイプが1(単独置換)であることも分かる。また、Lookupサブテーブルは1つである。

次にこのLookupサブテーブルの中身を見てみる。

Gsub_1

上の画像より、例えばグリフインデックス1941のグリフのエントリがCoverageテーブルに含まれている(カバレッジインデックスは42)ので、このグリフはグリフ置換の対象になるが、このグリフがグリフ置換によって、グリフインデックス19641のグリフに置換されることも分かる(Substitutes配列のインデックス42のエントリ)。

次にこれらグリフインデックス1941、19641のグリフをglyfテーブルで見ると、

Gsub_2

Gsub_3

句点「。」のグリフであるのだが、以上を踏まえると、

vertフィーチャーによって横書きの句点のグリフが縦書きの句点のグリフに置換されるように定義されていることが分かる。

Tahomaフォントはややこしいので省略・・・

最後に、T2FAnalyzerの方も最新版をアップロードしてあるのだが、現状、ルックアップタイプ1~4までのLookupサブテーブルにしか対応していないdespair。内部的にすべてのルックアップタイプのサブテーブルを解析しているのだが、GUIの方がまだ・・というより、しばらく他の事やろう。ははは。

| | コメント (2) | トラックバック (0)

T2FAnalyzer怒涛のアップデート

TrueType/OpenTypeフォント解析ツールT2FAnalyzerを再びアップデートしました。

前回の予定通りグリッドを置き換えました。合わせて、1024*768ピクセルの解像度の環境でも切れることなく表示される?ようレイアウトを調整しました。

T2fv1001_1

グリッドでフィルタ、特定の列で並べ替えが可能になりました。

T2fv1001_2

上の画像はglyfテーブルでグリフインデックス([Index]列)が1000未満のグリフに絞って表示しているところです。以前は、フィルタや並べ替えが出来なかったので、特定のグリフインデックスのグリフを表示するのが大変でしたdespair

インクリメンタルサーチもできます。例えば、グリフインデックスが1513のグリフを表示したい時、キーボードから1、5、1・・と順にタイプすると、インクリメンタルサーチが動作します。

T2fv1001_3

上の画像からはわかりづらいですが、「1153」の周りが黒で表示されています。

ほとんどのグリッドを置き換えたのですが、操作は全く同じです。インクリメンタルサーチは例外はありますが、基本的に[Index]列に対して動作します。

GSUBテーブルのLookupサブテーブルのGUIのレイアウトを設計したのですが、またもや、失敗しました。ははは。

使用していたコントロール(GUIの部品)をほとんど置き換えたので燃え尽きましたwobbly

| | コメント (0) | トラックバック (0)

フォント解析ツールT2FAnalyzerアップデート

True/OpenTypeフォント解析ツールT2FAnalyzerをバージョンアップしました。バージョン管理しっかりやってないのでやった方がいいのかも・・bearing。ヘルプファイルも、更新履歴のファイルもないし・・・

主な変更はGUIを変更し、より使いやすくしました。メインメニューとドッキング可能なウィンドウを導入しました。これで、解像度の低い環境でも大丈夫wink?今のところ、1280*1024ドット以上の解像度が推奨です。1280*1024ドットより低い解像度の環境だと表示項目の多いテーブルだと下の方が切れますbearing

画面はこんな感じです。

T2fana_1

元々、メインのフォントの解析するプログラムの開発が終わった後に、GUIを変更しようとしてたのですが、一部分だけ先に変更しておきました。GUI変更の第1段階という感じですかね。予定の第2段階ですが、これも前から計画にあったのですが、現在のT2FAnalyzerで使われてるグリッドで自由にソートや特定のキーワードなどで検索できず、ちょっと使いずらいなぁーと思ってたので、自分の他のソフトであるDBXInspectorで使われてるグリッドに変更しようと思ってます。これです。

T2fana_2

まぁ、予定なのでいつになるかは分かりませんが。作り途中のGSUBテーブルの解析の方も終わらせないといけないし・・

| | コメント (2) | トラックバック (0)

OpenTypeレイアウトへの道(4)・・・LookupList

ということで、今回は残りのLookupList。前々回のScriptList前回のFeatureList同様、退屈なお話になるが・・・

LookupListはフォント内のフィーチャーで使われるすべてのLookupテーブルを定義し、その構造は次のようになる。

LookupList
名前 説明
USHORT LookupCount Lookupテーブルの数
Offset(USHORT) Lookups[LookupCount] LookupテーブルへのLookupListの先頭からのオフセットの配列

LookupCountはLookupテーブルの数つまりフォント内のフィーチャーで使われるすべてのルックアップの数を表し、LookupsはLookupテーブルへのオフセットの配列である。

Lookupテーブルは、グリフの置換やポジショニングを適用するグリフ、適用する操作の種類や適用された結果を定義し、1つ以上のLookupサブテーブルから構成され、その構造は次のようになる。

Lookupテーブル
名前 説明
USHORT LookupType ルックアップタイプ
USHORT LookupFlag ルックアップフラグ
USHORT SubTableCount Lookupサブテーブルの数
Offset(USHORT) SubTables[SubTableCount] LookupサブテーブルのLookupテーブルの先頭からのオフセットの配列

LookupTypeはルックアップタイプつまり適用する操作の種類、SubTableCountはLookupサブテーブルの数を表し、SubTablesはLookupサブテーブルへのオフセットへの配列である。Lookupサブテーブルの構造は、ルックアップタイプによって異なるので、実際のGSUB、GPOSテーブルのところで説明しようと思う。

また、先ほどLookupテーブルの説明で、Lookupテーブルは「グリフの置換やポジショニングを適用するグリフ、適用する操作の種類や適用された結果を定義する」と書いたが、「グリフの置換やポジショニングを適用するグリフ」、「適用された結果」は、実際にはこのLookupサブテーブル内で定義される。特に前者の「グリフの置換やポジショニングを適用するグリフ」を定義するLookupサブテーブル内のテーブルをCoverageテーブルと呼ぶ。Coverageテーブルは、グリフ置換やポジショニングを行うグリフをグリフインデックスで指定するが、主に2つの指定の方法がある。

  1. 個々のグリフのグリフインデックスのリストとして指定する(フォーマット1)。
  2. 連続したグリフインデックスのレンジ(範囲)として指定する。つまり、開始グリフインデックス(StartIndex)と終了グリフインデックス(EndIndex)のペアとして指定する(フォーマット2)。

それぞれの構造は次のようになる。

フォーマット1 Coverageテーブル
名前 説明
USHORT CoverageFormat フォーマット識別子=1
USHORT GlyphCount グリフインデックスの配列内のグリフの数
GlyphID(USHORT) GlyphArray[GlyphCount] グリフインデックスの配列(グリフインデックスの昇順)

暑いよdespair

フォーマット2 Coverageテーブル
名前 説明
USHORT CoverageFormat フォーマット識別子=2
USHORT RangeCount Rangeレコードの数
RangeRecord RangeRecords[RangeCount] Rangeレコードの配列(レンジの開始グリフインデックスの昇順)

Rangeレコードの構造は次のようになる。

Rangeレコード
名前 説明
GlyphID(USHORT) StartIndex レンジの開始グリフインデックス
GlyphID(USHORT) EndIndex レンジの終了グリフインデックス
USHORT StartCoverageIndex レンジの開始グリフインデックスのCoverageインデックス

以上を踏まえて実際にフォントを解析してみる。

まずは、MSゴシックフォント(MSGOTHIC.TTC)のGSUBテーブルのLookupListから。

Lookupmsgothic

上の画像より計1つのルックアップが定義されていることがわかる。また、そのルックアップのルックアップタイプが1、そのルックアップ内に1つのLookupサブテーブルが定義されていることもわかる。ルックアップタイプやLookupサブテーブルの説明がまだなので一体何のことやらと思うと思うが、これらは次回と言う事で・・

ちなみに、前回の結果から分かるように、kana->JAN言語システムで定義されているvertフィーチャーはルックアップインデックス0のこのルックアップを使って定義されている。

次はTahomaフォント(tahoma.ttf)のGSUBテーブルのLookupList。

Lookuptahoma

上の画像より計6つのルックアップが定義されている。

以上。

というか、OpenTypeレイアウトへの道は予想通り険しかったdespair。上の説明で出てきた例えば、Lookupサブテーブルの構造はルックアップタイプに応じて異なるのだが、グリフ置換とグリフポジションニング用合わせると、20以上あるし・・・・・絶対無理。ははは。プログラムの方も追い付かないぜbearing。まぁ、とりあえず、主なルックアップタイプだけやろうかなと。

| | コメント (0) | トラックバック (0)

OpenTypeレイアウトへの道(3)・・・FeatureList

ということで、今回は前回の予告通りFeatureListである。

FeatureListはフォント内のすべてのフィーチャーを定義するが、その構造はScriptListのそれと似ている。

FeatureListの構造は次のようになる。

FeatureList
名前 説明
USHORT FeatureCount Featureレコードの数
FeatureRecord FeatureRecords[FeatureCount] Featureレコードの配列(タグのアルファベット順)

FeatureCountはFeatureレコードの数つまりフォント内のすべてのフィーチャーの数を表し、FeatureRecordsはFeatureレコードの配列である。

Featureレコードの構造は次のようになる。

Featureレコード
名前 説明
Tag FeatureTag フィーチャータグ
Offset(USHORT) Feature FeatureテーブルへのFeatureListの先頭からのオフセット

FeatureTagは、フィーチャーを識別するタグ、Featureは、Featureテーブルへのオフセットを表す。

Featureテーブルは1つ以上のルックアップを使ってフィーチャーを定義し、その構造は次のようになる。

Featureテーブル
名前 説明
Offset(USHORT) FeatureParams NULL
USHORT LookupCount ルックアップの数
USHORT LookupIndices[LookupCount] ルックアップのルックアップインデックスの配列

LookupCountはフィーチャーで使用するルックアップの数を表し、LookupIndicesは使用するルックアップのルックアップインデックスの配列である。それぞれのルックアップは、まだ説明していないLookupListで定義されるが、上記のルックアップインデックスを使って、そのフィーチャーのルックアップにアクセスされる。

前回と今回の上記を踏まえると、スクリプト、言語システム、フィーチャーそして今回出てきたルックアップの概念的な図は次のようになる。

Script_langsys_feat_lookup

以上を踏まえて、実際のフォントのFeatureListを解析してみた。

まずは、前回と同様に、MSゴシック(MSGOTHIC.TTC)のGSUBテーブルのFeatureListから。

Featuremsgothic

上の画像より計1つのフィーチャーvertが定義されていることが分かる。また、前回のScriptListの解析結果より、kana->JAN言語システム内にフィーチャーインデックスが0のフィーチャーが定義されていたが、そのフィーチャーがvertフィーチャーであった事も分かる。また、vertフィーチャーはルックアップインデックス0のルックアップを使って定義されていることも分かる。ルックアップはLookupListに定義されているので、ルックアップインデックス0のルックアップがどのように定義されているかはLookupListを解析しないと分からない。

vertフィーチャーは縦書きのためのフィーチャーであるが、何やらMSゴシックフォントは縦書き表示に対応しているようである。ははは・・(意味不明)。くどいようであるが、このvertフィーチャーによってどの横書き用のグリフがどの縦書き用のグリフに置換されるかはルックアップに定義されている。

同じように、Tahomaフォント(tahoma.ttf)のGSUBテーブルのFeatureListについて。

Featuretahoma

上の画像より計5つのフィーチャーが定義されていることが分かる。また、上の画像で選択されているligaフィーチャーはルックアップインデックス4、5の2つのルックアップを使って定義されていることが分かる。

| | コメント (0) | トラックバック (0)

OpenTypeレイアウトへの道(2)・・・ScriptList

前回の続きであるが、OpenTypeレイアウトテーブルは複雑でどういう順番で書いていけばいいのか悩むsad

で、今回は前回を踏まえて、GSUB、GPOSテーブルで使われる共通のテーブル構成について。ちなみに、GSUBテーブルはグリフ置換のためのテーブル、GPOSテーブルはグリフの位置を正しく決定(ポジショニング)するためのテーブルである。GSUB、GPOSテーブルは、それぞれそのヘッダーにScriptListFeatureListLookupListへのオフセットを持っているのだが、まずは、これら3つの構造から。

まずは、ScriptListについて。

OpenTypeフォントは、様々なスクリプトのグリフをレンダリングするために使われる1つ以上のグリフのグループを持てるのだが、それらはScriptListで定義される。

ScriptListの構造は次のようになる。

ScriptList
名前 説明
USHORT ScriptCount Scriptレコードの数
ScriptRecord ScriptRecords[ScriptCount] Scriptレコードの配列(タグのアルファベット順)

ScriptCountはScriptレコードの数を表し、ScriptRecordsはScriptレコードの配列である。

Scriptレコードの構造は次のようになる。

Scriptレコード
名前 説明
Tag ScriptTag スクリプトタグ
Offset(USHORT) Script ScriptテーブルへのScriptListの先頭からのオフセット

ScriptTagは前回説明した通り4バイトの印字可能なASCII文字で構成されるスクリプトを表す識別子である。ScriptはScriptテーブルへのオフセットである。

Scriptテーブルは、スクリプト内の言語システムを定義し、その構造は次のようになる。

Scriptテーブル
名前 説明
Offset(USHORT) DefaultLangSys デフォルトのLangSysテーブルへのScriptテーブルの先頭からのオフセットまたはNULL
USHORT LangSysCount LangSysレコードの数
LangSysRecord LangSysRecords[LangSysCount] LangSysレコードの配列(タグのアルファベット順)

DefaultLangSysは、スクリプト内のデフォルトの振る舞いを調整するLangSysテーブルへのオフセットを表す。LangSysCount、LangSysRecordsはそれぞれLangSysレコードの数とその配列である。

LangSysレコードの構造は次のようになる。

LangSysレコード
名前 説明
Tag LangSysTag 言語システムタグ
Offset(USHORT) LangSys LangSysテーブルへのScriptテーブルの先頭からのオフセット

LangSysレコードはScriptレコードと似た構成になっており、LangSysTagは言語システムタグ、LangSysはLangSysテーブルへのオフセットを表す。

LangSysテーブルは、前回の言語システムの説明の通り、言語システムで使用するフィーチャーを定義し、その構造は次のようになる。

LangSysテーブル
名前 説明
Offset(USHORT) LookupOrder NULL
USHORT ReqFeatureIndex 必須フィーチャーのフィーチャーインデックス、または、0xFFFF(必須フィーチャーがない場合)
USHORT FeatureCount フィーチャーの数(必須フィーチャーは除く)
USHORT FeatureIndices[FeatureCount] フィーチャーのフィーチャーインデックスの配列

ReqFeatureIndexは、言語システムの必須フィーチャーのインデックスを表し、FeatureCountは言語システムのフィーチャーの数を表し、FeatureIndicesはフィーチャーインデックスの配列である。ちなみに、それぞれのフィーチャーはまだ、説明していないFeatureListで定義されるが、上記のフィーチャーインデックスを使って言語システムのそれぞれのフィーチャーにアクセスされる。

以上を踏まえるとスクリプト、言語システム、フィーチャーの概念的な関係は次の図のようになる。

Script_langsys_feat

次回以降、残りのFeatureList、LookupListを順に見ていこうと思うが、ここらへんは具体的に実際のフォントでの解析結果も示したほうが分かりやすいのであるが、何せT2Analayzerの開発の方が追い付かないbearing。というより、解析するプログラムはだいたい出来てるのだが、GUIをどうするかで悩んでいる。

追記:一応、GUIをこしらえたので、実際にScriptListを解析してみる。まずは、お馴染みのMS ゴシック(MSGOTHIC.TTF)のGSUBテーブルのScriptListから。

Scriptmsgothic

上の画像より、kanaスクリプトが定義され、その中に、JAN言語システムが定義されているのが分かる。また、JAN言語システム内にフィーチャーインデックス0のフィーチャーが定義されている。フィーチャーはFeatureListで定義されているので、フィーチャーインデックス0のフィーチャーがどんなフィーチャーであるかは、FeatureListを解析してみないと分からない。

次は、Tahomaフォント(tahoma.TTF)のGSUBテーブルのScriptList。

Scripttahoma

上の画像よりarabスクリプトが定義されている。その中に言語システムは明示的に定義されていないので、テキスト処理クライアントで利用する場合は、デフォルトの言語システムを利用することになる。そして、そのタグなしのデフォルトの言語システム内に、フィーチャーインデックスがそれぞれ0、1、2、3、4の5つのフィーチャーが定義されているのが分かる。

| | コメント (0) | トラックバック (0)

OpenTypeレイアウトへの道

前回のOpenTypeフォントの投稿から随分、日が過ぎたが別件(この投稿のコメント欄)で久しぶりにやる気が沸いたので続きをやってみる。タイトルも「OpenTypeレイアウトへの道」となんだかカッコつけた名前にしてみたhappy02

今回からフォントがOpenTypeであるがためのテーブル、つまり、従来のTrueTypeフォントに追加された新しいOpenTypeレイアウトテーブルと呼ばれるGSUB、GPOS、GDEF、BASE、JSTFの5つのテーブルを見ていこうと思う(といっても、これらのテーブルは複雑だから、すべて説明するまでモチベーションが続かないと思うが・・・むしろ、1つのテーブルだけで終わるかもbearing)。で、いきなり、上記のテーブルの中を見ても混乱すると思うし、まずは、概要や用語について。

まず、OpenTypeレイアウトテーブルはグリフを置換(Substitution)、ポジショニング(Positioning)するための情報を提供するが、それらのデータはスクリプト(Script)、言語システム(Language System)、フィーチャー(Feature)、ルックアップ(Lookup)によって構成される。

スクリプトというのは、1つ以上の言語を表現するために使われる関連のあるグリフの集合のことでトップレベルに定義される。例えば、ラテン文字を表すラテンスクリプトは英語、フランス語、ドイツ語など多くの言語を表記するために使われる。対照的に、ひらがなやカタカナを表すスクリプトは日本語だけを表記するために使われる(漢字を表すスクリプトは日本語だけでなく中国語などでも使われる)。OpenTypeレイアウトでは単一のフォントで複数のスクリプトをサポートすることができる。また、スクリプト内に言語システムを定義する。

言語システムというのは、文字通り、言語のことである。また、言語システム内に言語を表すためのグリフの使用方法のルールであるフィーチャーを定義する。

フィーチャーというのは、言語を表すためのグリフの使用方法のルールのことである。例えば、日本語の縦書きの為に、横書き用のグリフを縦書き用のグリフに置換するためのvertフィーチャ、また、アラビア語では文字の字形が単語内での位置によって、語頭形(initial form)、語中形(medial form)、語末形(final form)などに変わるがそのための、init、medi、finaフィーチャーというのがある。フィーチャーはルックアップを使って定義される。

ルックアップというのは、グリフの置換やポジショニングを適用するグリフ、グリフに適用する操作の種類や適用された結果などを定義する。例えば、グリフ置換では、通常、どのグリフがどのグリフに置換されるか、つまり、置換元のグリフのとそれらの置換先のグリフなどを定義する。

ちなみに、これらスクリプト、言語システム、フィーチャーはOpenTypeフォント内の各テーブルを識別するのと同じように、タグと呼ばれる4バイトの印字可能なASCII文字(0x20-0x7E)で識別され、MicrosoftやAdobeによって既に登録済みタグがある。それらの登録済みのタグの一部を掲載してみる。

スクリプトタグ
スクリプト スクリプトタグ
Arabic arab
Armenian armn
Balinese bali
Bangali beng
Bangali v.2 bng2
・・・省略・・・
CJK Ideographic hani
Coptic copt
・・・省略・・・
Hebrew hebr
Hiragana kana
・・・省略・・・

漢字?を表すhaniタグ、ひらがな・カタカナ?を表すkanaタグがあることがわかるが、Microsoftの資料によると、スクリプトタグはMicrosoft Typographyによって定義され、各タグはUnicodeにおけるそれぞれの文字コードレンジに対応するそうである。

言語システムタグ
言語システム 言語システムタグ
Abaza ABA
Abkhazian ABK
・・・省略・・・
Greek ELL
English ENG
Erzya ERZ
Spanish ESP
・・・省略・・・
Italian ITA
Hebrew IWR
Javanese JAV
Yiddish JII
Japanse JAN
・・・省略・・・

英語を表すENGタグ、日本語を表すJANタグがあるのがわかる。

フィーチャータグ
フレンドリ名 フィーチャータグ
Access All Alternates aalt
Above-base Forms abvt
・・・省略・・・
JIS78 Forms jp78
JIS83 Forms jp83
JIS90 Forms jp90
JIS2004 Forms jp04
・・・省略・・・
Ruby Notation Forms ruby
・・・省略・・・
Vertical Writing vert
・・・省略・・・

vertタグは先ほど述べたように縦書きのためのタグ、rubyタグは振り仮名のためのタグである。

すべてのタグとその詳細は、Microsoftのタイポグラフィのサイトを参照ということで。

て、ここまでだと何のことだかというこだと思うが、次回に続く・・・

| | コメント (2) | トラックバック (0)

OpenTypeフォントの続き(8)・・・glyfテーブル

ということで、glyfテーブル

glyfテーブルはグリフのアウトラインを定義するテーブルである。TrueTypeアウトラインは2つの端点と1つの制御点(control point)で定義する2次Bスプライン曲線と直線でアウトラインを定義するので、WinAPIのPolyBezier関数でアウトラインを描画する場合は、3次ベジェ曲線に変換する必要がある。変換式は、2次Bスプライン曲線の端点の座標をそれぞれ(x0, y0)、(x2, y2)とし、制御点の座標を(x1, y1)とすると、

  • CP0 = (x0, y0)
  • CP1 = (x0 + 2*(x1 - x0)/3, y0 + 2*(y1 - y0)/3)
  • CP2 = (x1 + 1*(x2 - x1)/3, y1 + 1*(y2 - y1)/3)
  • CP3 = (x2, y2)

となる。CP0、CP3は端点、CP1、CP2は制御点。

フォントに含まれる各グリフのグリフデータは前回のlocaテーブルを使ってアクセスするが、各グリフのグリフデータはグリフヘッダーで始まる。グリフヘッダーは次のようになる。

グリフヘッダー
名前 説明
SHORT NumberOfContours 輪郭線(Contour)の数
SHORT xMin x座標の最小値
SHORT yMin y座標の最小値
SHORT xMax x座標の最大値
SHORT yMax y座標の最大値

NumberOfContoursはグリフを構成する輪郭線(Contour)の数を表す。0以上ならシンプルグリフを負なら複数の他のグリフから構成されるコンポジジットグリフを表す。xMinyMinxMaxyMaxはグリフを囲む最小の矩形であるバウンディングボックス(ブラックボックス)を表す。グリフヘッダーの後に、実際のグリフデータが続くが、シンプルグリフかまたは、コンポジットグリフかによってフォーマットが異なる。シンプルグリフの場合、次のようになる。

シンプルグリフ
名前 説明
USHORT EndPtsOfContours[NumberOfContours] 各輪郭線(Contour)の終点番号の配列
USHORT InstructionLength インストラクションの長さ(バイト数)
BYTE Instructions[InstructionLength] インストラクションの配列
Byte Flags[] 各輪郭線のフラグの配列
BYTE or SHORT XCoordinates[] x座標の配列
BYTE or SHORT YCoordinates[] y座標の配列

EndPtsOfContoursは各輪郭線の終点番号の配列である。グリフを構成する各点は0から始まる通し番号が付けられるが、グリフを構成する点の数NumOfPtsは

NumOfPts = EndPtsContours[NumberOfContours] + 1

で求まる。XCoordinatesYCoordinatesは各点の座標を表す配列である。ちなみに、XCoordinates、YCoordinatesは絶対座標ではなく直前の点からのベクトルになっている(最初の点は原点(0, 0)からのベクトル)。また、Flags配列によって、XCoordinates、YCoordniates配列の要素数が変わるので注意が必要である。

コンポジットグリフはややこしいので省略するとして、実際にglyfテーブルを解析してみる。

Opentype9_1

上の画像はTahomaフォント(TAHOMA.TTF)のglyfテーブルを解析した時の実行結果である。また、グリフインデックス42のグリフ(ラテンアルファベットの大文字のGに相当するグリフ)のグリフデータが表示されている。上の画像より輪郭線が1つで、点の数が49個であることが分かる。右下のアウトラインは上が実際のグリフデータから各点の絶対座標を求め単純に各点をオンカーブ点、オフカーブ点を全く考慮せず直線で結んだもの、下がETO_GLYPH_INDEXオプション付きのExtTextOut関数でグリフを表示したものである。ちなみに、輪郭線上の点をオンカーブ点(on-curve point)、輪郭線上にない点をオフカーブ点(off-curve point)と呼ぶ。

MS ゴシックフォントのglyfテーブルを解析した時の実行結果は次のようになる。

Opentype9_2

ところで、グリフの位置がずれるのだが、どうやって正しく配置するんだ??誰か教えて下さい。とりあえず、グリフのバウンディングボックスと水平方向はhmtxテーブルの左サイドベアリングを使って、垂直方向はOS2テーブルのWinAscentを使って配置してるが、垂直方向は結構ずれてる・・インストラクションや存在すればBASEやGPOSテーブルが影響するとは思うが。

とまぁ、OpenTypeフォントの事を始めて1ヶ月になるのだが、キリがいいので、前にも書いたように他の事をやろうかと。作ったプログラムはここから。PostScriptアウトライン、ビットマップ、OpenTypeテキストレイアウト関連のテーブルは一切まだ手をつけていないが、後々気が向いたらやろうかなと。kernテーブルやvheaテーブルなど他のテーブルはだいたい解析できるようになってます。プログラムはメジャーなC言語でなくCodeGear社のDelphiを使っているのだが、ソースコードを見たという人がいれば、ソースもアップロードします。

Opentype9_3

| | コメント (0) | トラックバック (0)

OpenTypeフォントの続き(7)・・・locaテーブル

ということで、hheaやhmtxを飛ばすが、やっとTrueTypeアウトライン関連のテーブルに突入。

今回はlocaテーブル。locaテーブルはグリフインデックスからglyfテーブル内のグリフデータの位置へのマッピングを定義するテーブルである。

locaテーブルの構造はショートフォーマットとロングフォーマットの二つのフォーマットがあり、headテーブルのIndexToLocFormatフィールドによってどちらのフォーマットか決まる。

各フォーマットの構造は次のようになる。

ショートフォーマット
名前 説明
USHORT Offsets[NumGlyphs+1] glyfテーブルの先頭からのオフセット(バイト単位)/2の配列

ロングフォーマット
名前 説明
ULONG Offsets[NumGlyphs+1] glyfテーブルの先頭からのオフセット(バイト単位)の配列

Offsetsフィールドは各グリフのグリフデータのglyfテーブルの先頭からのオフセットの配列で、このOffsetsフィールドを使って、実際のglyfテーブル内のグリフデータにアクセスする。ショートフォーマットの場合、オフセットを2で割った値が格納されているので、実際のオフセットはそれを2倍したものになる。NumGlyphsの値はフォント内のグリフ数を表すが、これはmaxpテーブルのNumGlyphsフィールドから取られる。また、Offsets配列の最後にダミーの要素(+1)が追加されているので、各グリフのグリフデータのサイズは

  • グリフインデックスNのグリフデータのサイズ = Offsets[N + 1] - Offsets[N]

と一様に求まる(上の式は、ロングフォーマットの場合。ショートフォーマットの場合は2倍する必要がある)。

ということで、実際にlocaテーブルを解析した実行結果を示す。

Opentype8_1

上の画像はTahomaフォント(TAHOMA.TTF)を解析した時の画像である。フォーマットはロングフォーマットである。また、上の画像より、グリフインデックス1、2、3のグリフデータのサイズが0になっていることが分かるが、これは、空白文字などのアウトラインも持たないグリフを表す。ちなみに、グリフインデックス0のグリフは、フォントにグリフが存在しないことを表す欠損(文字)グリフでなければならない。

| | コメント (0) | トラックバック (0)

OpenTypeフォントの続き(6)・・・cmapテーブル

今回はcmapテーブル

cmapテーブルは文字コードからグリフインデックスへのマッピングを定義するテーブルで、必須である。また、様々なプラットフォーム・エンコーディング向けに複数のサブテーブルを定義することができる。

cmapテーブルの構造は、cmapヘッダーで始まり、次のようになる。

cmap ヘッダー
名前 説明
USHORT Version テーブルバージョン
USHORT NumTables エンコーディングテーブル数
EncodingRecord EncodingRecords[NumTables] エンコーディングレコードの配列

NumTablesフィールドはcmapテーブルに含まれるエンコーディングサブテーブルの数を表し、EncodingRecordsフィールドはエンコーディングレコードの配列を表す。各エンコーディングレコードの構造は次のようになる。

エンコーディングレコード
名前 説明
USHORT PlatformID プラットフォームID
USHORT EncodingID プラットフォーム固有のエンコーディングID
ULONG Offset cmapテーブルの先頭からのエンコーディングサブテーブルへのオフセット(バイト単位)

PlatformIDEncodingIDフィールドの表す値は、nameテーブルの回で説明した通りである。Offsetフィールドはcmapテーブルの先頭からのエンコーディングサブテーブルへのオフセットを表し、この値を使って、各エンコーディングサブテーブルにアクセスする。次にエンコーディングサブテーブルの構造であるが、エンコーディングの特性に応じて、複数のフォーマットが定義されている。各フォーマットの概要は次のようになる。

エンコーディングサブテーブルフォーマット
種類 概要
フォーマット0 Byte encoding table
フォーマット2 High-byte mapping through table
フォーマット4 Segment mapping to delta values
フォーマット6 Trimmed table mapping
フォーマット8 mixed 16-bit and 32-bit coverage
フォーマット10 Trimmed array
フォーマット12 Segmented coverage

フォーマット0は、1バイトエンコーディングフォーマットである。フォーマット2は、混合8/16ビットエンコーディングフォーマットで、1バイトと2バイトの文字コードが混在する日本語のShift-JISなどで使われる。フォーマット4は2バイトエンコーディングフォーマットである。フォーマット8、10、12は4バイトエンコーディングフォーマットである。MicrosoftはWindows向けのサロゲートペアに対応したUnicodeフォントを作成する時、フォーマット4とフォーマット12を組み合わせて使うように推奨している。ちなみに、どのフォーマットが使われているかを判定するには、各エンコーディングサブテーブルの先頭2バイトがフォーマットの種類を表すFormatフィールドで始まるので、このフィールドを参照して判定できる。

各フォーマットの詳細は割愛するとして、実際にcmapテーブルを解析してみた。

Opentype7_1

上の画像はTahomaフォント(TAHOMA.TTF)を解析した時の実行結果である。MacintoshプラットフォームとMicrosoftプラットフォーム向けの2つのエンコーディングサブテーブルが含まれていることが分かる。Microsoftプラットフォーム向けのエンコーディングサブテーブルは、Unicodeエンコーディングでフォーマット4が使われていることも分かる。Macintoshプラットフォーム向けのエンコーディングサブテーブルは、下の画像が示す通り、フォーマット0が使われている。

Opentype7_2

実際に、TAHOMA.TTFファイルをMacintosh上でそのまま使えるかは分からないが、フォーマット0は1バイト文字コード向けのフォーマットなので、Macintosh上では、最大256個のグリフまでしかアクセスできないことになる。

最後にいつもならMS ゴシックフォントを解析するところだが、結果がTahomaフォントとあまり変わらないので、インターネット上で見つけた無料のフォントを解析してみた。

Opentype7_3

Macintoshプラットフォーム向けにShift-JISのエンコーディングサブテーブルが定義されていて、フォーマット2が使われていることが分かる。

| | コメント (0) | トラックバック (1)

OpenTypeフォントの続き(5)・・・OS/2テーブル

今回はOS/2テーブルである。OS/2テーブルはOS/2・Windows固有のメトリックスを定義するテーブルで必須である。前回と同様に、先に実行結果を示す。

Opentype6_1

上の画像はMS ゴシックフォントを解析した時の実行結果である。WeightClassはフォントの太さを表すフィールドで、windows.hで定義されているFW_NORMALやFW_BOLD等の値をとる。FsSelectionはフォントのスタイルを表すビットフィールドである。UnicodeRangeはフォントが提供しているグリフによってカバーされるUnicodeブロックを表すビットフィールドである。同様に、CodePageRangeはフォントが提供しているグリフによってカバーされるコードページ(文字コード)を表すビットフィールドである。上の画像より、日本語の文字コードであるShift-JIS(SHIFTJIS_CHARSET)に対応するビット17がセットされていることがわかる。また、上の画像からは判断できないが、MS ゴシックフォントには、ラテン文字やギリシャ文字のグリフも含まれているので、それらに対応するビットもセットされている(ANSI_CHARSETやGREEK_CHARSETなどに対応するビット)。

通常、Windowsでフォントを指定する時、LOGFONT構造体で表される論理フォントで指定するが、実際にはWindowsのフォントマッパによって、指定された論理フォントに近い物理フォントが選択される。この時、フォントマッパはフォントのこれらのフィールドをLOGFONT構造体のフィールドと比較していると思われる。例えば、LOGFONT構造体のlfWeightフィールドはOS/2テーブルのWeightClassフィールド、lfCharsetフィールドはOS/2テーブルのCodePageRangeフィールドという具合に(lfFaceNameフィールドはOS/2テーブルではなくnameテーブル)。

ところで、OS/2テーブルのUnicodeRangeやCodePageRangeフィールドの表す値は、Win32APIのGetTextCharsetInfoなどで返されるFONTSIGNATURE構造体として取得できるので、それらの値も括弧付きで示してある。他にもFamilyClassPanoseフィールドなどおもしろそうなフィールドもあるが、省略・・・

最後に、TahomaフォントのOS/2テーブルを解析した時の実行結果を示す。

Opentype6_2

というか、ずっとフォントの話ばっかりなので、しばらく他の話題にしようかと思っている。肝心のcmap・glyfテーブルやOpenTypeテキストレイアウトテーブルと呼ばれるGSUB・GPOSテーブルのことにほとんど触れていないが・・後、この作ってるツールをある程度完成したら、ダウンロードできるようにもしようかなと。

| | コメント (0) | トラックバック (0)

OpenTypeフォントの続き(4)・・・head・maxpテーブル

今回はheadテーブルmaxpテーブル

というより、書くのがだるくなってきたので、さらりと行こうと思う。despair

まずは、headテーブル。headテーブルはフォントのグローバルな情報を定義するテーブルで必須である。先に、MS ゴシックフォントを解析した時の実行結果を示す。

Opentype5_1

順番が前後するかも知れないが、まずは、UnitsPerEm。UnitsPerEmは、Em四角形辺りのユニット数を表す。MacStyleはフォントのスタイルを表すビットフィールドである。Windows固有のメトリックスを表すOS/2テーブルにもフォントのスタイルを表すfsSelectionフィールドがあるが、Windowsの場合、OS/2テーブルのfsSelectionを優先するようである。

Opentype5_2

上の画像は、Arial Boldタイプフェイス(わかりやすく言うと、ArialフォントのBoldスタイル、ファイル名はARIALBD.TTF)を解析した時の実行結果であるが、Boldスタイルを表すMacStyleのビット0がセットされていることが分かる。

で、次はmaxpテーブルである。これはフォントのプロファイルを表すテーブルである。これもheadテーブルと同じように、解析した実行結果を先に示す。

Opentype5_3

上の画像はTahomaフォント(TAHOMA.TTF)を解析した結果である。フィールド名を見れば分かるように、NumGlyphsは含まれるグリフの数を表す。Tahomaフォントの場合、1283個のグリフが含まれていることが分かる。MaxPointsMaxContoursは通常のグリフ(コンポジットグリフではないグリフ)を構成する点、輪郭線の数の最大値を表す。以下省略・・・

というか、タイポグラフィ関連の専門用語はややこしいなぁ。

追記:ちなみに、フォントに含まれるグリフの数を表すmaxpテーブルのNumGlyphsや他のテーブルのNumGlyphsはUSHORT型つまり2バイトの符号無し整数として宣言されているので、いうまでもなく、TrueTypeやOpenTypeフォントには65536個以上のグリフを含めることはできない。

| | コメント (0) | トラックバック (0)

OpenTypeフォントの続き(3)・・・nameテーブル

ということで今回はnameテーブル

nameテーブルは、フォントファミリ名・フォントの著作権注釈など、フォントに関する文字列データを定義するテーブルである。nameテーブルの構造は次のようになる。

nameテーブル
名前 説明
USHORT format フォーマットセレクタ(=0)
USHORT count Nameレコードの数
USHORT stringOffset テーブルの先頭からの文字列ストレージへのオフセット(バイト)
NameRecord nameRecords[count] nameレコードの配列
(Variable) 文字列ストレージ

Nameレコードの構造は次のようになる。

Nameレコード
名前 説明
USHORT platformID プラットフォームID
USHORT encodingID プラットフォーム固有のエンコーディングID
USHORT languageID 言語ID
USHORT nameID 名前ID
USHORT length 文字列の長さ(バイト)
USHORT offset 文字列ストレージの先頭からの文字列へのオフセット

プラットフォームIDはプラットフォームの種類を表すIDで次のような値になる。

プラットフォームID
プラットフォームID プラットフォーム名
0 Unicode
1 Macintosh
2 ISO
3 Microsoft
4 カスタム

MicrosoftのWindows向けのフォントでは、通常、プラットフォームID=3が使われる。次のエンコーディングIDは、プラットフォーム固有のエンコーディングを表すIDで、例えば、プラットフォームID=3のMicrosoftプラットフォームでは、次のような値になる。

エンコーディングID(プラットフォームID=3)
エンコーディングID 説明
0 シンボル
1 Unicode BMP面のみ
2 シフトJIS
3 PRC
5 Big5
6 Johab
7 予約済み
8 予約済み
9 予約済み
10 Unicode フルレパトリー

Windows向けのUnicodeフォントの場合、通常、エンコーディングID=1もしくは10、シンボルフォントの場合、0が使われる。次に言語IDであるが、Microsoftプラットフォーム(プラットフォームID=3)の場合、Windowsのロケール(LCID型のロケールID) を表す。最後に、名前IDであるが、これは文字列データの内容を表すIDで、次のような値になる。

名前ID
名前ID 説明
0 著作権注釈
1 フォントファミリ名
2 フォントサブファミリ名
3 フォント識別子
4 完全なフォント名
5 バージョン文字列
6 PostScript名
7 商標
8 製造社名
・・・以下省略・・・

以上を踏まえて、実際にnameテーブルを解析してみた。実行結果は次のとおり。

Opentype4_1_3

上の画像はMS ゴシックフォントのnameテーブルを解析したときの実行結果である。MicrosoftとMachintoshプラットフォーム向けの文字列データが含まれていることが分かる。また、Microsoftプラットフォーム(プラットフォームID=3)向けの文字列データは、Unicode(エンコーディングID=1)でエンコードされ、英語ロケール(ロケールID=0x0409)と日本語ロケール(ロケールID=0x0411)向けの2種類含まれていることもわかる。アプリケーション側でこれらの情報をユーザーに対して表示する時、アプリケーションの実行環境のロケールに合わせて切り替えれば、よりユーザーフレンドリになることは言うまでもない。

ちなみに、上の画像のNameRecords & String DataリストビューのString Data列は文字列データを16進数で表示した値である。String Data(Encoded)列は文字列データをプラットフォームID、エンコーディングIDで指定されたエンコーディングで符号化し、表示できるようUnicodeに変換して表示した値である。Macintosh向けの文字コードはよくわからないので、空白にしておいた。

最後に、Tahomaフォントのnameテーブルを解析した時の実行結果も載せておく。

Opentype4_2

追記:Macintosh向けの文字コードはよくわからないと書いたが、Windowsには、Mac用のいくつかのコードページがインストール・サポートされているので、Win32APIのMultiByteToWideCharを使ってUnicodeに変換してみた。

Opentype4_3

Macintoshプラットフォーム(PlatformID=1)のEncodingID=0(Roman)は、Windowsコードページ10000に対応するようなので、コードページ10000を指定して、MultiByteToWideCharを呼び変換してみたが、上手くいったようである。

| | コメント (0) | トラックバック (0)

OpenTypeフォントの続き(2)・・・OpenTypeテーブル

1回目でTahomaフォントのファイル(TAHOMA.TTF)に含まれるテーブルの一覧を列挙したが、今回はOpenTypeフォントのテーブルの概要について。

前にOpenTypeフォントは、グリフデータとしてTrueType形式のアウトラインもしくはPostScript形式のアウトラインを含むことができると書いたが、アウトラインの形式にかかわらず正しく機能するために、以下のテーブルが必須である。

必須テーブル
タグ 名前
cmap 文字・グリフマッピング
head フォントヘッダー
hhea 水平ヘッダー
hmtx 水平メトリックス
maxp 最大プロファイル
name 名前テーブル
OS/2 OS/2・Windows固有メトリックス
post PostScript情報

cmapテーブルは文字コードとグリフインデックスのマッピングを定義するテーブルで、1つ以上の文字エンコーディングをサポートするために、複数のマッピングサブテーブルを含むことができる。nameテーブルはフォントファミリ名・サブファミリ名・著作権注釈などのフォントに関連する文字列を定義するテーブルである。OS/2テーブルは、OS/2・Windowsに固有のメトリックスを定義するテーブルである。

TrueTypeアウトラインに基づくOpenTypeフォントでは、次のテーブルが使われる。

TrueTypeアウトライン関連のテーブル
タグ 名前
cvt コントロール値テーブル
fpgm フォントプログラム
glyf グリフデータ
loca インデックス・位置
prep CVTプログラム

PostScriptアウトラインに基づくOpenTypeフォントでは、次のテーブルが使われる。

PostScriptアウトライン関連のテーブル
タグ 名前
CFF PostScriptフォントプログラム
VORG Vertical Origin

OpenTypeフォントは小さなフォントサイズのために最適化されたビットマップを含むことができるが、そのために次のテーブルが使われる。

ビットマップグリフ関連のテーブル
Tag 名前
EBDT ビットマップデータ
EBLC ビットマップ位置データ
EBSC ビットマップスケーリングデータ

アドバンストタイポグラフィテーブル
Tag 名前
BASE ベースラインデータ
GDEF グリフ定義データ
GPOS グリフ配置データ
GSUB グリフ置換データ
JSTF ジャスティフィケーションデータ

その他のテーブル
Tag 名前
DSIG デジタル署名
gasp グリッドフィティング/スキャンコンバーション
hdmx 水平デバイスメトリックス
kern カーニング
LTSH Liner threshold data
PCLT PCL 5 data
VDMX 垂直デバイスメトリックス
vhea 垂直メトリックスヘッダー
vmtx 垂直メトリックス

ということで、次回以降、これらのテーブルの内のいくつかを見ていこうと思う。というより、上に挙げた大半のテーブルを理解していないdespair

| | コメント (0) | トラックバック (1)

OpenTypeフォントの続き(1)・・・TTC

今回は前回最後に触れたTrueType Collectionについてである。

TrueType Collection(TTC)とは、1つのファイルに複数のOpenTypeフォントを格納するためのファイルフォーマットで、拡張子は通常.TTCである。また、複数のOpenTypeフォントをただ単純に1つのファイルにパッケージングするのではなく、フォント間でグリフを共有して、ファイルサイズを抑えるための仕組みが備わっている。

TTCの構造は次のようになる。(前回同様、概念的な図である。)

Opentype2_1_2

TTCはTTCヘッダーから始まる。つまり、TTCの先頭(オフセット0)にTTCヘッダが格納されていなければならない。また、TTCヘッダの構造は次のようになる。

TTCヘッダ Version1.0
名前 説明
TAG TTCTag TrueType Collection ID:'ttcf'
ULONG Version TTCヘッダのバージョン:0x00010000
ULONG numFonts TTC内のフォント数
ULONG offsetTable[numFonts] TTC内の各フォントへのファイルの先頭からのオフセット(バイト)

TTCヘッダ Version2.0
名前 説明
TAG TTCTag TrueType Collection ID:'ttcf'
ULONG Version TTCヘッダのバージョン:0x00020000
ULONG numFonts TTC内のフォント数
ULONG offsetTable[numFonts] TTC内の各フォントへのファイルの先頭からのオフセット(バイト)
ULONG DsigTag DSIGテーブルが存在するかを表すタグ。存在すれば、0x44534947('DSIG')
ULONG DsigLength DSIGテーブルのサイズ(バイト)
ULONG DsigOffset DSIGテーブルへのファイルの先頭からのオフセット(バイト)

numFontsはTTC内のフォント数を表す。OffsetTableは各フォントへのファイルの先頭からのオフセットの配列で、TTC内のフォントの数つまりnumFontsの数だけ要素がある。このオフセットの配列を使って、TTC内の各フォントにアクセスする。TTC内の各フォントの構造は前回説明したものと同じである。

ここで注意が必要なのは、TTC内の各フォント内のテーブルへのアクセスである。前回、各テーブルへはテーブルディレクトリエントリのOffsetフィールドを使ってアクセスできると書いたが、このテーブルディレクトリエントリのOffsetフィルードはTTCファイルであろうとなかろうと、常にファイルの先頭からのオフセットを表す。

以上を踏まえて、TTCヘッダを解析するプログラムを作成してみた。実行結果はこんな感じである。

Opentype2_2

MSゴシック系のTTCファイル(MSGOTHIC.TTC)を解析した時の実行結果である。このTTCファイルに3つのOpenTypeフォントが含まれていることがわかる。ちなみに、Windowsのエクスプローラでこのファイルを見れば分かるように、このファイルにはMS ゴシック、MS Pゴシック、MS UI Gothicの3つのフォントが含まれていることが分かる。

Opentype2_3

| | コメント (0) | トラックバック (0)

OpenTypeフォント

今回からOpenTypeフォント

OpenTypeフォントはTrueType Open V2.0フォントとも呼ばれ、Microsoft社とAdobe社によってTrueTypeフォントを拡張して開発されたフォントである。また、グリフデータとして、TrueType形式のグリフデータもしくはPostScript形式のグリフデータを含むことができるアウトラインフォントであるが、小さいフォントサイズ用にビットマップ形式のグリフデータを含むこともできる。

で、今回から、このOpenTypeフォントのファイルを解析してみる。ちなみに、TrueType形式のグリフデータを含むOpenTypeフォントのファイルの拡張子は通常.TTF.OTFである。

OpenTypeフォントの構造は、フォントに関する様々な情報が格納された名前(タグと呼ばれる)付きのテーブルの集まりから構成され、下の図のようになる。

Opentype1_1

OpenTypeフォントは、最初にバージョン番号やフォント内のテーブル数が格納されたオフセットテーブル(Offset Table)から始まる(オフセット=0)。そしてすぐ後に、フォント内の各テーブルのタグ名や開始位置(オフセット)やサイズが格納されたテーブルディレクトリ(Table Directory)が続く。ちなみに、上の図は概念的な図である。

オフセットテーブルの構造は次のようになる。

オフセットテーブル
名前 説明
Fixed sfnt version 0x00010000 for version 1.0
USHORT numTables テーブル数
USHORT searchRange
USHORT entrySelector
USHORT rangeShift

numTablesはフォント内のテーブル数である。テーブルオフセットのすぐ後に続くテーブルディレクトリはテーブルディレクトリエントリの配列で、numTable個のエントリが含まれる。テーブルディレクトリエントリの構造は次のようになる。

テーブルディレクトリエントリ
名前 説明
ULONG tag 4バイト識別子(タグ名)
ULONG checkSum テーブルのチェックサム
ULONG offset テーブルのファイルの先頭からのオフセット(バイト)
ULONG length テーブルのサイズ(バイト)

tagはテーブルに付けれたタグの名前で、印字可能なASCII文字でなければならない。offsetはテーブルのファイルの先頭からのオフセット、lengthはテーブルのサイズを表す。つまり、offsetとlengthを使ってフォント内の各テーブルにアクセスする。

以上のことを踏まえて、OpenTypeフォント内に含まれているテーブルを列挙するプログラムを作成してみた。

Opentype1_2

上はWindows2000に含まれているTahomaフォントのファイル(ファイル名TAHOMA.TTF)を解析した時の実行結果である。Tahomaフォントのファイルには、21個のテーブルが含まれ、例えば、cmapやnameというタグ名の付いたテーブルが含まれていることが分かる。また、各テーブルのファイルの先頭からのオフセットやサイズも分かる。ちなみに、OpenTypeフォントファイルのバイトオーダーはモトローラ形式のビッグエンディアンなので、Intel系のCPUを使うWindows上で読み書きする時には、バイトオーダーをリトルエンディアンに変換する必要がある。

また、Tahomaフォントは欧文用のフォントで、本来は日本語環境のユーザーに馴染み深いMSゴシックなどのフォントの実行結果を載せたいところであるが、MS ゴシックやMS明朝などのフォントはTrueType Collectionというファイルフォーマットになっているので、今回は割愛した。

ということで次回に続く?。

| | コメント (0) | トラックバック (0)