PDF 内部構造 テキスト

提供:yonewiki
2022年9月26日 (月) 12:19時点におけるYo-net (トーク | 投稿記録)による版 (ページの作成:「PDF 内部構造に戻る。 == '''概要''' ==  PDFの最も重要な機能であるテキスト機能を使ったPDFファイルの作り方について触れていきます。いままでの知識も必要になります。自分で作れるようになると楽しさがアップです。WordみたいなことがPDFだけで出来るようになっていきます。しかも、かなり柔軟に設定もできるので、作りたい…」)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)

PDF 内部構造に戻る。

概要

 PDFの最も重要な機能であるテキスト機能を使ったPDFファイルの作り方について触れていきます。いままでの知識も必要になります。自分で作れるようになると楽しさがアップです。WordみたいなことがPDFだけで出来るようになっていきます。しかも、かなり柔軟に設定もできるので、作りたいと思った文書ファイルが思うがままです。こういうのを理解すると自分でPDFファイルを超柔軟な設定が扱えるようなプログラムを作りたくなってきます。


テキスト状態

 テキスト状態についての設定をするオペレータを紹介していきます。まずは、以下のようなソースで基本的な文書が作れることを示します。 <syntaxhighlight2 lang="text"> %PDF-1.7 %粤マモ 1 0 obj << /Kids [2 0 R]

  /Type /Pages
  /Count 1

>> endobj 2 0 obj << /Rotate 0

  /Parent 1 0 R
  /MediaBox [0 0 1033 1462]
  /Resources 3 0 R
  /Type /Page
  /Contents [4 0 R]

>> endobj 3 0 obj << /Font

  << /F0 
     << /BaseFont /Times-Italic
        /Subtype /Type1
        /Type /Font
     >>
  >>

>> endobj 4 0 obj << >> stream %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %ここからが方眼紙作成のグラフィックストリーム。 % .....(省略)..... % %ここまでが方眼紙作成のグラフィックストリーム。 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %テキスト文字列部分ストリーム開始 BT %BT(=Begin Text) /F0 36 Tf % F0=Times-Italicで 36 Tfのフォントサイズ 1 0 0 1 100 800 Tm %Tmは現在のテキストマトリクスを移動(平行移動/回転/拡大縮小) 50 TL % TLはテキストレディング垂直(上下)余白 50 ポイントになる。テキストの左下が50ポイント間隔 (Create a PDF file. How the text works.) Tj T* %Tjはオペランドの配列()の中を現在のグリフ設定で描画。 %T*は次の行へ移動 3 Tc % Tcは文字の間隔 (Create a PDF file. How the text works.) Tj T* 10 Tw % Twは単語の間隔 (Create a PDF file. How the text works.) Tj ET % ET(=End Text) %テキスト文字列部分ストリーム終了 endstream endobj 5 0 obj << /Pages 1 0 R

  /Type /Catalog

>> endobj xref 0 6 trailer << /Root 5 0 R

  /Size 6

>> startxref 0 %%EOF </syntaxhighlight2>

 このような設定でPDF生成すると以下のようなPDF表示を得ることができます。

テキストの例


 方眼紙になっているので、文字列が50ポイント置きに配置されていることがわかります。ポイント単位で細かく文字を配置できる気配がします。そしてテキスト状態を変えるオペレータを記述すると最初は文字列の間隔が変わり、次に単語単位の間隔も帰れました。英語の斜体文字のグリフの扱いは複雑なので、どこがどれくらい広がったのか、論理的な説明は難しいので、ここでは、だいたい変化させれるということにとどめておきます。おいおい、検証することにしましょう。


 Createの最後のeの文字に注目すると文字間隔を3ポイントにした後の真ん中の行と一番最初の行で比較すると、Createの文字の間は5つで、3 ポイント× 5 つ で15 ポイントです。eの左端が15ポイントくらいずれた場所に配置されている感じなので文字間隔が数値どおり広がっていると考えて問題ないでしょう。それにくわえて単語間となる部分の文字間隔は2つ分と単語間隔1つ分16ポイントくらい移動しているようにも見えます。


オペランド オペレータ 簡単な説明
文字列 Tj オペランド配列で指定されてた文字列の描画をする
数値 TL 垂直文字間隔の設定。規定値は100
数値 T* 次の行に移動
数値 Tc 文字間隔のポイント値をオペランドに指定
数値 Tw 単語間隔のポイント値をオペランドに指定
数値 Tf フォントサイズ
数値 Tz 水平間隔。指定した値/100の値。規定値が100で1を意味する
数値,数値,文字列 " オペランドに文字間隔,単語間隔,文字列を一括で指定するオペレータ
文字列 ' T* 文字列 Tj という構造の動作を文字列 ' だけで指定できる
文字列に数値を含む TJ オペランドに文字間隔と文字列を一括で指定するオペレータ
数値6個 Tm 最初の数値はcmで指定する値と同じ意味だが、テキストがグラフィックス座標系を基準とした上に配置される点では同じオペレータではない。
数値 Tr 整数値1指定でモード切替。
数値 Ts ベースラインモードを整数値で切り替え。


 注意したいのはTJとTjの違いです。Tj は 文字列を指定しているので、PDFtkを動かす環境の文字コードSJISやテキストの文字コードを利用して、そのまま出力する。TJは文字コード番号を指定している。間違うと、文字コード番号が見つからないというような意味のエラーがでる可能性が高い。

 

日本語を扱う

日本語PDFファイルサンプル

 残念ながら上記のような設定ではテキストで文字列配列の部分に日本語を入力しただけでは日本語は扱えません。簡単ではないってことですね。フォント設定にコツがいります。文字列配列の指定もエスケープシーケンスを意識した設定が必要です。で、実際に手作業で組んでいくのはかなり難しいので、ここでは既に日本語への変換方法を知っているアプリケーションにPDFを作成してもらって、そこから解析していくという、手法をとります。わかってる人たちはスゴイよ。自分は信頼できるアプリケーションとして、Windows10でMicrosoftWordを使います。やっぱね。この組み合わせは安定しているよ。完璧なアプリケーションとは言えないかもしれなけれど、PDFを出力する基本部分くらいなら、100%大丈夫だよね。さすがは世に出てきているだけのことはある。そんな感じです。個別のフォントでどういう設定にするべきかを調べようと思うと、また別のフォント解析アプリを使ったり、作ったりして立ち向かう必要がありますが、そういう基本的なことをWordとWindows10の組み合わせではやってくれます。


 ありがたい。で、さっそく出力しました。それだけだと埋め込みフォントっていう、もう一歩先進的なことをやってしまいますので、これを埋め込まないフォントとして扱うように、ちょろっとイカサマ的な操作をします。そうして出来上がるのが以下のような文書です。文書の内容は「美しいMS ゴシックフォントの世界」としました。あんまり長文にすると、わけわからなくなってくるので、16文字ね。


 サンプルソース:JpTextPoweredByWord-text.pdf‎
 サンプルPDF :JpTextPoweredByWord.pdf‎


※WindowsとかのMSゴシックフォントが存在する環境でのみ表示される日本語テキスト表示PDFです。通常PDFではフォント情報を埋め込む方式を使うので、環境によって表示されない、このような環境依存方式を採用することはありません。これは簡単に表示させるための作戦でしかないです。埋め込まないPDFは表示が環境依存ですが、そのかわりファイルサイズが軽くなります。Webページと同じだね。

日本語テキスト表示の例


 少し長いサンプルソースなので、全部載せて、説明はしません。日本語を表示するために、使われているフォントやコード関連のオペレータの意味については解読して、日本語文書ファイルを作れるようにしていきます。


 文字出力のオペレータは両方大文字のTJになっています。そのまま英文を出力するときはTjでした。間違えないでください。

