Type 0 フォント (CID フォント)

Type 0 フォント
Type 0 フォントは、複数のフォントを子孫に持つフォントです。
PDF の場合は、CID フォント (CIDFontType0CIDFontType2 のサブタイプのフォント) を子孫に持ちます。

Type 0 フォントは、日本語など、多数のグリフが含まれる OpenType/TrueType フォントなどで使います。

※ PDF 1.6 からは、OpenType フォントをまるごと埋め込んで使用することができます。
※ OpenType フォントの CFF テーブルだけを埋め込むなら、PDF 1.3 からでも使えます。
※ CMap は別途、PostScript で記述して定義する必要があります。

サブセットフォント(フォントから、必要なグリフだけを抽出したもの)で埋め込む場合は、フォント内の必要なグリフとテーブルの情報を抜き出した上で、フォントファイルを再構築する必要があるため、フォントフォーマットの知識が必要になります。

こちらのサイトでは、OpenType/TrueType のフォーマット仕様も解説していますので、フォントフォーマットについては、そちらを参考にしてください。
CID について
CID は「Character IDentifier」の略で、グリフの各文字に割り振られた、10進数の識別番号のことです。

CID は、Adobe によって、各言語ごとに文字コレクションとして規定されており、
2019年時点での日本語の最新版は Adobe-Japan1-7 となっています。

この最新版では、2万3060グリフが定義されています。
Type 0 フォント辞書
まずは、ルートとなる Type 0 のフォント辞書を定義する必要があります。
(単一のフォントであっても、必ず Type0 と CIDFontType0/CIDFontType2 の2つの辞書の定義が必要になります)

Typename(必須) オブジェクトのタイプ。常に /Font
Subtypename(必須) フォントのサブタイプ。
Type 0 フォントの場合は、/Type0
BaseFontname(必須) フォントの PostScript 名。

子孫が CIDFontType0 (OpenType/CFF) の場合は、「CIDFont 辞書の BaseFont 名」+ '-' + 「この辞書の Encoding エントリの名前、または CMap の CMapName エントリの名前」を指定します。
埋め込みフォントの場合は、フォント名だけで構いません。

子孫が CIDFontType2 (TrueType) の場合、CIDFont 辞書の BaseFont 名のみを指定します。
Encodingname or stream(必須) 定義済みの CMap 名、または、文字コードを CID にマッピングする CMap ストリーム。
子孫が、埋め込みではない CIDFontType2 の場合、定義済みの CMap 名を指定します。

横書きと縦書きでは、別の名前を指定する必要があります。
DescendantFontsarray(必須) 子孫の CIDFont 辞書を指定する、要素が一つの配列。
※指定できるのは一つだけですが、必ず配列で指定します。
ToUnicodestreamグリフの CID 番号を Unicode のコード値にマッピングするための、CMap ストリーム

14 0 obj
<< /Type /Font
  /Subtype /Type0
  /BaseFont /HeiseiMin-W5-90ms-RKSJ-H
  /Encoding /90ms-RKSJ-H
  /DescendantFonts [ 15 0 R ]
>>
endobj
CIDFont 辞書
このフォント辞書では、Type 0 の子孫となるフォントを定義します。
Type 0 フォントの DescendantFonts エントリで、この辞書を指定します。

「Type 1 フォント (Type 0)」と「TrueType フォント (Type 2)」の2つのタイプが存在します。
OpenType または CFF フォントであれば前者、TrueType であれば後者を使います。

※ここでも、グリフの幅などは、常に 1 em = 1000 として指定します。

Typename(必須) オブジェクトのタイプ。常に /Font
Subtypename(必須) フォントのサブタイプ。
/CIDFontType0 : OpenType または CFF
/CIDFontType2 : TrueType
BaseFontname(必須) PostScript 名。

CIDFontType0 の場合、フォントの PostScript 名。
CIDFontType2 の場合、前回説明した TrueType フォントの場合と同じ形式で指定します。

サブセットフォントの場合、先頭にサブセットタグを付けることができます。
CIDSystemInfodictionary(必須) 文字コレクションを定義するエントリを含む辞書
FontDescriptordictionary(必須。間接参照) フォントの詳細情報を記述する辞書。
前回説明したものと同じですが、CIDFont のみで指定できるエントリがあります。
DWintegerグリフのデフォルトの幅。
default = 1000
Warray各グリフの幅を、可変フォーマットの配列で指定します。
default = なし (すべてのグリフで DW エントリの値を使う)。

2通りの記述形式があります。
配列内で、以下のいずれか、または両方の形式を使って記述します。

