2012. 10. 21

FONTXファイルの使いかた


DOS/V diskette

電子工作でよく使われるようになったFONTX2形式のフォント・ファイルについて、その歴史とファイル・フォーマットについて簡単に解説します。

DOS/V~FONTX~電子工作

DOS/Vのしくみ

DOS/V system

'90年代初頭、日本IBMからDOS/V (IBM DOS J4.0/V)が発売されました。DOS/Vは、英語DOSにいくつかの日本語入出力のためのデバイス・ドライバを追加したもので、日本語表示機能を持たないIBM ATおよび互換機にハードウェアの追加なしに日本語表示を実現することができました。DOS/Vシステムは、右の図のような構成となります。なお、その名の最後に付く"V"はVGAというビデオ・サブシステムを示し、「IBM日本語DOS Version 4.0 for VGA」という意味になります。

ディスプレイ・ドライバ($DISP.SYS)は、ビデオBIOS(INT 10h)を拡張して、アプリケーションの文字出力要求を処理します。文字はビットマップ画面に描画されるため、日本語を含む任意の文字を出力することができます。これにより、「ごく一部」の英語アプリケーションはそのまま日本語を扱えるようになりました。ごく一部というのは、当時のDOSアプリケーションは、ビデオBIOSを介さずVRAMに直接書き込むものが一般的だったためです。それでも僅かな変更で日本語化できるように、仮想VRAMという機能が提供されています。しかし、2バイト文字という根本的な問題もあり、そう簡単にはいきませんでしたが。

日本語表示に必要なフォントは、フォント・ドライバ($FONT.SYS)が管理します。フォント・ドライバは、ロード時にフォント・ファイルをメモリに読み込み、システムBIOS(INT 15h)を介してフォント・サーバとして機能します。また、漢字ROMのあるマシン(PS/55など)ではそれを有効利用します。200Kバイト程度になるフォント・データはEMB領域に置かれるため、コンベンショナル・メモリを圧迫することはありませんが、これによりDOS/Vの動作条件は80286以上のCPUとなっています。

DOS/Vの隆盛とFONTX

さて、当時の日本のビジネスパソコンはNEC PC-9800シリーズの独占状態で、これといって目新しいことが無かったため、物好きなハッカーたちがこれを見逃すはずがありません。DOS/VはAPI仕様が広く公開されていたこともあり、機能を拡張された互換ドライバが相次いでリリースされることになります。雨後の筍のごとくDOS/V誌が創刊されたのもこの頃です。

たとえば、ディスプレイ・ドライバでは、SVGAアダプタに対応してVGAを越えるサイズの画面で広大なテキスト表示を実現したり、グラフィック・アクセラレータを駆使した表示の高速化などが行われました。逆にポケットPC用にCGAに対応したものもありました。後に、日本IBMもこれに追随して純正のドライバを同様に拡張し、その際/Vの意味をVGAからVariableへと再定義しています。ディスプレイ・ドライバについては、私も自作して楽しんでいました(まだVectorのライブラリに残っています)。

フォント・ドライバでは、複数のフォントをロードして動的に切り替えられたり、ロード先にEMS領域や機種固有のメモリ領域を選択できるなどの拡張が行われ、柔軟な運用が可能になり、動作環境も広がっています。これはディスプレイ・ドライバほど多くはなく、IBM純正以外のドライバとしてはFONTXが主流となり、多くのフォント・ドライバもフォント・ファイルとしてFONTX形式を利用するようになりました。このため、DOS/V用として出回るフォント・ファイルもほぼFONTX形式に統一されています。

死の淵から蘇るFONTX

間もなく時代はWindowsへと移り、DOS/Vは終焉を迎えることになります。そして、膨大なDOSのソフトウェア資産とともにFONTX形式のフォントも忘れ去られた存在となりました。ところが、その後何年か経ち'00年代も半ばになると、電子工作界において高性能なマイコンや安価なグラフィック・ディスプレイの利用が普及してきました。それに伴い、その日本語表示のために再びFONTXファイルが利用されるようになったのです。FONTX形式はファイル・フォーマットがとても単純で、何よりフリーのフォントが多く出回っていたので、最も手軽に利用できるビットマップ・フォントといえます。このように、電子工作というマイナーな世界ではありますが、FONTX形式は再び日の目を見ることになります。

