XeTeX

현대 엔진 셋 가운데 XeTeX만은 PDF를 쓰지 않습니다. 조판이 끝나면 .xdv(extended DVI)라는 바이트 열을 뱉고, 그것을 xdvipdfmx라는 별도의 프로그램이 PDF로 바꿉니다. 사용자 눈에는 xelatex document.tex 한 번이지만 안에서는 두 단계입니다. 이 구조를 아는지 여부가 이미지나 글꼴 임베딩에서 막혔을 때 로그를 읽는 방식을 바꿉니다. 이 페이지에서는 XeTeX이 LaTeX에 가져온 본론—UTF-8을 그대로 읽고, 운영체제에 설치된 글꼴을 fontspec으로 이름 지정하는 구조—과 .xdv의 실체, 그리고 pdfLaTeX 원고를 XeLaTeX으로 옮겼을 때 실제로 깨지는 것들을 다룹니다.

XeTeX을 누가 무엇을 위해 만들었는가

시작은 Jonathan KewSIL International에서 하던 일의 일부였고, 첫 공개는 2004년 4월, Mac OS X 전용이었습니다. SIL은 전 세계 소수 언어의 문자와 정서법을 다루는 단체여서, 요구 사항은 처음부터 분명했습니다. 유니코드로, 그리고 그 언어들을 위해 실제로 만들어진 글꼴로 조판할 것. 그래서 초기 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을 운영체제와 폰트 산업의 실물에 접속시키는 층이라고 이해하는 것이 가장 정확합니다.

버전 번호에도 작은 이야기가 있습니다. xetex --versionXeTeX 3.141592653-2.6-0.999996 (TeX Live 2024)를 돌려줍니다. 맨 앞의 3.141592653은 Knuth의 TeX으로, 그는 버그를 고칠 때마다 한 자리씩 늘려 원주율에 가까워지게 하고 있으며, “자신의 사후에 이루어질 절대적으로 마지막 변경”으로 버전을 π 자체로 바꾸고 그 시점에 남아 있는 버그는 모두 기능이 된다고 밝혀 두었습니다. pdfTeX과 LuaTeX도 같은 자릿수를 물려받았으니 세 엔진이 같은 앞머리를 공유합니다. 끝의 0.999996은 XeTeX 자신의 번호이고, 함께 실린 NEWS 파일을 보면 해마다 2월에 한 걸음씩, 2019년 0.999991에서 2024년 2월 0.999996까지 나아갔습니다. 9를 늘려 가며 1 바로 앞을 걷고 있는 셈입니다. 다만 “1로 수렴시킨다”고 밝힌 문헌은 찾을 수 없으므로, 이는 규칙이 아니라 관찰된 사실로만 적어 둡니다.

운영체제 글꼴을 이름으로 부르기: fontspec\setmainfont

\usepackage{fontspec}를 읽어 들이고 \setmainfont{Helvetica Neue}처럼 운영체제가 아는 이름을 그대로 쓰면 그것으로 끝입니다. TFM을 만들 필요도, map 파일에 줄을 더할 필요도 없습니다. 이를 xelatex으로 조판한 뒤 pdffonts로 보면 HelveticaNeueCID TrueType으로 묻혀 있는 것을 확인할 수 있습니다. 본문·산세리프·고정폭은 각각 \setmainfont, \setsansfont, \setmonofont이고, 제목용으로 별도의 일족을 세우려면 \newfontfamily\headingfont{...}를 씁니다. 그런데 TeX Live에 함께 실린 글꼴은 사정이 다르고, 대부분이 처음 막히는 지점도 바로 거기입니다. 다음 절에서 다룹니다.

latex
\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}

여기서 중요한 것은 무엇을 쓰지 않는가입니다. inputencfontenc도 읽어 들이지 않습니다. UTF-8 입력과 유니코드 글꼴이 기본이라, pdfLaTeX 시절의 문자 인코딩 주문은 통째로 사라집니다. 글꼴을 찾지 못했을 때의 메시지는 ! Package fontspec Error: The font "..." cannot be found.이며, 이때 먼저 의심할 것은 TeX이 아니라 운영체제 쪽의 이름 해석입니다. 세부 지정—Ligatures, Numbers=OldStyle, SmallCapsFeatures, 날 OpenType 태그를 넘기는 RawFeature, 셰이퍼를 고르는 Renderer(HarfBuzz / AAT / Graphite)—는 fontspec의 주제이므로 그쪽 페이지에 넘깁니다. 수식까지 유니코드 글꼴로 맞추려면 unicode-math를 함께 씁니다.

The font "..." cannot be found. — 동봉 글꼴이 이름으로 잡히지 않을 때

XeTeX이 글꼴 이름을 묻는 곳은 운영체제의 글꼴 데이터베이스이지 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은 운영체제의 글꼴 기구(macOS라면 Core Text)에 물어보기 때문에, 배포판이 실어 보냈을 뿐 시스템에 등록된 적 없는 글꼴은 이름으로 찾을 수 없습니다.

해결책은 간단합니다. 동봉 글꼴은 파일 이름으로 부르는 것입니다. \setmainfont{texgyretermes-regular.otf}는 통과합니다. 대소문자를 구별하므로 TeXGyreTermes-Regular.otf는 통과하지 않습니다. 다만 이렇게 쓰면 굵은꼴과 이탤릭의 자동 짝짓기가 사라지므로 BoldFont, ItalicFont, BoldItalicFont를 명시해야 합니다. 위 예시가 바로 그 형태이며 xelatex에서도 lualatex에서도 오류 0, 빠진 문자 0으로 통과합니다. 배포판 글꼴을 시스템에 등록해 버리는 방법(리눅스의 texlive-fontconfig 설정 등)도 있지만 환경마다 다르고 협업자 쪽에서 재현되지 않으므로, 원고에 적을 것이라면 파일 이름 지정이 더 확실합니다. “XeLaTeX에서 글꼴을 못 찾는다”는 말을 들으면 이름보다 먼저 파일 이름을 시켜 보십시오.

