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仕様書の訳じゃないです。根本的に間違っている可能性があります。

2008-07-01

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

1.コンポーネントを動的に追加する

AというSwingコンポーネントにBというコンポーネントを動的に貼り付けたい時は以下のようにします。(参考資料1)

JPanel A = new JPanel();
JButton B = new JButton("ボタンだよ。");
A.revalidate();//一回だけでいい。
//A.setLayout(new FlowLayout());

//ボタンを押すと呼び出されるメソッド
private void putB(java.awt.event.MouseEvent evt){
  A.add(B);
}

注意点としてはレイアウトを指定してやることです。特にNetBeansデフォルトのデザイン(フリーデザイン)のままだと何をやっても追加できないのでフローレイアウトやボックスレイアウトにしてやる必要があります。

2.符号なし値の読み込み

FontBox(参考資料2)のソースより抜粋

//TTFDataStream.java
//FIXEDの読み込み
    public float read32Fixed() throws IOException
    {
        float retval = 0;
        retval = readSignedShort();
        retval += (readUnsignedShort()/65536);
        return retval;
    }
    public abstract short readSignedShort() throws IOException;

//ULONGの読み込み((readInt、readLongというメソッドが存在しますが、それらの符号なしの読み込みは存在しません。またreadLongは8バイトの読み込みを行います。(参考資料3)))
    public long readUnsignedInt() throws IOException
    {
        long byte1 = read();
        long byte2 = read();
        long byte3 = read();
        long byte4 = read();
        if( byte4 < 0 )
        {
            throw new EOFException();
        }
        return (byte1 << 24) + (byte2 << 16) + (byte3 << 8) + (byte4 << 0);
    }

 FIXEDの読み込みは前16bit(2バイト)を「符号あり」でとり、後ろ16bit(2バイト)を符号なしで其々取っています。後ろの方を65536で割っているのは前に説明したとおり小数点の場所をずらしていることと同義です。つまり、整数部と小数点部を取っていることになります。最後に二つ足し合わせて終わりです。主にバージョンの表示に使われています。

 符号なしの32bit(4バイト)の読み込みはオーソドックスに行われています。すなわち1バイトずつ読み込んでシフトし、合計を出しています。シフトの意味は基数で倍してやることです。例えば10進数で考えると25<<3は25000になります。(参考資料4)

 ここにはオーダーの話が入っていません。TrueTypeフォントは全てMSbです。

3.画面が欠けてしまうのを防ぐ

ツリーを展開するとマウスのところの描画がかけてしまいます。Swingコンポーネント全体にそういった傾向が見られます。そういう時はアクションを起こした後にコンポーネントをrepaintします。

JTree tree = new JTree();
//ツリーをダブルクリックした時に呼び出されるメソッド
private void (){
 //操作
  tree.repaint();
}

4.次回

  • cmapのformat4など

5.参考資料

  1. .setLayout(new FlowLayout());
  2. SourceForge.net: FontBox
  3. DataInputStream (Java 2 Platform SE 5.0)
  4. Javaの道:演算子(4.シフト演算子)

2008-06-29

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

1.ツールの作成

JTreeを使うときはDefaultMutableTreeNodeを使う。NetBeansコンポーネントを追加したら、右クリックからコードのカスタマイズ。バインドとか良く分からない。ダブルクリックのイベントを登録するには参考資料2のようにする。まずクリックのイベントを登録し、条件を判断してアクションを起こす。

            ___     ________
     ____,../     \   | |          |
    ノ   /           \ | |          |
  /   /               | |          |
 |     |::..           ...::::| |          |
 ヽ    -一ー_~、⌒)^),-、;;;;;:::/| |_________|
  ヽ ____,ノγ⌒ヽ)ニニ- ̄   | |  |

2.サンプル

f:id:project_the_tower2:20080630004325p:image

Fontをメニューバーから読み込むとTreeに展開します。メンバーをダブルクリックすると右側に値が出ます。

URL: http://www28095u.sakura.ne.jp/fonttooljava/tool02.zip


目標:

  1. im.UnsignedIntがないので実装する。じゃないと負数になる。
  2. Cmapの実装。中途半端でやめてしまった。

3.参考資料

  1. JTreeクラス - Swing
  2. JTree - Java Swing

2008-06-25

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

1.ツールの作成

