PicoPDF

OpenTypeフォント抽出 🔗

PDFにフォントを埋め込むためにOpenTypeから使用するグリフのみを抽出してPDFに埋め込む。
フォーマットの仕様はMicrosoft、Appleの仕様を参照すること。
ここではPDFにフォントを埋め込むための抽出処理について解説する。

OpenType 🔗

OpenTypeフォントはTrueType(TTF)形式とCompact Font Format(CFF)形式のデータを格納できるようにしたものである。
CFF形式のデータはsfntフォーマットのCFFテーブルとして格納される。

TTF形式とCFF形式では次の違いがある。

相違点 TrueType(TTF)形式 Compact Font Format(CFF)形式
SFNTバージョン 0x00010000 または 0x74727565(true) 0x4F54544F(OTTO)
共通テーブル cmap、head、hhea、hmtx、maxp、name、OS/2、post cmap、head、hhea、hmtx、maxp、name、post
グリフ格納テーブル glyf、loca CFF または CFF2
拡張子 .TTF、.OTF .OTF
アウトライン 2次ベジェ曲線 PostScriptベース、3次ベジェ曲線

Microsoftの仕様ではTTFのSFNTバージョンは0x00010000のみだが、Appleの仕様では0x74727565がある。
Microsoftの仕様ではOS/2が必須テーブルとされているが、Appleの仕様ではオプションとされている。
Macにインストールされているフォントでは0x74727565やOS/2テーブル無しのTTFフォントがある。

OpenTypeとTrueTypeの比較がされることがあるが、一般にOpenTypeとされているのはCFFフォントであることが多い。

1つのフォントファイルに複数のフォントファイルを格納できるフォントコレクション(ヘッダ ttcf、拡張子 .TTC)がある。

CFFテーブル 🔗

CFFフォントではCFFテーブルにCompact Font Format形式のデータが格納されている。

フォントの構造 🔗

コンピュータでは文字はUnicodeなどのコードポイント(文字コード)で現わされる。
フォントファイル内では文字コードではなくGlyph ID(GID)で格納される。
GIDは0から連番で始まり、GIDの0は.notdefというフォントファイル内に文字が無いことを現わす。
これは同じ文字コードでも異なるグリフが割り当たることがあるためである。
文字コードとGIDを変換するのがcmapである。

---
title: 文字とGIDの関係
---
graph LR;
    subgraph char ["文字"]
      A;
      B;
      C;
      あ;
    end
    
    subgraph code ["文字コード"]
      U+0041;
      U+0042;
      U+0043;
      U+3042;
    end
    
    subgraph gid ["GID"]
      0["0 (.notdef)"];
      1;
      2;
      3;
      4;
    end
    
    A  --encoding--> U+0041;
    B  --encoding--> U+0042;
    C  --encoding--> U+0043;
    あ --encoding--> U+3042;
    
    U+0041 --cmap--> 1;
    U+0042 --cmap--> 2;
    U+0043 --cmap--> 3;
    U+3042 --cmap--> 4;

フォント抽出 🔗

フォントファイルには多数のグリフデータが格納されている。
日本語のフォントファイルであれば数万種が格納されている。
PDFにフォントファイルを埋め込む際に使用しない文字のグリフデータまで含めるとファイルサイズが肥大化する。
そのためPDFファイル内で使用しているグリフデータのみを抽出したフォントファイルを作成して埋め込みたい。

---
title: 「a」と「あ」のグリフデータ抽出
---
graph LR;
    subgraph old_gid ["元のGID"]
      old0["0 (.notdef)"];
      old1["1 (A)"];
      old2["2 (B)"];
      old3["3 (C)"];
      old4["4 (あ)"];
    end
    
    subgraph new_gid ["新しいGID"]
      new0["0 (.notdef)"];
      new1["1 (A)"];
      new2["2 (あ)"];
    end
    
    old0 --抽出--> new0;
    old1 --抽出--> new1;
    old4 --抽出--> new2;

TTF、CFFの共通部分抽出 🔗

グリフデータを抽出にあたり、必須テーブルのグリフ数、グリフデータに関するパラメータを変更する必要がある。

テーブル 抽出方法
cmap 抽出したGIDに合わせた変換が必要
head 変換不要
hhea numberOfHMetricsを抽出したグリフ数に設定
hmtx hMetricsを抽出したGIDに合わせた変換が必要
maxp numGlyphsを抽出したグリフ数に設定
name 変換不要
OS/2 変換不要
post 変換不要

cmapの抽出 🔗

PDFでOpenTypeフォントを使用する場合はGIDを指定することになる。
OpenType内のcmapテーブルがあるが、PDFではTo Unicode CMapを利用する。
そのため文字コードからGIDへの変換を行うcmapは重要ではなく、UVSを実装する意味は薄い。
最低限のcmapのみを抽出すればよい。

プラットフォーム 🔗

フォントデータ内には様々なプラットフォームやエンコード向けに複数のcmapが含まれている。
プラットフォームは次の種類があるがPDFに埋め込む場合はUnicodeにのみ対応すれば十分である。

プラットフォーム 名前
0 Unicode
1 Macintosh
2 ISO (非推奨)
3 Windows
4 Custom

プラットフォームUnicodeにおけるエンコーディング 🔗

プラットフォームにUnicodeを選択した場合は次のエンコーディングを指定する必要がある。
PDF埋め込みにおいて、採用すべきは基本多言語面のみであれば3、サロゲートペアを扱う場合は4までである。

プラットフォーム 0:Unicode のエンコーディング:

エンコーディング 名前 補足
0 Unicode 1.0 (非推奨)  
1 Unicode 1.1 (非推奨)  
2 ISO/IEC 10646 (非推奨)  
3 Unicode 2.0 基本多言語面のみ U+0000~U+FFFF
4 Unicode 2.0 U+0000~U+10FFFF
5 Unicode variation sequences Format 14専用
6 Unicode full repertoire Format 13専用

フォーマット 🔗

日本語のフォントファイルであればFormat 12のみ実装すればよい。

サブテーブル 文字コード範囲 格納可能グリフ数
Format 0 U+0000~U+00FF 256個
Format 2 U+0000~U+FFFF 65536個
Format 4 U+0000~U+FFFF 65536個
Format 6 U+0000~U+FFFF(連続したコード) 65536個
Format 8 U+0000~U+10FFFF 65536個
Format 10 32bit(使用されない) 65536個
Format 12 U+0000~U+10FFFF 65536個
Format 13 32bit(多対一) 65536個
Format 14 UVS(SVS+IVS、異字体) 65536個

1つのフォントファイルに格納できるグリフ数は65536個が上限である。
GID 0は.notdefになるため、実際に使用できるグリフ数は上記の格納グリフ数より1個少なくなる。

補足:Windows環境での単体フォントファイル 🔗

PDF埋め込みではなくWindows環境で単体のフォントファイルとして扱うのであればプラットフォーム 3:Windowsを含む必要がある。
最低限エンコーディング 1、Format 4を含まないと有効なフォントファイルとみなされない。
それ以外を実装する意味はないであろう。

プラットフォーム 3:Windows のエンコーディング:

エンコーディング 名前 補足
0 Symbol  
1 Unicode 基本多言語面 Format 4
2 ShiftJIS  
3 PRC  
4 Big5  
5 Wansung  
6 Johab  
7 予約済み  
8 予約済み  
9 予約済み  
10 Unicode full repertoire Format 12