フォントファイル 完全理解ガイド

フォントの内部構造を視覚的に理解し、 自作プログラムでフォントを作成できるようになる

A
a
📐

視覚的理解

複雑な構造を図解で分かりやすく

🔧

実践的ツール

インタラクティブなエディタ付き

💻

コード例

実際に動くサンプルコード

はじめに

このサイトでは、フォントファイルの内部構造を徹底的に解説し、最終的には自分でプログラムを書いてフォントを作成できるレベルまで理解を深めることを目標としています。

フォントファイル ヘッダー情報 グリフデータ メトリクス情報 A B C

フォントファイル形式の基礎

主要なフォント形式

TrueType (.ttf)

TrueType 2次ベジェ曲線
  • Apple & Microsoft共同開発
  • 2次ベジェ曲線で輪郭を定義
  • 比較的シンプルな構造

OpenType (.otf)

OpenType 3次ベジェ曲線
  • TrueTypeの拡張版
  • PostScript輪郭も使用可能
  • 高度なタイポグラフィ機能

WOFF/WOFF2

Web Font 圧縮フォーマット 圧縮
  • Web用に最適化
  • TTF/OTFを圧縮
  • メタデータ付き

フォントファイルの基本構造

ファイルヘッダー テーブルディレクトリ cmap glyf hmtx head cmap: 文字コードとグリフIDのマッピング glyf: グリフ(文字形状)データ hmtx: 水平メトリクス情報 head: フォント全体のヘッダー情報

バイナリ構造の例

0x0000: 00 01 00 00  // sfnt version (TrueType)
0x0004: 00 0D        // テーブル数: 13
0x0006: 00 80        // searchRange
0x0008: 00 03        // entrySelector
0x000A: 00 60        // rangeShift

0x000C: 63 6D 61 70  // "cmap" テーブルタグ
0x0010: 00 00 12 34  // checksum
0x0014: 00 00 01 00  // offset
0x0018: 00 00 02 56  // length
                    

TrueTypeフォント構造の詳細

グリフ輪郭の定義方法

0 1 2 3 4 TrueTypeグリフの構造 赤い点: On-Curve Points (輪郭上の点)

ポイントデータ構造

struct GlyphPoint {
    int16_t x;        // X座標
    int16_t y;        // Y座標
    uint8_t flags;    // フラグ(On/Off curve等)
};

// フラグのビット定義
#define ON_CURVE_POINT    0x01
#define X_SHORT_VECTOR    0x02
#define Y_SHORT_VECTOR    0x04
#define REPEAT_FLAG       0x08
#define X_IS_SAME_OR_POS  0x10
#define Y_IS_SAME_OR_POS  0x20
                        

TrueTypeテーブル構造

必須テーブル

テーブル名 用途 構造
cmap 文字コード→グリフIDマッピング Platform ID, Encoding ID, サブテーブル
glyf グリフデータ 輪郭データ、複合グリフ情報
head フォントヘッダー バージョン、作成日、バウンディングボックス
hhea 水平ヘッダー アセンダー、ディセンダー、行間
hmtx 水平メトリクス 文字幅、左サイドベアリング
loca インデックス グリフデータのオフセット
maxp 最大値プロファイル グリフ数、ポイント数の最大値
name 名前テーブル フォント名、著作権情報
post PostScript情報 PostScript名、メトリクス

2次ベジェ曲線の仕組み

// 2次ベジェ曲線の計算
function quadraticBezier(p0, p1, p2, t) {
    // B(t) = (1-t)²P₀ + 2(1-t)tP₁ + t²P₂
    const x = Math.pow(1-t, 2) * p0.x + 
              2 * (1-t) * t * p1.x + 
              Math.pow(t, 2) * p2.x;
    
    const y = Math.pow(1-t, 2) * p0.y + 
              2 * (1-t) * t * p1.y + 
              Math.pow(t, 2) * p2.y;
    
    return {x, y};
}
                    

OpenTypeフォント構造の詳細

OpenTypeの拡張機能

