フォントとPDFへの埋め込みについて (続き)
前々回、前回と調べてきたNoto Sans JPフォントを埋め込むと正常なPDFが生成されない件について、少し進展がありましたので調べた内容を書いていきたいと思います。
簡単にまとめてしまうとNoto Sans JPフォントが (仕様として正しいかどうかまでは確認できませんでしたが) 多少気持ち悪い作りになっていたため、PDF生成ソフトウエアがうまく処理できなかったことが原因だろうという結論となりました。
Noto Sans JPフォントはOTFフォント (CFF形式) で作られていて、OTFフォントはざっくり言うとTrueTypeフォントの字形データの代わりにCFF形式のCIDフォントデータを使用したものになります。
まずとっかかりとしてCIDフォントについて調べました。
CIDフォントについて
Adobe社のPostScriptで使用するフォントは当初8ビット、最大255字形 (未定義文字 “.netdef” を含めると256字形) しかサポートしていませんでした。
日本語対応する際、当初はエイヤで決めたようなフォント形式 (OCFフォント) が使用されていましたが、多国語化における以下の課題に対応するため、新しいフォント形式が導入されました。
- 複数文字コードへの対応 – 日本語の場合、JISコード、シフトJISコード、EUCコード、ユニコードなど多種の文字コード体系が使用されており、さらにJISの版により文字コードと字形の対応が異なるため、文字コード体系ごとにフォントを用意すると多数のフォントと多数のフォントを記録する記憶域が必要になる。
- 縦書きなどへの対応 - 長音「―」のように縦書きと横書きで字形の異なる文字があるため、それぞれのフォントに対して縦書き用と横書き用の2種類を用意する必要がある。
これらの課題に対応するために導入されたと考えられるのがCIDフォントになります。
CIDフォントでは文字コードと字形を分離し、旧字や俗字などの異体字、縦書き用の字形などすべての文字コードを包含する字形に対して字形を示すID (CID) を設定し、文字コード体系ごとに文字コードとCIDを結びつけるデータ (CMap) を用意することで単一のフォントファイルで多数の文字コード体系に対応できるようにしました。
さらにフォントごとにCIDと字形の関係が異なる、同じ「あ」という文字に対してあるフォントではCID 〇〇を別のフォントではCID ××を指定しなければならないとなると、CMapファイルはフォントごとに専用になってしまいます。
これを避けるため、CIDフォントではCIDと字形の関係を定めた文字集合 (Character Set) を言語ごとに規定し、同じ文字集合に対応したフォントデータであればCMapファイルを共通できるようにしました。
日本語であれば “Adobe-Japan1” が標準の文字集合となり、2021年8月時点で文字集合の版ごとに ”Adobe-Japan1-0” から ”Adobe-Japan1-7” が定められています。
版 (Supplement) を示す「0」から「7」で字形の入れ替えはなく、文字が追加されるだけですので、CMapファイルが “Adobe-Japan1-4”、フォントデータが “Adobe-Japan1-7” というようにCMapファイルの版が古い場合には何も問題なく出力が行われ、逆にCMapファイルの版の方が新しい場合には、フォントデータに含まれないCID値が指定されると未定義 “.notdef” が出力されるものの、フォントデータに含まれるCID値が指定された場合には正しい文字が出力されます。
ところで2021年8月現在、最新の日本語文字集合 “Adobe-Japan1-7” には約23,000字形が登録されています。これに対して通常使用される文字はJISコード (JIS X 0208) の約7,000字に非漢字等を加えた9000字形ほどです。
しかし、「令和」を1文字に押し込んだ組み文字は “Adobe-Japan1-7” でしか規定されていないため、「令和」の組み文字を含むフォントでは ”Adobe-Japan1-7” の約23,000字形をすべてデザインするか、14,000個ほどの未定義 “.notdef” の字形を埋め込むかが必要となります。
これではあまりに無駄なのでCIDフォントではCIDを実際の字形のインデクスに変換するテーブル (“charset” テーブル) を用意し、CIDからcharsetテーブルを引いて字形を取得する構成となっています。
これをまとめると、CIDフォントを使用したシステムではシステムの使用する文字コード体系からCMapテーブルを選択して文字コードをCIDに変換します。
使用するフォントデータのcharsetテーブルでCIDを字形インデクスに変換して、字形データを取得し、文字画像を生成するという流れで文字コードを文字画像に変換します。
このようにCIDフォントは文字コードと字形を分離し、フォントとは独立して用意されるCMapテーブルとフォントデータ内に含まれるcharsetテーブルで文字コードから字形データを取得する構成となっています。
TrueTypeフォントについて
次にOTFフォントのもう一つの源流であるTrueTypeフォントについて調べます。
TrueTypeフォントもCIDフォントと同じくcmapを使用して文字コードを字形データのインデクス (GlyphID) に変換する形式をとっています (どちらが先かは調べが付きませんでした)。
ただし、TrueTypeフォントはCIDフォントと違い、cmapテーブルをフォントデータに内包しているため、CIDフォントにおける文字集合のような規定は必要なく、charsetテーブルに対応するテーブルもありません。
また、異体字や縦書きに関してはcmapテーブルから取得したGlyphIDを指定された体系、もしくは縦書き指示に従って対応するGlyphIDに変換するGSUBテーブルを用意することで文字コード体系ごとに1個のcmapテーブルがあれば十分な仕組みとしています。
TrueTypeフォントでの字形データ取得手順をまとめると、文字コード体系および縦書き横書きの別からcmapテーブルおよびGSUBテーブルを選択し、文字コードをcmapテーブルによってGlyphIDに変換した後必要なGSUBテーブルによりGlyphIDを変換して必要なGlyphIDを取得し、GlyphIDに対応する字形データを取得するという手順になります。
再びOTFフォント (CFF形式) について
TrueTypeフォントの枠組みにCIDフォントのフォントデータを組み込んだOTFフォントではcmapテーブルおよびGSUBテーブルで取得したGlyphIDが字形データへのインデクスとなり、charsetテーブルは使用しません。
OTFフォントのcmapテーブルおよびGSUBテーブルはフォントデータに内包されているため、フォントデータの外部にあるフォント館共通のCMapファイルを使用するCIDフォントとは異なりGlyphIDに相当するインデクスが何らかの標準に従う必要はありません。
このため、charsetによるコード変換は不要と判断したと考えられます。
PDFのフォント埋め込みとOTFフォント
以上、OTFフォントについて概略を見てきました。
次にPDFではOTFフォントがどのように埋め込まれるかについて考えていきます。
PDFデータにOTFフォントを埋め込む場合、OTFフォントに内包されているCIDフォントデータを埋め込むことが必要なります (もちろんTrueTypeなど他の形式に変換して埋め込むことも可能ですが、通常はそんな面倒なことはしないと思います)。
次にCMapファイルの埋め込みを行います。
CIDフォントのCMapファイルはcharsetテーブルの存在を前提にしたデータですので、charsetテーブルを使用しないOTFフォント中のcmapテーブルはそのままでは使用できません。
OTFフォントのcmapテーブルをそのまま (必要に応じてGSUBテーブルの内容を反映して) 埋め込んだ場合、埋め込まれたCIDフォント中のcharsetテーブルがスルー (入力されたCID値を字形インデクスとして出力する) でなければ文字化けが発生します。
OTFフォントのcmapテーブルをCMapテーブルに変換するのは面倒なので、標準CMapを使用する場合があるようです。
標準の文字集合 (Adobe-Japan1など) に適合したフォントデータを、ユニコードなど標準の文字コード体系を使用したシステムで使用する場合、CMapテーブルを埋め込む代わりに標準CMapテーブル名を指定することができます。
また、標準の文字集合に適合しないフォントの場合 (この場合は文字集合として “Adobe-Identity-0” が指定されます) にはCIDを直接指定する “Identity-H” もしくは “Identity-V” といった標準CMapテーブル名を指定して、文字コードの代わりにCID値を直接指定します。
Microsoft Print to PDFでNoto Sans JPフォントを使用するWORDファイルを出力し、そのPDFデータを確認したところ、フォントデータの文字集合は “Adobe-Identity-0” となっており、CMapには “Identity-H” が指定されているため、文字コードとしてCID値が指定されなければいけないはずですが、OTFフォントのcmapテーブルおよびGSUBテーブルから取得したと思われる値が文字コードとして指定されており、それが埋め込まれたCIDフォントのcharsetテーブルで変換されたために文字化けして出力されたと (PDFファイルに埋め込まれたフォントデータを確認していないので、確定的なことは言えませんが) 考えられます。
ところで、Adobe Acrobatなどを使用すると文字列の検索やコピー・ペーストが可能となっています。
“Adobe-Japan1-7” のような標準文字集合に適合したフォントデータと標準CMapを使用している場合、PDFデータ中の文字コードから該当する文字のユニコードによる文字コードを決定することができるので、文字列の検索およびコピー・ペーストが正しく実行できます。
しかし標準の文字集合に適合しない “Adobe-Identity-0” 文字集合が指定されたフォントデータの場合には文字コードとしてフォント自体が規定しているCID値が指定されているため、検索などに使用できる文字コードをPDFデータからは決定することができません。
このような場合にはCID値からユニコードによる文字コードを決定するためのCMapテーブル (“ToUnicode” CMap) をPDFに記録する必要があります。
当然ながら “ToUnicode” CMapテーブルは文字コードとして使用されているCID値から引けなければいけないので、OTFフォントのユニコードcmapテーブルと必要なGSUBテーブルからGlyphIDを決定したのちにCIDフォントデータ中のcharsetテーブルを使用してCID値に変換したうえで “ToUnicode” CMapテーブルを作成する必要があります。
コピー・ペーストで文字化けを起こす、検索のできないPDFを生成したサードパーティー製PDF変換ソフトウエアおよびmacOS標準のPDF出力は、この “ToUnicode” CMapテーブルを生成する際にcharsetテーブルへの参照を行わなかったと思われ、自分の調べた範囲ではCIDではなくGlyphID (字形データへのインデクス) が設定されていました。
まとめ
ここまで調べてきたように今回の事象はPDF変換ソフトウエアの動作に起因するものと考えられますが、その要因としてOTFフォントでは内包するCIDフォントのcharsetテーブルを使用しないが、PDFに埋め込まれるとcharsetテーブルが使用されるという点があると考えています。
回避方法としてPDFファイルに埋め込みを行う際にcharsetテーブルを書き換えてCIDと字形データへのインデクスを一致させる対応も考えられますが、これを行うとCIDと字形との関係が不定となり、不具合を誘発する可能性があるため、仕様として禁止されています。
OTFフォントの埋め込みを行う際、標準文字集合に適合しないフォントでは文字コードからOTFフォントのcmap, GSUBテーブルを使用して字形データへのインデクスを取得し、CIDフォントデータのcharsetテーブルでCIDを逆引きして文字コードを決定するとともに、そのデータを使用して“ToUnicode” CMapテーブルを作成する必要があると考えます。
ただし、フォントの中には単一の字形データを複数の文字コードが参照する (同一の字形に複数の文字コードが割り当てられている) 文字が存在するものがあり、その場合はそもそも “ToUnicode” CMapテーブルを使用して文字コードの逆引きを行うこと自体が無理と思われます。
以上、とりあえず調べただけで間違いもあるかもしれませんが、調べた結果として報告させていただきます。
次はPDFのフォント埋め込みを確認するなど、もう少し調査を進めていこうと考えています。
0コメント