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 최대의 공헌인 마이크로 타이포그래피(문자 돌출과 글꼴 신축)를 TeX Live 2024에서 직접 돌린 출력과 함께 살펴봅니다.
pdflatex과 latex과 pdftex의 차이
셋은 같은 실행 파일입니다. TeX Live 2024의 bin 디렉터리를 보면 pdflatex도 latex도 pdftex를 가리키는 심볼릭 링크이고, amstex와 csplain도 마찬가지입니다. 다른 것은 시작할 때 읽어 들이는 포맷뿐입니다. 포맷이란 매크로 한 벌을 미리 전개해 굳혀 둔 .fmt 파일이며, fmtutil.cnf의 pdflatex pdftex ... *pdflatex.ini라는 한 줄이 “pdflatex이라는 이름으로 불리면 pdftex 엔진에 LaTeX 매크로를 얹어 실행하라”고 지시합니다. 그래서 엔진(프로그램)과 포맷(명령 체계)은 서로 다른 축이며, pdfLaTeX이란 pdfTeX 엔진과 LaTeX 포맷의 조합을 가리키는 이름일 뿐입니다.
$ 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”이 아니라 DVI 모드로 도는 pdfTeX이라는 사실입니다. 실행하면 배너에 This is pdfTeX, ... (preloaded format=latex)이 뜨고 결과물은 .dvi입니다. 다른 하나는 pdftex 포맷이 pdfetex.ini로 만들어진다는 점입니다. 여기서 e는 e-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 출구를 막지는 않았습니다. PostScript \special에 기대는 패키지, 특히 PSTricks에서는 지금도 DVI → PS → PDF 경로가 필요할 때가 있고, \pdfoutput=0은 그 길을 열어 두기 위해 남아 있습니다.
함정은 위치입니다. \pdfoutput은 첫 페이지가 출력되기 전에 정해져 있어야 하며, 도중에 바꾸면 pdfTeX이 ! pdfTeX error (setup): \pdfoutput can only be changed before anything is written to the output.으로 멈추고 이어서 ! ==> Fatal error occurred, no output PDF file produced!가 나옵니다. PDF도 DVI도 남지 않습니다. LaTeX은 드라이버 판정을 꽤 이른 시점에 하므로, 안전한 자리는 \documentclass보다 앞, 파일 맨 위입니다.
% 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이 실제로 하는 일: 문자 돌출과 글꼴 신축
\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), 곧 마진 커닝입니다. 행 끝의 마침표나 하이픈을 판면 바깥으로 살짝 내밀어, 기계적으로 맞춘 가장자리를 오히려 시각적으로 곧게 보이도록 하는 기법이며, 활자 시대의 “매달기”를 일반화한 것입니다. 뒤의 것이 글꼴 신축입니다. 글자꼴을 가로로 최대 2 %(20/1000)까지 늘이거나 줄이고, 그 여유를 줄바꿈 판단 자체에 반영합니다. 낱말 사이 간격이 고르게 되고, 문단을 타고 흐르는 흰 줄기(리버)도 함께 옅어집니다.
이 둘은 원래 TeX의 발명이 아닙니다. 착상은 hz-program으로 거슬러 올라갑니다. 서체 디자이너 Hermann Zapf가 1988년부터 함부르크 URW에서 Peter Karow 등과 함께 만든 조판 프로그램으로, 목표는 “구멍도 강줄기도 없는 고른 회색 지면”이었습니다. URW는 이를 특허로 등록했고(유럽 특허 EP 0466953, 권리는 2010년 7월 만료), 알고리즘은 결국 Adobe로 넘어가 InDesign에 들어갔습니다. 그런데 같은 착상은 다른 길도 갔습니다. 베트남 출신의 Hàn Thế Thành이 hz를 분석해 TeX에 구현한 것입니다. 체코 마사리크 대학 정보학부 박사 논문 『Micro-typographic extensions to the TeX typesetting system』(2000년 10월, 지도 Jiří Zlatuška)이 그 결과입니다. 하나의 착상이 상업 소프트웨어의 정점과 누구나 쓸 수 있는 무료 구현 양쪽에 닿은 셈이고, \usepackage{microtype}은 그 후자입니다.
\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}두 기능 모두 날것의 프리미티브로도 다룰 수 있습니다. 돌출 쪽은 \pdfprotrudechars(0은 끄기, 1은 켜기, 2는 폭 계산에도 반영)와 글자별 돌출량을 정하는 \lpcode(왼쪽)·\rpcode(오른쪽)입니다. 신축 쪽은 \pdfadjustspacing(=2면 신축을 줄바꿈에 반영), 글자별로 늘어나는 정도를 정하는 \efcode, 그리고 글꼴의 신축 인스턴스를 선언하는 \pdffontexpand입니다. 실무에서 이것들을 직접 건드릴 일은 거의 없습니다. microtype이 서체마다 설정 파일(Computer Modern이면 mt-cmr.cfg)을 갖추고 값을 대신 골라 주기 때문입니다. 그래도 이름을 알아 두면 로그에 뜨는 경고가 무슨 이야기인지 읽힙니다.
| 프리미티브 | 기능 | 무엇을 제어하는가 |
|---|---|---|
\pdfprotrudechars | protrusion | 0은 끄기, 1은 켜기, 2는 폭 계산에도 반영 |
\lpcode / \rpcode | protrusion | 글자별 왼쪽·오른쪽 돌출량(1000분율) |
\pdfadjustspacing | expansion | =2이면 신축을 줄바꿈 판단에 반영 |
\efcode | expansion | 글자별로 늘어나는 정도. XeTeX에는 없습니다 |
\pdffontexpand | expansion | 글꼴의 신축 인스턴스를 선언합니다 |
글꼴은 TFM 메트릭과 map 파일로 결정됩니다
pdfTeX은 치수와 글자꼴을 서로 다른 곳에서 가져옵니다. 치수는 .tfm(TeX Font Metric)에서 옵니다. kpsewhich cmr10.tfm은 fonts/tfm/public/cm/cmr10.tfm을 돌려주는데, 이 파일에는 각 문자의 너비·높이·깊이와 커닝 표만 있을 뿐 윤곽선은 하나도 없습니다. 조판이 끝나고 PDF를 조립할 단계가 되어서야 map 파일을 찾아, 거기 적힌 실제 글꼴(Type 1 .pfb나 TrueType)을 읽습니다. cmr10에 해당하는 항목은 딱 한 줄, cmr10 CMR10 <cmr10.pfb입니다. 차례로 TFM 이름, PostScript 이름이며 <는 “이 파일을 묻어 넣어라”라는 뜻입니다. TeX Live 2024에서 updmap이 만든 pdftex.map은 45,443줄이며, 이 텍스트 파일 하나가 배포판 전체 글꼴의 대응표입니다.
$ 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은 CMR10을 Type 1로, XeLaTeX과 LuaLaTeX은 LMRoman10-Regular를 CID Type 0C로 묻어 넣습니다. 지면은 어느 쪽이든 Computer Modern처럼 보이지만, 지나온 경로도 묻힌 파일도 전혀 다릅니다. map 파일은 문서 안에서 \pdfmapfile과 \pdfmapline으로 덧붙일 수 있는데, 흥미롭게도 이 둘은 XeTeX에도 남아 있습니다. 8비트 시대의 유산은 생각보다 넓게 살아 있습니다.
8비트 엔진의 한계: ! Bad character code와 fontspec이 거부하는 이유
글꼴 하나당 256자. 이것이 pdfTeX의 가장 단단한 천장입니다. \font\x=cmr10 \x \char"1234이라고 쓰면 ! Bad character code (4660).으로 멈춥니다. 같은 입력을 LuaTeX에 넣으면 아무 일도 없습니다. 내부적으로 글꼴이 “넓기” 때문입니다. 이 256칸의 벽 때문에 악센트가 붙은 라틴 문자는 T1 같은 글꼴 인코딩으로 다시 채워 넣어야 했고, 그리스 문자와 기호는 별도의 글꼴로 밀려나야 했습니다. 이 천장을 보고 나면 fontenc이 무엇을 위한 도구인지 단번에 이해됩니다.
오해는 입력 쪽에 있습니다. UTF-8 원본 자체는 2018년 이후의 LaTeX이라면 inputenc를 읽지 않아도 통과합니다. 다만 통과하는 것은 “LaTeX이 선언해 둔 문자”뿐입니다. 본문에 →를 쓴 .tex은 pdflatex으로 아무 문제 없이 컴파일되지만, 日을 쓰면 ! LaTeX Error: Unicode character 日 (U+65E5) not set up for use with LaTeX.으로 멈춥니다. 즉 pdfTeX의 제약은 “UTF-8을 못 읽는다”가 아니라 “미리 마련된 문자만 조판할 수 있다”입니다. 게다가 운영체제에 설치된 OpenType 글꼴을 이름으로 부르는 길이 없어서, fontspec을 읽어 들이면 곧바로 ! Fatal Package fontspec Error: The fontspec package requires either XeTeX or LuaTeX.으로 끝납니다. 이 세 가지—256칸, 선언된 문자만, 시스템 글꼴 불가—가 XeTeX이나 LuaTeX으로 옮겨 가는 이유의 거의 전부입니다.
- 머무를 이유: 기존 템플릿과의 호환, 속도, 그리고 30년치 실전 검증. 영문 위주 논문이라면
pdflatex이 사고가 가장 적은 선택입니다. - 옮길 이유: 다국어 유니코드, 운영체제 글꼴, 일본어 조판, Lua 기반 처리. 이 중 하나라도 정말 필요하다면 우회책을 쌓기보다 갈아타는 편이 빠릅니다.
- 흔한 사고: pdfLaTeX 그대로
fontspec을 읽어 들이기.fontspec은 XeLaTeX과 LuaLaTeX의 것입니다. - 또 하나의 사고:
\pdfoutput을\documentclass뒤에 쓰기. PDF도 DVI도 남기지 못한 채 실행이 죽습니다.
이미지: 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 기능에 손을 뻗는 프리미티브도 갖추고 있습니다. \pdfliteral은 날것의 PDF 그리기 연산자를 그대로 흘려 넣고, \pdfobj는 PDF 객체를 만들며, \pdfannot은 주석(링크, 폼 필드)을 놓습니다. hyperref의 링크와 북마크가 바로 이 층 위에 서 있습니다. 문서 전체 설정으로는 제목·저자 같은 메타데이터를 쓰는 \pdfinfo, 압축 강도를 정하는 \pdfcompresslevel, 모든 페이지에 속성을 붙이는 \pdfpageattr가 있습니다.
\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에서의 표기 |
|---|---|---|
\pdfoutput | PDF와 DVI 중 무엇을 낼지 정합니다 | \outputmode |
\pdfliteral | 날것의 PDF 그리기 연산자를 넣습니다 | \pdfextension literal |
\pdfobj / \pdfannot | PDF 객체나 주석을 직접 만듭니다 | \pdfextension obj / annot |
\pdfinfo | 제목·저자 같은 메타데이터를 씁니다 | \pdfextension info |
\pdfcompresslevel | 출력 PDF의 압축 수준(0–9) | \pdfvariable compresslevel |
\pdfximage | PNG / JPEG / PDF 이미지를 읽어 들입니다 | \saveimageresource |
\pdfsavepos | 페이지 위 현재 위치를 기록합니다 | \savepos |
\pdfprotrudechars | 돌출을 켭니다 | \protrudechars |
\pdfadjustspacing | 글꼴 신축을 켭니다 | \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에서 프리미티브를 하나씩 확인해 보면 XeTeX에는 \lpcode와 \rpcode는 있지만 \efcode는 없습니다. 돌출 스위치도 \pdfprotrudechars가 아니라 이름이 다른 \XeTeXprotrudechars입니다. 그래서 XeLaTeX에서는 돌출은 되지만 글꼴 신축은 원리적으로 구현할 수 없고, \usepackage[expansion=true]{microtype}으로 명시하면 ! Package microtype Error: Font expansion does not work with xetex.으로 멈춥니다. 옵션 없이 읽어 들이면 로그에서 신축 관련 줄이 아무 말 없이 사라질 뿐이라, 이유도 모른 채 “microtype을 넣었는데 효과가 약하다”고 느끼게 됩니다.
지금도 pdflatex을 고르는 기준
주로 영문이고, 학술지나 공동 연구자가 템플릿을 이미 정해 두었으며, 마감이 가깝다면 pdfLaTeX입니다. 같은 417쪽짜리 수식 포함 문서를 이 기계에서 재어 보면 pdfLaTeX이 세 엔진 가운데 가장 빨랐고, 기존 패키지와 부딪힐 확률도 가장 낮습니다. 요구하는 것도 적습니다. \usepackage{microtype} 한 줄을 넣고, 그림은 PNG / JPEG / PDF로 두고, EPS를 쓴다면 shell escape를 끄지 않는 것입니다. 반대로 일본어나 시스템 글꼴이 필요한 문서에 pdfTeX용 우회책을 쌓아 올리는 것은 수지가 맞지 않습니다. 그 선을 넘으면 XeLaTeX이나 LuaLaTeX의 몫입니다. pdfTeX 자체는 지금 대체로 유지보수 모드이고 새 개발은 LuaTeX으로 옮겨 갔지만, pdftex.map의 45,443줄과 30년치 템플릿 자산은 당분간 어디로도 가지 않습니다.