pdfTeX

pdftex --version が返すのは pdfTeX 3.141592653-2.6-1.40.26 という三段構えの数字です。先頭の 3.141592653 は pdfTeX 自身の番号ではなく、Knuth の TeX が円周率へ収束していく途中経過で、XeTeX も LuaTeX も同じ桁を引き継いでいます。pdfTeX はその TeX に「PDF を直接書く」機能を足したエンジンで、pdflatex と打ったときに実際に動いているのはこれです。いまも LaTeX の既定エンジンであり続けている理由、フォントを TFM と map ファイルで解決する古い設計、そして pdfTeX 最大の貢献である microtype(protrusion と font expansion)を、手元の TeX Live 2024 で実際に走らせた出力とともに追います。

pdflatexlatexpdftex の違い

三つとも同じ実行ファイルです。 TeX Live 2024 の bin を覗くと pdflatexlatexpdftex へのシンボリックリンクで、amstexcsplain も同じ先を指しています。違うのは起動時に読み込まれる フォーマット だけです。フォーマットとはマクロ一式をあらかじめ展開して固めた .fmt ファイルで、fmtutil.cnfpdflatex pdftex ... *pdflatex.ini という一行が「pdflatex という名前で呼ばれたら pdftex エンジンに LaTeX のマクロを積んで動け」と指示しています。だから「エンジン(プログラム)」と「フォーマット(命令の体系)」は別の軸で、pdfLaTeX とは pdfTeX エンジン × LaTeX フォーマットの組み合わせを指す言葉です。

shell
$ readlink $(which pdflatex) latex amstex
pdftex
pdftex
pdftex

$ grep -E '^(latex|pdflatex|pdftex) ' $(kpsewhich fmtutil.cnf)
latex     pdftex language.dat -translate-file=cp227.tcx *latex.ini
pdflatex  pdftex language.dat -translate-file=cp227.tcx *pdflatex.ini
pdftex    pdftex language.def -translate-file=cp227.tcx *pdfetex.ini

この表を読むと副産物として二つのことが分かります。ひとつは、latex というコマンドが「古い TeX」ではなく pdfTeX を DVI モードで動かしたもの だということ。実際 latex を走らせるとバナーは This is pdfTeX, ... (preloaded format=latex) と出て、できるのは .dvi です。もうひとつは pdftex フォーマットが pdfetex.ini から作られていること——ee-TeX で、\protected\unexpanded、レジスタの拡張、\middle といった現代の LaTeX が当然の前提にしている拡張群を指します。三つのエンジンで \eTeXversion が定義済みかを調べると、pdfTeX・XeTeX・LuaTeX すべてが「定義済み」を返します。e-TeX はもはやオプションではありません。一方 \pdftexversion は pdfTeX にしかないので、パッケージがエンジンを判定するときの目印になります。

PDF を直接書く——そして \pdfoutput=0 で DVI に戻す

出力形式を切り替えるのは整数パラメータ \pdfoutput ひとつです。正の値なら PDF、0 なら古典的な DVI。Knuth のオリジナルの TeX は DVI(device independent)しか吐かず、PostScript や PDF への変換は別プログラムの仕事でした。pdfTeX はその工程をページビルダーの直後に繰り込んだわけですが、DVI の出口を塞いだわけではありません。PSTricks のように PostScript の \special に依存するパッケージでは、いまでも DVI → PS → PDF の経路が必要になることがあり、\pdfoutput=0 はそのために残されています。

落とし穴は場所です。\pdfoutput最初の1ページが出力される前 に決めなければならず、途中で変えると ! pdfTeX error (setup): \pdfoutput can only be changed before anything is written to the output. が出て、続けて ! ==> Fatal error occurred, no output PDF file produced! でその run は終わります。PDF も DVI も残りません。LaTeX ではドライバ判定がかなり早い段階で走るので、安全なのは \documentclass よりも前、ファイルの一番上です。

latex
% Force DVI output even when the file is compiled with pdflatex.
% This line must come before \documentclass.
\pdfoutput=0
\documentclass{article}
\begin{document}
This run writes a .dvi file, not a .pdf.
\end{document}

