フォントの仕組み(NFSS / エンコーディング)

LaTeX の警告に出てくる OT1/cmr/m/n/10 という文字列は、暗号ではなく 住所 です。LaTeX はフォントを「これ」と名指しせず、エンコーディング/ファミリ/シリーズ/シェイプ/サイズ という 5 つの座標で指し示します。この仕組みを NFSS と呼びます。このページでは、その 5 座標がどう決まるのかを一次資料と実測で追いかけます——なぜ \bfseries は太字に b ではなく bx を要求するのか、\fontseries{c} のあとで太字にすると本当に bc になるのか、そして Font shape ... undefined という置換警告が何を告げているのか。

NFSS — フォントは 5 つの座標で決まる

LaTeX2e のフォント選択は NFSS(New Font Selection Scheme、新フォント選択方式) という仕組みで動いており、その中核は「どんなテキストフォントも 5 つの属性で完全に決まる」という一点です。5 つとは エンコーディング(encoding)・ファミリ(family)・シリーズ(series)・シェイプ(shape)・サイズ(size)。NFSS を設計したのは Frank Mittelbach と Rainer Schöpf で、カーネルのソース ltfssbas.dtx の著作権表示は 1989 年にさかのぼります。それ以前の LaTeX 2.09 では \bf\it を同時にかけることすらできませんでした——太さと形が別の軸だという発想がなかったからです。

  • エンコーディング — フォント内で字形が並ぶ順序(入力文字と字形スロットの対応)。本文用では OT1(TeX text)と T1(TeX extended text、通称 Cork)が代表格。後半で詳述します。
  • ファミリ — 字の骨格を共有するフォントの集まり。Computer Modern Roman が cmr、そのサンセリフが cmss、タイプライタが cmtt。商用系では Times が ptm、Helvetica が phv、Courier が pcr
  • シリーズ — 太さ(weight)と幅(width)を 1 本にまとめた 軸。m(標準)、b(ボールド)、bx(ボールドエクステンデッド)、c(コンデンス)、bc(ボールドコンデンス)、sb(セミボールド)など。
  • シェイプ — 字の形。n(直立)、it(イタリック)、sl(スラント)、sc(スモールキャップス)。まれに ui(斜めでないイタリック)も。
  • サイズ — デザインサイズ。10pt のような寸法で、単位を省くと pt とみなされます。

シリーズが太さと幅を 1 本にまとめている点だけは、慣れるまで引っかかります。標準の表記では太さが ulub(ウルトラライト〜ウルトラボールド)、幅が ucux(ウルトラコンデンス〜ウルトラエクスパンデッド)で、両者を連結して 1 つの値にし、ただし m は——太さも幅も標準のときに 1 文字だけ残すのを除いて——省きます。だから「ボールド・標準幅」は b、「ボールド・広幅」は bx、「標準の太さ・細幅」は c、「ボールド・細幅」は bc。この 5 つを並べた エンコーディング/ファミリ/シリーズ/シェイプ/サイズ が、LaTeX 内部でのフォントの正式名です。いま何が選ばれているかは \showthe\font で確認できます。

log
% \showthe\font in a default article, and what the log answers:
> \OT1/cmr/m/n/10 .

% the same five values head every overfull-box report:
Overfull \hbox (121.83368pt too wide) detected at line 3
\OT1/cmr/m/n/10 Supercalifragilisticexpialidocious

% and the first four of them name a shape that had to be substituted:
LaTeX Font Warning: Font shape `T1/cmss/bx/sc' undefined
(Font)              using `T1/cmss/bx/n' instead on input line 4.

なぜ \bfseries は b ではなく bx を要求するのか

答えは「\bfseries\bfdefault を読んでいないから」です。カーネルの \bfdefault は確かに b ですが、\bfseries は現在のファミリが \rmdefault\sfdefault\ttdefault のいずれかと一致するかをまず調べ、一致すれば 専用の既定値 \bfseries@rm / \bfseries@sf / \bfseries@tt を使います。そして latex.ltx はこの 3 つを bx で初期化しています。\bfdefault が使われるのは、現在のファミリがその 3 つのどれでもないときだけ——つまり本文フォントで \bfseries を打つかぎり、\bfdefault は素通りされます。

latex.ltx
% TeX Live 2024, latex.ltx (the LaTeX2e kernel), abridged
\def\bfseries@rm{bx}
\def\bfseries@sf{bx}
\def\bfseries@tt{bx}