GSUB (Glyph Substitution)

ff リガチャ置換

GPOS (Glyph Positioning)

AV AV カーニング調整

OpenTypeレイアウトテーブル

// OpenTypeレイアウトテーブルの構造
struct GSUB_Header {
    uint16_t majorVersion;
    uint16_t minorVersion;
    uint16_t scriptListOffset;
    uint16_t featureListOffset;
    uint16_t lookupListOffset;
    uint32_t featureVariationsOffset;  // version 1.1
};

// フィーチャーテーブル
struct Feature {
    uint16_t featureParams;      // NULL or offset
    uint16_t lookupIndexCount;
    uint16_t lookupListIndices[]; // 可変長配列
};
                    

CFFアウトライン(PostScript)

3次ベジェ曲線(CFF/Type2) P0 P1 P2 P3 赤: アンカーポイント、青: コントロールポイント
// 3次ベジェ曲線の計算
function cubicBezier(p0, p1, p2, p3, t) {
    // B(t) = (1-t)³P₀ + 3(1-t)²tP₁ + 3(1-t)t²P₂ + t³P₃
    const x = Math.pow(1-t, 3) * p0.x + 
              3 * Math.pow(1-t, 2) * t * p1.x + 
              3 * (1-t) * Math.pow(t, 2) * p2.x + 
              Math.pow(t, 3) * p3.x;
    
    const y = Math.pow(1-t, 3) * p0.y + 
              3 * Math.pow(1-t, 2) * t * p1.y + 
              3 * (1-t) * Math.pow(t, 2) * p2.y + 
              Math.pow(t, 3) * p3.y;
    
    return {x, y};
}
                    

グリフデータ構造の詳細

単純グリフ vs 複合グリフ

単純グリフ

基本的な輪郭データ

複合グリフ

component 1 component 2 複数のグリフを組み合わせ

グリフデータのバイナリ構造

// 単純グリフのヘッダー
struct SimpleGlyphHeader {
    int16_t numberOfContours;  // 輪郭数(正の値)
    int16_t xMin;
    int16_t yMin;
    int16_t xMax;
    int16_t yMax;
};

// 複合グリフのヘッダー
struct CompositeGlyphHeader {
    int16_t numberOfContours;  // -1(複合グリフを示す)
    int16_t xMin;
    int16_t yMin;
    int16_t xMax;
    int16_t yMax;
};

// 複合グリフのコンポーネント
struct CompositeComponent {
    uint16_t flags;
    uint16_t glyphIndex;
    // 変換マトリックスまたはオフセット値が続く
};
                

グリフ描画の座標系

X Y Ascender Baseline Descender (0,0) 0 1000 2000 0 1000 2000

ヒンティング情報

TrueTypeインストラクション

// TrueTypeヒンティング命令の例
PUSHB[0]  5      // スタックに5をプッシュ
MDRP[10]         // Move Direct Relative Point
PUSHB[0]  10
MIRP[01]         // Move Indirect Relative Point
IUP[1]           // Interpolate Untouched Points
                    
ピクセルグリッドへの適合 ヒンティング前 ヒンティング後

フォントメトリクスと配置

垂直メトリクス

Line Gap Ascender Cap Height x-Height Baseline Descender Hxpg Ascent Descent

水平メトリクス

A V Advance Width LSB RSB カーニング適用前後 AV AV

メトリクステーブルの構造

// hmtx(水平メトリクス)テーブル
struct HorizontalMetrics {
    uint16_t advanceWidth;    // 文字送り幅
    int16_t  leftSideBearing; // 左サイドベアリング
};

// hhea(水平ヘッダー)テーブル
struct HorizontalHeader {
    Fixed    version;
    FWord    ascent;           // アセンダー
    FWord    descent;          // ディセンダー(負の値)
    FWord    lineGap;          // 行間
    uFWord   advanceWidthMax;  // 最大文字送り幅
    FWord    minLeftSideBearing;
    FWord    minRightSideBearing;
    FWord    xMaxExtent;
    int16_t  caretSlopeRise;
    int16_t  caretSlopeRun;
    int16_t  caretOffset;
    int16_t  reserved[4];
    int16_t  metricDataFormat;
    uint16_t numOfLongHorMetrics;
};
                