microtype が実際にしていること——protrusion と font expansion

\usepackage{microtype} の一行が入れているのは二つの独立した機能です。.log を開くと Package microtype Info: Character protrusion enabled (level 2).Package microtype Info: Automatic font expansion enabled (level 2), stretch: 20, shrink: 20, step: 1 の二行が並んでいます。前者が protrusion(マージンカーニング)——行末のピリオドやハイフンを版面の外へわずかにはみ出させ、機械的に揃えた端をむしろ光学的にまっすぐ見せる技法で、活版時代の「ぶら下げ」を一般化したものです。後者が font expansion——字形を水平方向に 2%(20/1000)まで伸縮させ、その余地を 行分割の判断そのものに織り込む 機能です。単語間の空きが均されるので、段落に走る白い筋(リバー)が減ります。

この二つはもともと TeX の発明ではありません。着想は hz-program——書体設計家 Hermann ZapfPeter Karow らとハンブルクの URW で 1988 年から進めた組版プログラムで、狙いは「行間に白い穴も川もない、均一な灰色の版面」でした。URW はこれを特許化し(欧州特許 EP 0466953、権利は 2010 年 7 月に満了)、アルゴリズムは最終的に Adobe に渡って InDesign に組み込まれます。ところが同じ着想はもう一方の道も進みました。ベトナム出身の Hàn Thế Thành が hz を解析し、チェコのマサリク大学情報学部の博士論文『Micro-typographic extensions to the TeX typesetting system』(2000 年 10 月、指導は Jiří Zlatuška)として TeX に実装したのです。商用の頂点に載った技術と、無償で誰でも使える実装が、同じ一つのアイデアから出ている——\usepackage{microtype} はその後者の側です。

latex
\documentclass{article}
\usepackage{microtype} % protrusion + expansion, sensible defaults
\begin{document}
With microtype loaded, pdfTeX nudges punctuation into the margin
and flexes glyph widths by a hair, so justified text looks far
more even. Nothing else in the document has to change.
\end{document}

生の primitive で操作することもできます。protrusion 側は \pdfprotrudechars(0 は無効、1 で有効、2 は幅の計算にも反映)と、文字ごとのはみ出し量を決める \lpcode(左端)・\rpcode(右端)。expansion 側は \pdfadjustspacing=2 で伸縮を行分割に織り込む)と、文字ごとの伸びやすさを決める \efcode、そしてフォントの伸縮版を宣言する \pdffontexpand。実務でこれらを直接触ることはまずありません——microtype が書体ごとの設定ファイル(Computer Modern なら mt-cmr.cfg)を持っていて、パッケージが適切な値を選んでくれるからです。ただし名前を知っておくと、ログの警告が何の話をしているのかが読めるようになります。

プリミティブ担当役割
\pdfprotrudecharsprotrusion0 で無効、1 で有効、2 は幅の計算にも反映
\lpcode / \rpcodeprotrusion文字ごとの左端・右端のはみ出し量(1000 分率)
\pdfadjustspacingexpansion=2 で伸縮を行分割の判断に織り込む
\efcodeexpansion文字ごとの伸びやすさ。XeTeX にはこれが無い
\pdffontexpandexpansionフォントの伸縮版を宣言する

フォントは TFM と map ファイルで決まる

pdfTeX は 寸法字形 を別々の場所から取ります。寸法は .tfm(TeX Font Metric)——kpsewhich cmr10.tfm が返すのは fonts/tfm/public/cm/cmr10.tfm で、この中身は各文字の幅・高さ・深さとカーニング表だけ、字の形は一切入っていません。組版が終わって PDF に埋め込む段になって初めて map ファイル が引かれ、そこに書かれた実体(Type1 の .pfb や TrueType)が読まれます。cmr10 に対応する行はたった一行、cmr10 CMR10 <cmr10.pfb です。左からフォント名、PostScript 名、< は「このファイルを埋め込め」の意味。TeX Live 2024 で updmap が生成した pdftex.map45,443 行 あり、この一枚のテキストファイルが配布物のフォント全部の対応表になっています。

