LaTeX로 그린 그림은 내보내기 전에 이미 벡터 이미지입니다. 축은 곡선으로, 라벨은 폰트의 글리프로 존재합니다. 그런데 이미지로 내보내는 단계에 이르면 그 정보가 허무하리만치 쉽게 버려집니다. 게다가 버릴지 말지를 결정하는 지점은 많은 사람이 생각하는 것보다 훨씬 앞——DVI를 거치느냐 PDF를 거치느냐 하는 한 가지입니다. dvisvgm의 매뉴얼은 이렇게 적고 있습니다. PDF와 PostScript 파일에 든 문자는 항상 경로 요소로 변환된다. 즉 PDF를 거치는 순간, 그림 속의 글자는 글자이기를 그만둡니다. 이 페이지는 standalone, pdfcrop, dvisvgm, 그리고 래스터화까지를 바로 그 축을 따라 늘어놓습니다.
그림만 한 쪽에 담기: standalone 클래스
내보내기의 첫걸음은 종이를 그림 크기에 맞춰 버리는 것입니다. Martin Scharrer의 standalone 클래스(TeX Live 2024 수록판은 v1.3b)는 내용의 치수를 그대로 지면의 치수로 삼습니다. 여기서 \documentclass[border=2pt,preview]{standalone}에 $\displaystyle \int_{-\infty}^{\infty} e^{-x^2}\,dx = \sqrt{\pi}$만 넣고 pdflatex로 돌렸더니, 만들어진 PDF의 쪽 크기는 85.779 × 27.993 pt——수식의 외곽 그 자체였습니다. border를 적지 않았을 때의 기본값은 사방 0pt입니다. 클래스 자체는 0.50001bp라는 어중간한 값(반올림 때문에 글리프의 끝이 잘리는 것을 막는 값)을 박아 두었지만, 함께 배포되는 standalone.cfg가 옵션 처리 직전에 \standaloneconfig{border=0pt}로 덮어씁니다. TikZ 그림이라면 \documentclass[tikz,border=2pt]{standalone}을 쓰고 tikzpicture를 \begin{document} 바로 아래에 두면 됩니다. 줄바꿈이 필요한 본문 조각을 잘라 내고 싶다면 varwidth=10cm처럼 폭을 주면 됩니다. standalone이 안쪽에서 쓰는 preview 패키지는 본래 Emacs의 AUCTeX에 딸린 preview-latex을 위해 만들어진 것으로, “문서의 일부만 잘라 조판한다”는 발상이 거기서 왔습니다.
% fig.tex — a figure that is its own page
\documentclass[tikz,border=2pt]{standalone}
\begin{document}
\begin{tikzpicture}
\draw[thick,->] (0,0) -- (3,0) node[right] {$x$};
\draw[thick,->] (0,0) -- (0,2) node[above] {$y$};
\draw[blue,thick] (0,0) .. controls (1,2) .. (3,1);
\end{tikzpicture}
\end{document}이미 있는 PDF의 여백 자르기: pdfcrop
그림이 보통 문서 안에 있어 다시 만들 수 없을 때는 pdfcrop을 쓰면 됩니다. Heiko Oberdiek의 Perl 스크립트(TeX Live 2024 수록판은 v1.42)로, 쪽 내용의 외접 사각형을 구해 트림 박스를 거기까지 줄입니다. 실측해 보니 레터 크기 612 × 792 pt짜리 한 쪽 문서가 pdfcrop doc.pdf doc-crop.pdf를 거쳐 345 × 539 pt가 되었습니다. 여백을 0으로 만들고 싶지 않다면 --margins 10처럼 지정할 수 있고, 그러면 사방에 10 pt씩 돌아와 365 × 559 pt가 됩니다. 주의할 점이 하나 있습니다. pdfcrop은 Ghostscript를 부릅니다. 스크립트 첫머리에도 “Requirements: Perl5, Ghostscript”라고 적혀 있습니다. gs가 없는 환경에서는 돌아가지 않습니다. 덧붙여, 내보낼 그림 자체를 standalone으로 만들 수 있다면 pdfcrop은 필요 없습니다. 잘라 내기보다 처음부터 여백을 만들지 않는 편이 빠르고 정확합니다.
pdfcrop doc.pdf doc-crop.pdf # 612x792 pt becomes 345x539 pt
pdfcrop --margins 10 doc.pdf doc-crop.pdf # keep 10 pt on every side
pdfcrop --hires doc.pdf doc-crop.pdf # use the high-resolution bounding boxSVG로 만들기: dvisvgm, 그리고 DVI 경로가 유리한 이유
웹에 올릴 그림이라면 SVG가 첫 후보입니다. Martin Gieseking의 dvisvgm(TeX Live 2024 수록판은 3.2.2)은 DVI, EPS, PDF를 SVG로 바꾸는데, 어느 입구로 들어가느냐에 따라 나오는 것이 달라집니다. \def\pgfsysdriver{pgfsys-dvisvgm.def}를 \documentclass 앞에 두고 latex로 DVI를 만든 뒤 dvisvgm fig.dvi를 돌렸더니, 나온 SVG에는 축과 곡선이 <path>로, $x$, $y$, $y=f(x)$ 라벨이 <text> 요소로 들어 있었습니다. 글자는 글자 그대로였고, <defs>에 박힌 벡터 폰트를 참조했습니다. 그런데 PDF로 들어가면 이렇게 되지 않습니다. dvisvgm 매뉴얼은 PDF와 PostScript 파일에 든 문자는 항상 경로 요소로 변환된다고 못 박고 있습니다. 실제로 poppler의 pdftocairo -svg로 같은 그림의 PDF를 SVG로 바꾸자 <text> 요소는 하나도 없었습니다.
% figdvi.tex — the DVI route, which keeps the labels as real text
\def\pgfsysdriver{pgfsys-dvisvgm.def}
\documentclass[tikz,border=2pt]{standalone}
\begin{document}
\begin{tikzpicture}
\draw[thick,->] (0,0) -- (3,0) node[right] {$x$};
\draw[blue,thick] (0,0) .. controls (1,2) .. (3,1);
\end{tikzpicture}
\end{document}latex figdvi.tex # produce a DVI, not a PDF
dvisvgm figdvi.dvi # SVG with <text> elements and vector paths
# the PDF route needs a helper and outlines all text:
dvisvgm --pdf fig.pdf # requires mutool with Ghostscript 10.01 or newerPDF에서 SVG를 만들고자 할 때는 현실적인 장애가 하나 더 있습니다. dvisvgm --pdf는 예전에 PDF를 Ghostscript에 읽혔지만, 매뉴얼에 따르면 Ghostscript 10.01.0에서 PDF 인터프리터가 새로 바뀌면서 이 방법을 쓸 수 없게 되었고, 대신 MuPDF의 mutool을 찾게 되었습니다. 이 글을 쓴 환경(Ghostscript 10.03.0, mutool 없음)에서 dvisvgm --pdf fig.pdf를 실행하면 ERROR: can't retrieve number of pages from file fig.pdf로 멈춥니다. 즉 PDF 경로는 의존을 하나 더 요구합니다. 게다가 Ghostscript가 파일을 처리하는 경우에는 매뉴얼의 규칙대로 문자가 어차피 외곽선이 되므로, DVI 경로를 고를 이유가 두 겹인 셈입니다. 하나 더 기억해 두면 좋은 것은 -e(--exact-bbox)입니다. 기본값은 TFM 치수로 외접 사각형을 정하므로, 글자 모양이 실제로 그 밖으로 나가 있으면 끝이 잘릴 수 있습니다. 그림 가장자리에서 글자가 깎였다면 먼저 이것을 붙여 보십시오.
SVG의 폰트를 어떻게 할까: --font-format과 --no-fonts
<text>를 남길 수 있다는 것은 좋은 일이지만, 결정이 하나 더 필요합니다. dvisvgm의 기본값은 SVG 폰트(<font>와 <glyph>를 <defs>에 박는 형식)인데, 명세에는 있지만 지원하지 않는 환경이 많은 것이 현실입니다. dvisvgm 자신의 매뉴얼도 --no-fonts 설명에서 경로 출력이 “SVG 폰트를 지원하지 않는 대부분의 응용 프로그램과 더 잘 호환된다”고 적고 있습니다. 같은 TikZ 그림으로 세 가지를 비교하면, 기본 SVG 폰트가 5,790바이트, --font-format=woff2가 4,815바이트(@font-face의 src에 데이터 URI로 WOFF2를 박는 형식), --no-fonts가 8,290바이트로 <text> 요소는 0개, 글리프는 모두 <path>가 되었습니다. 실무의 판단은 단순합니다. 웹에 올릴 것이면 --font-format=woff2——글자를 선택할 수 있고 브라우저가 확실히 그립니다. Illustrator, Inkscape, PowerPoint 등 다른 소프트웨어에 건넬 것이면 --no-fonts——용량은 늘지만 잘못 그려질 여지가 없습니다.
| 옵션 | SVG 안의 문자 | 같은 그림에서의 크기 | 알맞은 용도 |
|---|---|---|---|
--font-format=svg | <text> + SVG 폰트(기본값) | 5,790바이트 | 지원하는 소프트웨어가 적어 실무에서는 권하지 않음 |
--font-format=woff2 | <text> + @font-face에 WOFF2 삽입 | 4,815바이트 | 웹 공개. 글자를 선택할 수 있고 가장 작음 |
--no-fonts | <text> 없음. 모든 글리프가 <path> | 8,290바이트 | 다른 그래픽 소프트웨어에 건넬 때. 표시가 깨지지 않음 |
PNG로 만들기: 슬라이드와 옛 환경을 위해
SVG를 받지 않는 상대가 아직 많고, 그럴 때는 PNG로 내립니다. 해상도는 -r로 지정하며 300 dpi를 하한, 인쇄나 고밀도 디스플레이용이라면 600 dpi를 기준으로 삼으면 됩니다. 위에서 만든 85.779 × 27.993 pt 수식 PDF를 600 dpi로 래스터화하자, Ghostscript(gs -sDEVICE=pngalpha -r600)로도 poppler의 pdftocairo -png -r 600 -transp로도 약 715 × 233 픽셀의 투명 PNG가 나왔습니다. 72 pt가 1인치이므로 85.779 ÷ 72 × 600 ≈ 715입니다. 이 계산이 맞아떨어지는지가 해상도 지정이 실제로 먹혔는지 확인하는 방법입니다. 투명이 필요 없다면 -sDEVICE=png16m(pdftocairo라면 -transp를 빼면 됩니다)으로 흰 배경이 됩니다. 많은 해설이 ImageMagick의 magick -density 300 in.pdf out.png를 권하지만, ImageMagick은 PDF 렌더링을 Ghostscript에 넘깁니다. TeX Live에도 들어 있지 않고 이 글을 쓴 환경에도 없어서 동작을 확인하지 않았습니다. 실제로 그리는 것이 Ghostscript라면 직접 부르는 편이 의존이 한 단계 적습니다.
# Ghostscript, transparent background, 600 dpi
gs -q -dNOPAUSE -dBATCH -sDEVICE=pngalpha -r600 \
-dTextAlphaBits=4 -dGraphicsAlphaBits=4 \
-sOutputFile=eq.png eq.pdf
# poppler, same result, shorter to type
pdftocairo -png -r 600 -transp -singlefile eq.pdf eq명령 하나로 PNG까지: standalone의 convert=와 --shell-escape
매번 명령을 세 번 치는 것이 번거롭다면 standalone의 클래스 옵션 convert=에 맡길 수 있습니다. 조판이 끝나자마자 클래스 자신이 변환 명령을 셸에 던집니다. 백엔드는 imagemagick(기본), ghostscript, pdf2svg 중에서 고를 수 있고, 해상도 기본값은 density=300입니다. ImageMagick이 없는 환경에서도 ghostscript를 지정하면 돌아갑니다. 실제로 \documentclass[border=2pt,convert={ghostscript,density=600,outext=.png}]{standalone}로 두고 pdflatex --shell-escape eqc.tex를 실행하자 로그에 gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=png16m -r600 -sOutputFile=eqc.png eqc.pdf가 찍히고 715 × 233 PNG가 명령 하나로 나왔습니다. 다만 외부 명령을 띄우므로 제한된 셸 이스케이프로는 부족하고 --shell-escape가 필요합니다. 그림이 많은 프로젝트라면 변환을 빌드 스크립트 쪽에 두는 편이 --shell-escape를 상시로 쓰지 않아도 되어 안전합니다.
% eqc.tex — build the PNG in the same run
\documentclass[border=2pt,convert={ghostscript,density=600,outext=.png}]{standalone}
\usepackage{amsmath}
\begin{document}
$\displaystyle \int_{-\infty}^{\infty} e^{-x^2}\,dx = \sqrt{\pi}$
\end{document}
% then: pdflatex --shell-escape eqc.tex여기까지의 흐름——조각만 조판하고, 잘라 내고, 벡터나 래스터로 낮추는 것——을 GUI로 묶은 것이 TeX2img입니다. 수식을 붙여 넣고 형식과 해상도만 고르면 투명도와 안티앨리어싱까지 알아서 처리해 줍니다. 한 번뿐인 내보내기에는 압도적으로 빠르지만, 그림이 수십 장이고 판을 거듭하는 문서라면 앞 절들의 명령을 빌드에 넣는 편이 결국 편합니다. 덧붙여 TeX2img는 독립된 응용 프로그램이라 TeX Live에 들어 있지 않고 이 글을 쓴 환경에도 없어서 동작을 확인하지 않았습니다.
어떤 형식으로 내보낼까
- 웹에 올릴 그림 → SVG.
latex로 DVI를 만들고dvisvgm --font-format=woff2를 거칩니다. 글자가<text>로 남고 확대해도 열화되지 않습니다. - 다시 LaTeX 문서에 넣을 그림 → PDF 그대로 둡니다.
standalone으로 만들면 여백이 없어\includegraphics에 그대로 넘길 수 있습니다. - 슬라이드, Word, 소셜 미디어 → PNG.
gs -sDEVICE=pngalpha -r600또는pdftocairo -png -r 600 -transp를 씁니다. 흰 배경이 필요할 때만 투명을 뺍니다. - Illustrator나 Inkscape에서 손볼 그림 →
--no-fonts로 만든 SVG. 폰트가 없는 환경에서도 모양이 변하지 않습니다. - 사진이 들어간 그림 → 사진 부분만 JPEG, 선화와 라벨은 SVG나 PNG. 전체를 JPEG로 만들면 글자의 가장자리가 지저분해집니다.
dvisvgm --pdf가 동작하지 않을 때 → 먼저 DVI 경로로 바꿉니다. 그래도 PDF에서 만들어야 한다면 MuPDF의mutool을 설치합니다.