\DeclareRobustCommand\bfseries{%
  \not@math@alphabet\bfseries\mathbf
  \expand@font@defaults
  \maybe@update@bfseries@defaults
  \ifx\f@family\rmdef@ult      \fontseries\bfseries@rm
  \else\ifx\f@family\sfdef@ult \fontseries\bfseries@sf
  \else\ifx\f@family\ttdef@ult \fontseries\bfseries@tt
  \else                        \fontseries\bfdefault
  \fi\fi\fi
  \UseHook{bfseries}%
  \selectfont
}

では、なぜ既定が bx なのか。理由は Knuth が実際に切ったフォントの本数にあります。TeX Live 2024 の Computer Modern を数えると、素の太字 cmbcmb10.tfm の 1 本しか存在しません。一方ボールドエクステンデッドの cmbxcmbx5cmbx6cmbx7cmbx8cmbx9cmbx10cmbx12 と 7 本あります。脚注や添字まで含めてまともに太字を出せるのは cmbx のほうだけで、だから NFSS は Computer Modern に対して bx を要求するのです。

ここまでなら単なる Computer Modern 固有の事情ですが、カーネルはちゃんと後始末をしています。\begin{document} の時点で走る \init@series@setup が、\rmdefaultcmr, cmss, cmtt, lcmss, lcmtt, lmr, lmss, lmtt のどれでもなければ \bfseries@rmb に落とす のです。つまり bx は Computer Modern と Latin Modern だけの遺産で、Times や TeX Gyre に切り替えた瞬間、\bfseries は静かに b を要求するようになります。実測でも \renewcommand\rmdefault{ptm} を置くと \bfseries@rmb\bfseries の結果も b になりました。

文書の設定\bfseries が要求するシリーズ理由
article (default)bx\rmdefaultcmr なので \bfseries@rm の初期値がそのまま残る
\usepackage{lmodern}bxlmr もカーネルの Computer Modern 系リストに入っている
\renewcommand\rmdefault{ptm}b\init@series@setup がリスト外と判定して \bfseries@rm を下げる
\fontseries{b}\selectfontb低水準で名指しした値は既定値マクロを通らない
\DeclareFontSeriesDefault[rm]{bf}{sb}sbローマン体の太字だけを狙って書き換える正式な作法

実務上の結論は明快です。太字の指定を変えたいなら \renewcommand{\bfdefault}{b} よりも \DeclareFontSeriesDefault[rm]{bf}{b} を使ってください。前者も動きます(\bfdefault が書き換わると \maybe@update@bfseries@defaults が 3 つの専用既定値を上書きします)が、rm だけ・sf だけといった狙い撃ちができません。そして「太字にしたのに太くならない」という症状に出会ったら、まず疑うのはこの一致・不一致です——フォントが bx を持っていないだけかもしれません。

シリーズは合成される — cb を重ねると bc

2020 年の LaTeX2e(latexrelease.sty\IncludeInRelease{2020/02/02})から、シリーズの指定は 上書きではなく合成 になりました。細幅(c)で組んでいる途中に太字を要求すると、b で置き換わるのではなく bc(ボールドコンデンス) に合流します。この合流表を作っているのが \DeclareFontSeriesChangeRule で、カーネルには 200 行以上の規則が並んでいます。{c}{b}{bc}{} は「現在 c のときに b を求められたら bc にせよ、代替は無し」と読みます。