shell
$ kpsewhich cmr10.tfm cmr10.pfb
/usr/local/texlive/2024/texmf-dist/fonts/tfm/public/cm/cmr10.tfm
/usr/local/texlive/2024/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb

$ grep -m1 '^cmr10 ' $(kpsewhich pdftex.map)
cmr10 CMR10 <cmr10.pfb

# The same one-line document, compiled by three engines:
$ pdffonts min-pdflatex.pdf
INJFRF+CMR10                        Type 1      Builtin      yes yes yes
$ pdffonts min-xelatex.pdf
HOLJOD+LMRoman10-Regular-Identity-H CID Type 0C Identity-H   yes yes yes
$ pdffonts min-lualatex.pdf
YIRABR+LMRoman10-Regular            CID Type 0C Identity-H   yes yes yes

同じ一行の文書を三つのエンジンで組んで pdffonts にかけると、この設計の違いがそのまま出力に現れます。pdfLaTeX が埋め込むのは Type 1CMR10、XeLaTeX と LuaLaTeX が埋め込むのは CID Type 0CLMRoman10-Regular——同じ Computer Modern 系の見た目でも、通っている経路も埋め込まれるファイルもまったく別物です。map ファイルは \pdfmapfile\pdfmapline で文書側から追加できますし、興味深いことにこの二つは XeTeX にも残っています。8 ビット時代の資産は、思ったより広く生き続けています。

8 ビットエンジンの限界——! Bad character code と fontspec が使えない理由

1 フォントに 256 字まで。 これが pdfTeX の一番硬い天井です。\font\x=cmr10 \x \char"1234 と書くと ! Bad character code (4660). で止まります。同じ入力を LuaTeX に食わせるとエラーになりません——エンジンの内部でフォントが「幅い(wide)」からです。この 256 字の壁があるために、アクセント付きラテン文字は T1 などのフォントエンコーディングで詰め替え、ギリシャ文字や記号は別フォントに逃がす、という設計が必要になりました。fontenc が何をしている道具なのかは、この天井を知ると腑に落ちます。

誤解されやすいのは入力側です。UTF-8 のソースそのものは、2018 年以降の LaTeX なら inputenc を読まなくても通ります。 ただし通るのは「LaTeX が宣言している文字」だけ。試しに を本文に書いた .texpdflatex で普通にコンパイルできますが、 を書くと ! LaTeX Error: Unicode character 日 (U+65E5) not set up for use with LaTeX. で止まります。つまり pdfTeX の制約は「UTF-8 が読めない」ではなく「手当てされた文字しか組めない」です。そして OS にインストールされた OpenType フォントを名前で呼ぶ道は無く、fontspec を読み込むと即座に ! Fatal Package fontspec Error: The fontspec package requires either XeTeX or LuaTeX. が出ます。この 3 点——256 字、宣言済み文字だけ、システムフォント不可——が、人を XeTeX や LuaTeX に向かわせる理由のほぼすべてです。

  • 留まる理由: 既存テンプレートとの互換、速度、そして 30 年ぶんの実績。欧文中心の論文なら pdfLaTeX がいちばん事故が少ない選択です。
  • 移る理由: Unicode の多言語、OS のフォント、日本語、Lua による処理。どれか一つでも本気で要るなら回避策を積むより乗り換えるほうが早いです。
  • よくある事故: pdfLaTeX のまま fontspec を読み込む。fontspec は XeLaTeX / LuaLaTeX のものです。
  • もう一つの事故: \pdfoutput\documentclass より後に書く。PDF も DVI も残らずに run が落ちます。

画像は PNG・JPEG・PDF そのまま、EPS は自動変換

pdfTeX が直接読み込めるのは PNG・JPEG・PDF の三つで、低レベルの入口は \pdfximage、実務では graphicx\includegraphics がその上に立ちます。EPS だけは PDF に変換してからでないと埋め込めませんが、いまはそれも自動です。TeX Live の既定である restricted shell escape が有効なら、\includegraphics{fig.eps} は裏で repstopdf を呼び、隣に fig-eps-converted-to.pdf を作って埋め込みます。ここに落とし穴があります——-no-shell-escape で走らせると変換が起きず、しかもエラーも警告も出ません。代わりにファイル名だけを刷った空の枠が組まれ、PDF は正常に出来上がります。CI で画像が消える事故の典型がこれです。