일본어 글꼴을 보면 이 규칙이 반대편에서 뚜렷해집니다. 운영체제가 아는 이름은 통하고, TeX Live만 아는 이름은 통하지 않습니다. TeX Live 2024이 깔린 이 macOS에서 시험해 보면 \setmainfont{Hiragino Mincho ProN}, \setmainfont{Hiragino Sans}, \setmainfont{YuMincho}는 모두 XeLaTeX에서 통과합니다. 시스템 글꼴 목록에 그대로 나오는 이름이기 때문입니다. 반면 \setmainfont{Noto Sans JP}는 운영체제 쪽에 설치해 두지 않았다면 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 오류 줄”과 “마지막 드라이버 줄” 두 덩어리로 나눠 읽는 편이 가장 빠릅니다.

shell
# 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.tex

pdfLaTeX에서 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와는 다른 물건이며, 결국 엔진을 옮긴 의미가 거의 사라집니다.

로그 한 줄로 둘을 구별할 수 있습니다. [T1]{fontenc}를 남긴 채 을 쓰면 Missing character: There is no 日 ("65E5) in font ec-lmr10!가 나옵니다. 코드 포인트가 TeX식 16진 "65E5이고 글꼴 이름이 8비트 글꼴입니다. 같은 문서를 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돌출만 켜지고 신축은 없습니다그대로 읽어 들여도 됩니다
babel동작하지만 RTL과 복잡한 문자에는 약합니다polyglossia를 검토합니다

XeLaTeX에서 글꼴 신축이 듣지 않는 이유

엔진에 그 기능이 없기 때문입니다. microtype의 두 기둥 가운데 XeTeX이 구현한 것은 돌출뿐입니다. 프리미티브를 하나씩 확인해 보면 XeTeX에는 글자별 돌출량을 정하는 \lpcode\rpcode는 있지만, 늘어나는 정도를 정하는 \efcode가 없습니다. 돌출 스위치조차 pdfTeX의 \pdfprotrudechars가 아니라 이름이 따로인 \XeTeXprotrudechars입니다. XeLaTeX에서 \usepackage{microtype}을 읽어 들이면 로그에 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 동봉판은 HarfBuzz 8.3.0을 연결합니다. 엔진 쪽에도 전용 프리미티브가 늘어서 있습니다. 문서 도중에 입력 인코딩을 바꾸는 \XeTeXinputencoding, 문자에 클래스를 주고 이웃한 문자에 따라 동작을 바꾸는 \XeTeXcharclass\XeTeXinterchartoks, 언어별 줄바꿈 규칙을 고르는 \XeTeXlinebreaklocale, PDF 텍스트 추출을 위해 실제 문자를 써 넣는 \XeTeXgenerateactualtext, 그리고 이미지를 읽는 \XeTeXpicfile\XeTeXpdffile입니다. 언어 전환은 polyglossia, RTL 조판은 bidi(아랍어 등을 요청하면 polyglossia가 자동으로 읽어 들입니다), 중국어·한국어·일본어는 xeCJK가 맡습니다. 다만 일본어 세로쓰기나 까다로운 금칙이 필요한 원고라면 luatexja나 upLaTeX 쪽이 순탄합니다.

XeLaTeX을 고를 자리와 피할 자리

  • 가지고 있는 글꼴을 그대로 쓰고 싶을 때. OpenType나 TrueType 이름을 \setmainfont에 적으면 끝이고, 설정 파일을 건드릴 일이 없습니다.
  • 유니코드를 그대로 쓰고 싶을 때. UTF-8 입력이 기본이며 인코딩 관련 프리앰블이 통째로 사라집니다.
  • 다국어·RTL 문서. polyglossiabidi 조합은 아랍어와 히브리어에서 오래 검증되었고, 글자꼴 정형은 HarfBuzz가 맡습니다.
  • 피할 이유 하나 — 글꼴 신축이 필요할 때. XeTeX에는 \efcode가 없어 microtype의 신축이 아예 동작하지 않습니다. 조판 품질이 최우선이라면 pdfLaTeX이나 LuaLaTeX입니다.
  • 피할 이유 둘 — 조판을 프로그램으로 다루고 싶을 때. XeTeX에는 스크립트 언어가 내장되어 있지 않습니다. 저수준 개입이 필요하면 LuaLaTeX으로 옮깁니다.
  • 피할 이유 셋 — 일본어 세로쓰기. xeCJK의 상정 범위를 넘으므로 luatexja나 upLaTeX을 일찍 시험해 보는 편이 빠릅니다.

한 줄로 요약하면 운영체제 글꼴을 이름으로 쓰고 싶을 뿐이라면 XeLaTeX, 조판 자체에 손을 대고 싶다면 LuaLaTeX입니다. 셋을 나란히 놓은 본격적인 비교는 “어떤 엔진을 고를까” 페이지에 있습니다. 끝으로 협업 관련 주의 하나. 글꼴 이름은 운영체제마다 다르고, \setmainfont{Helvetica Neue}는 동료의 Linux 기계에서 거의 틀림없이 실패합니다. 공유하는 원고라면 먼저 위처럼 파일 이름으로 지정한 동봉 글꼴로 빌드가 통과하는 상태를 만들고, 거기서 필요한 만큼만 바꿔 넣는 편이 안전합니다. PDF만 제출한다면 보내기 전에 pdffonts로 모든 글꼴이 묻혀 있는지 확인하십시오.