日本語PDF仕組み

 Windowsでのコマンドラインは、自分はSJISで動かしています。PDFtkはファイルをSJISで書かれている認識して処理しています。そうするとファイル中で日本語を使うときは、2種類の方法になります。ひとつはSJISの文字コードの数値あるいはテキストそのままを入れて文字を表現する。テキストをそのまま入れる場合は文字コードのレベルで数値化したレベルのPDFファイルとして認識してSJISとして処理されるという概念を常に意識します。もう一つは、SJIS以外の文字コードを使うとして、オペレータで文字コードはこういうものを使っていますと指定して、文字コードの数値で入力するという方法です。そのまま日本語の文字列をPDFファイル内に配置する訳にはいかないことが面倒なところです。ということは、テキストエディタだけでPDFを構成するのは簡単ではないと想像できると思います。この他、イメージなんかも全部テキストで表現するとしたら同じような理由でテキストエディタだけで操作するのは、困難を極めます。自分でPDF編集アプリをつくらないと楽にはならないでしょう。でも、理解するということは、そういうことをやっていくきっかけにはなるはずです。


 簡単じゃないのはいやだな。


日本語PDFフォント情報

 では、日本語を入力することを考えていきましょう。


 要(かなめ)は、フォント情報をページ構成の一部として取り込む部分です。英字でもやった。リソースオブジェクトとして設定した辞書のなかに設定する /Font オペレータですね。一般にはフォント設定は3個のオブジェクトに分けて書くことが多いようです。以下のような構成です。


<syntaxhighlight2 lang="text"> 2 0 obj <<

  /Resources 
  <<
     /Font << /F0 3 0 R >>
  >>

>> 3 0 obj %Resourceで定義されたフォントに関する辞書 <<

  /Type Font
  /Name /F0
  /Subtype /Type0
  /BaseFont /#82l#82r#83S#83V#83b#83N %KozMin /KozMinPr6N-Regular
  /DescendantFonts[4 0 R]

>> 4 0 obj %Fontで定義されたDescendantFontに関する情報 <<

  /Type /Font
  /SubType /CIDFontType2 %KozMin /CIDFontType0 
  /BaseFont #82l#82r#83S#83V#83b#83N  %KozMin /KozMinPr6N-Regular
  /CIDSystemInfo
  <<
     /Registry (Adobe)
     /Ordering (Identity) %KozMin (Japan1)
     /Supplement 0 % KozMin 6
   >>
   /FontDescriptor 5 0 R

>> 5 0 obj %DescendantFontで定義されたFontDescriptor情報 <<

  /Type /FontDescriptor
  /FontName #82l#82r#83S#83V#83b#83N %KozMin /KozMinPr6N-Regular
  /Flags 6
  /StemV 6 0 R
  /Ascent 859
  /Descent -140
  /ItalicAngle 0
  /FontBBox 7 0 R
  /CapHeight 679

>> … </syntaxhighlight2>

 という具合にリソースの定義部で、/Fontフォントが定義され、その中で/DescendantFonts。そしてさらにその中で/FontDescriptorについての定義がされる。コメントの%KozMinは、Macのフォントの小塚明朝の場合はこんな感じの記述という例で、完全に動作するとは保証できないのであしからず。この3段論法みたいなフォント情報はひとつにまとめて書くことが出来る。まとめて記述するには、以下のような感じ。Adobe Acrobat Readerには小塚明朝が表示できる様に組み込まれていてWindowsでも使えるはずですが、自分は上手くやれませんでした。なんかもうちょっとコツがあるのかも知れない。ちな、この小塚フォントをWindowsの汎用フォントフォルダに移動して普通に使って文書に埋め込んだりして配布して使うのは規約違反らしいです。Acrobatで見るためだけに使いなさいと言う事らしい。

<syntaxhighlight2 lang="text"> 2 0 obj <<

  /Resources 
  <<
     /Font << /F0 3 0 R >>
  >>

>> 3 0 obj %Resourceで定義されたフォントに関する辞書 <<

  /Type /Font
  /Subtype /Type0
  /BaseFont /#82l#82r#83S#83V#83b#83N
  /ToUnicode 4 0 R
  /Encoding /Identity-H
  /DescendantFonts 
  [
     <<
        /Type /Font
        /BaseFont /#82l#82r#83S#83V#83b#83N
        /W 11 0 R
        /CIDToGIDMap /Identity
        /CIDSystemInfo 
        <<
           /Supplement 0
           /Ordering (Identity)
           /Registry (Adobe)
        >>
        /Subtype /CIDFontType2
        /FontDescriptor 
        <<
           /FontName /#82l#82r#83S#83V#83b#83N
           /StemV 1000
           /Ascent 859
           /Flags 6
           /Descent -140
           /ItalicAngle 0
           /FontBBox [-1000 -140 1000 859]
           /Type /FontDescriptor
           /CapHeight 679
        >>
     >>
  ]

>> endobj 4 0 obj << >> stream

endstream endobj … </syntaxhighlight2>

 三つを一つに纏めれましたが、この三つは意味が違うので注意が必要です。/F1という名前のフォントはType0という扱いをします。という意味合いで、このファイルでの扱いを、決めるための定義なのでこのPDFファイルのための独自情報です。ここでType0になっているのは、複数のフォントを、まとめれるという特徴があるのでよく使われるフォントタイプです。その中にある/DescendantFontsにはこういうフォントを組み合わせて使いますという具合です。Descendantは日本語で子孫という意味です。実際に利用するフォントの構造を指定しています。MSゴシックがTrueTypeのCIDキー付きフォントだということを意味しています。実際はOpen Typeの構造に準拠していますが、TrueTypeとしても動作する様になっている形式です。OpenTypeは使えないと言うアプリケーションは多いWindowsの世界では良く取られる対処法です。


 そして、/FontDescriptorはストリームで対応する文字コードについてのフォント情報が無かったとき、この例ではType0フォントのMSゴシックが無い場合、どのフォントの情報に従うのかという意味を持っています。そしてどの様なフォントなのかを説明しなければなりません。Descriptorとは説明する者という意味です。

日本語PDFフォント情報のオペレータ個別の意味

/SubType

 今回たまたまWordから生成されたPDFが使っただけ部分で順番にみていくと/SubTypeが定義されていて、サブタイプの意味は以下の通りになっています。