インタラクティブ視覚化ツール

フォントファイルビューア

テーブル一覧

テーブル詳細

グリフプレビュー

グリフエディタ

ポイントリスト

ID X Y Type

実践:フォント作成

最小限のTrueTypeフォントを作る

// 最小限のTrueTypeフォント生成(Python)
import struct
import datetime

class MinimalTTF:
    def __init__(self):
        self.tables = {}
        
    def create_head_table(self):
        # headテーブルの作成
        head = struct.pack(
            '>HHHHHQQhhhhhhhhhhh',
            0x00010000,  # version
            0x00010000,  # fontRevision
            0,           # checkSumAdjustment
            0x5F0F3CF5,  # magicNumber
            0x0001,      # flags
            2048,        # unitsPerEm
            0,           # created(後で設定)
            0,           # modified(後で設定)
            0,           # xMin
            0,           # yMin
            1000,        # xMax
            1000,        # yMax
            0,           # macStyle
            7,           # lowestRecPPEM
            2,           # fontDirectionHint
            0,           # indexToLocFormat
            0            # glyphDataFormat
        )
        self.tables['head'] = head
        
    def create_simple_glyph(self):
        # 四角形のグリフを作成
        glyph_data = struct.pack(
            '>hhhhh',
            1,     # numberOfContours
            100,   # xMin
            100,   # yMin
            900,   # xMax
            900    # yMax
        )
        
        # 輪郭終点
        glyph_data += struct.pack('>H', 3)  # endPtsOfContours[0]
        
        # 命令長
        glyph_data += struct.pack('>H', 0)  # instructionLength
        
        # フラグ
        flags = bytes([0x01, 0x01, 0x01, 0x01])  # ON_CURVE_POINT
        glyph_data += flags
        
        # X座標
        x_coords = struct.pack('>hhhh', 100, 900, 900, 100)
        glyph_data += x_coords
        
        # Y座標
        y_coords = struct.pack('>hhhh', 100, 100, 900, 900)
        glyph_data += y_coords
        
        return glyph_data
                    

フォント作成のベストプラクティス

1. 座標系の理解

  • フォントデザインの単位(通常2048または1000)
  • ベースライン基準の座標系
  • 整数座標への丸め処理

2. アウトラインの最適化

  • 不要なポイントの削除
  • 滑らかな曲線の生成
  • 極値点の配置

3. メトリクスの設定

  • 適切な文字幅の設定
  • ベースラインの統一
  • カーニングペアの調整

フォント検証ツール

作成したフォントの検証

// フォント検証関数
function validateFont(fontData) {
    const errors = [];
    const warnings = [];
    
    // ヘッダー検証
    if (fontData.head.unitsPerEm < 16 || fontData.head.unitsPerEm > 16384) {
        errors.push('Invalid unitsPerEm value');
    }
    
    // 必須テーブルの確認
    const requiredTables = ['cmap', 'head', 'hhea', 'hmtx', 
                            'maxp', 'name', 'OS/2', 'post'];
    
    requiredTables.forEach(table => {
        if (!fontData.tables[table]) {
            errors.push(`Missing required table: ${table}`);
        }
    });
    
    // グリフ検証
    for (let i = 0; i < fontData.numGlyphs; i++) {
        const glyph = fontData.glyphs[i];
        if (glyph.numberOfContours < -1) {
            errors.push(`Invalid contour count in glyph ${i}`);
        }
    }
    
    return {errors, warnings};
}
                    

次のステップ

  • 複雑なグリフ形状の作成
  • OpenType機能の実装(リガチャ、カーニング)
  • 可変フォント(Variable Fonts)の作成
  • ヒンティングの最適化
  • 多言語対応(Unicode範囲の拡張)