FONTXフォントの使いかた

FONTXファイルの構造

FONTXファイルは、BDFファイルなどのテキスト形式とは違い、次の図に示すようにバイナリ形式となっています。FONTXファイルには半角フォントと全角フォントの2種類があり、それらは文字コード・フラグによって識別されます。半角フォントファイルには8ビット・コードの256個のフォントイメージが格納されます。全角フォント・ファイルは、16ビット・コード(シフトJIS)ですが、65536個分の格納スペースを固定長フィールドとして確保するのは効率が悪いので、図に示すように有効な文字コードの範囲を示すコード・ブロック・テーブルが設けられ、必要なものだけ格納されます。各ブロックは文字コードの小さい順にテーブルに記述され、フォントイメージもそれにしたがって順に詰めて格納されます。

半角FONTXファイルの構造
OffsetSizeフィールド内容
06ファイル・シグネチャ("FONTX2")
68フォント名
141フォント幅 WF(ドット)
151フォント高さ HF(ドット)
161文字コード・フラグ(0:ANK)
17※1フォントイメージ
(※1:フォントサイズ×256)
全角FONTXファイルの構造
OffsetSizeフィールド内容
06ファイル・シグネチャ("FONTX2")
68フォント名
141フォント幅 WF (ドット)
151フォント高さ HF (ドット)
161文字コード・フラグ (1:シフトJIS)
171コード・ブロック数 NB
182ブロック1開始コードコード・ブロック・テーブル
(リトル・エンディアン)
202ブロック1終了コード
14+4*NB2ブロックNB開始コード
16+4*NB2ブロックNB終了コード
18+4*NB※2フォントイメージ
(※2:フォントサイズ×各ブロックのコード数の総和)

フォントイメージは、次の図に示すようにデータの各ビットが文字のドットに対応します。フォントの幅が8の倍数に一致しないときは、左詰めで格納されます。

Font example

フォント・イメージのサイズは、1文字あたり(WF + 7) / 8 * HF [バイト] となります。それぞれの文字コードに対応するフォントイメージのファイル先頭からの位置は、次のようになります。

FONTXファイルの編集には専用ツールが必要になりますが、なぜかWindows用のフォント・エディタが存在しません。Windows時代にはFONTX自体が不要になってしまったため当然と言えばそうですが、FONTXを使う以上は無いと困るのでFONTXエディタを作ってみました。

コーディング例

例として下のリストに、メモリ上に置かれたFONTXファイルから指定された文字コードのフォントを得る関数を示します。引数にはFONTXデータと文字コードを指定し、指定された文字コードに対応するフォントイメージを指すポインタを返します。文字コードが無効なときはヌルポインタを返します。

const uint8_t* get_font (   /* Returns pointer to the font image (NULL:invalid code) */
    const uint8_t* font,    /* Pointer to the FONTX file stored on the memory */
    uint16_t code           /* Character code */
)
{
    unsigned int nc, bc, sb, eb;
    uint32_t fsz;
    const uint8_t *cblk;


    fsz = (font[14] + 7) / 8 * font[15];  /* Get font size */

    if (font[16] == 0) {  /* Single byte code font */
        if (code < 0x100) return &font[17 + code * fsz];
    } else {              /* Double byte code font */
        cblk = &font[18]; nc = 0;  /* Code block table */
        bc = font[17];
        while (bc--) {
            sb = cblk[0] + cblk[1] * 0x100;  /* Get range of the code block */
            eb = cblk[2] + cblk[3] * 0x100;
            if (code >= sb && code <= eb) {  /* Check if in the code block */
                nc += code - sb;             /* Number of codes from top of the block */
                return &font[18 + 4 * font[17] + nc * fsz];
            }
            nc += eb - sb + 1;     /* Number of codes in the previous blocks */
            cblk += 4;             /* Next code block */
        }
    }

    return 0;   /* Invalid code */
}