三つの現代エンジンのうち、XeTeX だけは PDF を書きません。 組版が終わると .xdv(extended DVI)というバイト列を吐き、それを xdvipdfmx という別のプログラムが PDF に変換します。ユーザーから見れば xelatex document.tex の一発ですが、内部では二段構えです。この構造を知っているかどうかで、画像やフォント埋め込みで詰まったときのログの読み方が変わります。このページでは、XeTeX が LaTeX に持ち込んだ本題——UTF-8 をそのまま読み、OS にインストールされたフォントを fontspec で名前指定する仕組み——と、.xdv の実体、そして pdfLaTeX の原稿を XeLaTeX に移したときに実際に壊れるものを扱います。
XeTeX は誰が何のために作ったのか
Jonathan Kew が SIL International での仕事のなかで書いたのが始まりで、最初の公開は 2004 年 4 月、Mac OS X 専用 でした。SIL は世界中の少数言語の文字と表記を扱う団体で、要件は最初から「Unicode と、その言語のために作られた実際のフォントで組版できること」。だから初期の XeTeX は当時の Mac の組版技術 AAT(Apple Advanced Typography) の上に建てられました。2006 年に Linux へ移植され、続いて Windows でも動くようになり、TeX Live 2007 から全プラットフォーム同梱になります。TeX Live 2024 の xetex --version は著作権表示に SIL International, Jonathan Kew and Khaled Hosny と出し、リンクしているライブラリとして ICU 74.2、HarfBuzz 8.3.0、Graphite2 1.3.14、FreeType2 2.13.2、そして macOS では Core Text と Cocoa を列挙します。XeTeX とは、TeX を OS とフォント業界の現物に接続するための層だと言えます。
バージョン番号にも小さな物語があります。xetex --version が返すのは XeTeX 3.141592653-2.6-0.999996 (TeX Live 2024)。先頭の 3.141592653 は Knuth の TeX の番号で、Knuth はこれをリリースのたびに一桁ずつ伸ばして円周率に近づけており、「自分の死後に行う絶対的に最後の変更」としてバージョンを π そのものにし、残っているバグはすべて仕様(feature)になる、と書き残しています。pdfTeX も LuaTeX もこの桁を引き継いでいるので、三つのエンジンは同じ先頭を共有しています。最後の 0.999996 は XeTeX 自身の番号で、同梱の NEWS を見ると 0.999991(2019 年)から毎年 2 月に一つずつ、0.999996(2024 年 2 月)まで進んでいます。9 を伸ばしながら 1 の手前を歩き続けているわけです——ただし「1 に収束させる」と明言した文献は見当たらないので、これは観察できる事実としてだけ書いておきます。
OS のフォントを名前で呼ぶ——fontspec と \setmainfont
\usepackage{fontspec} を読み込み、\setmainfont{Helvetica Neue} と OS が知っている名前 をそのまま書けば、それで終わりです。TFM を作る必要も、map ファイルに行を足す必要もありません。実際にこれを xelatex で組んで pdffonts にかけると HelveticaNeue が CID TrueType として埋め込まれているのが確認できます。本文・サンセリフ・等幅はそれぞれ \setmainfont・\setsansfont・\setmonofont、見出し用に別の一族を立てたいときは \newfontfamily\headingfont{...} です。ところが TeX Live に同梱されているフォントは事情が違い、そこが最初につまずく場所になります。次の節で扱います。
\documentclass{article}
\usepackage{fontspec} % no inputenc, no fontenc needed
% An OS font is named the way the system knows it, e.g.
% \setmainfont{Helvetica Neue}
% A font that ships with TeX Live is safest named by file name:
\setmainfont{texgyretermes-regular.otf}[
BoldFont = texgyretermes-bold.otf,
ItalicFont = texgyretermes-italic.otf,
BoldItalicFont = texgyretermes-bolditalic.otf,
]
\setsansfont{texgyreheros-regular.otf}
\setmonofont{texgyrecursor-regular.otf}
\newfontfamily\headingfont{texgyreadventor-regular.otf}
\begin{document}
Unicode goes in literally: naïve, Straße, ¿cómo?, œuvre.
\textbf{Bold} and \textit{italic} come from the files named above.
\[ E = mc^2 \]
\end{document}ここで大事なのは、inputenc も fontenc も読み込まないことです。UTF-8 入力と Unicode フォントが既定なので、pdfLaTeX 時代の文字コードのおまじないは丸ごと消えます。フォントの見つからないときのエラーは ! Package fontspec Error: The font "..." cannot be found. で、これが出たらまず疑うべきは TeX ではなく OS 側の名前解決 です。細かい指定——Ligatures、Numbers=OldStyle、SmallCapsFeatures、生の OpenType タグを渡す RawFeature、シェイパーを選ぶ Renderer(HarfBuzz / AAT / Graphite)——は fontspec 側の話題なので、そちらのページに譲ります。数式まで Unicode フォントで揃えたいときは unicode-math を併用します。
The font "..." cannot be found. — 同梱フォントが名前で見つからない
XeTeX がフォント名を尋ねる先は OS のフォントデータベースであって、TeX Live のディレクトリではありません。 これが実務でいちばん多くの人が最初につまずく場所です。TeX Live 2024 を入れた macOS でそのまま試すと、\setmainfont{Helvetica Neue} は通るのに、\setmainfont{TeX Gyre Termes}・\setmainfont{Latin Modern Roman}・\setmainfont{TeX Gyre Pagella} はすべて ! Package fontspec Error: The font "..." cannot be found. で落ちます。同じ三つを LuaLaTeX で試すとどれも通ります。理由は探し方の違いです。LuaTeX の luaotfload は TeX のディレクトリツリー自体を走査して索引を作る のに対し、XeTeX は OS のフォント機構(macOS なら Core Text)に問い合わせる ので、システムに登録されていない配布物のフォントは名前で引けません。
解決策は簡単で、同梱フォントはファイル名で呼ぶ ことです。\setmainfont{texgyretermes-regular.otf} は通ります(大文字小文字は区別されるので TeXGyreTermes-Regular.otf は通りません)。ただしこの書き方だと太字と斜体の自動対応が付かないので、BoldFont・ItalicFont・BoldItalicFont を明示します。上のサンプルはその形にしてあり、xelatex でも lualatex でもエラーゼロ・欠落文字ゼロで通ります。配布物のフォントを OS 側に登録してしまう手(Linux 系の texlive-fontconfig 設定など)もありますが、環境ごとに違ううえ共同作業では再現しないので、原稿に書くならファイル名指定のほうが確実です。「XeLaTeX でフォントが見つからない」と言われたら、まず名前ではなくファイル名を試してください。
日本語フォントを見ると、この規則が逆側からはっきりします。OS が知っている名前は通り、TeX Live しか知らない名前は通りません。 macOS + TeX Live 2024 のこの環境で試すと、\setmainfont{Hiragino Mincho ProN}、\setmainfont{Hiragino Sans}、\setmainfont{YuMincho} はいずれも XeLaTeX で通ります——OS のフォントパネルに出ている名前だからです。一方 \setmainfont{Noto Sans JP} は、OS 側に入れていなければ cannot be found になります。つまり判定は簡単で、「システム設定のフォント一覧に出るか」を見ればよいわけです。出るなら XeTeX も見つけます。出ないなら、たとえ TeX Live に入っていても名前では届きません。なお日本語の縦書きや厳しい禁則が要る原稿では、xeCJK より luatexja か upLaTeX のほうが素直です。
.xdv とは何か——XeTeX が PDF を書かない理由
.xdv は DVI の拡張版 です。xelatex -no-pdf を走らせるとファイルとして取り出せて、先頭のバイトは f7 07——DVI の pre コマンドに続く形式 ID が、標準 DVI の 2 ではなく 7 になっています。中を覗くと XeTeX output 2026.08.13:0452 というコメントに続いて、使ったフォントが ファイルパスで 書かれています(lmroman10-regular.otf のように)。つまり .xdv は「どのフォントファイルの何番目のグリフを、どの座標に置くか」までは決まっているが、フォント自体はまだ埋め込まれていない中間形式です。だから最後にフォントを開いてサブセット化し PDF に詰める仕事が残り、それを xdvipdfmx が担当します。
普通に xelatex を走らせても .xdv はディスクに残りません。XeTeX はこのバイト列を ドライバの標準入力にパイプで流している からです。-output-driver=CMD でドライバを差し替えられるので、cat するだけのスクリプトを指定すれば流れているものをそのまま捕まえられます(実測で 820 バイト、末尾は DVI 伝統の df df df df)。この設計から実務上の帰結が二つ出ます。ひとつは graphicx などがドライバ指定を求めたとき、答えは xetex(= xdvipdfmx)だということ。もうひとつは、エラーがどちらの段階のものかを切り分ける必要があるということです。xelatex と打ったのに、画像やフォント埋め込みの失敗は xdvipdfmx の行として出ます。ログは「最初の TeX のエラー行」と「最後のドライバの行」を分けて読むのが速いです。
# Stop after the first stage and keep the intermediate XDV file.
$ xelatex -no-pdf document.tex
Output written on document.xdv (1 page, 820 bytes).
# The second byte is the DVI format id: 7 for XDV, 2 for plain DVI.
$ xxd document.xdv | head -2
00000000: f707 0183 92c0 1c3b 0000 0000 03e8 1d20 .......;.......
00000010: 5865 5465 5820 6f75 7470 7574 2032 3032 XeTeX output 202
# Run the second stage by hand. xdvipdfmx has been the default
# driver on every platform since XeTeX 0.997.
$ xdvipdfmx document.xdv
document.xdv -> document.pdf
# The driver is a replaceable external command.
$ xelatex -output-driver="/path/to/save-stdin.sh" document.texpdfLaTeX から XeLaTeX に移すと何が壊れるか
いちばん危ないのは、エラーにならずに動いてしまう場合です。 古いプリアンブルをそのまま xelatex に食わせると、多くはコンパイルが通ります。\usepackage[utf8]{inputenc} は Package inputenc Warning: inputenc package ignored with utf8 based engines. という警告一行で無視され、それだけなら実害はありません。問題は \usepackage[T1]{fontenc} と \usepackage{lmodern} の組です。この二行が残っていると XeLaTeX は 8 ビットの NFSS 経路にとどまり、埋め込まれるのは ec-lmr10 という TFM フォント——pdffonts で見ると Type 1C です。fontspec を使った場合の CID Type 0C とは別物で、つまり XeTeX に移った意味がほとんど無くなります。
この違いはログの一行で見分けられます。[T1]{fontenc} を残したまま 日 を書くと Missing character: There is no 日 ("65E5) in font ec-lmr10! と出ます——コードポイントが TeX 流の 16 進 "65E5 で、フォント名が ec-lmr10。fontspec に切り替えた同じ文書では Missing character: There is no 日 (U+65E5) in font [lmroman10-regular]... になり、表記が U+65E5 に、フォント名が OpenType のファイル名に変わります。 前者が出ていたら 8 ビット経路に落ちている合図です。合成用の結合ダイアクリティカルマークでも同じことが起きます(ec-lmr10 には U+0301 が無く、OpenType 経路では通ります)。移行するときは inputenc / fontenc / lmodern / times などの旧フォント指定をまとめて削除し、fontspec に一本化するのが正解です。
| 旧プリアンブルの行 | XeLaTeX での挙動 | 対処 |
|---|---|---|
\usepackage[utf8]{inputenc} | 警告一行で無視される | 削除する |
\usepackage[T1]{fontenc} | 8 ビットの NFSS 経路にとどまる。無言 | fontspec に置き換える |
\usepackage{lmodern} | ec-lmr10 を埋め込む(Type 1C) | \setmainfont{lmroman10-regular.otf} に |
microtype | protrusion のみ有効。expansion は無い | そのまま読み込んでよい |
babel | 動くが RTL や複雑文字では手薄 | polyglossia を検討する |
XeLaTeX で font expansion が効かない理由
エンジンにその機能が無いからです。 microtype の二本柱のうち、XeTeX が実装しているのは protrusion だけです。primitive の有無を一つずつ調べると、XeTeX には文字ごとのはみ出し量を決める \lpcode と \rpcode はありますが、伸縮の度合いを決める \efcode がありません。protrusion の入口も pdfTeX の \pdfprotrudechars ではなく \XeTeXprotrudechars という別名です。実際 \usepackage{microtype} を XeLaTeX で読み込むと、ログには Character protrusion enabled (level 2). は出るのに、pdfLaTeX や LuaLaTeX なら並んで出るはずの Automatic font expansion enabled の行が 黙って消えます。明示的に要求すると、はっきり止まります——! Package microtype Error: Font expansion does not work with xetex.
複雑な文字体系と RTL——HarfBuzz と \XeTeX... プリミティブ
XeTeX が広く使われる最大の理由は、アラビア文字やインド系文字のような、字が文脈で形を変える文字体系にきちんと対応することです。字形の整形(シェイピング)は HarfBuzz が担当します。0.9999(2013 年 5 月)で従来の ICU LayoutEngine から HarfBuzz に切り替わり、TeX Live 2024 同梱版は 8.3.0 をリンクしています。エンジンの側にも専用の primitive が並びます——入力エンコーディングを文書の途中で切り替える \XeTeXinputencoding、文字にクラスを与えて隣接時の挙動を変える \XeTeXcharclass と \XeTeXinterchartoks、行分割規則を言語で指定する \XeTeXlinebreaklocale、PDF のテキスト抽出用に実文字を書き込む \XeTeXgenerateactualtext、そして画像を読む \XeTeXpicfile と \XeTeXpdffile。言語切り替えは polyglossia、RTL 組版は bidi(polyglossia がアラビア語などで自動的に読み込みます)、中国語・韓国語・日本語は xeCJK が担当します。ただし日本語の縦組みや込み入った禁則が要る原稿では、luatexja や upLaTeX のほうが素直です。
XeLaTeX を選ぶ場面と、避けたほうがいい場面
- 手元のフォントをそのまま使いたいとき。 OpenType / TrueType の名前を
\setmainfontに書けば終わりで、設定ファイルを触る必要がありません。 - Unicode をそのまま書きたいとき。 UTF-8 入力が既定で、エンコーディングの下準備がまるごと不要になります。
- 多言語・RTL の文書。
polyglossiaとbidiの組み合わせはアラビア語やヘブライ語で実績があり、HarfBuzz が字形整形を引き受けます。 - 避けたほうがいい場面その一——font expansion が要るとき。 XeTeX に
\efcodeは無いので、microtypeの伸縮は原理的に効きません。組版品質を最優先するなら pdfLaTeX か LuaLaTeX です。 - 避けたほうがいい場面その二——Lua で処理を書きたいとき。 XeTeX にスクリプト言語は埋め込まれていません。低レベルの介入が要るなら LuaLaTeX に寄せます。
- 避けたほうがいい場面その三——日本語の縦組み。
xeCJKの想定を超えるので、luatexjaか upLaTeX を早めに試すほうが早道です。
ひとことでの判断基準は「OS のフォントを名前で使いたいだけなら XeLaTeX、組版そのものに手を入れたいなら LuaLaTeX」です。三者を並べた本格的な比較は「どのエンジンを選ぶか」のページにあります。最後に共同作業の注意をひとつ。フォント名は OS ごとに違い、\setmainfont{Helvetica Neue} は同僚の Linux 機ではまず落ちます。共有する原稿は、まず上のようにファイル名で指定した同梱フォントでビルドが通る状態を作り、そこから必要な分だけ差し替えるのが安全です。PDF だけを提出する場合は、pdffonts でフォントがすべて埋め込まれていることを確認してから出してください。