サブタイプ名 タイプ 簡単な説明
Type0 Type0 コンポジットフォント。複合型、階層構造をもつフォント。
Type1 Type1 エンコード形式を持つグリフ定義。
MMType1 Type1 マルチプルマスタフォント。
Type3 Type3 PDFグラフィックスオペレータストリームで定義するフォント
TrueType TrueType TrueTypeフォント形式に基づくフォント
CIDFontType0 CIDFont グリフ形式がType1に基づくCIDフォント
CIDFontType2 CIDFont グリフ形式がTrueTypeに基づくCIDフォント


 専門用語のオンパレードですね。上記の設定のようにSubtypeが記述される箇所によって、CIDFontType2って言うたり、Type0フォントって言ったりしてる。この点がややこしく感じますが少し前に記述したように、このファイルで扱うフォント形式としての宣言と参照するフォントの形式の定義になっています。


 簡単にフォントファイル形式の現状を説明するならば、TrueTypeやCIDFontType TrueType系はマイクロソフト牽引しているフォントファイルの形式です。TrueTypeそのものはAppleが開発したものですが、マイクロソフトが先にWindowsというOSを流行らせたため、この形式を採用したマイクロソフトが先導する形になってファイル形式の定義自体もマイクロソフトの都合のいいように扱われていった感じです。あるフォントの属性値の意味がマイクロソフトによって捻じ曲げられたりした可能性もあります。Adobeが牽引したのがType0やType1という形式のフォントです。Apple Computerは主にデザイナーに愛されたコンピュータでAdobe製品との親和性が高かったため、こちらを使うことになります。TrueTypeとType1ともにCIDFontという形式を採用します。フォントファイルは文字コード体系毎に存在していましたが、CIDFontはCMapという文字コードとフォントファイル内のグリフIDの対応表みたいなものをセットにするという概念をもっています。そして現在は両陣営ともにOpenTypeというものに統合しようと決めたというのが現在の状態です。フォントの仕組みの標準化という動きもあって、現状よりももっともっとOpenTypeに代わっていくはずです。置き換わる途中でまた新しい仕組みが生まれる可能性もあります。OpenTypeはビットマップフォントにも対応しているし絵文字やマルチバイト・国際化そういうものの全てに対応できるようになっています。フォントを制作している大きな会社もこの動きに追随するべく、置き換えが進んでいます。しかし、過去の遺産も多く、古いフォントファイルじゃないと動作しないアプリが使われている業界もあったりと、移行は簡単ではないという感じですね。なんやかんやで、結局はお金かかります。誰かが頑張ってフォント作っている以上はね。対価がないと移行できない。そんなこんなで、Type1やCIDFontTypeというものは使われなくなってきているのが現状です。新しいAdobe製品ではType1フォントは読み込めないようになっていったりしています。CIDFontもわりとあたらしい技術ですが、近く非対応になることがきまっています。あたらしくないか?自分の体内時計がおかしいのかな。


 困ってる人いるんだろう。Microsoftは慌てず騒がずやっていくようです。現在のWindowsフォントシステムとTrueTypeが優れているという自負でしょうね。


 上記のサンプル例では出来る限りシンプルな形式にするため、CMapをUnicodeに変換するストリームの内容も消したため、文字列を選択してコピーしてテキストにペーストした場合、文字化けします。ふむー。いろいろ気を使わないとだめだな。PDF手ごわい。CMapはフォントの文字番号を意味しています。PDFではこの情報を、Unicodeとの対応を示すことで、コピーした時にテキストとして有用な文字コード番号に関する情報を得て、テキスト編集がしやすいような工夫がされています。PDFでは文字選択によるコピーをさせないようなモードもありますし、文字の形状をフォントファイルの中から得て、テキスト描画時に指定された文字情報はフォントから抜き出すための番号であり、その番号が対応するテキストは何かというのを個別のものとして管理しています。/ToUnicode 4 0 R という部分がその部分で、この参照先のオブジェクトに本来であれば、文字コードに対応する部分の情報をつけておくのが普通です。 ここまでやって日本語は使える状態になったといえるでしょう。

 ここではひとまず、表示に関わる設定のみにしぼって説明してから、発展させていくものとして話を進めます。

/BaseFont

 /FontNameでもフォントの名前を指定しますが、通常は同じ値になります。この値はPDFを更に紙や仮想プリンタでPDFや他の形式へ印刷するときにPostスクリプトが使われるときのフォント名の指定として使われます。そこで改めてフォントが参照されて、フォントの輪郭・形状を印刷機にわたりグリフ情報が活用されます。Fontの孫情報であるDescendantFont情報で指定するFontNameと同じでないと印刷するときに違うフォントが使われてしまいチグハグなことになってしまいます。ここで注目してほしいのは値の指定方法が少し特殊なことになっていることです。PDFのソースファイルの形式がSJISであれば、普通に

 /BaseFont MSゴシック

のように日本語フォント名を使っていも良いですが、SJISではない場合はコマンドラインの文字コードに合わせる必要があります。自分の場合はSJISなのでSJISのコードを使いました。指定方法が少し特殊で、2バイト目に文字を使っています。これはアルファベットの1バイトコードに割り当てられた数字で、1文字の値になっています。


 /FontName /#82l#82r#83S#83V#83b#83N

 % 82(l=0x6C) : M

 % 82(r=0x72) : S

 % 83(S=0x53) : ゴ

 % 83(V=0x56) : シ

 % 83(b=0x62) : ッ

 % 83(N=0x4E) : ク

という具合です。気になる人はSJISの2バイト文字一覧表を確認してみると良いでしょう。こんな面倒なこと本当にやるのっていう感じですが、テキストエディタによっては楽々に変換できます。サクラエディタだと、特に簡単でSJIS形式でファイルを保存して、文字コードを知りたい一文字毎に、カーソルを合わせると値を見ることができます。後ろの一文字だけをアルファベットに置き換えるには、置き換えたい文字を範囲選択して、メニューの[変換]-[文字コード]の UTF-8→SJIS を選択するとよいでしょう。pdftkコマンドにかけるテキストをSJISにするなら、普通に日本語を打ち込むだけで良いです。#82#6Cのように番号に変換しなくても良いです。間違えてUTF8形式で保存してしまうと。文字化けの原因になるので、最初からSJISコードの数字にしておいた方が、テキストが何形式でもSJIS文字番号として解釈するため、文字化けの原因にもならず、フォントがおかしな設定になって、見つかりません的なことにはなりません。複雑やね。コマンドが解釈する文字コードとテキストが保有する文字コード。テキストの表示処理に使われてる文字コードいろいろあります。そうなんですよ。テキストがSJISで保存してるんだけど、表示処理自体はUTF-8になってたりすることもあります。UTF-8の文字コードでプログラムは処理していて、保存するときにSJISとかの形式にして保存するという意味です。サクラエディタはそのような仕組みになっています。PDFtkをSJIS環境のコマンドで動かすと、#82l#82r#83S#83V#83b#83Nのように日本語文字のフォント名を置き換えます。また次もSJISで処理されてもうまくうごくように工夫されているのかもしれないし。自分の考えすぎかもしれない。こんな方式あるんだ。的なことって、この界隈には沢山あります。知らないことだらけですからね。うちらは後塵を拝しています。


 文字コードの仕組みを細かく追っていける冷静な人になれるといいね。それくらいまで理解できていないと文書ファイルを自由自在に操るには壁になるよ。おちついて考えれば理解できるはずだし、わかっている方が、間違いの起こりにくい世界の全体が見渡せる。あー。こういう仕組みをつけておけば使う人は困らないのかー。みたいなことの創造力につながる。大事だよね。わかんなくても生きていけるけどね。どうなんだろうね。


 ここまで、理解した場合にやってみたくなるのは違うフォント名をあてがうとどうなるかです。ちなみにAdobeJapan1配列のフォント。例えば、ヒラギノフォントとかに差し替えるて、PDFを生成すると、文字化けが起こります。「美しいMSゴシックの…」のローマ字部分は半角にはなったもの化けませんでした。ヒラギノといっても大日本スクリーンのTrueTypeヒラギノフォントです。これはフォントごとにグリフIDが異なるために起こる問題です。同じ動作になるものもあります。例えば、MS-PGothicに変更しても大丈夫です。MS-PGothicはMSPゴシックというフォントのPostScriptNameなので、シフトJISコードに変換する必要はないです。PostScriptNameを調べるのは簡単ではないです。特定のフォント管理アプリNexusFontのようなものを導入していれば確認できます。フォント編集を行うFontForgeでも良いです。Meiryoなんかに変更してもうまく行くかと思ったのですが、これはダメでした。いやはや、これは大変そうですね。


 フォントによっては、それこそ既存のフォントを勝手に編集して自作した個人的なモノが使われてPDFファイルが広く世に出回っているパターンもあります。CMap情報が無い場合はテキストとしての情報を構築するのをあきらめるPDF出力アプリがあります。ワードでさえ、そのように判断します。この場合、文字のように見えて、すべてがグリフ情報からベジェ曲線グラフィックに置き換えられます。まだまだ先の発想になりますが、すべてを理解したのち、PDF原稿から文字を抜き出す的な完全な自動化はテキストという考え方だけでは無理です。さらにOCRのような処理まで含めて考えることで、PDFから文字が抜き出せるということになります。簡単には、抜き出せる部分もあるので、特定のPDFに対しては文字が抽出できるようなものは作れると思います。


 とにもかくにも、置き換えがうまくいかないのは、ワードが生成したMSゴシックフォントのエンコード方式がIdentity-Hと指定されていることが、他のフォントファイルとは、一致しない場合がある要因となっています。このあたりの理解を深めないと勘違いする。逆引きのようなことは難しいことなので、まずは、作るという方向から構造を知りましょう。本来のPDFの役割ですから。逆引きは便利だけど応用とか裏技とか秘儀的なことです。テキストを画面に美しく表示するのがPDFのあるべき姿だったのでした。


 埋め込みすることがほとんどなので、あまり役に立たない情報ですが、フォントを埋め込まない場合でCID(Character "ID"entity)FontのCIDFontType0(OpenType CFF(CollectionFontFileコレクションフォントファイル))を子孫(DescendantFonts)に持つ場合はBaseFont名の後ろに"-"を足したうえでCMap名を付けておくことが一般的です。