latex.ltx
% the rule table (latex.ltx, from ltfssaxes.dtx), a handful of the 200-odd entries
\def\DeclareFontSeriesChangeRule#1#2#3#4{%
  \@namedef{series@#1@#2}{{#3}{#4}}}
\DeclareFontSeriesChangeRule {c}{b}{bc}{}     % condensed + bold      -> bc
\DeclareFontSeriesChangeRule {b}{c}{bc}{b}    % bold + condensed      -> bc, else b
\DeclareFontSeriesChangeRule {bx}{c}{bc}{bx}  % bold extended + cond. -> bc, else bx
\DeclareFontSeriesChangeRule {m}{b}{b}{bx}    % medium + bold         -> b,  else bx

ただし規則は無条件では発火しません。\selectfont はまず置換なしで合成し、その結果の エンコーディング/ファミリ/シリーズ/シェイプ実在するかを確かめて、無ければ元に戻してから置換つきでやり直します。Computer Modern には細幅がそもそも無いので、\fontseries{c}\selectfont は警告を出して m に戻り、続けて太字にしても b にしかなりません——「合成なんて起きていない」と結論しがちなのはこのためです。細幅を実装したフォントで試せば規則はきちんと働きます。Helvetica(phv)は T1m / c / b / bc を揃えているので、下の例で実物を確認できます。

document.tex
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{helvet}          % sets \sfdefault to phv
\begin{document}\makeatletter
\sffamily                    % -> phv/m   (Helvetica)
\fontseries{c}\selectfont    % -> phv/c   font file phvr8tn (Helvetica Narrow)
\bfseries                    % -> phv/bc  font file phvb8tn (Helvetica Bold Narrow)
\typeout{[series] \f@series\space -> \fontname\font}
\makeatother\end{document}

% [series] bc -> phvb8tn
%
% The same document with Computer Modern instead reports:
%   LaTeX Font Warning: Font shape `OT1/cmr/c/n' undefined
%   [series] m  (the condensed request was substituted away before bold arrived)

低水準セレクタと \selectfont

5 属性を 1 つずつ指定するのが 低水準セレクタ です——\fontencoding{T1}\fontfamily{ptm}\fontseries{b}\fontshape{it}\fontsize{12}{14}。決まりは 1 つだけ、設定しただけでは何も起きず、\selectfont を呼んで初めて反映される。前節でその理由も見えました。\fontseries は値をその場で書き込まず、\delayed@f@adjustment という「保留リスト」に積むだけで、実際の合成・存在確認・置換は \selectfont が一括して行うのです。だから設定とテキストのあいだに \selectfont を挟み忘れると、その文字は前のフォントで組まれます。

document.tex
\documentclass{article}
\usepackage[T1]{fontenc}
\begin{document}
% Correct: set the attributes, commit with \selectfont, then the text
{\fontfamily{ptm}\fontseries{b}\selectfont Bold Times}

% Wrong: the words before \selectfont are still in the old font
{\fontfamily{ptm} Some \fontseries{b}\selectfont text.}

% \usefont gives four attributes at once and commits on the spot
{\usefont{T1}{ptm}{b}{it} Bold italic Times}

% Change only the size; reuse the current baselineskip
\makeatletter
{\fontsize{14}{\f@baselineskip}\selectfont Fourteen point}
\makeatother
\end{document}

5 属性のうち 4 つをまとめて渡す近道が \usefont{エンコーディング}{ファミリ}{シリーズ}{シェイプ} で、サイズは現在値を引き継ぎ、その場で確定します。グループ { } で囲めば効果はその範囲に限定されます。\fontsize の第 2 引数に内部マクロ \f@baselineskip を渡しているのは、行送りを変えずに文字サイズだけ変えるための定石です(内部マクロを書き換えるのは禁物ですが、読み出して渡すのは差し支えありません)。ただし——これらを 直接書く必要はめったにありません\textbf\fontseries を、\sffamily\fontfamily を呼んでいるだけで、低水準に降りるのは特定のフォントを名指しするときか、新しいフォントをクラスやパッケージに組み込むときに限られます。

既定値マクロ — \rmdefault と仲間たち

\textrm\rmfamilyどのファミリを選ぶか はハードコードされておらず、マクロに保持されています。\rmdefault(ローマン)、\sfdefault(サンセリフ)、\ttdefault(タイプライタ)の 3 つで、article クラスでの既定値はそれぞれ cmrcmsscmtt。だから本文フォントを差し替えたいときは、命令ではなくこのマクロを書き換えます。前節で見たとおり、この 3 つの値は「太字に何を要求するか」まで左右するので、単なる別名ではなく 文書のフォント方針そのもの だと思っておくのが正確です。

preamble.tex
% Re-point the three meta-families for the whole document
\renewcommand{\rmdefault}{ptm}  % roman   -> Times
\renewcommand{\sfdefault}{phv}  % sans    -> Helvetica
\renewcommand{\ttdefault}{pcr}  % mono    -> Courier

% The body font itself is these four (defaults: OT1, \rmdefault, m, n)
%   \encodingdefault \familydefault \seriesdefault \shapedefault
% and \familydefault points at \rmdefault, so the line above changes the body.

% Retarget bold and medium per meta-family (LaTeX2e 2020-02-02 and later)
\DeclareFontSeriesDefault[rm]{bf}{b}
\DeclareFontSeriesDefault[sf]{md}{l}

.fd ファイルと \DeclareFontShape — 座標を実ファイルに結ぶ

5 つの座標を実際のフォントファイルに結びつけているのが フォント定義ファイル(.fd です。名前は <エンコーディングを小文字にしたもの><ファミリ>.fd——OT1cmr なら ot1cmr.fdT1phv なら t1phv.fd——で、LaTeX は必要になった時点でこれを自動的に読み込みます。手元のどこにあるかは kpsewhich ot1cmr.fd で分かります。中身は \DeclareFontFamily 1 行と、シリーズ・シェイプの組み合わせごとの \DeclareFontShape の並びです。

ot1cmr.fd
% TeX Live 2024, tex/latex/base/ot1cmr.fd (excerpt)
\DeclareFontFamily{OT1}{cmr}{\hyphenchar\font45 }
\DeclareFontShape{OT1}{cmr}{m}{n}%
     {<5><6><7><8><9><10><12>gen*cmr%
      <10.95>cmr10%
      <14.4>cmr12%
      <17.28><20.74><24.88>cmr17}{}
%%%%%%% bold series
\DeclareFontShape{OT1}{cmr}{b}{n}
     {%
      <5><6><7><8><9><10><10.95><12>%
      <14.4><17.28><20.74><24.88>cmb10%
      }{}
%%%%%%%% bold extended series
\DeclareFontShape{OT1}{cmr}{bx}{n}
   {%
      <5><6><7><8><9>gen*cmbx%
      <10><10.95>cmbx10%
      <12><14.4><17.28><20.74><24.88>cmbx12%
      }{}

読み方は角括弧のサイズ範囲を追うだけです。<5><6><7><8><9>gen*cmbx は「5〜9 ポイントではファミリ名にサイズを継ぎ足した実フォント(cmbx5cmbx6…)を使う」、<12><14.4>...cmbx12 は「12 ポイント以上は cmbx12 を拡大して使う」。この b の行が上で述べた cmb10 の一本槍を示していて、5 ポイントから 24.88 ポイントまで全部 cmb10 を伸縮しているのが見て取れます。ほかに ssub(silent substitution、警告なしの代替)と sub(警告つきの代替)の指定もあり、たとえば t1phv.fd には \DeclareFontShape{T1}{phv}{bx}{n}{<->ssub * phv/b/n}{} という行があります——Helvetica には bx が無いので、黙って b を出す、という宣言です。

Font shape ... undefined — フォント置換の警告を読む

この警告は「そんな組み合わせは .fd に無かったので、いちばん近いものを勝手に使った」という報告です。必ず 2 行で出て、1 行目に 要求した 4 座標、2 行目に 実際に使った 4 座標 と入力行番号が入ります。エラーではないので組版は最後まで走りますが、出てきた PDF は要求どおりではありません。上の例では、細幅を持たない Computer Modern に c を求めたので m が使われ、T1/cmss にスモールキャップスが無いので直立の n が使われました。

log
% a missing shape inside a family that does exist
LaTeX Font Warning: Font shape `T1/cmss/bx/sc' undefined
(Font)              using `T1/cmss/bx/n' instead on input line 4.

% a family that does not exist at all: \DeclareFontSubstitution kicks in
LaTeX Font Warning: Font shape `T1/nosuchfont/m/n' undefined
(Font)              using `T1/cmr/m/n' instead on input line 4.

% and at the end of the run, the summary line that is easy to miss
LaTeX Font Warning: Some font shapes were not available, defaults substituted.

原因は 3 つに絞れます。(1) そのフォントにその字面が無い ——Helvetica にイタリックは無く sl で代用される、といった類。無いものは足せないので、別のフォントを選ぶか代用を受け入れます。(2) ファミリ名の綴りが違う ——2 行目に cmr が出てきたら、\DeclareFontSubstitution の最終手段が働いた合図で、たいていは打ち間違いかパッケージ未読込です。(3) エンコーディングを取り違えた ——OT1 にしか .fd が無いファミリを T1 で呼んだ場合など。なお、警告の最後にまとめて出る Some font shapes were not available, defaults substituted. の 1 行は流し読みで飛ばしがちですが、これが出ていたら本文中のどこかで意図しない書体が使われています。

フォントエンコーディング — OT1 / T1 / TU の違い

フォントエンコーディング は、入力された文字を フォントのどのスロット(字形)に対応づけるか の取り決めです。5 属性の 1 番目でありながら、\textbf のような作者向け命令が用意されていない唯一の属性でもあります——切り替えは fontenc のような パッケージの仕事 だからです。OT1 は Knuth のオリジナルの 7 ビット「TeX text」(既定)。128 文字しか入らないので、éñ基底文字とアクセントの合成\accent)で作られ、その合成のせいで アクセント付きの語はハイフネーションされず、PDF からのコピー&ペーストも崩れます。T1TeX extended text、1990 年の TUG コーク会議に由来して Cork エンコーディング とも)は 8 ビット 256 字形で、アクセント付き文字を 独立した 1 つの字形 として持つため、この 2 つの問題が同時に消えます。

エンコーディングが「字形の並び順の取り決め」でしかないことを、OT1 の £ 記号ほどよく示す例はありません。OT1 には英ポンド記号のスロットが無いので、ot1enc.def\textsterlingイタリック体のドル記号のスロット を呼び出します(Computer Modern ではそこに £ が入っている)。しかも現在のフォントが傾いていない場合には \fontshape{ui} に切り替えてから呼びます——さもないと £ だけ斜めに出るからです。ot1cmr.fdui の宣言に「この定義は LaTeX の \pounds に使われているので削除してはならない」という警告コメントが付いているのは、そのためです。

TUTeX Unicode)は、システムにインストールされた OpenType フォントを Unicode のコードポイントで直接使うためのエンコーディングで、XeLaTeX と LuaLaTeX の既定です。これは古い EU1(XeTeX 用)と EU2(LuaTeX 用)を置き換えたもので、fontspec の変更履歴によれば TU の導入は v2.5(2016 年 1 月 30 日)、既定に昇格したのが v2.5c(2017 年 1 月 20 日) です。いま TeX Live 2024 の fontspec.styEU1EU2 の文字列は 1 つも残っていません。ほかに T2A(キリル)、LGR(ギリシャ)、TS1Text Companion、通貨記号や著作権記号など本文用の記号)があり、数式には OML(数式イタリック)・OMS(数式記号)・OMX(大型数式記号)が割り当てられています。なお TS1 の記号命令は 2020 年にカーネル本体へ移されており、いまの textcomp パッケージはひとつも記号を定義していません——詳しくは記号のページに譲ります。

エンコーディング内容ビット幅・備考
OT1Knuth のオリジナル TeX text(既定)7 ビット。アクセントは合成 → ハイフネーション不可
T1TeX extended text(Cork)。西欧言語8 ビット 256 字形。アクセントは単一字形 → ハイフネーション可
TUTeX Unicode。OpenType フォントを直接Xe / LuaLaTeX の既定。EU1 / EU2 を置き換えた
T2Aキリル文字8 ビット(T2B / T2C も同族)
LGRギリシャ文字現在ギリシャ語で主流の 256 字形
TS1Text Companion(本文用の記号)通貨・著作権など。命令は 2020 年にカーネルへ移動
OML / OMS / OMX数式イタリック / 数式記号 / 大型数式記号数式専用。本文では使わない

inputenc と fontenc の違い — 入口と出口

混同しやすい 2 つを切り分けておきます。入力エンコーディング は、.tex ソースの バイト列をどんな文字として読むか(UTF-8 など)の取り決めで、歴史的には inputenc パッケージの担当。フォントエンコーディング は、その読み取った文字を 出力フォントのどの字形に流し込むか の取り決めで、fontenc の担当です。入口(inputenc)と出口(fontenc) と覚えれば取り違えません。現代の pdfLaTeX ではソースの文字コードは UTF-8 が既定 になったので、inputenc を明示的に読み込む必要はほぼ無くなりました。一方フォントエンコーディングは依然として指定する価値があります。fontencpdfLaTeX 向け のパッケージで、XeLaTeX・LuaLaTeX では代わりに fontspec を使います(TU が既定なので、そもそもこの問題が起きにくい)。

pdfLaTeX なら \usepackage[T1]{fontenc} を書く

pdfLaTeX で組むなら、プリアンブルに \usepackage[T1]{fontenc} を書くのが定石です。文書のフォントエンコーディングを T1 に切り替え、OT1 既定の弊害——アクセント付きの語がハイフネーションされない、PDF からコピペすると崩れる——を一掃します。fontenc のドキュメントも、これでフランス語・ドイツ語・イタリア語・ポーランド語など広範な西欧言語が扱え、アクセント付きの語も LaTeX がハイフネーションし、出力をコピー&ペーストできると述べています。オプションに複数のエンコーディングを並べたときは 最後に挙げたものが既定 になる(\encodingdefault がそれに設定される)ので、ギリシャ語を交える文書なら \usepackage[LGR,T1]{fontenc} と書いて本文は T1 のまま、必要なところだけ LGR に切り替えます。

document.tex
\documentclass{article}
\usepackage[T1]{fontenc}   % font encoding -> T1 (Cork)
% Input is UTF-8 by default on pdfLaTeX, so inputenc is usually unneeded.
\begin{document}
% With T1 these words hyphenate and survive copy-and-paste out of the PDF.
Na\"ive r\"esum\"e, \"uberfl\"ussig, Stra\ss{}e.
\end{document}