CID開始値 [幅1 幅2...] : CID 開始位置から順に、各グリフの幅を指定
CID先頭 CID終端 幅 : 指定した CID の範囲をすべて指定幅に
DW2array縦書き時のグリフのデフォルト情報を指定する、2つの数値の配列。

1つ目の数値は、縦書きの Y 原点までのデフォルトの高さ (通常は Ascent と同じ)。
2つ目の数値は、グリフのデフォルトの幅。
(縦書きの X 原点は、全角幅の半分の位置 = 500 となります)

※縦書きは、文字を下方向に移動するので、用紙上では、負の値で、下方向へ移動します。

default = [880 -1000] (多くのフォントは、この値であることが多い)
W2array縦書き時の各グリフの情報を定義する配列。
default = なし (全てのグリフで DW2 エントリの値を使う)

W エントリと同じような形で、以下の2通りで記述します。
原点の水平位置は、基本的に 500 です。

CID開始値 [移動幅 原点水平位置 原点垂直位置...]
CID先頭 CID終端 移動幅 原点水平位置 原点垂直位置

※ poppler で、2番目の CID 範囲指定の形式を使うと、幅が正しく適用されませんでした。1番目の形式だけで使った方が良いかもしれません。
CIDToGIDMapstream or name(埋め込み CIDFontType2 のみ) CID からグリフインデックスへのマッピング。

TrueType は CID フォントではないので、定義済みの CMap を使う場合 (Identity-H/V は除く)、CID に対応する GID へのマッピングが必要になります。

ストリームの場合は、バイナリデータの配列。
(CID に対応する GID の値を並べる。各 2 byte でビッグエンディアン順にセット)

名前の場合は、マッピング ID。
/Identity の場合、CID とグリフインデックスは同じになる。

default = /Identity
CIDSystemInfo 辞書
CIDFont 辞書の CIDSystemInfo エントリで指定する辞書です。
文字コレクションの情報を定義します。

RegistryASCII string(必須) 文字コレクションの発行者 (Adobe など)
OrderingASCII string(必須) 文字コレクションの名前 (Japan1 など)
Supplementinteger(必須) 文字コレクションのサプリメント番号。

元の文字コレクションのサプリメント番号は 0 です。
文字コレクションで追加の CID が割り当てられるたびに、番号が増加します。

Adobe-Japan1-7 なら、それぞれ、「(Adobe)」「(Japan1)」「7」となります。

Adobe の定義済みの文字コレクションに準拠しない場合は、Adobe-Identity-0 として指定します。

/CIDSystemInfo <<
  /Registry (Adobe)
  /Ordering (Japan1)
  /Supplement 5
>>
横書きと縦書き
Type0 フォントは、以下のような構成になります。

Type0 - CIDFont - FontDescriptor

1つのフォントで、横書きと縦書きを両方使う場合は、横書きと縦書きそれぞれで、Type0 と CIDFont 辞書を作成する必要があります。ただし、FontDescriptor は共通で使用して構いません。

Type0 フォントの Encoding エントリでは、横書き用と縦書き用のそれぞれの名前を指定してください。フォント名は同じでも構いません。
(描画時に指定したフォントの Type0 が横書き用のエンコーディングなら横書きで、縦書き用のエンコーディングなら縦書きで描画されます)

CIDFont も共通で使用することはできますが、その場合、/W と /W2 で同じグリフに値が指定されていると、一部問題が出る場合があるので、注意してください。

Type0 (Identity-H) - CIDFont (/W) - FontDescriptor
Type0 (Identity-V) - CIDFont (/W2) - FontDescriptor
サブセット
サブセットフォント」とは、フォントから、実際に使用されているグリフだけを抜き出して、再構築したフォントです。

日本語フォントなどの場合は、一つのフォント内に多くのグリフが含まれるため、フォントをまるごと埋め込むと、PDF ファイルのサイズが大きくなってしまいますが、実際に PDF 内で使われるグリフはその中の一部であるため、必要なグリフだけ抽出すれば、ファイルサイズを減らすことができます。

フォントの埋め込みが許可されているか、また、サブセットの埋め込みが許可されているかは、フォントによって異なるため、PDF 作成時は、埋め込み可能なフォントであるかを確認してください。
なお、OpenType/TrueType フォントの場合は、OS/2 テーブル内に、フォント埋め込みに関するフラグが設定されています。

サブセットフォントを埋め込む場合、元のフォントの PostScript 名の前に、任意の6文字による「サブセットタグ」を付けることが出来ます。
これにより、PDF 内の同じフォントの、複数のサブセットを区別させることができます。