/Encoding

 エンコード方式を設定する部分です。PDFファイルから文字を選択してコピーすることがうまくいくということは、保証されていません。PDFファイルを作る側の都合になります。それもこれも、このエンコードも絡みます。他にもこの後で説明する /CIDtoGID、/toUnicode も関係する。フォントには、何種類かのエンコード方式があります。既存のCMap名がある場合はその名前がエンコード方式にあたりますので、その名前に対応する /で始まる文字列 を記述します。CMapの構造をPDFの中で直接記述することもできます。別の参照にCMapストリームとして記述することになります。


 CMAPの存在の大きな理由としては、文字コードだけでフォントファイルに収められた文字絵柄の番号のグリフ(Glyph ID )がきまるということではないからです。


×:文字コード → Glyph番号


 ただし、文字の向きと文字コードとGlyph番号がほぼ一致しているパターンはゼロではないです。


 CMapは縦書き横書きだけでも分かれています。ほとんどのCMAPがなんらかの形で縦書き横書き用にわかれています。縦横だけに限りません。同じ文字コードでも特殊な用途では、CMapを切り替えるだけで表示してほしいグリフがあったりします。まず、代表的なCMap名として3つ


 /Identity /Identity-H /Identity-V


があります。アイデンティティ。固有の。というとかっこいいですが、CMapの名前としては、そのまんま。っていう意味合い強い。-Hとついているのは、水平のH(Horizontal)。水平方向のためのCMapつまり横書き。Vは垂直(Vertical)で縦書きですね。ちゃんと準備されているフォントならば水平と垂直ではフォントが保持している文字間情報の数値が異なっていて、字詰めの美しさが違います。記号の向きとか、「」とかはあきらかに違う表示がされるように工夫されています。" " とか。いいだしたらきりがないと思える数々についてすべて対応している。フォントって凄絶(せいぜつ)ね。


 Identityはフォント内部の番号をそのまま使いますという意味です。フォントファイルに別の意味のCMapがあって、それで扱った方が早い場合でもIdentityは使えます。最強です。人間が文字コードと図柄を突き合わせて読むために必要な情報を構築するのには骨が折れます。


Identityと同じ扱いなのにもっとべつCMapを使って整合性をとって文字を選んだりするおそろしいとおまわりをするプログラムが作ったPDFでなければ、法則を見つけて、プログラムで文字コードの体系に置き換えたりすることは可能だと思います。いづれにしても人間の手でやっていたら終わりませんけど。


 このIdentity系が使われている場合 テキストエディタから印刷生成されてフォントのことはよく知っているアプリが吐き出した感じがします。フォント内部で扱われている番号そのものをPDF生成のPostScriptにデータを渡しています。フォントの生データから、文字を抜き出していますからね。縦書きか横書きかくらいまで把握していて、フォントを使っている時点でCMap空間を把握して利用している。

 他にも見たことある名前をあげるとしたら


 78-EUC-H 78-EUC-V 78-H 78ms-RKSJ-H 78ms-RKSJ-V 78-RKSJ-H 78-RKSJ-V 78-V 83pv-RKSJ-H 90msp-RKSJ-H 90msp-RKSJ-V 90ms-RKSJ-H 90ms-RKSJ-V 90pv-RKSJ-H 90pv-RKSJ-V Add-H Add-RKSJ-H Add-RKSJ-V Add-V Adobe-Japan1-0 Adobe-Japan1-1 Adobe-Japan1-2 Adobe-Japan1-3 Adobe-Japan1-4 Adobe-Japan1-5 Adobe-Japan1-6 EUC-H EUC-V Ext-H Ext-RKSJ-H Ext-RKSJ-V Ext-V H Hankaku Hiragana Identity-H Identity-V Katakana NWP-H NWP-V RKSJ-H RKSJ-V Roman UniJIS2004-UTF16-H UniJIS2004-UTF16-V UniJIS2004-UTF32-H UniJIS2004-UTF32-V UniJIS2004-UTF8-H UniJIS2004-UTF8-V UniJISPro-UCS2-HW-V UniJISPro-UCS2-V UniJISPro-UTF8-V UniJIS-UCS2-H UniJIS-UCS2-HW-H UniJIS-UCS2-HW-V UniJIS-UCS2-V UniJIS-UTF16-H UniJIS-UTF16-V UniJIS-UTF32-H UniJIS-UTF32-V UniJIS-UTF8-H UniJIS-UTF8-V UniJISX02132004-UTF32-H UniJISX02132004-UTF32-V UniJISX0213-UTF32-H UniJISX0213-UTF32-V V WP-Symbol)


 いっぱいある。ほんとうに切り分けされて表示される動作が違うのか気になるけど、ある。実在する。その中からVer1.3までには以下のようなCMapが使えることになっている。CMapは何かの文字コードと同じになっている場合があり、その場合は関連した文字コード名のCMap名に同じGlyph値を横流しするような文字コード体系名が入れ込まれていることがあります。近年のフォントはUnicodeになっていることが多い。

中国語(簡体字)

名前 説明
GB-EUC-H Microsoft Code Page 936 lfCharSet 0x86 GB 2312-80文字セット
GB-EUC-V 上記の縦書き版
GBpc-EUC-H Macintosh GB 2312-80文字セット EUC-CNエンコーディング Script Manager Code 2
GBpc-EUC-V 上記の縦書き版
GBK-EUC-H Code Page 936 lfCharSet 0x86 GBK文字セット GBKエンコーディング
GBK-EUC-V 上記の縦書き版
UniGB-UCS2-H Adobe-GB1文字コレクション用Unicode(UCS2)エンコーディング
UniGB-UCS2-V 上記の縦書き版


中国語(繁体字)

名前 説明
B5pc-H Macintosh Big Five文字セット Big Fiveエンコーディング Script Manager code2
B5pc-V 上記の縦書き版
ETen-B5-H Microsoft Code Page 950 lfCharSet 0x88 Big Five文字セット ETen拡張
ETen-B5-V 上記の縦書き版
ETenms-B5-H Microsoft Code Page 950 lfCharSet 0x88 Big Five文字セット ETen拡張の半角ラテン文字をプロポーショナル形式で置換
ETenms-B5-V 上記の縦書き版
CNS-EUC-H CNS 11643-1992文字セット EUC-TWエンコーディング
CNS-EUC-V 上記の縦書き版
UniCNS-UCS2-H Adobe-CNS1文字コレクション用Unicode(UCS-2)エンコーディング
UniCNS-UCS2-V 上記の縦書き版