PDF を自分で書くエンジンなので、PDF の機能に手を伸ばす primitive も揃っています。\pdfliteral は生の PDF 描画演算子をそのまま流し込み、\pdfobj は PDF オブジェクトを作り、\pdfannot は注釈(リンクやフォーム部品)を置きます。hyperref のリンクやしおりは、この層の上に建っています。文書全体の設定としては、タイトルや著者を書き込む \pdfinfo、圧縮の強さを決める \pdfcompresslevel、全ページに属性を付ける \pdfpageattr があります。

latex
\pdfinfo{
  /Title (My Report)
  /Author (A. Author)
}
\pdfcompresslevel=9
% a thin rule drawn with a raw PDF operator
\pdfliteral{0 0 m 100 0 l 0.4 w S}
プリミティブpdfTeX での役割LuaTeX での綴り
\pdfoutputPDF か DVI かを決める\outputmode
\pdfliteral生の PDF 描画演算子を差し込む\pdfextension literal
\pdfobj / \pdfannotPDF オブジェクト・注釈を直接作る\pdfextension obj / annot
\pdfinfoタイトルや著者などのメタデータを書く\pdfextension info
\pdfcompresslevel出力 PDF の圧縮率(0〜9)\pdfvariable compresslevel
\pdfximagePNG / JPEG / PDF 画像を読み込む\saveimageresource
\pdfsaveposページ上の現在位置を記録する\savepos
\pdfprotrudecharsprotrusion を有効にする\protrudechars
\pdfadjustspacingfont expansion を有効にする\adjustspacing

pdfTeX の \pdf... は後のエンジンに何が残ったか

LuaTeX は pdfTeX を土台に作られているので中身の大半を受け継ぎましたが、膨れ上がった \pdf... 名前空間は整理されました。 三つの窓口 \pdfextension\pdfvariable\pdffeedback にキーワードを渡す形になり、\pdfliteral\pdfextension literal\pdfoutput\outputmode\pdfximage\saveimageresource に、\pdfprotrudechars\pdfadjustspacing\protrudechars\adjustspacing になりました。一方で \lpcode\rpcode\efcode は名前のまま残っています。pdfTeX 時代の知識はおおむねそのまま通用しますが、綴りが変わる点だけは覚えておく価値があります。

XeTeX の継承のしかたは中途半端で、そこが実務に効いてきます。手元の TeX Live 2024 で primitive の有無を一つずつ調べると、XeTeX には \lpcode\rpcodeある のに \efcodeない。protrusion 用の入口も \pdfprotrudechars ではなく \XeTeXprotrudechars という別名です。だから XeLaTeX では protrusion は効くのに font expansion は原理的に実装できず、\usepackage[expansion=true]{microtype} と明示すると ! Package microtype Error: Font expansion does not work with xetex. で止まります。指定せずに読み込んだ場合はログから expansion の行が黙って消えるだけなので、気づかないまま「microtype を入れたのに効きが薄い」と感じることになります。

いま pdfLaTeX を選ぶ基準

欧文中心で、投稿先や共同研究者のテンプレートが決まっていて、締切が近いなら pdfLaTeX です。 同じ 417 ページの数式入り文書を三つのエンジンで組んだ実測では pdfLaTeX が最速で、そのうえ既存パッケージとの相性でつまずく確率がいちばん低い。やることも少なくて済みます——\usepackage{microtype} を一行入れ、図は PNG / JPEG / PDF で持ち、EPS を使うなら shell escape を切らない。逆に、日本語やシステムフォントが要る文書で pdfTeX に回避策を積み重ねるのは割に合いません。そこから先は XeLaTeX か LuaLaTeX の領分です。pdfTeX 自身は現在ほぼ保守モードで、新しい開発は LuaTeX に移っていますが、pdftex.map の 45,443 行と 30 年ぶんのテンプレート資産は当分どこにも行きません。