PostScript 名が "Test" だった場合は、その前に、6文字のタグ名 (通常は大文字の英字) と、'+' を付けます。
例: EOODIA+Test
CMap
Type0 フォントを使って、PDF 内でテキストを描画する場合、文字列のエンコーディングを指定する必要があります。
Type0 フォントにおいて、文字コードと CID をマッピングするのが、CMap です。

Type0 フォント辞書の Encoding エントリで、CMap を指定します。
エントリの値には、「定義済みの CMap 名」か「CMap ストリーム」を指定します。

定義済みの CMap は、あらかじめ用意されているもので、各文字エンコーディングでの CID とのマッピング情報が定義されています。
定義済みの CMap 以外のエンコーディングを使いたい場合は、ストリームで CMap を記述して、指定します。

CMap には、横書き用と縦書き用があります。

例えば、「。」の場合は、横書き時と縦書き時でグリフ形状が異なるため、別の CID が使われることになります。
このように、横書きと縦書きでは別のマッピングを行う必要があるので、CMap も横書き用と縦書き用に分けられます。

※ストリームで記述する CMap については、後で解説します。
定義済みの CMap
定義済みの CMap には、「中国語 (簡体字)、中国語 (繁体字)、日本語、韓国語、汎用」の5つの種類があります。
ここでは、日本語と汎用の CMap 名のみ紹介します。

※最後の H,V は、横書き用と縦書き用という意味です。

日本語
83pv-RKSJ-HMac OS、KanjiTalk6 拡張機能を備えた JIS X 0208 文字セット、
Shift-JIS エンコード、スクリプトマネージャーコード1
90ms-RKSJ-H
90ms-RKSJ-V
Microsoft, CP932。(Shift-JIS)
NEC および IBM 拡張付き JIS X 0208 文字セット
90msp-RKSJ-H
90msp-RKSJ-V
90ms-RKSJ-H と同じですが、半角ラテン文字をプロポーショナルに置き換えます
90pv-RKSJ-HMac OS、KanjiTalk7 拡張付き JIS X 0208 文字セット、
Shift-JIS エンコード、スクリプトマネージャーコード1
Add-RKSJ-H
Add-RKSJ-V
Fujitsu FMR 拡張機能を備えた JIS X 0208 文字セット、Shift-JIS エンコーディング
EUC-H
EUC-V
JIS X 0208 文字セット、EUC-JP エンコーディング
Ext-RKSJ-H
Ext-RKSJ-V
NEC 拡張付き JIS C 6226(JIS78)文字セット、Shift-JIS エンコード
H
V
JIS X 0208 文字セット、ISO-2022-JP エンコーディング
UniJIS-UCS2-H
UniJIS-UCS2-V
Adobe-Japan1 文字コレクション用の Unicode(UCS-2)エンコード
UniJIS-UCS2-HW-H
UniJIS-UCS2-HW-V
UniJIS-UCS2-H と同じですが、プロポーショナルラテン文字を半角に置き換えます
UniJIS-UTF16-H
UniJIS-UTF16-V
Adobe-Japan1 文字コレクションの Unicode(UTF-16BE)エンコード。
JIS X 0213:1000 文字セットのすべての文字のマッピングが含まれます。
汎用
Identity-H
Identity-V
文字コードと CID が同じ。
文字列は、常に1文字 = 2byte となります。CID 番号をそのまま文字コードとして記述します。
各 PDF バージョンごとの文字コレクションの対応
CMapPDF 1.2PDF 1.3PDF 1.4PDF 1.5
83pv-RKSJ-HAdobe-Japan1-1Adobe-Japan1-1Adobe-Japan1-1Adobe-Japan1-1
90ms-RKSJ-H/VAdobe-Japan1-2Adobe-Japan1-2Adobe-Japan1-2Adobe-Japan1-2
90msp-RKSJ-H/V-Adobe-Japan1-2Adobe-Japan1-2Adobe-Japan1-2
90pv-RKSJ-HAdobe-Japan1-1Adobe-Japan1-1Adobe-Japan1-1Adobe-Japan1-1
Add-RKSJ-H/VAdobe-Japan1-1Adobe-Japan1-1Adobe-Japan1-1Adobe-Japan1-1
EUC-H/V-Adobe-Japan1-1Adobe-Japan1-1Adobe-Japan1-1
Ext-RKSJ-H/VAdobe-Japan1-2Adobe-Japan1-2Adobe-Japan1-2Adobe-Japan1-2
H/VAdobe-Japan1-1Adobe-Japan1-1Adobe-Japan1-1Adobe-Japan1-1
UniJIS-UCS2-H/V-Adobe-Japan1-2Adobe-Japan1-4Adobe-Japan1-4
UniJIS-UCS2-HW-H/V-Adobe-Japan1-2Adobe-Japan1-4Adobe-Japan1-4
UniJIS-UTF16-H/V---Adobe-Japan1-5
Identity-H/VAdobe-Identity-0Adobe-Identity-0Adobe-Identity-0Adobe-Identity-0