日本語

名前 説明
83pv-RKSJ-H Macintosh JIS X 0208文字セット KanjiTalk6 拡張 Shift-JISエンコーディング Script Manager code 1
83pv-RKSJ--V 上記の縦書き版
90ms-RKSJ-H Microsoft Code Page 932 lfCharSet 0x80 JIS X 0208文字セット+NECおよびIBM拡張
90ms-RKSJ-V 上記の縦書き版
90msp-RKSJ-H 上記と同じで半角ラテン文字をプロポーショナル形式で置換
90msp-RKSJ-V 上記の縦書き版
90pv-RKSJ-H Macintosh JIS X 0208文字セット KanjiTalk7 拡張 Shift-JISエンコーディング Script Manager code 1
90pv-RKSJ-V 上記の縦書き版
Add-RKSJ-H JIS X 0208文字セット+富士通FMR拡張 Shift-JISエンコーディング
Add-RKSJ-V 上記の縦書き版
EUC-H JIS X 0208文字セット EUC-JPエンコーディング
EUC-V 上記の縦書き版
Ext-RKSJ-H JIS C 6226(JIS78)文字セット+NEC拡張 Shift-JISエンコーディング
Ext-RKSJ-V 上記の縦書き版
H JIS X 0208文字セット ISO-2022-JPエンコーディング
V 上記の縦書き版
UniJIS-UCS2-H Adobe-Japan1文字コレクション用Unicode(UCS-2)エンコーディング
UniJIS-UCS2-V 上記の縦書き版
UniJIS-UCS2-HW-H 上記と同じで半角ラテン文字をプロポーショナル形式で置換
UniJIS-UCS2-HW-V 上記の縦書き版


韓国語

名前 説明
KSC-EUC-H KS X 1001:1992文字セット EUC-KRエンコーディング
KSC-EUC-V 上記の縦書き版
KSCms-UHC-H Microsoft Code Page 949 lfCharSet 0x81KS X 1001:1992文字セット8822追加ハングル UHC Unified Hangul Codeエンコーディング
KSCms-UHC-V 上記の縦書き版
KSCpc-EUC-H Macintosh KS X 1001:1992文字セット+Mac OS KH拡張 Script Manager Code 3
KSCpc-EUC-V 上記の縦書き版
UniKS-UCS2-H Adobe-Korea1文字コレクション用Unicode(UCS-2)エンコーディング
KSCpc-EUC-V 上記の縦書き版


 CMapはフォントファイルにあるものなので、自分でPDFファイル生成する場合は対になるべきCMapを見つけるコツがいるってことになりますね。あるいは自分で全部の文字に対するCMapを記述するという手もありますが、いずれにしてもグリフIDと文字コードの関係は知らないと対応できません。


例えば、今回の場合は、以下のようなストリームを作ると対応するCMapを自作したことになるようです。ToUnicodeのオブジェクトとして、割り当てると、PDFでの文字選択のコピー&ペースト処理がうまく動作するようになります。


<Syntaxhighlight2 lang="text">

  /CIDInit /ProcSet findresource begin 
     12 dict begin 
        begincmap 
        /CIDSystemInfo 
           << 
              /Registry (Adobe) 
              /Ordering (UCS) 
              /Supplement 0 
           >> def 
           /CMapName /Adobe-Identity-UCS def 
           /CMapType 2 def
           1 begincodespacerange 
              <0000> <FFFF> 
           endcodespacerange 
           17 beginbfchar 
              <0003> <0020> % 
              <0030> <004D> %M
              <0036> <0053> %S
              <0838> <3044> %い
              <084B> <3057> %し
              <0862> <306E> %の
              <089B> <30A9> %ォ
              <08A1> <30AF> %ク
              <08A6> <30B4> %ゴ
              <08A9> <30B7> %シ
              <08B5> <30C3> %ッ
              <08BA> <30C8> %ト
              <08C7> <30D5> %フ
              <08E5> <30F3> %ン
              <0B61> <4E16> %世
              <23FA> <754C> %界
              <2A3E> <7F8E> %美
           endbfchar 
        endcmap 
        CMapName currentdict /CMap defineresource pop 
     end 
  end 

</Syntaxhighlight2>


 CMapは文字コードと縦書き、横書き、異体字etcによる図柄の変更のような情報から1つのグリフに置き換えるもので、近年のWindowsやMacにあるような既存のデータはフォント内にCMap情報が内蔵されています。つまりCIDFontType0(Type1方式)やCIDFontType2(TrueType)のようなもので、OpenType(*.otf *.otc)やTrueType(*.ttf *.ttc)となっています。突き合わせの詳細を見るにはフォントから抜き出さないと見れない場合がほとんどです。CMapファイルが外だしになっているCIDFontが使える昔ながらのアプリもあります。この場合CMap情報が書かれたファイルが一つのフォルダにまとめられていることが多いです。CIDFontと平たく言ってしまうとTrueTypeフォントやOpenTypeフォントのことでもあるので誤解を招きやすいですが、もっと古いCIDFontというのがありました。有名どころのアプリではTex関連のツール。同じことですがgs(GhostScript)なんかがCMapフォルダを保持しているツールです。Acrobat Readerも保持していたような。あまりCMapを個人で編集することはないので、誰かが作ってくれていて、しらずしらずに使っているのではないでしょうか。


 さきほど列挙したような有名どころのCMapファイルが入っていると思います。TrueTypeやOpenTypeが流行ってからの新しいCMapファイルは単独として存在していない可能性もあります。どちらにしてもCMapファイルを作るのは大変です。個人的に作り始めたら1か月くらいかかるんじゃないかな。既存のモノをちょこっといじってあたらしく作るというのなら個人でできると思います。特定の文字を置き換えて表示させることもできるはずです。ToUnicodeのような仕組みがあれば、変更された文字をコピーした時の動きも変更できます。


 まずは、既存のフォントからCMapを抜き出して、どういうものに対応しているか見てみることができます。全文字の対応をひとつひとつ見比べるのはできないので、縦書き?横書き?文字コードは何を受け入れて、どのように変換する?絵文字は?とかそういうざっくりした特徴毎にはまとめられています。全部を見なくても、どれを使えばCMapがうまく表示したいグリフに案内してくれるか、予想はつきます。そういうことができる力量は必要です。


 では、抜き出してみましょう。抜き出すには、有名どころの専用アプリケーションを使うのがよいと思います。Windowsなら3種類が有力です。


・Adobe製のafdkoというツール。Python環境の構築が必須です。

・RT's free softさんのWINTTXというツール。コマンドプロンプトを使って実行ができるexeがついてます。ダウンロードしてきてPathをとおして使います。

・イギリスのバベルストーンという会社が作ったBabelMapというツール。ちょっとやれることが多いUI付きのツールです。やりたいことを的確にやれる方法を覚えないと使えないかも

  • グリフ番号の対応表を見る
メニュー[Fonts]-[Overview of All Font]でひらくWindowで所望のフォントを選択してInfomationボタンを押す。ウィンドウを開いて、下の方にCMAPがplatfomIDとかの分類ごとに表示されるので、その中から希望のモノを選択してCmap copy subtableを選択して、Excelとかの表計算アプリに貼りつけると見やすい。


 この他、含まれているフォントを見るのも便利。昔Microfostから無償提供されていたFont Toolの代わりにはなります。便利。


 いずれのツールをつかっても、同じような結果が得られて、拡張子が ttx のファイルが生成できますがより正しいものが得られるのはttxコマンドが使えるようになるツールですね。手っ取り早く確認するだけならバベル。


 以下のようなコマンドで実行できます。フォントファイルのあるフォルダで実行して、同じディレクトリに置けばよいでしょう。C:¥$Env:windir¥Fontsフォントフォルダで dir コマンドを使えばファイル名も見れます。

