フォントツール(仮想フォント / 一覧)

投稿規程に「フォントをすべて埋め込むこと」と書かれていて、自分の PDF がそうなっているか分からない。イタリックにしたはずの箇所が斜めにならない。fontspecNumbers=OldStyle と書いたのに何も変わらない。こうした場面で効くのは推測ではなく 調べる道具 です。このページでは LaTeX まわりのフォント検査ツールを、実際に走らせた出力とともに並べます——pdffontsfc-listotfinfo\fontname\font、字形一覧を刷る nfssfont、そして 仮想フォント(.vf を人間可読に開く vftovp。ついでに、なぜ TeX のフォント名が phvbc8t のような暗号なのかも解けます。

いま組まれているのはどのフォントか — \fontname と \showthe\font

文書中のその一点で何が選ばれているかは、\fontname\font が実ファイル名で、\showthe\font が NFSS の 5 座標で答えます。前者は cmr10cmbx10cmti10 のように TeX が実際に読み込んだメトリックの名前 を返し、後者はログに > \OT1/cmr/m/n/10 . と刷ります。この 2 つが食い違うことはありません——後者が要求、前者が結果です。両方を見れば「要求どおりのフォントが来たのか、途中で置換されたのか」が一目で分かります。

log
% \typeout{fontname = \fontname\font} in a default article
fontname = cmr10
bf fontname = cmbx10
it fontname = cmti10

% \showthe\font stops the run and prints the NFSS name
> \OT1/cmr/m/n/10 .

% \showfont is NOT a LaTeX command; it belongs to ConTeXt.
% In LaTeX it fails with:
! Undefined control sequence.

ここで一つ訂正しておきます。\showfont は LaTeX の命令ではありません。 現在のフォント情報を出す命令としてしばしば紹介されますが、LaTeX2e のカーネルにもパッケージにも存在せず、実行すると ! Undefined control sequence. になります(同名の命令は ConTeXt にあります)。LaTeX で相当することをしたいなら、上の 2 つに加えて内部マクロを直接読む手があります——\makeatletter の中で \f@encoding / \f@family / \f@series / \f@shape / \f@size を並べれば、5 座標を好きな書式で \typeout できます。

フォント読み込みそのものを追いたいときは tracefnt パッケージです。\tracingfonts=2 にすると、実フォントを読むたびに「どの外部フォントを、どの NFSS 名として読んだか」がログに出ます。フォントが置換されたときの原因追跡には、置換警告よりこちらのほうが情報量があります。ただし出力は大量になるので、問題のある \section だけを含む最小文書に切り出してから走らせるのが現実的です。

log
% \usepackage{tracefnt} + \tracingfonts=2, document set in T1
LaTeX Font Info:    External font `ecbx1000 at10.0pt' loaded as
(Font)              T1/cmr/bx/n/10  on input line 6.
LaTeX Font Info:    External font `ecti1000 at10.0pt' loaded as
(Font)              T1/cmr/m/it/10  on input line 6.

PDF に埋め込まれたフォントを調べる — pdffonts

pdffonts(Poppler / Xpdf 付属)は、できあがった PDF に どのフォントが、どんな形式で、埋め込まれているか を 1 行 1 フォントで表にします。見るべき列は 3 つ——emb が埋め込みの有無、sub がサブセット化(使った字だけ抜き出したか)、uni が Unicode への逆対応(PDF から文字をコピーできるか)です。投稿前の確認はここだけで済み、emb の列が全部 yes なら「フォント未埋め込み」で差し戻される心配はありません。

terminal
# a document compiled with pdfLaTeX and \usepackage[T1]{fontenc}
$ pdffonts pf.pdf
name                                 type              encoding         emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
OSZFFG+SFRM1000                      Type 1            Custom           yes yes yes      4  0
PCDLRW+SFBX1000                      Type 1            Custom           yes yes yes      5  0
IEKZWO+SFTI1000                      Type 1            Custom           yes yes yes      6  0
GFIBPT+CMMI10                        Type 1            Builtin          yes yes yes      8  0

# the same document via XeLaTeX + fontspec with an OpenType face
$ pdffonts pfx.pdf
PPMQXL+TeXGyrePagella-Regular-Identity-H CID Type 0C   Identity-H       yes yes yes      4  0
AXDTQU+TeXGyrePagella-Bold-Identity-H    CID Type 0C   Identity-H       yes yes yes      6  0

この出力は engine の指紋にもなります。pdfLaTeX が Type 1 フォントを Custom / Builtin エンコーディングで埋め込むのに対し、XeLaTeX や LuaLaTeX が fontspec 経由で OpenType を使うと CID Type 0CIdentity-H になる——他人から受け取った PDF が何で組まれたかは、これでほぼ判別できます。名前の頭に付く 6 文字(OSZFFG+ など)はサブセットの識別子で、同じフォントを別々にサブセット化すると別の接頭辞が付きます。逆に embno の行があれば、そのフォントは閲覧者の環境任せです——欧文の基本 14 書体なら大抵は問題になりませんが、投稿では弾かれます。

システムにあるフォントを調べる — fc-list と otfinfo

fontspec に渡す名前は フォント側の名前 でなければならず、思いつきで書いても通りません。インストール済みのフォントを名前とファイルの対応つきで一覧するのが fc-list(fontconfig 付属)です。引数なしなら全件、fc-list : family なら家族名だけ、パターンを渡せば絞り込めます。LuaTeX 側には同じ役目の luaotfload-tool があり、--find で名前解決、--update でフォント名データベースの再構築ができます。なお fontconfig は Linux では標準ですが、macOS には既定で入っていません(Homebrew などで別途入れる形になります)。

terminal
# every installed font, one line each (this machine reports 2799 lines)
$ fc-list | wc -l
2799

# narrow to one family: file, then all its names, then the style
$ fc-list "Hiragino Mincho ProN" file family style
/System/Library/Fonts/HiraginoMinchoProN.ttc: Hiragino Mincho ProN,\
  Hiragino Mincho ProN W6:style=W6,Regular

# what would fontconfig pick for a generic request?
$ fc-match sans
Verdana.ttf: "Verdana" "Regular"

そのフォントが 何をできるのか を調べるのは otfinfo(TeX Live 同梱、lcdf-typetools)の仕事です。-i で名前・版・著作権、-f で OpenType の 機能タグ-s で対応スクリプトと言語、-t でテーブル一覧。とりわけ -f は実用価値が高く、fontspecNumbers=OldStyleLetters=SmallCaps を書く前にここで onumsmcp の存在を確かめれば、「書いたのに効かない」の大半は未然に防げます。機能が無いフォントに機能名を書いても、静かに無視されるだけだからです。

terminal
$ otfinfo -i $(kpsewhich texgyrepagella-regular.otf)
Family:              TeX Gyre Pagella
Subfamily:           Regular
PostScript name:     TeXGyrePagella-Regular
Version:             Version 2.501;PS 2.501;ffdkm 0.1
Copyright:           Copyright 2006-2018 for TeX Gyre extensions by B. Jackowski,
                     J.M. Nowacki, et al. Vietnamese characters were added by Han The Thanh.

$ otfinfo -f $(kpsewhich texgyrepagella-regular.otf)
c2sc	Small Capitals From Capitals
dlig	Discretionary Ligatures
kern	Kerning
liga	Standard Ligatures
lnum	Lining Figures
onum	Oldstyle Figures
size	Optical Size
smcp	Small Capitals
ss01	Stylistic Set 1

この -i の出力には小さな縁も写っています。TeX Gyre Pagella のベトナム語文字を追加したのは Hàn Thế Thành——pdfTeX の作者であり、このサイトの microtype のページで扱うマイクロタイポグラフィをその博士論文で TeX にもたらした人物です。フォントの著作権表示は、案外そういう来歴を残しています。

フォントの全字形を刷る — nfssfont.tex と fonttable

フォントに実際どの字形が入っているかを 目で確かめる には字形テーブルを刷ります。TeX に昔から付属する対話ツールが nfssfont.tex で、起動すると Input external font name, e.g., cmr10 と尋ねてくるので cmr10 などと答え、\table で表、\bye で終了。ここで一点、よく出回る tex nfssfont という起動法は 動きません——nfssfont.tex の 48 行目は \documentclass{article} で、これは LaTeX 文書です。plain TeX で起動すると ! Emergency stop. で落ちます。正しくは latex nfssfont または pdflatex nfssfont です。

terminal
$ pdflatex nfssfont
Input external font name, e.g., cmr10
(or <enter> for NFSS classification of font): cmr10
Now type a test command or \help for help [ \table \bye ]: \table
...
Output written on nfssfont.pdf (1 page, 71803 bytes).

# \help lists the whole repertoire:
#   \init switches to another font;  \stop or \bye finishes the run;
#   \table prints the font layout in tabular format;
#   \text prints a sample text, assuming TeX text font conventions;
#   \sample combines \table and \text;
#   \lowers \uppers \digits \math \punct \names \bigtest ...

# Knuth's original is plain TeX and does start under tex:
$ tex testfont
Name of the font to test = cmr10
Now type a test command (\help for help): \table

対話が面倒なら、同じことを文書の中でやる fonttable パッケージ があります。\fonttable{cmr10} はフォント名(正確には .tfm の名前)を受け取って全字形の表を組みます(Zapf Dingbats を覗くなら \fonttable{pzdr})。\xfonttable{T1}{cmr}{m}{n} は NFSS の 4 属性で指定します。表の範囲は \fontrange{<下限>}{<上限>} で 16 字ブロック単位に絞れ、最大 256 字形。字形の格子ではなく そのフォントで組んだ例文 を見たいなら \fonttext{cmr10} です。このパッケージのコードの多くは nfssfont.tex の改変版で、字形テストの命令群は Knuth の testfont.tex の再実装——つまり同じ道具の 3 つの顔だと思ってかまいません。なお nfssfont の起動時に何も入力せずに改行すると、フォント名ではなく NFSS の 5 属性で 指定するモードに入ります。

仮想フォント(.vf)— 実在しない字形をこしらえる層

仮想フォント(.vf は、TeX が見る 1 つの論理フォントを、実際の字形や低水準の組版命令の列へ翻訳する層です。TeX FAQ の言い方を借りれば、仮想フォントは「あちこちの断片を寄せ集めてフォントの字形を作る手段」で、断片は 別のフォント・罫線・位置指定つきの組版命令 から取ってこられます。抽象的に聞こえますが、実物を開くと一発で腑に落ちます。T1 エンコーディングの Times(ptmr8t)を vftovp で開くと、ff 合字の位置にはこう書いてあります——f を置き、−0.025 em 戻し、もう一度 f を置く。素の Times には ff の字形が無いので、仮想フォントがその場で組み立てているのです。

terminal
$ vftovp $(kpsewhich ptmr8t.vf) $(kpsewhich ptmr8t.tfm) ptmr8t.vpl
$ head -5 ptmr8t.vpl
(VTITLE )
(FAMILY UNSPECIFIED)
(CODINGSCHEME EXTENDED TEX FONT ENCODING - LATIN)
(DESIGNSIZE R 10.0)
(MAPFONT D 0 (FONTNAME ptmr8r) ...)

# slot O 33 (octal) is the ff ligature of T1 - assembled from two f glyphs
(CHARACTER O 33
   (CHARWD R 0.640991)
   (CHARHT R 0.677991)
   (MAP
      (SETCHAR C f)
      (MOVERIGHT R -0.025)
      (SETCHAR C f)
      )
   )

仕組みは役割分担です。TeX 本体は字幅・高さ・深さという 寸法だけ 知ればよく、それは対になる .tfm(TeX Font Metric) から読みます。各字形を「実際にどう描くか」は .vf に書かれ、これを読むのは DVI ドライバ(dvips や dvipdfmx)です。つまり仮想フォントは 常に .tfm.vf の 2 つで 1 組。バイナリのままでは読めないので、人間可読のテキスト形式 .vpl(Virtual Property List) と相互変換する 2 本のプログラムが、どのディストリビューションにも入っています。欧文メトリック単体にも .pl(Property List)という同じ関係の形式があり、tftopl で開くと FONTDIMEN の 7 パラメータと LIGTABLE の合字・カーニング表が読めます。

  • vftovp.vf.tfm(バイナリ)→ .vpl(テキスト)。引数の順は 仮想フォント・メトリック・出力。既存の仮想フォントを 読み解く ときはこちら。
  • vptovf.vpl(テキスト)→ .vf.tfm(バイナリ)。引数の順は 記述・仮想フォント・メトリック。編集した記述を焼き戻します。
  • tftopl / pltotf — 欧文メトリック単体の相互変換。.tfm の中身(FONTDIMENLIGTABLE)を読むときに使います。
  • ppltotf / ptftopl — 上の 2 本の和文版。pTeX 系のバイナリ JFM を扱います。
  • いずれも拡張子は省略でき、それぞれ .vf / .tfm / .vpl が補われます。

できあがった .tfm.../fonts/tfm/<供給元>/.vf.../fonts/vf/<供給元>/ という texmf ツリーの定位置に置き、mktexlsr でファイル名データベースを更新すれば kpsewhich ptmr8t.vf で見つかるようになります。TFM 名と実際のフォントファイルの対応表(フォントマップ)を管理するのは updmap の役目です。ただし現代の作業で .vpl を手書きすることはまずありません——PostScript や OpenType フォントから TeX 用のメトリックと仮想フォントを生成するのは fontinst の仕事です。仮想フォントは「一から書く」ものではなく、生成されたものを 開いて読み、必要なら微調整する 対象だと考えるのが実情に合っています。

なぜ TeX のフォント名は暗号なのか — texdoc fontname

答えは「8 文字しか使えなかったから」です。texdoc fontname で開ける Fontname の規約は、その理由を隠しません——DOS のファイルシステムと、CD-ROM の配布に使われた ISO 9660 規格との互換性のために、名前を 8 文字に制限すると明記されています。使える文字は小文字の a〜z、数字、アンダースコアだけ。その 8 文字を S TT W [V...] [N] [E] [DD]——供給元・書体・太さ・変種・エンコーディング・幅・デザインサイズ——に割り当てる、というのが規約の全体です。

phvbc8t の部分規約上の位置意味
p供給元(S)Adobe。supplier.map の注記いわく「PostScript の p」
hv書体(TT)Helvetica(typeface.maphv helvetic
b太さ(W)ボールド(weight.mapb Bold
c幅(E)コンデンス。NFSS 側では b と合流して bc になる
8tエンコーディング(N)variant.map8t ECEncoding CorkEncoding — つまり T1

この表を一度読んでおくと、.fd ファイルやログに並ぶ名前が急に喋りはじめます。ptmr8t は Adobe の Times レギュラー T1、pcrr8t は Courier レギュラー T1、ptmb8c は Times ボールドの TS18c は Text Companion)。7t は「TeX text エンコーディング」つまり OT18r は生の Type 1 を受け止めるための TeX Base 1 で、T1 用の仮想フォントが土台にする実フォントがこれです——前節で ptmr8t.vfptmr8rMAPFONT に持っていたのは、そういう意味でした。なお Fontname は 8 文字の窮屈さも認めていて、規約自身が長い名前の扱いについて別節を設けています。

和文のメトリック — JFM と文字クラス

和文の組版には欧文の .tfm では足りないものがあります。漢字や仮名は基本的に 正方形(全角)の枠 に収まり、字間は単語間スペースではなく 約物(句読点・括弧)の前後でだけ 詰めたり空けたりするからです。この情報を担うのが JFM(Japanese Font Metric、和文用フォントメトリック) で、pTeX・upTeX は .tfm に似たバイナリの JFM を、LuaTeX-ja は同じ役割を Lua のテーブル で記述します。前者は ppltotf / ptftopl でテキストと相互変換でき、後者はそもそもテキストなので直接読めます。

欧文 TFM との最大の違いは、JFM が 文字を「文字クラス(character class)」にまとめて扱う ことです。LuaTeX-ja のマニュアルが定めるとおり、JFM の寸法はすべて デザインサイズを単位とする浮動小数点数 で書かれ、zw(全角の幅)と zh(全角の高さ=height+depth)を基準に、各クラスごとに widthheightdepthitalic(イタリック補正)を与えます。クラス 0 は必ず存在し、他のどのクラスにも属さない大多数の和文文字がここに入ります。約物は別クラスに分け、クラスとクラスの間に挿入するグルー/カーン を表で持たせる——これが約物まわりの空きを生む仕組みです。

jfm.lua
-- A LuaTeX-ja JFM (excerpt); all lengths are in design-size units
luatexja.jfont.define_jfm {
  version = 3,
  dir = 'yoko',            -- horizontal writing
  zw = 1.0, zh = 1.0,       -- full width, full height
  [0] = {                  -- class 0: most kanji and kana
    chars = { '漢' },
    width = 1.0, height = 0.88, depth = 0.12, italic = 0.0,
  },
  [1] = {                  -- a class for stops and commas
    chars = { '。', '、' },
    width = 0.5, height = 0.88, depth = 0.12, italic = 0.0,
  },
}

pTeX 系では、和文の JAglue(和文文字どうし、および和文と欧文のあいだに入る空き)が組版時に自動挿入されます。クラス間の空きは JFM が決め、クラス指定のない和文どうしには既定の kanjiskip、和文と欧文の境には xkanjiskip が入ります。これらは、行頭・行末に来てはいけない文字を制御する 禁則処理(kinsoku) と並んで和文組版の体裁を支えます。LuaTeX-ja には用途別の標準 JFM が付属しており、jfm-ujis.lua が標準(upTeX 用メトリック upnmlminr-h.tfm を基にしたもので、多くの字が正方形)、jfm-jis.lua は pTeX で広く使われる jis.tfm 相当、jfm-min.lua は pTeX 既定の min10.tfm 相当です。フォント定義では \jfont\F=HaranoAjiMincho-Regular:jfm=ujis のように jfm= で選びます。