“Hello” 한 단어만 담은 LaTeX 문서는 DVI로 252바이트입니다. 같은 한 페이지를 pdflatex으로 PDF로 만들면 11,287바이트가 됩니다. 늘어난 11킬로바이트의 대부분은 본문이 아니라 글꼴 자체로, 실제로 쓰인 다섯 글자만 잘라낸 Type 1 프로그램이 9,002바이트를 차지합니다. LaTeX의 PDF 생성은 결국 “파일 안에 무엇을 실어 보낼 것인가”라는 결정의 연속입니다. 이 페이지에서는 PDF에 이르는 두 경로, PDF 버전과 압축 지정, 용지 크기가 실제로 어떻게 기록되는지, 그리고 글꼴 임베드를 pdffonts로 확인하는 방법을 모두 실측값과 함께 살펴봅니다.
PDF로 가는 두 경로: 직접 출력과 DVI 경유
PDF를 쓰는 주체는 엔진 자신이거나, DVI를 변환하는 별도의 프로그램입니다. 직접 경로에서는 pdflatex(pdfTeX)와 lualatex(LuaTeX)가 페이지를 조판하면서 PDF 연산자를 곧바로 내보냅니다. DVI 경로에서는 latex나 일본어용 (u)platex가 먼저 DVI를 쓰고 dvipdfmx가 이를 번역합니다. 흥미로운 것은 xelatex입니다. 겉보기에는 명령 하나지만 실제로는 후자에 속합니다. XeTeX은 .xdv라는 확장 DVI를 쓰고 이를 xdvipdfmx에 넘깁니다. TeX Live 2024의 실제 설치를 들여다보면 사정이 분명해집니다. dvipdfmx는 xdvipdfmx를 가리키는 심볼릭 링크이며, 일본어 DVI 경로와 XeTeX의 .xdv 경로를 하나의 같은 바이너리가 처리합니다. 증거는 출력에 남습니다. xelatex으로 만든 PDF의 Producer는 xdvipdfmx (20240305)입니다.
# TeX Live 2024: one binary serves both DVI routes
$ ls -l $(which dvipdfmx)
lrwxr-xr-x 1 root wheel 9 May 4 2024 .../dvipdfmx -> xdvipdfmx
# direct
$ pdflatex paper.tex
# via DVI
$ latex paper.tex && dvipdfmx paper.dvi
# via DVI, the Japanese way
$ uplatex paper.tex && dvipdfmx paper.dvi같은 원고를 다섯 경로로 통과시키면 결과는 모두 “A4 한 쪽”이지만 내부는 조금씩 다릅니다. Producer 문자열이 다른 것은 당연하고, 눈여겨볼 것은 기본 PDF 버전과 MediaBox 수치입니다. A4의 너비 210 mm는 정확히 595.2755905… 포인트이고, 반올림 방식은 변환기마다 다릅니다. pdfTeX와 LuaTeX는 \pdfdecimaldigits=3에 따라 595.276을, dvipdfmx는 595.28을, Ghostscript(ps2pdf 경유)는 아예 정수 595로 적습니다. 실무에서 문제가 되는 일은 거의 없지만, “같은 A4인데 비교가 어긋난다”는 현상의 정체가 바로 이것입니다.
| 명령 | PDF를 쓰는 프로그램 | 기본 PDF 버전 | A4 MediaBox 너비 |
|---|---|---|---|
pdflatex | pdfTeX 자신 | 1.5 | 595.276 |
lualatex | LuaTeX 자신 | 1.5 | 595.276 |
xelatex | .xdv를 거쳐 xdvipdfmx | 1.5 | 595.28 |
latex + dvipdfmx | dvipdfmx | 1.5 | 595.28 |
uplatex + dvipdfmx | dvipdfmx. 일본어의 표준 경로 | 1.5 | 595.28 |
latex + dvips + ps2pdf | Ghostscript | 1.4 | 595 |
“pdflatex에서는 EPS를 못 쓴다”는 이제 옛말입니다
TeX Live 2024에서는 \usepackage{graphicx}에 \includegraphics{fig.eps}라고 쓰고 pdflatex을 돌리면 별도 설정 없이 그림이 들어갑니다. pdfTeX이 EPS를 읽게 된 것은 아닙니다. 드라이버 파일 pdftex.def가, LaTeX이 실행 중이고 셸 이스케이프가 켜져 있으며(TeX Live 기본값인 restricted로 충분합니다) \DoNotLoadEpstopdf가 정의되지 않았다면 epstopdf-base를 알아서 불러옵니다. 실제로 돌려 보면 원고 옆에 fig-eps-converted-to.pdf라는 중간 파일이 생기고, 삽입되는 것은 바로 그 파일입니다. 이제 \usepackage{epstopdf}를 직접 쓸 필요가 없습니다. 반대로 -no-shell-escape를 붙이면 변환이 일어나지 않고, 오류도 경고도 없이 그림만 사라집니다. 이 조용한 실패가 더 성가십니다.
이 자동 변환에는 함정도 있는데, pdftex.def의 주석 자체가 “이것이 틀릴 수도 있다” 고 경고합니다. fig.pdf와 fig.eps가 모두 있고 진짜 원본이 PDF 쪽이라면, EPS에서 다시 만들어진 PDF에 덮어써지기 때문입니다. 그럴 때는 \documentclass보다도 앞에 \newcommand{\DoNotLoadEpstopdf}{}를 둡니다. 한편 DVI 경로(dvipdfmx)는 예전부터 EPS를 처리해 왔습니다. 뒤에서 Ghostscript를 불러 PDF로 바꾼 뒤 삽입하며, 중간 파일도 남기지 않습니다. EPS 자산이 많은 기존 워크플로가 DVI 경로와 잘 맞는 이유가 여기에 있습니다.
% keep a hand-made fig.pdf from being overwritten by fig.eps
\newcommand{\DoNotLoadEpstopdf}{}
\documentclass{article}
\usepackage{graphicx}
\begin{document}
\includegraphics{fig} % extension omitted: pdf, png, jpg are tried first
\end{document}출력 드라이버는 정말 자동으로 판정되나
graphicx와 color에 관해서는 답이 “예”입니다. 둘 다 올바른 저수준 명령을 내보내려면 출력 드라이버(pdftex, luatex, xetex, dvipdfmx, dvips, dvisvgm)를 알아야 하는데, 설정 파일 graphics.cfg가 \pdfoutput, \XeTeXversion, \luatexversion의 존재를 조사해 엔진을 알아맞히고 pdftex.def, luatex.def, xetex.def, dvips.def 중 무엇을 읽을지 정합니다. 그러니 드라이버 옵션을 손으로 쓰지 마세요. 박아 넣은 지정은 컴파일 방법을 바꾸는 순간 거짓이 됩니다.
그런데 hyperref만은 따로 다뤄야 합니다. (u)platex로 DVI를 만들면서 \usepackage{hyperref}라고만 쓰면 로그에 Package hyperref Info: Driver (default): hdvips.가 나옵니다. dvips용 \special을 써 버리는 것입니다. 그 DVI를 dvipdfmx에 넘기면 dvipdfmx:warning: Unknown token "SDict"가 줄줄이 나오고, 링크도 책갈피도 사라진 PDF가 만들어집니다. 이 경로에서는 \usepackage[dvipdfmx]{hyperref}라고 명시하세요. “드라이버를 지정하지 말라”는 graphicx 이야기이며 DVI 경로의 hyperref에는 해당하지 않습니다(자세한 내용은 “책갈피와 메타데이터”를 참조).
PDF 버전 지정: \pdfminorversion은 어느 엔진에서 통하나
\pdfminorversion은 pdfTeX 전용입니다. LuaTeX에서 쓰면 ! Undefined control sequence.가 나고, XeTeX에서도 같은 오류가 납니다. LuaTeX은 프리미티브를 이름 공간으로 재편했으므로 \pdfvariable minorversion=4라고 씁니다. XeTeX에는 대응하는 프리미티브가 아예 없고, PDF를 쓰는 쪽은 xdvipdfmx이므로 드라이버에 넘깁니다. -output-driver="xdvipdfmx -V 4"처럼요. DVI 경로라면 가장 간단해서 dvipdfmx -V 4 paper.dvi입니다. 기본값의 출처도 추적할 수 있습니다. TeX Live의 pdftexconfig.tex에 \pdfminorversion = 5가 들어 있고, “기본값은 PDF 1.5”의 정체는 그것뿐입니다.
다만 2024년 시점에서는 이 세 가지 표기를 굳이 구분해 외울 필요가 많이 줄었습니다. LaTeX 커널의 새 진입점 \DocumentMetadata{pdfversion=2.0}을 \documentclass 앞에 두면 pdflatex, lualatex, xelatex 어느 쪽에서도 PDF 2.0이 나옵니다(실측 결과 세 엔진 모두 PDF version: 2.0). \pdfminorversion이 마이너 번호만 건드리는 데 비해 pdfversion은 메이저 번호까지 지정합니다. \pdfminorversion=0은 PDF 1.0이 될 뿐 결코 2.0에 닿지 못합니다.
| 엔진·경로 | PDF 버전 지정 방법 | 비고 |
|---|---|---|
\pdfminorversion=5 | pdfTeX(pdflatex) | LuaTeX/XeTeX에서는 ! Undefined control sequence. |
\pdfvariable minorversion=5 | LuaTeX(lualatex) | 같은 표기로 compresslevel 등도 지정할 수 있습니다 |
dvipdfmx -V 4 | DVI 경로와 XeTeX | XeTeX에서는 -output-driver="xdvipdfmx -V 4" |
\DocumentMetadata{pdfversion=2.0} | 세 엔진 공통 | 메이저 번호도 지정 가능. \documentclass 앞에 둡니다 |
PDF 압축: \pdfcompresslevel과 \pdfobjcompresslevel 실측
압축 수준을 9까지 올려 봐야 6과의 차이는 2바이트입니다. \lipsum[1-40]으로 만든 여덟 쪽 문서로 재면 \pdfcompresslevel=0이 79,508바이트, =1이 45,730, =6이 43,323, =9가 43,321입니다. 효과가 있는 것은 0 → 1 한 걸음뿐이고 그 이상은 오차입니다. 또 하나의 손잡이인 \pdfobjcompresslevel은 전혀 다른 구조로, 페이지 내용이 아니라 PDF 자체의 객체 정의를 객체 스트림으로 묶어 압축합니다. 같은 문서에서 43,321 → 41,019바이트, 약 5%가 여기서 나옵니다.
이 둘을 잇는 함정이 여기 있습니다. 객체 스트림은 PDF 1.5에서 도입된 기능이므로, 오래된 뷰어를 위해 \pdfminorversion=4로 낮추면 \pdfobjcompresslevel이 조용히 무효가 됩니다. 정확히는 조용하지 않아서 로그에 이렇게 남습니다. pdfTeX warning (Object streams): \pdfobjcompresslevel > 0 requires PDF-1.5 or greater. Object streams disabled now. 실측도 일치합니다. \pdfminorversion=4의 출력은 \pdfobjcompresslevel=0의 출력과 바이트 수까지 같습니다(둘 다 43,321바이트). “버전을 낮췄더니 파일이 조금 커졌다”면 원인은 대개 이것입니다. dvipdfmx에도 같은 관계가 있어서 -V 4를 붙이면 8,310바이트 PDF가 10,055바이트가 됩니다. 압축 강도는 -z 0부터 -z 9까지로 지정하는데, 여기서도 -z 6과 -z 9의 차이는 6바이트였습니다.
% pdfTeX defaults, as set by TeX Live in pdftexconfig.tex
\pdfminorversion = 5
\pdfcompresslevel = 9 % 0 = off; 1 already captures most of the gain
\pdfobjcompresslevel = 2 % object streams; needs PDF 1.5 or later
% LuaTeX spells the same knobs differently
\pdfvariable minorversion = 5
\pdfvariable compresslevel = 9
\pdfvariable objcompresslevel = 2생성된 PDF를 눈으로 직접 읽고 싶다면 압축을 전부 끄는 것이 가장 빠릅니다. 엔진마다 프리미티브를 달리 쓸 것 없이 한 줄이면 됩니다. \DocumentMetadata{uncompress} — 실측으로 52,622바이트 PDF가 91,347바이트짜리, 텍스트 편집기로 열리는 PDF가 됩니다. 이번 실행이 어떤 연산자를 냈는지 확인하거나, 어떤 패키지가 어떤 객체를 넣었는지 추적할 때 쓰세요.
[letterpaper]라고 썼는데 A4로 나온다: 용지 크기는 어디서 정해지나
클래스 옵션은 PDF의 용지 크기를 정하지 않습니다. TeX Live 2024에서 \documentclass[letterpaper]{article}을 pdflatex으로 돌리고 pdfinfo로 보면 Page size: 595.276 x 841.89 pts (A4)라고 나옵니다. 이유는 단순합니다. PDF의 MediaBox를 쓰는 것은 \pdfpagewidth와 \pdfpageheight이고, letterpaper는 그쪽을 건드리지 않은 채 판면(\textwidth 등)만 설정하기 때문입니다. 게다가 TeX Live의 pdftexconfig.tex는 시작 시점에 이미 \pdfpageheight = 297 true mm, \pdfpagewidth = 210 true mm를 설정해 둡니다. 결국 클래스는 A4 종이 위에 letter 크기의 판면을 앉힌 셈이 됩니다.
확실하게 통하는 방법은 셋입니다. geometry를 불러오거나(\usepackage[letterpaper]{geometry}는 \pdfpagewidth까지 챙깁니다), 프리미티브를 직접 쓰거나(\pdfpagewidth=8.5in — XeTeX도 받아들입니다), DVI 경로라면 변환할 때 지정합니다(dvipdfmx -p letter paper.dvi). 실측에서 셋 다 612 x 792 pts (letter)가 되었습니다. 그리고 네 번째, 가장 현대적인 답이 있습니다. \DocumentMetadata{}를 두면 MediaBox가 \pdfpagewidth가 아니라 \paperwidth・\paperheight 에서 쓰이게 됩니다. 즉 클래스 옵션이 드디어 이깁니다. 같은 소스가 이 한 줄의 유무만으로 A4와 letter로 갈립니다. 이행 과정에서 놓치면 쪽번호 위치가 어긋나는 정도로 끝나지 않습니다.
# TeX Live 2024, same source, four ways of asking for US letter
$ pdflatex letter.tex && pdfinfo letter.pdf | grep "Page size"
Page size: 595.276 x 841.89 pts (A4) # [letterpaper] alone: ignored
$ pdflatex geom.tex && pdfinfo geom.pdf | grep "Page size"
Page size: 612 x 792 pts (letter) # \usepackage[letterpaper]{geometry}
$ latex letter.tex && dvipdfmx -p letter letter.dvi
Page size: 612 x 792 pts (letter) # decided by the converter
$ pdflatex dm.tex && pdfinfo dm.pdf | grep "Page size"
Page size: 612 x 792 pts (letter) # \DocumentMetadata{} present글꼴은 임베드되었나: pdffonts 읽는 법
pdffonts paper.pdf의 emb 열이 모두 yes라면 글꼴은 임베드되어 있습니다. 이름 자체에도 단서가 있어, 임베드된 글꼴은 OREBYP+CMR10처럼 대문자 여섯 글자와 + 로 된 접두사가 붙습니다. 이는 서브셋 처리, 곧 문서에서 실제로 쓴 글자만 잘라 냈다는 표시이며, 반대로 접두사 없이 CMR10이라고만 적혀 있으면 그 글꼴은 임베드되지 않았을 가능성이 큽니다. 서두의 “Hello” PDF를 들여다보면 임베드된 글꼴 기술자에 /CharSet (/H/e/l/o/one)이라고 적혀 있습니다. H, e, l, o와 쪽번호의 1뿐입니다. 서브셋이란 이런 것입니다.
$ pdffonts paper.pdf
name type encoding emb sub uni object ID
----------------------------- ---------- --------- --- --- --- ---------
OREBYP+CMR10 Type 1 Builtin yes yes yes 4 0
PTKKKD+CMTI10 Type 1 Builtin yes yes yes 5 0
# the same source through latex + dvipdfmx: Type 1C instead of Type 1
$ pdffonts paper-dvipdfmx.pdf
FUYUFD+CMR10 Type 1C Builtin yes yes yes 4 0
# a font that was NOT embedded: bare name, emb = no
Helvetica Type 1 Standard no no no 7 0type 열에도 정보가 있습니다. 같은 “Hello” 한 쪽을 비교하면 pdflatex 출력은 Type 1, dvipdfmx 출력은 Type 1C입니다. 둘 다 완전히 같은 Computer Modern의, 완전히 같은 다섯 글자 서브셋인데 임베드된 글꼴 프로그램의 크기는 9,002바이트 대 720바이트로 열두 배가 넘게 벌어집니다. dvipdfmx가 Type 1을 CFF(Compact Font Format, PDF에서는 Type 1C)로 재부호화한 뒤 넣기 때문이며, 서두에서 본 총 크기 차이(DVI 경유 2,145바이트 대 pdflatex 11,287바이트)도 거의 이 한 가지로 설명됩니다. LuaTeX과 XeTeX은 기본적으로 Latin Modern의 OpenType 판을 쓰므로 CID Type 0C로 들어갑니다. 모두 제대로 된 임베드이며 인쇄에도 투고에도 문제가 없습니다.
인쇄소·투고처에 보내기 전에 확인할 것
두 명령이면 끝납니다. pdffonts로 모든 글꼴이 임베드되었는지 확인하고, pdfinfo로 페이지 치수와 PDF 버전을 봅니다. 점검은 그게 전부입니다. 사고의 유형도 정해져 있습니다. emb 열에 no가 섞여 있는 경우(외부에서 만든 PDF 도판을 넣을 때 가장 쉽게 딸려 옵니다), A4인 줄 알았는데 letter인 경우, 투고 규정보다 새로운 PDF 버전인 경우입니다. pdfinfo의 Page size 줄은 모든 쪽을 훑지 않으므로 범위를 줄 수도 있습니다. pdfinfo -f 1 -l 99 paper.pdf로 중간에 치수가 바뀌지 않았는지 확인하세요.
pdffonts paper.pdf—emb열이 모두yes인가, 이름마다ABCDEF+접두사가 붙어 있는가.pdfinfo paper.pdf—Page size가 의도대로인가,PDF version이 투고 규정 범위 안인가.- 영문 중심이고 빠르게 → 직접 경로(
pdflatex). 기본값 그대로 두어도 됩니다. - 시스템 글꼴을 쓰거나 Unicode 중심 →
lualatex또는xelatex(직접 경로). (u)platex로 일본어 → DVI 경로(dvipdfmx). 용지는-p, PDF 버전은-V로 지정합니다.- EPS 자산이 많다 → DVI 경로는 중간 파일 없이 읽어 들입니다. 직접 경로도 자동 변환이 되지만
*-eps-converted-to.pdf가 흩어집니다.