ttx -t cmap -y 0 msgothic.ttc


 -t cmap はcmapをフォントファイルから取り出すためのオプションで -y 0 で ttc や otcのようなフォントコレクションにおいて何番目のフォントの情報を抜き出すかという意味になります。標準フォントはたいてい0に入ってると思う。1がプロポーショナル形式 とかですね。日本語が含まれるようなモノならば、わりかしい大きいサイズのテキストは出力されます。


 出力されたCMapには、規格化されたプラットフォームやフォーマットに基づいたCMap向けの番号が使われています。これが何を意味しているのかを調べれば全体が把握できるようになると思います。


 マイクロソフトが公開しているCMap番号の情報を見るのがいいです。簡単に説明を抜き出すと

<Syntaxhighlight2 lang="text">

 <cmap>
   <tableVersion version="0"/>
   <cmap_format_14 platformID="0" platEncID="5" format="14" length="680" numVarSelectorRecords="4">
   
     <map uvs="0xe0100" uv="0x5026" name="glyph22091"/>

</Syntaxhighlight2>

 CMapストリームの書き方は以下に Adobe Technical Notes

 1.#5014, Adobe CMap and ClDFont Files Specification

 2.#5092, CID-Keyed Font Technology Overview

 3.#5099 Developing CMap Resources for CID-Keyed Fonts

 の資料があります。

 https://adobe-type-tools.github.io/font-tech-notes/pdfs/5014.CIDFont_Spec.pdf

 https://adobe-type-tools.github.io/font-tech-notes/pdfs/5092.CID_Overview.pdf

 https://adobe-type-tools.github.io/font-tech-notes/pdfs/5099.CMapResources.pdf


 え?すごくないか、この資料。読み込むにはパワーいるわぁ。CMapをうまく操作したくなったら読もうかな。


 簡単にMSゴシックフォントのCMapを見てみる。


1.最初にPlatformID 0 platEncID 5 Format14が記載されている。これはUnicodeのU+FFFFより大きい値のグリフの対応表があります。Glyph22000番あたりに122個が割り当てられている。PlatformID 0 はUnicode で platEncID5はUnicode Variation Sequences for use with subtable format なので、当然正しい内容が記載されている。こちらはグリフ番号がついてるので対応は見やすい。

2.次に platformID 3 platEncID 1 Format 4 でplatformID 3 はWindows platEncID 1 は Unicodeですね。ここでがっつりとUnicode Mapを使ってGlyph番号の指定がされています。uni0000 からuniFFEE で 15760個のグリフが対応を指定されています。こちらはグリフ番号が含まれない情報です。上からグリフ番号が1から始まる連番です。行番号のついてるテキストエディタで開いて引き算しながら眺めるもよろし、エクセルにはりつけて、横にオートフィルとかで連番を振りなおすもよろしです。

3.次は platformID 3はWindows、platEncID10 はUnicode Full repertoire のformat 12 領域化されたカバレッジ網羅率による記述があります。16126個あります。差分情報が入り込んでいます。異体文字セレクタとかで呼び出されるグリフUnicode番号も保持しています。


 Wordが出力したPDFではすべてが元のグリフ番号を保持していて、そのまま、謎の番号で対応する文字を出力し、ToUnicodeで謎の番号に対するUnicodeにおける元の文字番号を指定しています。これは、メイリオフォントを設定しても同じでした。マイクロソフトが作ったわけではないフォントでポストスクリプト名が無いものについては、フォントをベジェ曲線として書き出してしまいます。ここまで、わかった状態でPDF出力の試験をすることで、更にわかってくることもあるものです。謎の順番で登録されている。このような謎の順番が何種類あるか知るにはどうしたらいいのか。ひとつひとつフォントの中身を見るしかないのか。MSゴシックはUnicode Fontで32008書体分がCMapで定義されているになっていると言える。


 元の番号と、グリフ体系がわかっていないとPDFを作るのはわりかし大変だなぁ。プログラムからどの対応するUnicode番号のグリフ番号が何番かを探せるようにしないと無理があるな。メイリオも違う番号になっているし。


 文字コードから、どのフォント内のどのグリフ番号を使うかというのを決める作業の自動化を文字列ベースでおこなえるような仕組みが無いと、任意のフォントで日本語を入力することはできないということを頭にいれておきましょう。プログラムで使うフォントが選択できるテキストエディタなんかの機能を盛り込んだときには、この細かい手続きを比較的意識することなくできているということですね。


 テキストエディタを保有するプログラムを制作する場合、CMapには何らかの文字コードに対応したグリフ番号の対応が記載されているので、文字コードから一意に決まるわけではなくても、状況に応じたデフォルトのグリフ番号が決まり、それが表示されている感じですね。プリンタへの印刷機能を盛り込んでいる場合でも、印刷処理をシステムにまかせているおかげで細かいことを考えずに印刷する機能も盛り込めているはずです。PDFへの印刷処理をするときは、このグリフ番号を取得する必要があるので、ここまで意識したプログラムをやったことがあるかというのが、この仕組みを利用できる重要な要素になると思います。


 ここでは、これ以上、Encodingで何を指定するべきかを考えても、なにも解決しないので、全体の機能を見渡したうえで、対処法を見つけた方が良いので、次に進みましょう。

/ToUnicode

 GIDに対応するUnicode番号が書かれた埋め込みCMapストリームへの参照を指定することで、GIDだけを使って書かれた、テキストについてコピー&ペーストにような文字コードとしての仕組みが必要な機能を支援します。GIDはグリフ番号で、CIDは文字の番号なので、文字コードの意味を持つ番号です。


 CMapのストリームに関してはコチラの記事で記述します。

 

/DescendantFonts

 Descendantは子孫とかって意味の英語。PDF内で扱うFontオブジェクトにぶら下がる実際のフォントを1つだけ明記する配列を指定することになっています。FontオブジェクトがこのPDFでのフォントの扱い方に関する情報であるのに対して、実際のフォント情報の内容を指定します。DecendantsFontの対応する配列内の /Type には必ず /Font という辞書名文字列を指定します。


 /DescendantFonts :/BaseFont

 必須。通常は親ノードの/BaseFont と同じフォント名 辞書名文字列になります。フォント名に半角スペースが含まれる場合は、対応する文字コード番号#20に置き換えます。コンソールの取り扱い文字コード、テキストの文字コードがSJISでもUTF-8でも同じ値です。

 

 /DescendantFonts :/CIDToGIDMap

 CID(文字コード)からGID(グリフ番号)への対応を指定。辞書名文字列を設定する場合、指定できるのは /Identity のみです。省略した場合も/Identityが指定されたものとして扱われます。この辞書はストリームを指定するオプションで Type 2 の CIDFont を使う場合のみ有効となるものです。使っている実例をみたことがないので、Glyph値cが1 なら1=<02><03>がインデックスのバイト番号です。0=<00><01> 1=<02><03>で、CIDはグリフ値3がMSゴシックフォントのように half space " "を表すCID 0x20=GID(グリフ値)=3なら

xx_cid1_Hi1 xx_cid1_Hi2  xx_cid1_Lo1 xx_cid1_Lo2 
xx_cid2_Hi1 xx_cid2_Hi2  xx_cid2_Lo1 xx_cid2_Lo2 
<00> <00> <00> <20>