フォントフォーマットを解析するツールを作ることにしました。


          ____        ) GUIのプログラムなんて余裕だお
        /⌒  ⌒\      ) 
      /( ●)  (●) \    )/⌒Y⌒Y⌒Y⌒Y⌒Y⌒Y⌒Y⌒Y⌒Y⌒Y⌒Y⌒Y⌒Y丶
     / ::::::⌒(__人__)⌒::::: \
    |      |r┬-|     |
     \       `ー'´     /
     ノ            \
   /´               ヽ                 カ
  |    l   l||l 从人 l||l      l||l 从人 l||l   カ    タ
  ヽ    -一''''''"~~``'ー--、   -一'''''''ー-、.     タ
   ヽ ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒))
      ┌┬┬┐┌┬┬┬┐┌┬┬┬┐┌┬┬┬┐
   ,. - ''"| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ρ ̄`l
    ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ノ ̄ ̄



3日後・・・
       ____
     /      \
   /  _ノ  ヽ、_  \    自分にはむりだお。
  /  o゚⌒   ⌒゚o  \   
  |     (__人__)    |   
  \     ` ⌒´     /    

2.完成

それでも何とか完成。

f:id:project_the_tower2:20080625013606p:image

    i^,\ _,,_ /^l
    lノ  / i|l \ヾノ  こんなウンコみたいなプログラムに8時間もかかってしまったニャ。
   シ " ( ●)  (● )ミ  ソースコードは恥ずかしすぎて公開できないニャ・・・
  メ  = ⌒(__人__)⌒=ヽ
 彡           ;ミ
   ヾ         ン
   /     ""  |

zip:http://www28095u.sakura.ne.jp/fonttooljava/fonttool.zip

3.参考資料

  1. みんなのプログラミング無料講座 Java_Swing Lesson6
  2. InputStream (Java 2 プラットフォーム SE v1.4.0)
  3. SourceForge.net: FontBox
  4. ja: NetBeans 日本語サイト

2008-06-11

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

1.最終目的

a.フォントファイルからデータを読み出して表示する。(フォントビューワー?)

ソースもあるし、がんばればできそう。

b.フォントを結合する。(合成フォント)

かぶさるグリフのどちらかを除去して、必要な値を再計算すればできる・・・かもしれない。

c.グリフデータをダイナミックに結合する(DynamicFont)

SVGFontがいつまでたっても普及しないので、必要なグリフを必要な分だけサーバーから送り、クライアント側でフォントを組み立てるということを既存のTTFでやってみる。頻繁にテーブルへのデータの追加が予想されるので途中挿入可能な可変長の配列にデータを落とし、必要に応じてストリームに変換するということを考えている。

2.ファイルの入出力

 ファイルの入出力はストリームという概念を使う。ストリームはクラスでインスタンス化して使う。ストリームは大きく分けて入力と出力、その中でバイナリファイルを読み込むかテキストファイルを読み込むかで4つに分かれる。

 基本的にストリームはファイルの先頭から末尾までの[流れ]なので途中から読み込んだり、現在位置を戻したりできない。そのようなことをしたい時は[RandomAccessFile]を利用する。またストリームの途中にデータを挿入したりすることはできない。上書きはできる。(参考資料1参照)

3.パイプ

入力と出力をつなぐ役割を持つ。中間ファイルを作成しなくても良いという点で容量と速度においてメリットがある。(参考資料2参照)

4.ファイルをバッファに格納する

ByteArrayOutputStreamを使う。大き目のファイルでも大丈夫。

5.可変長の配列

ストリームの途中に要素の追加はできないので、一度ファイルから可変長の配列(LinkedList)にデータを入れる。

6.固定小数点数(fixed-point number)

小数点部が16bitの固定小数点数を取るには以下のようにする。参考資料3のサンプルは非常に分かりやすい。

  1. in.readIntで数値を取る。
  2. Floatにキャストする。
  3. .小数点をずらす。(シフト演算子を使って(>> 16)とするのはFloatに対してはできない。Intに対して行うと小数点以下が消えてしまう。方法としては2^16=65536で割ってやる。)
((float)in.readInt())/65536;

なにか間違っている気がする。

7.参考資料

  1. Java 入門 | I/O Streams
  2. Javaの道:入出力(3.パイプ入出力)
  3. 固定小数点数