PDF のバージョンによって、各 CMap が対応している文字コレクションが異なります。

定義済みの CMap を使う場合、CIDFont 辞書の CIDSystemInfo エントリでは、各 CMap に応じた文字コレクションを指定する必要があります。
テキストの文字エンコーディング
Type 0 フォントを使って、グラフィック命令で文字列を描画する時、Tj 命令などのオペランドとして指定する文字列は、
"Type 0 フォント辞書の Encoding で指定されたエンコーディング" で記述する必要があります。

定義済みの CMap を指定した場合は、各 CMap ごとに指定された文字エンコーディングで、バイト文字列を記述します。
(UTF-16 など、2 byte 単位の場合は、ビッグエンディアン順で記述)
エンコーディングの指定がある CMap の場合
例えば、UniJIS-UTF16-H の場合は、Unicode の UTF-16 エンコーディングであるため、以下のように記述します。

% あ(U+3042) + い(U+3044)
<30423044> Tj

PDF 構文として、Unicode 文字列を記述する時は、先頭に BOM (FE,FF) を付けることになっていましたが、グラフィックでのテキスト指定時は、文字エンコーディングが直接指定されているので、Unicode 文字を使う時に BOM を付けるは必要ありません。

90ms-RKSJ-H の場合は、Shift-JIS エンコードであるため、同じく "あい" を表示する時は、以下のようになります。

% あ(82A0) + い(82A2)
<82A082A2> Tj

EUC-H であれば、EUC エンコードとなります。
Identity-H/V の場合
CMap が Identity-H または Identity-V の場合は、明示的なエンコーディングの指定はありません。
この場合、文字コードの値が、直接、CID またはグリフインデックスの値となります。
つまり、文字コードから CID への変換を行う過程が省かれます。

文字コードは常に1文字 = 2 byte とし、ビッグエンディアン順で記述します。

なお、このエンコーディングの場合に問題となるのは、PDF で描画された文字を、Unicode などの文字列として取得できないことです。
このままでは、PDF 内のテキストのコピーや抽出が行えません。

これを解決するために、PDF では、CID/GID から Unicode のコード値を取得するための情報を指定することができます。
これによって、各文字を Unicode に変換することで、文字列として取得することができます。

マッピングは CMap ストリームで定義し、Type 0 フォント辞書の ToUnicode エントリで指定します。
使用例
ここでは、埋め込みではなく、OS にインストールされているフォントを使う場合の例です。
TrueType
% リソース辞書
4 0 obj
<< /Font << /FONT1 6 0 R >>
>>
endobj

% コンテンツストリーム
5 0 obj
<< /Length 42 >>
stream
/FONT1 24 Tf
BT
10 500 Td
<30423044> Tj
ET
endstream
endobj

% Type 0 フォント辞書
6 0 obj
<< /Type /Font
   /Subtype /Type0
   /BaseFont /IPAPGothic
   /Encoding /UniJIS-UTF16-H
   /DescendantFonts [7 0 R]
>>
endobj

% CIDFont 辞書
7 0 obj
<< /Type /Font
   /Subtype /CIDFontType2
   /BaseFont /IPAPGothic
   /CIDSystemInfo <<
     /Registry (Adobe)
     /Ordering (Japan1)
     /Supplement 5
   >>
   /FontDescriptor 8 0 R
>>
endobj

% FontDescriptor 辞書
8 0 obj
<< /Type /FontDescriptor
   /FontName /IPAPGothic
   ...
>>
endobj
OpenType
% Type 0 フォント辞書
6 0 obj
<< /Type /Font
   /Subtype /Type0
   % 'NotoSansMonoCJKjp-Regular' + '-' + 'Identity-H'
   /BaseFont /NotoSansMonoCJKjp-Regular-Identity-H
   /Encoding /Identity-H
   /DescendantFonts [7 0 R]
>>
endobj

% CIDFont 辞書
7 0 obj
<< /Type /Font
   /Subtype /CIDFontType0
   /BaseFont /NotoSansMonoCJKjp-Regular
   /CIDSystemInfo <<
     /Registry (Adobe)
     /Ordering (Identity)
     /Supplement 0
   >>
   /FontDescriptor 8 0 R
>>
endobj

8 0 obj
<< /Type /FontDescriptor
   /FontName /NotoSansMonoCJKjp-Regular
   ...
>>
endobj