のような、グリフ番号0から順にCID値が8バイトで連続するストリームになるということでしょう。みたことないけど。知らんけど。使うことないだろうけど、認証をとるようなPDFエディタを作ったらここもきっちり網羅しておかないと駄目なんだろう。わからないことの方が莫大にあるので、認証なんて一生取れないだろうけど。

obj x 0
<< 

>>
stream
<00> <00> <00> <00> %グリフ値 0 0は未定義文字のデフォルト表示文字だったような。CMapならば1から始まる。
<00> <00> <00> <00> %グリフ値 1
<00> <00> <00> <00> %グリフ値 2
<00> <00> <00> <20> %グリフ値 3
…
…
endstram

 

 /DescendantFonts :/DW

 1グリフ毎の横幅のデフォルト値。全グリフに適用される整数値。規定値は0  font内部の指定値に左右されますが、等幅半角文字なら1024emあるいは、1000、等幅全角文字なら2048emあるいは2000emとなっているものが多いでしょう。なので最大の2048を適用しておいて、それでは、他の文字が具合が悪いので、半角文字の一文字毎に次の/Wを使って指定することになります。範囲指定できるので、それほど大変ではありません。

/DW 2048
 /DescendantFonts :/W

 指定グリフ毎の横幅の値。全グリフに適用される配列整数値。規定値はなし。但しDW値は適用される。以下のような形式をとる。単位はem

<Syntaxhighlight2 lang="text" line=1> /W [

  100 [ 1024 1000 1024]
  103 128 1024
  160 223 1024
  33088 40956 2048
  57408 64508 2048
]

</syntaxhighlight2>

3行目は100 が そのあとの配列の1番目、配列が残り二つあるので、101から連番で101=1000、102=1024のように割り当てられる。個別指定が楽。

4行目は数字が3つ続く形式で1番目の数字から2番目の数値が文字番号の範囲で、3つめがグリフ幅の値を指定している。したがって103~128番の文字番号がグリフ横幅1024となる。範囲指定が楽。他の行も同じく。


 /DescendantFonts :/DW

 1グリフ毎の縦幅に関するデフォルト値。全グリフに適用される2つの整数値を持つ配列。規定値は[880 -1000]。横書きの設定も生かしつつ、縦書きをするための仕組みとして、v というベクトルを考える。vは横書き原点(ベースラインの右端)を縦書き原点(文字の水平の真ん中の上の頂点)に移すベクトルとして、そのベクトルのx成分は文字幅の半分。つまり、/W あるいは /DWで設定した値の半分。例えば文字幅が2024なら半分の1024。y成分を指定する値の一つとして表現する。そして、vによって移った原点を真下に送る値をもう一つの指定値で表現する。以下のように座標値を数式で表現できる。ベクトルのyは上方向がプラス、xは右方向がプラスとすると


 v= (x座標量 = 文字幅/2, y座標量 = vのyの大きさ)


 縦書きのy方向への移動量をベクトルw1で表現するとx座標量は常に0で

 w1 = (x座標量 = 0, y座標量 = w1のyの大きさ)

 と書ける。このときのvのyの大きさとw1のyの大きさを指定すると縦書きに必要な移動量が決まるため、/DW2では、この2つの値の整数値を設定した配列をオペランドで指定する。規定値の


 /DW2 [880 -1000]

 は、上方向に880em移動したところを原点に下方向に1000em移動することを意味する。あたらしく決まった原点にたいして、次の文字を縦書きになるように新しい文字の幅の真ん中を中心にして、文字の縦幅の上の頂点が重なるように配置していく。ということを意味している。

   配列の最初の値が大きいほど上の文字との近づく。その次の値が大きいと文字幅が小さくなり次の文字が詰まってくる。そういうイメージだ。


 /W2では、ベクトルvのxの大きさも指定するので、/Wと/DWのように同じ意味の引数になっていない関係に注意したいところ。

 

 /DescendantFonts :/W2

 指定グリフ毎の縦幅の値。全グリフに適用される配列整数値。規定値はなし。但しDW2の値は適用される。以下のような形式をとる。単位はem

<Syntaxhighlight2 lang="text" line=1> /W2 [

  100 [ -1000 500 880 -1024 512 884 -1000 500 880]
  103 128 -1024 512 884
  160 223 -1024 512 884
  33088 40956 -2048 1028 2048
  57408 64508 -2048 1028 2048
]

</syntaxhighlight2>


 3行目は100 が そのあとの配列の1・2・3番目で縦文字幅を調整、配列が残り二つあるので、101から連番で101が4・5・6番目で、102が7・8・9番目のように割り当てられる。個別指定が楽。DW2とW2とで引数の数が異なりW2の方が3つで一つ多い、DW2の2番目の値がW2の例の一番最初の値と同じ。DW2の最初の値がW2の例の3番目と同じ。2番目は v ベクトルのxの座標量も指定している。文字の中心をこの辞書で個別に設定していけることを意味している。


 4行目は数字が5つ続く形式で1番目の数字から2番目の数値が文字番号の範囲で、のこりの3つがグリフ幅の値を指定している。したがって103~128番の文字番号がグリフ縦幅上に884em移動して中心は512emとなり、文字幅として-1024を移動するような動作をするグリフ群となる。この方法は範囲指定が楽。他の行も同じ動きで記述されていく。

 

 /DescendantFonts :/Subtype

 ここでは実際に使うフォントファイルの形式を指定します。このCIDFont辞書のエントリでは/CIDFontTye0か/CIDFontTye0のどちらかを指定します。TrueType系なら/CIDFontType2。OpenType系なら/Subtype /CIDFontType0と指定します。間違えても細かいことをしない限りか?現段階の大雑把な操作ではあまり問題はおこらないようです。いやOpen TypeのようでTrueTypeとして動くようなフォントが多いWindowsのおかげか、

 /DescendantFonts :/FontDescriptor

 Descriptor=説明するもの。という英訳ができるわけです。この辞書の中で呼び出したフォントファイルの詳細な扱い方を設定します。呼び出したフォントが Type0(複合型フォント) あるいは Type3(内部で定義するフォントプログラム)の場合。このフォントデスクリプタは設定しません。フォントのことをよく知っていないと設定できないような値が続きます。

辞書名文字列 キー オペランド型 説明
Type 辞書名 /FontDescriptor と記述しなければなりません。
Ascent 数値 アクセント記号を除いたグリフの最大高さベースラインが 0 基準となる大きさ
CapHeight 数値 アクセント記号を除いたアルファベット大文字の最大高さ。
Descent 数値 グリフの最大低さ深さベースラインが 0 基準となる大きさ
Flags 整数値 等幅フォントであるかなど、特性を示す番号
FontBBox 矩形 グリフのem単位座標系でフォントを囲むような値。文字を選択したときに強調される矩形だと思います。
FontName 辞書名 フォントのPostScript名。日本語名でも英語名でもいい。
ItalicAngle 数値 左に傾く度数をプラスとした斜体文字向けの角度指定。値が0以外の場合は通常マイナスの値になるはず。
StemV 数値 文字の軸の幅。全体の最大付近の幅みたいな値になります。
AvgWidth 数値 規定値0。0なら指定されていないとみなす。フォントの平均幅。
FontFile ストリーム Type1形式のフォントプログラム埋め込みストリーム
FontFile2 ストリーム TrueType形式のフォントプログラム埋め込みストリーム
FontFile3 ストリーム Type1、TrueType形式以外のフォントプログラム埋め込みストリーム。形式名はストリーム内辞書のSubTypeで指定。
Leading 数値 テキスト行間のベースライン同士の空き縦幅。規定値 0。0は指定なしの扱い。
MaxWidth 数値 フォントのグリフ最大幅
MissingWidth 数値 フォント辞書Widths配列で幅が指定されていない場合に使われる幅。規定値は 0。
StemH 数値 StemVに対応する。垂直方向の幅の値
XHieght 数値 アルファベット小文字向けのHeightに対応する値。
CharSet 文字列 Type1フォントのみで有効な値。サブセット化するために特定の文字を指定する/から始まる名前をリスト化した文字列。この情報が無い場合はサブセットであることを示すものはFontNameにタグがつけられているかどうかになります。


