JIS X0213:2004フォントを読む

id:korakurider/20071018/p1 では、SqueakIPAフォントの JISX0213:2004準拠のものを開こうとして、現在のSqueakでは実装されていないデータ構造であることがわかりました。Windows XPでは 従来のJIS X0208のフォントを使えばよかったわけですが、Windows Vistaの場合はMS明朝なども含めて標準のフォントが JISX0213:2004となっており、JIS X0208のフォントは改めて追加インストールしなければなりません。つまりVista上では現状のSqueakTrueTypeフォントを使うことが敷居が高いということです。
http://www.apple.com/jp/macosx/features/japanese.html によれば、新しいMacXではヒラギノを JIS X0208と JISX0213:2004両方使い分けられるようなので、こんな問題は起きなさそうですね)

だいそれたことはできないと思いますが、とりあえずIPAフォントSqueakで読み込めないか調べていきます。
まずは、ひっかかっているCMAPテーブルを読んでみます。

フォント構造の仕様書

TrueTypeフォントの技術情報については、Microsoft Typography groupのサイトが詳しいです。 http://www.microsoft.com/typography/default.mspx
(正確には OpenTypeの)フォント構造の仕様は、http://www.microsoft.com/typography/otspec/default.htm に公開されています。今回問題になったCMAPテーブルの構造は、http://www.microsoft.com/typography/otspec/cmap.htm にあります。

ツール

本当に正しく読み込めているか確認するのに、フォント構造をダンプするツールが必要です。前述の MSのサイトに、いくつかツールが公開されていますが、その中の ttfdump というものが使えそうです。http://www.microsoft.com/typography/tools/tools.aspx
たとえば、以下を実行すると CMAPテーブルの内容を解析して表示してくれます。

ttfdump c:\windows\fonts\ipam.ttf -tcmap

CMAPの解読コードを書く

エラーが起きる過程をデバッガで追いかけると、TTCFontReader>>decodeCMapFmtTable: で、フォーマット12(Segmented Coverage 32bit)という形式のテーブルを処理できないために起こっていることがわかりました。フォント構造の仕様書をもとに読み込むコードを書いて見ます。

!TTCFontReader methodsFor: 'as yet unclassified' stamp: 'KR 10/19/2007 20:25'!
decodeCmapFmtTable: entry
	| cmapFmt length cmap firstCode entryCount segCount segments offset code |
	cmapFmt _ entry nextUShort.
	((cmapFmt = 0) or: [cmapFmt = 4] or: [cmapFmt = 6]) 
		ifTrue: [
			length _ entry nextUShort.
			entry skip: 2. "skip version"]
		ifFalse: [
			entry skip: 2. "reserved"
			length _ entry nextULong.
			entry skip: 4. "language"
		].
		

	cmapFmt = 0 ifTrue: "byte encoded table"
                       (既存コードと同じなので中略)
	cmapFmt = 4 ifTrue: "segment mapping to deltavalues"
                       (既存コードと同じなので中略)
	cmapFmt = 6 ifTrue: "trimmed table"
                       (既存コードと同じなので中略)
	cmapFmt = 12 ifTrue: "segmented coverage"
		[|groupCount groups |
			groupCount _ entry nextULong.
			groups _ Array new: groupCount.
			groups _ (1 to: groupCount) collect: [:e | Array new: 3].
			1 to: groupCount do: [:i |
				(groups at: i) at: 1 put: entry nextULong. "StartCharCode"
				(groups at: i) at: 2 put: entry nextULong.  "endCharCode"
				(groups at: i) at: 3 put: entry nextULong.  "startGlyphID"
			].
			groups inspect.
			self halt. "とりあえず読み込むところまで"
		].
	^ nil! !

これで読み込んだ結果は、さきほどのttfdumpで表示した内容と一致することが確認できました。

Squeakでサポートするには?

JIS X0213:2004ということは、0xFFFF までのUCS-2の枠を超えて、UCS-4の文字コードを使うことになります。
実際、ttfdumpでIPAフォントのCMAPを見てみると、こんな感じです(抜粋)。

                      Char 0000FF9B -> Index 7444
                      Char 0000FF9C -> Index 7445
                      Char 0000FF9D -> Index 7446
                      Char 0000FF9E -> Index 7447
                      Char 0000FF9F -> Index 7448
                9859. Char 0000FFE0 -> Index 82
                      Char 0000FFE1 -> Index 83
                9860. Char 0000FFE2 -> Index 120
                9861. Char 0000FFE3 -> Index 18
                9862. Char 0000FFE4 -> Index 6965
                9863. Char 0000FFE5 -> Index 80
                9864. Char 0002000B -> Index 8202
                9865. Char 00020089 -> Index 9460
                9866. Char 000200A2 -> Index 9470
                9867. Char 000200A4 -> Index 9473
                9868. Char 000201A2 -> Index 9481
                9869. Char 00020213 -> Index 9497
                9870. Char 0002032B -> Index 9539
                9871. Char 00020371 -> Index 9548
                9872. Char 00020381 -> Index 9546
                          (中略)
               10161. Char 0002A6B2 -> Index 11895

Squeakのフォント関係のオブジェクト構造を見てみると、UCS-2の16bitのコードをそのままテーブルのインデックスに使っていることがわかります。ですからまずUCS-4をサポートできるようにしなくてはなりません。ただし、上のダンプでもわかるように、UTF32のサイズのテーブルにしてしまうと、中身がすかすかになり非常にメモリ効率が悪いので、スパースなテーブル構造を考えないといけません。
ただ、仮にデータ構造ができたとしても、http://ja.wikipedia.org/wiki/JIS_X_0213 にも書かれているように、グリフ置換という特別な機能を実装しないと文字を表示できません。
freetype2のプラグインを作ればここまでできるかもしれませんが、あまりにも敷居が高すぎるので、当面現状のTrueType関係のオブジェクト構造はあまり変更しなくてよいよう、フォーマット12のcmapからUCS-2の範囲に限定したグリフを取り扱えるようにすることを目標としたいと思います。