Hatena::ブログ(Diary)

Webと文字

よければ、はてブしてください。( ´∀`) George.Nagaoka@gmail.com

JavaScript IME:海外からブラウザで日本語を変換 新URL 旧URL:
多言語入力ブックマークレット:ブラウザでロシア語、中国語、アラビア語・・・
【軍曹が】携帯電話開発の現状【語る】をAA化した
AAのデータベース
趣味のページ

2008-07-04

TrueTypeフォントのフォーマットを調べる その8

1.cmap - Character To Glyph Index Mapping Table

*1

 Cmapテーブルは文字コード(文字集合)と字形を対応させるテーブルです。文字コードとは文字に対する一意のIDのことを云います。文字の形(字形、グリフとも呼ばれる)にどのようなIDを振るかを決めたものを文字集合といいます。文字集合は代表的なものにUnicodeがありますが、日本ではJIS中国台湾などではGBやBig5、韓国朝鮮などのハングル圏ではJohabと各々の文字を使う共同体の中で発展してきた歴史があります。ここで問題になるのは、同じ字体を表すIDが文字コードによって違うということです。例えば以下の例を見てください。下のIDはどちらも「文」を示しています。

文字コードID
Shift-JIS95B6
Unicode6587

このように同じグリフであっても文字コードによって違うIDが割り振られています。これらはOSなどのプラットフォームによる影響も受けます。もし、素直に文字コードとグリフを結びつけるとOS文字コードの数だけフォントが必要になってしまいます。

 そこでCmapです。Cmapは文字コードプラットフォーム別に、文字コードIDとフォント内に格納されているグリフIDの対応テーブルを複数持っています。(グリフはフォント内で配列のように存在しており、グリフIDは配列インデックスに当ります。)これにより一つのフォントで複数のプラットフォーム文字コードに対応させることができます。

 MicrosoftのTTFの仕様書ではプラットフォームは以下のように定義されています。

IDPlatformSpecific encoding
0Apple Unicodenone
1MacintoshScript manager code
2ISOISO encoding
3MicrosoftMicrosoft encoding

文字コードは以下のように定義されています。

Encoding IDDescription説明+
0Symbol記号
1Unicode世界標準となりつつある
2ShiftJIS主に日本
3Big5台湾
4PRC
5Wansung中国
6Johab韓国

また規則や推奨として以下のことが上げられます。

2.フォーマット

TypeDescription説明
USHORTTable version number (0).テーブルバージョン番号
USHORTNumber of encoding tables, n.エンコーディングテーブルの数

次に以下のエントリを含む配列[n]が続きます。プラットフォームIDやエンコーディングIDは1で説明したものを使用します。

TypeDescription
USHORTPlatform ID.
USHORTPlatform-specific encoding ID.
ULONGサブテーブルのオフセット。ファイル全体のオフセットではないことに注意。

サブテーブルの実体は文字コードとグリフIDのマッピングです。TTFではエンコーディングに応じたマッピングを行えるようにいくつかのフォーマットを用意しています。フォーマットは以下のように定義されています。

formatdescription
Format 0Byte encoding table
Format 2High-byte mapping through table
Format 4Segment mapping to delta values
Format 6Trimmed table mapping

注意すべきことはサブテーブルエントリからはサブテーブルがどのようなフォーマットを利用しているのか分からないところです。フォーマットを判別するにはサブテーブルの始めの4バイトがすべてフォーマット番号を表すことを利用して判別するしかありません。

3.format0

アップルの標準変換テーブルです。単純に一対一のマッピングをします。注意として256個のグリフしか扱えません。使う機会も見る機会も余りありません。

TypeNameDescription
USHORTformatFormat number is set to 0.
USHORTlengthThis is the length in bytes of the subtable.
USHORTversionVersion number (starts at 0).
BYTEglyphIdArray[256]An array that maps character codes to glyph index values.

FontBoxでは以下のように実装されています。

//CMAPEncodingEntry.java
byte[] glyphMapping = data.read( 256 );
glyphIdToCharacterCode = new int[256];
for( int i=0;i<glyphMapping.length; i++ ){
  glyphIdToCharacterCode[i]=(glyphMapping[i]+256)%256;
}

256で割った余りを出しているところがよく分かりません。

4.format2

日本語、中国語韓国語文字コードに便利な1バイトと2バイトが混じったエンコーディングです。

TypeNameDescription
USHORTformatFormat number is set to 2.
USHORTlengthLength in bytes.
USHORTversionVersion number (starts at 0)
USHORTsubHeaderKeys[256]Array that maps high bytes to subHeaders: value is subHeader index* 8.
4 words structsubHeaders[ ]Variable-length array of subHeader structures.
4 words-structsubHeaders[ ]
USHORTglyphIndexArray[ ]Variable-length array containing subarrays used for mapping the low byte of 2-byte characters.

subHeadersの構造は以下の様になっています。

TypeNameDescription
USHORTfirstCodeFirst valid low byte for this subHeader.
USHORTentryCountNumber of valid low bytes for this subHeader.
SHORTidDeltaSee text below.
USHORTidRangeOffsetSee text below.

FontBoxのソースでは未実装になっていたので以下にshowttfのソースを示します。

	    int max_sub_head_key = 0, cnt, last;
	    struct subhead { uint16 first, cnt, delta, rangeoff; } *subheads;

	    for ( i=0; i<256; ++i ) {
		table[i] = getushort(ttf)/8;	/* Sub-header keys */
		if ( table[i]>max_sub_head_key )
		    max_sub_head_key = table[i];	/* The entry is a byte pointer, I want a pointer in units of struct subheader */
	    }
	    subheads = malloc((max_sub_head_key+1)*sizeof(struct subhead));
	    for ( i=0; i<=max_sub_head_key; ++i ) {
		subheads[i].first = getushort(ttf);
		subheads[i].cnt = getushort(ttf);
		subheads[i].delta = getushort(ttf);
		subheads[i].rangeoff = (getushort(ttf)-
				(max_sub_head_key-i)*sizeof(struct subhead)-
				sizeof(short))/sizeof(short);
	    }
	    cnt = (len-(ftell(ttf)-(info->encoding_start+encoff)))/sizeof(short);
	    /* The count is the number of glyph indexes to read. it is the */
	    /*  length of the entire subtable minus that bit we've read so far */
	    glyphs = malloc(cnt*sizeof(short));
	    for ( i=0; i<cnt; ++i )
		glyphs[i] = getushort(ttf);
	    last = -1;
	    for ( i=0; i<256; ++i ) {
		if ( table[i]==0 ) {
		    /* Special case, single byte encoding entry, look i up in */
		    /*  subhead */
		    /* In the one example I've got of this encoding (wcl-02.ttf) the chars */
		    /* 0xfd, 0xfe, 0xff are said to exist but there is no mapping */
		    /* for them. */
		    if ( last!=-1 )
			index = 0;	/* the subhead says there are 256 entries, but in fact there are only 193, so attempting to find these guys should give an error */
		    else if ( i<subheads[0].first || i>=subheads[0].first+subheads[0].cnt ||
			    subheads[0].rangeoff+(i-subheads[0].first)>=cnt )
			index = 0;
		    else if ( (index = glyphs[subheads[0].rangeoff+(i-subheads[0].first)])!= 0 )
			index = (uint32) (index+subheads[0].delta);
		    /* I assume the single byte codes are just ascii or latin1*/
		    if ( index!=0 && index<info->glyph_cnt ) {
			if ( info->glyph_unicode[index]==0 )
			    info->glyph_unicode[index] = i;
			else
			    info->dups = makedup(index,i,info->dups);
		    }
		} else {
		    int k = table[i];
		    for ( j=0; j<subheads[k].cnt; ++j ) {
			int enc;
			if ( subheads[k].rangeoff+j>=cnt )
			    index = 0;
			else if ( (index = glyphs[subheads[k].rangeoff+j])!= 0 )
			    index = (uint16) (index+subheads[k].delta);
			if ( index!=0 && index<info->glyph_cnt ) {
			    enc = (i<<8)|(j+subheads[k].first);
			    if ( info->glyph_unicode[index]==0 )
				info->glyph_unicode[index] = enc;
			    else
				info->dups = makedup(index,enc,info->dups);
			}
		    }
		    if ( last==-1 ) last = i;
		}
	    }
	    free(subheads);
	    free(glyphs);

正直よく分からないので説明は次回。

5.

  1. FontBox:「その7」の記事の参考資料
  2. showttf:「その3」の記事の参考資料
  3. vanillaの日記: OpenTypeフォントの続き(6)・・・cmapテーブル

*1仕様書の訳じゃないです。根本的に間違っている可能性があります。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証