CharSetで指定する各文字名の正式な名称は PDFリファレンスの最後の付録のあたりに記述があります。

 

  /DescendantFonts :/FontDescriptor : /Flags

 フラグ番号毎の意味。数字はビット位置です。2進数の桁数の位置です。フラグは複数を一括で指定することもできます。

1.FixedPitch

等幅フォントです。日本語の等幅は半角と全角で幅が違うので、該当しません。等幅だけど正式にはプロポーショナル扱いです。


2.Serif

明朝フォントのようなウロコのついた文字です。


3.Symbolic

記号の集まりのようなフォント


4.Script

手書きフォント、英字の場合は筆写体。


6.NonSymbolic

ラテン文字またはそのサブセット


7.Italic

斜体文字


17.AllCap

小文字が含まれないフォント


18.SmallCap

大文字と小文字の字形は分かれて登録されているものの、文字サイズが同じ大きさになっている。


19.ForceBold

小さな文字になっても、最小の細さは保持されて太字指定がくずれない。


 2と6と19が複合した場合は 100 0000 0000 0010 0010 = 10進数で262178となります。したがって

/flags 262178

 と設定します。

 

  /DescendantFonts :/FontDescriptor : /CharSet

 サブセットっていうのは、Fontファイルに含まれる文字の一部をだけを抜き出して保持するようなフォントです。特に埋め込みする場合は全部の文字を保持することは意味がありません。このサブセットというフォントを再構成して任意の6文字を先頭につけたものを作ります。例えば


 EOODIA+Poetica


 というようなフォント名はサブセット化されているフォント名の代表です。その中に含まれている文字を文字列で指定することでサブセット化されていて、どの文字が含まれているかを指定するものになっています。

 

 /DescendantFonts :/CIDSystemInfo

 ここにCIDFontの情報を記述します。必須の情報です。説明の要素が強い情報です。PDFのビューワーから、この情報にアクセスして、どんなCIDFontが使われているのかを知る術になっているようです。


この中に3つの辞書を設定します。


  • /Registry
 このCID情報の登録者文字列を設定します。個人でフォントを登録することは少ないので会社名であることが多いようです。よく使われるのが (Adobe) です。文字列です。
  • /Ordering
 文字のエンコーディング方式名を設定します。Identityならそのまま、UCSはユニコードシーケンス。といった具合です。(Japan1) ならAdobeJapan1です。
  • /Supplement
 補足です。ここには文字のエンコーディング方式の世代番号のようなものを登録します。整数です。0~65535くらいまで設定できます。AdobeJapan1の場合は登録文字数によって 0~7 に分けられています。7はUnicode文字セットのほとんどを収録しているようなフォントになります。

 

SubType Type3の利用によるテキスト出力

 Type3 は PDFのフォントストリーム部分にPDFでのグラフィック描画処理を流用してフォントプログラムを構成する方法。試しにやってみたけど、今のところ、エラーになりまくりです。

 自分が作ったType3フォント2文字は以下のように生成したものの、Font リソース部で名前をつけて参照先をしていして実体化させようとしたところで、PDFオープン時にエラーになる。悲しい。


 途中経過としては以下のようなスクリプトだ。インターネットに出回っているType3のPDFはうまくFontの定義は出来ているみたい。でもマネしてもダメだな。もう少し理解しないと駄目だな。


 ページの処理中にエラーが発生しました。文書を読み取り中に問題が発生しました(18)となるType3指定例。解決編までは、しばし待たれよ。それっていつだよ。だな。

<Syntaxhighlight2 lang="text"> …

… 4 0 obj <<

  /Font 
  << 
     /F0 15 0 R 
     /F1 6 0 R 
     /F2 14 0 R 
     %/Type3Font 16 0 R %Type3
  >> 

>> endobj …

… stream … %/Type3Font 10.0 Tf %(abababab) Tj T* … endstream …

16 0 obj <<

  /Name/Type3Font
  /Type /Font 

% /Resources % << % /ProcSet [/PDF /ImageB] % >>

  /SubType /Type3 
  /FontBBox [0 0 750 750]
  /FontMatrix [0.001 0 0 0.001 0 0] 
  /FirstChar 97 
  /LastChar 98 
  /Widths [1000 1000] 
  /Encoding 17 0 R 
  /CharProcs 18 0 R 

>> endobj

17 0 obj <<

  /Type /Encoding 
  /Differences [97 /square /triangle] 

>> endobj

18 0 obj <<

  /square 19 0 R 
  /triangle 20 0 R 

>> endobj

19 0 obj <<

>> stream

  1000 0 0 0 750 750 d1 
  0 0 750 750 re
  f

endstream endobj

20 0 obj <<

>> stream

  1000 0 0 0 750 750 d1 
  0 0 m 
  375 750 l 
  750 0 l 
  f 

endstream endobj

xref 0 21 …

… </Syntaxhighlight2>  上の方にある行

%/Type3Font 16 0 R %Type3

 のコメントを外すとエラーでコメント化したままなら問題ないです。惜しいところまで来ている。凡ミスしてるんだろうなぁ。わからん。落ち着け俺。うまくいっているインターネット上から入手したType3フォントを使っているPDFあるんだから、うまくいくはず。

フォントプログラムの埋め込み

 ここまでフォントの原理を理解することに焦点を合わせて、プログラムを埋め込まない使い方をしましたが、埋め込まない日本語PDFはISOの意に沿わない形式と言えるそうです。なので埋め込まないといけないのですが、フォントファイルから埋め込みフォントの記述方法については詳しく述べられた文献がみつからないため、続編記事には時間がかかりそうです。


 なんやかんやで今まで、色々なわからないことを潰してきましたので、なんとかしていくでしょう。 2022年8月上旬現在。

 やってみたんですけど。Wordとかで埋め込まれたフォントストリームをバイナリ―エディタで切り出して、元と同じファイル名の拡張子を付けると、抽出された文字だけのサブセットフォントになっています。バイナリーエディタで編集するときは stream 0x0a…0x0a endstream という具合に改行コード 0a があると思うので、0aも切り取ります。フォントファイルのバイナリーは…の部分そのものということ。


 なのでフォントファイルの編集方法がわかれば、埋め込み用のフォントプログラムは作れそうです。でも編集方法がわかるくらいなら、フォントエディタでも作れるくらいのスキルがあるわけで、なかなかの壁だな。PDFで使った文字コードすべてを検出して、フォントファイルから、使った文字コードに対応するグリフだけを残して、あとは消したフォントサブセットをつくる。これができればPDF文書の内製化および発展的使い方も見えてくる。あるいは、印刷機能を理解する方面からPDFConverterとストリームのやり取りをするという方法に戻るか。それでは、なんだか面白くない。せっかくPDFの構造を理解することに積極的になったのだから、やりこみたいような気もする。


 素人はフォントファイルを編集するFontForgeとかの先人達が築き上げた立派なフォントエディタでいじったものを張り付けて埋め込むという作業でもしてないさいって感じになるのかな。フォントファイルの構造解析しないとだめだな。また勉強だ。意外と根本の原理がわかっていないでコンピュータつかってるんだなぁって思わされる。フォントひとつすら理解できていないだもんな。恥ずかしい限り。


再び、2022年8月上旬現在。あまり時間かからず確かめれてる。

 

PDF 内部構造に戻る。