latex을 쳤는데 PDF가 나오지 않습니다. 고장이 아닙니다. LaTeX의 가장 오래된 명령은 PDF가 아니라 DVI 를 쓰고, 그다음은 dvipdfmx, dvips, dvisvgm 같은 DVI 변환기 의 몫이기 때문입니다. 이 형식의 역사는 꽤 깁니다. 커누스의 DVItype 소스에 따르면 첫 DVItype 프로그램은 1979년 David Fuchs가 설계했으니, PDF가 태어나기 십수 년 전입니다. pdflatex이 PDF를 곧바로 만들어 내는 오늘날에도 이 2단 경로가 사라지지 않는 데에는 분명한 이유가 있습니다. 이 페이지에서는 TeX Live 2024에 들어 있는 각 변환기가 무엇을 먹고 무엇을 내놓는지, 일본어의 uplatex → dvipdfmx 경로가 왜 여전히 정석인지, 그리고 변환 단계에서 나오는 오류를 어떻게 읽는지를 다룹니다.
pdflatex이 있는데도 DVI를 거치는 이유
DVI(device independent)는 “몇 쪽의 이 좌표에, 이 글꼴의 이 글자를 놓아라”라는 지시만 늘어놓은 중간 형식입니다. 글꼴 실체도 들어 있지 않고 색도 이미지도 모릅니다. 이 가벼움이 핵심입니다. TeX 본체는 조판에만 집중하고 종이에 무엇을 어떻게 얹을지는 장치 쪽 사정으로 보아 변환기에 통째로 넘깁니다. 1979년의 설계 판단이 오늘날 PostScript, PDF, SVG로 가는 출구가 되어 있는 셈입니다. 그러니 DVI가 남아 있는 것은 호환성 때문만이 아닙니다. pdflatex은 PDF만 쓸 수 있지만, DVI 한 부는 세 방향으로 갈라질 수 있습니다. 게다가 PSTricks 계열 패키지는 PostScript 인터프리터를 전제로 한 \special 명령으로 동작하므로 경로 어딘가에 인터프리터가 있어야 하고, latex → dvips가 그것을 곧바로 대 주는 길입니다. 일본어 pTeX 계열 엔진에 이르면 PDF를 직접 쓰는 구현 자체가 없습니다.
dvipdfmx와 xdvipdfmx와 extractbb는 하나의 프로그램
TeX Live 2024의 bin 디렉터리를 들여다보면 dvipdfmx, dvipdfm, extractbb, ebb 넷이 모두 xdvipdfmx를 가리키는 심볼릭 링크입니다. 즉 다섯 개의 이름을 가진 하나의 실행 파일이고, 프로그램은 자신이 어떤 이름으로 불렸는지를 보고 동작을 바꿉니다. dvipdfmx --version은 “This is dvipdfmx Version 20240305”라고 밝히고, xdvipdfmx --version은 같은 날짜로 xdvipdfmx라고 밝힙니다. 같은 파일입니다. 이 사실을 알면 XeTeX 쪽 이야기도 한 번에 정리됩니다. xelatex -no-pdf는 PDF가 아니라 .xdv(확장 DVI)를 쓰고, 그것을 xdvipdfmx가 PDF로 만듭니다. 같은 바이너리이므로 dvipdfmx sample.xdv라고 쳐도 통합니다. 조상인 dvipdfm 은 Mark A. Wicks가 만들었고, CJK 지원과 글꼴 임베딩, 오브젝트 스트림을 더해 dvipdfmx로 확장한 것은 Jin-Hwan Cho와 히라타 슌사쿠 등입니다.
ls -l $(dirname $(which dvipdfmx))/dvipdfmx
# dvipdfmx -> xdvipdfmx
dvipdfmx sample.dvi # sample.dvi -> sample.pdf
dvipdfmx -p a4 -o out.pdf sample.dvi
xelatex -no-pdf paper.tex # writes paper.xdv, not paper.pdf
xdvipdfmx paper.xdv # paper.xdv -> paper.pdf| 옵션 | 역할(TeX Live 2024 기본값) |
|---|---|
-o FILE | 출력 파일 이름. -는 표준 출력. 기본값은 입력 이름의 확장자를 .pdf로 바꾼 것 |
-p a4 | 용지 크기를 이름으로 지정. 기본은 a4. 목록은 dvipdfmx --showpaper |
-V 5 | PDF 버전. 기본 5는 PDF 1.5. 투고처가 1.4를 요구하면 -V 4 |
-s 1-3,5 | 처리할 물리적 페이지 범위를 제한. 쉼표로 여러 구간 지정 가능 |
-l | 가로 방향. 용지의 너비와 높이를 바꿉니다 |
-z 9 | zlib 압축 수준 0~9. 기본은 9. 원본 PDF를 들여다볼 때는 -z 0 |
-f FILE.map | 글꼴 맵 파일을 추가로 읽음. 임베드할 글꼴을 바꿔 끼울 때 쓰는 통로 |
! LaTeX Error: Cannot determine size of graphic이 나오면 드라이버 지정을 의심합니다
DVI 경로에서 그림을 넣으면 대부분 이 오류를 가장 먼저 만납니다. ! LaTeX Error: Cannot determine size of graphic in fig.pdf (no BoundingBox). 원인은 이미지 파일이 아니라 graphicx가 어느 드라이버용으로 설정되어 있는가입니다. TeX Live 2024의 graphics.cfg는 DVI를 쓰는 실행에서 기본으로 dvips.def를 고르는데, dvips는 PostScript만 넣을 수 있어 PDF나 PNG의 크기를 읽을 방법이 없습니다. 프리앰블에 \usepackage[dvipdfmx]{graphicx}라고 명시하면 이 환경에서는 PDF도 PNG도 추가 파일 없이 그대로 통과합니다. 실제로 확인해 보니 .xbb를 하나도 만들지 않고 셸 이스케이프도 쓰지 않은 채 latex이 그림을 읽어들였고, dvipdfmx가 PDF에 넣었습니다. 그래도 크기를 못 읽는 오래된 이미지에는 extractbb fig.pdf를 실행합니다. 만들어지는 .xbb는 %%BoundingBox: 0 0 612 792 같은 몇 줄짜리 텍스트이고, 위의 심볼릭 링크대로 그것을 쓰는 프로그램은 dvipdfmx 자신입니다.
% name the driver explicitly on the DVI route
\documentclass{article}
\usepackage[dvipdfmx]{graphicx}
\begin{document}
\includegraphics[width=3cm]{fig.pdf}
\end{document}dvips — PostScript만 할 수 있는 일을 위해 남아 있는 길
dvips -o paper.ps paper.dvi는 DVI를 PostScript로 만듭니다. Tomas Rokicki가 만든 프로그램이지만 man 페이지의 저자 항목에는 “extended to virtual fonts by Don Knuth”라고 적혀 있어, 가상 글꼴 지원은 커누스 본인이 더한 것이고 탐색 경로와 설정은 Karl Berry가 맡은 공동 작업임을 알 수 있습니다. 지금 dvips를 쓸 이유는 셋으로 좁혀집니다. PostScript 연산에 의존하는 그림, 무엇보다 PSTricks. 인쇄소가 .ps를 요구하는 입고. 그리고 dvips -E로 그림 한 장을 떼어 내는 일입니다. -E는 내용에 딱 맞는 바운딩 박스(이번 실행에서는 %%BoundingBox: 148 88 326 668)를 붙인 EPS를 씁니다. 끝에 PDF가 필요하면 Ghostscript에 딸린 ps2pdf에 통과시키는 것이 정석입니다.
dvips -t a4 -o paper.ps paper.dvi # DVI -> PostScript
ps2pdf paper.ps # PostScript -> PDF (Ghostscript)
dvips -E -o figure.eps figure.dvi # one tightly cropped EPS| 옵션 | 역할 |
|---|---|
-o FILE | 출력할 PostScript 파일 이름. 생략하면 프린터로 보내려 할 수 있습니다 |
-t a4 | 용지 종류: a4, letter, legal, 그리고 landscape |
-E | 한 쪽을 EPS로 씁니다. 한 쪽짜리 문서 전용이며, 넣은 그림은 박스 계산에 들어가지 않습니다 |
-P NAME | 프린터 설정 config.NAME을 읽습니다. 해상도와 글꼴 처리 방식이 바뀝니다 |
-p 3 -l 7 | 3쪽부터 7쪽까지만 출력합니다 |
여기에 모르면 하루가 통째로 녹는 함정이 있습니다. 비트맵 이미지를 dvips 경로에 넣지 마십시오. 드라이버가 dvips인 채라면 \includegraphics{dot.png}는 위의 no BoundingBox에서 멈춥니다. 그런데 .xbb를 손으로 만들어 억지로 통과시키면, 이번에는 LaTeX도 dvips도 아무 말 없이 .ps를 다 써 버립니다. 망가진 곳은 그 뒤입니다. 그 .ps를 ps2pdf에 흘려보내자 Ghostscript가 Error: /syntaxerror in (binary token, type=137)을 뱉고 GPL Ghostscript 10.03.0: Unrecoverable error, exit code 1로 죽었습니다. PostScript는 PNG를 모르니 당연한 결과지만, 고약한 것은 마지막의 마지막까지 아무도 항의하지 않는다는 점입니다. 그러니 dvips를 쓴다면 그림은 EPS로 통일하고, PNG와 JPEG와 PDF를 섞고 싶다면 dvipdfmx 경로로 갑니다. 판단은 이 한 줄이면 충분합니다.
dvisvgm — 수식을 SVG로, 그리고 글꼴 형식이 크기를 바꾼다
웹에 수식을 올릴 때, 이미지로 만들면 확대에서 뭉개지고 MathML로 하면 환경마다 다르게 나옵니다. dvisvgm(Martin Gieseking, 2005년부터 개발)은 DVI를 그대로 SVG로 변환하므로 TeX의 조판 결과가 벡터인 채로 남습니다. 이 기기의 TeX Live 2024 판은 dvisvgm 3.2.2이며, --eps로 EPS도, --pdf로 PDF도 입력으로 받습니다. 여기서 가장 중요한 것이 --font-format입니다. 같은 한 쪽을 변환해 크기를 재 보면, 기본값인 SVG 임베드 글꼴에서 13,156바이트, --font-format=woff2에서 8,361바이트, 그리고 반대로 글리프를 모두 경로로 바꾸는 --no-fonts에서는 23,563바이트로 불어났습니다. 게다가 경로화한 SVG에는 <text> 요소가 하나도 남지 않아 검색도 복사도 되지 않습니다. “외곽선으로 만들면 더 가볍다”는 적어도 여기서는 성립하지 않습니다.
| 설정 | 같은 쪽의 실측 크기와 성질 |
|---|---|
(default) | 13,156바이트. SVG 글꼴을 임베드하고 <text> 요소가 14개 남습니다 |
--font-format=woff2 | 8,361바이트. 가장 작고 텍스트도 남습니다. 웹 배포의 첫 후보 |
--no-fonts | 23,563바이트. 모두 경로가 되고 <text>는 0개. 검색도 복사도 불가 |
-z | 5,383바이트. gzip으로 압축한 .svgz. 서버가 지원한다면 가장 작습니다 |
latex equation.tex # -> equation.dvi
dvisvgm --font-format=woff2 equation.dvi # -> equation.svg
dvisvgm --pdf figure.pdf # PDF input
dvisvgm --libgs=/path/to/libgs.dylib f.dvi # only if PS specials are used보고가 끊이지 않는 메시지가 하나 더 있습니다. processing of PostScript specials is disabled (Ghostscript not found) — Ghostscript가 설치된 기기에서 말입니다. 이 환경이 바로 그 경우로, gs는 /usr/local/bin/gs에 있는데도 dvisvgm은 이렇게 말합니다. 찾는 대상이 다르기 때문입니다. dvisvgm은 gs 실행 파일을 돌리는 것이 아니라 실행 시점에 libgs라는 공유 라이브러리를 불러옵니다. MacTeX에 딸려 오는 Ghostscript는 실행 파일만 두고 가므로 라이브러리를 찾을 수 없습니다. \special을 쓰는 그림(PSTricks나 일부 TikZ 출력)을 SVG로 만들어야 한다면 공유 라이브러리가 있는 Ghostscript를 설치하거나 --libgs=로 위치를 알려 줍니다. 반대로 수식과 \includegraphics뿐인 DVI라면 이 경고는 무시해도 됩니다.
일본어의 uplatex → dvipdfmx 경로와 ptex2pdf
일본어 조판에서 DVI 경로가 살아남은 것은 복고 취향이 아니라 필연입니다. pTeX 계열 엔진(ptex, eptex, uptex, uplatex)에는 PDF를 직접 쓰는 기능이 없습니다. 세로쓰기, 금칙 처리, 한자와 로마자 사이의 여백 같은 일본어 고유의 처리는 pTeX가 맡고, PDF화와 일본어 글꼴 임베딩은 dvipdfmx가 맡습니다. 이 분업의 양쪽에 사십 년이 쌓여 있습니다. 그래서 절차는 반드시 두 단계입니다. uplatex paper.tex로 .dvi를 만들고, dvipdfmx paper.dvi로 PDF를 만듭니다. 문서 쪽에서도 \documentclass[uplatex,dvipdfmx]{jsarticle}처럼 드라이버를 명시해 두어야 graphicx와 color가 올바른 \special을 써 냅니다.
이 두 단계를 한 명령으로 묶는 것이 ptex2pdf 입니다. TeX Live 2024에 들어 있는 것은 version 20200520.0으로, texjporg(일본어 TeX 개발 커뮤니티)와 Norbert Preining이 만든 texlua 스크립트입니다. 명령 자체는 texmf-dist/scripts/ptex2pdf/ptex2pdf.lua를 가리키는 심볼릭 링크이며, 엔진이 끝나면 자동으로 dvipdfmx를 부릅니다. 엔진은 플래그 조합으로 고르고, 일본어 논문의 표준인 upLaTeX는 -l -u입니다. 세부 옵션은 -ot로 TeX 쪽에, -od로 dvipdfmx 쪽에 넘깁니다. 다만 ptex2pdf는 컴파일 횟수를 세어 주지 않습니다. 상호 참조나 참고문헌이 있는 문서라면 횟수까지 챙기는 latexmk에 맡기는 편이 확실합니다.
| 플래그 | 호출되는 엔진 |
|---|---|
(none) | ptex — plain TeX의 pTeX 판 |
-l | platex — pLaTeX. JIS X 0208 범위를 다루는 전통적 엔진 |
-u | uptex — 내부적으로 유니코드를 쓰는 pTeX |
-l -u | uplatex — upLaTeX. 오늘날 일본어 논문의 기본 선택 |
# the two steps, written out
uplatex paper.tex # -> paper.dvi
dvipdfmx paper.dvi # -> paper.pdf
# the same thing in one command
ptex2pdf -l -u -ot '-kanji=utf8 -synctex=1' paper.tex
ptex2pdf -l -u -s paper.tex # stop at the DVI
# what to check when the converter fails
dvipdfmx nosuch.dvi
# dvipdfmx:fatal: Could not open specified DVI (or XDV) file: nosuch.dvi
# No output PDF file written.변환 단계에서 나오는 오류와 그 뜻
| 메시지 | 실제로 일어난 일 |
|---|---|
dvipdfmx:fatal: Could not open specified DVI | .dvi가 없습니다. pdflatex으로 조판해 DVI가 만들어지지 않은 경우가 가장 많습니다. 종료 코드 1 |
dvips: DVI file can't be opened | 마찬가지로 입력을 찾지 못합니다. dvips는 이름을 추측하지 않으니 확장자까지 확인합니다 |
dvips: ! Bad DVI file: id byte not 2 | XeTeX가 쓴 .xdv를 dvips에 넘겼습니다. XDV를 읽는 것은 xdvipdfmx뿐입니다 |
no BoundingBox | graphicx가 dvips용으로 설정되어 있습니다. \usepackage[dvipdfmx]{graphicx}를 시도합니다 |
Ghostscript not found | dvisvgm이 libgs를 찾지 못합니다. gs 실행 파일이 있는 것과는 별개입니다 |
제출 전 점검도 경로마다 볼 자리가 다릅니다. PDF로 낸다면 dvipdfmx 뒤에 독자가 쓸 뷰어로 열어 글꼴 임베딩, 용지 크기, 책갈피, 링크를 확인합니다. 인쇄소로 넘긴다면 dvips가 만든 .ps를 ps2pdf에 통과시키기 전후로 EPS와 PostScript \special이 살아남았는지 봅니다. 편집기의 정방향・역방향 검색을 쓴다면 latex -synctex=1을 붙이면 DVI 경로에서도 .synctex.gz가 만들어집니다. 여기서는 860바이트짜리 파일이 생겼고 dvipdfmx를 돌린 뒤에도 그대로 남았습니다. .dvi, .pdf, .synctex.gz는 job name을 공유하므로, 출력을 다른 디렉터리로 보낸다면 빌드 도구 쪽에서 그 경로를 일관되게 지정하십시오.
그렇다고는 해도 이 명령들을 손으로 순서대로 칠 일은 많지 않습니다. latexmk는 $pdf_mode를 바꾸는 것만으로 latex → dvipdfmx나 (u)platex → dvipdfmx 경로를 챙겨 주고, .dvi가 낡았는지도 스스로 판단합니다. 손으로 치는 때는 경로 자체를 의심할 때, 곧 이 페이지가 필요해진 때뿐입니다. 뒤집어 말하면 변환기의 이름과 인자를 한 번 익혀 두면 빌드 도구가 무엇을 하고 있는지도 읽을 수 있게 됩니다.