일본어 행에는 단어 사이 공백이 없습니다. 그런데도 LaTeX으로 조판한 일본어 문단은 양끝이 가지런히 맞습니다. 비밀은 \kanjiskip이라는 접착제로, 자연 너비는 0pt이지만 늘어납니다. jsarticle 10pt에서 실측하면 0.0pt plus 0.92473pt minus 0.0924pt입니다. 평소에는 아예 없다가, 행을 죄거나 늘려야 하는 순간 모든 글자 사이에서 조금씩 솟아납니다. 서양 조판이 단어 사이 공백에 맡기는 일을, 일본어는 행 안의 모든 글자 이음매에 얇게 나누어 놓는 셈입니다. 이 페이지에서는 이 일본어 글루와 행두・행말의 금칙 처리, 세로쓰기, 루비——TeX이 일본어를 위해 증설했고 서양어에는 대응물이 없는 장치——를 실측값과 함께 살펴봅니다.
\kanjiskip과 \xkanjiskip — 치지 않은 공백이 들어가는 구조
일본어 조판에서 자동으로 들어가는 글루는 두 가지입니다. \kanjiskip은 일본어 문자끼리의 사이, \xkanjiskip은 일본어와 서양어의 경계입니다. 결정적으로 다른 것은 자연 너비로, jsarticle 10pt에서 \kanjiskip은 0.0pt plus 0.92473pt minus 0.0924pt, \xkanjiskip은 2.5pt plus 1.49994pt minus 0.59998pt입니다. 앞의 것은 평소 너비가 0이고 필요할 때만 늘어납니다. 그래서 글자 격자는 흐트러지지 않는데 행은 가지런해집니다. 뒤의 것은 늘 2.5pt가 있습니다. 그래서 日本語 and abc라고 쓰면 공백을 치지 않았는데도 영어 단어 앞뒤가 벌어집니다. 이 둘을 구분해 두면 일본어 자간을 둘러싼 논의의 대부분은 "지금 어느 글루 이야기인가"로 정리됩니다.
% measure it yourself: put this in the body and read the .log
\typeout{kanjiskip = \the\kanjiskip}
\typeout{xkanjiskip = \the\xkanjiskip}
\sbox0{日本語テキスト}\typeout{plain = \the\wd0}
\sbox0{日本語ABCテキスト}\typeout{mixed = \the\wd0}
\sbox0{ABC}\typeout{latin = \the\wd0}실제로 재 보면 이렇게 나옵니다. jsarticle 10pt에서 日本語テキスト는 64.72778pt, ABC는 21.8056pt입니다. 단순히 더하면 86.53338pt여야 하는데, 日本語ABCテキスト의 실측은 91.53339pt입니다. 차이 5.00001pt는 \xkanjiskip의 2.5pt가 語와 A 사이, C와 テ 사이 두 곳에 들어간 몫입니다. 증거로 \noautoxspacing을 선언하고 같은 상자를 재면 86.53339pt로 돌아가 덧셈 그대로가 됩니다. 하나 더, 日本語의 실측은 27.74048pt로 한자 한 글자 9.24683pt의 정확히 세 배입니다. 글자 사이에 \kanjiskip이 들어 있는데도 너비가 늘지 않은 것은 자연 너비가 0이기 때문입니다.
| 파라미터 | pTeX 계열에서의 표기 | LuaTeX-ja에서의 표기 | jsarticle 10pt의 기본값 |
|---|---|---|---|
kanjiskip | \kanjiskip=... | \ltjsetparameter{kanjiskip=...} | 0.0pt plus 0.92473pt minus 0.0924pt |
xkanjiskip | \xkanjiskip=... | \ltjsetparameter{xkanjiskip=...} | 2.5pt plus 1.49994pt minus 0.59998pt |
prebreakpenalty | \prebreakpenalty 뒤에 문자 | \ltjsetparameter{prebreakpenalty={...}} | 구두점・닫는 괄호에서 10000 |
postbreakpenalty | \postbreakpenalty 뒤에 문자 | \ltjsetparameter{postbreakpenalty={...}} | 여는 괄호에서 10000 |
jcharwidowpenalty | \jcharwidowpenalty=... | \ltjsetparameter{jcharwidowpenalty=...} | 500 |
inhibitxspcode | \inhibitxspcode 뒤에 일본어 문자 | \ltjsetparameter{jaxspmode={...}} | 전각 여는 괄호 2, 닫는 괄호와 。 1, 한자 3 |
xspcode | \xspcode 뒤에 서양 문자 | \ltjsetparameter{alxspmode={...}} | A는 3, .은 2, (는 1 |
이 기본값은 클래스에 따라 달라지고, 그것이 판면의 성격을 정합니다. jlreq에서 같은 측정을 하면 \kanjiskip은 0.0pt plus 2.5pt, \xkanjiskip은 2.5pt plus 2.5pt minus 1.25pt입니다. jlreq의 10pt에서는 한자가 정확히 10pt이므로 늘어나는 폭은 전각의 0.25배가 됩니다. jsclasses의 0.1배에 비하면 2.5배입니다. 즉 jlreq는 자간을 더 크게 벌려 행을 맞추는 설계이고, 행 끝에서 밀어 넣고 밀어내는 거동이 눈에 띄게 달라집니다. 클래스를 바꾼 뒤 "조판 인상이 달라졌다"고 느껴지면 먼저 이 두 값을 \typeout으로 찍어 비교해 보세요.
전각 괄호 주위에 공백이 들어가지 않는 이유 — 전각・반각과 \inhibitxspcode
괄호 자신이 "이쪽에는 공백을 넣지 말라"고 선언하고 있기 때문입니다. \inhibitxspcode는 일본어 문자마다 \xkanjiskip이 붙는 방식을 0~3으로 지정하는 것으로, jsarticle의 기본값에서는 전각 여는 괄호 (가 2, 닫는 괄호 )와 마침표 。가 1, 보통 한자가 3입니다. 재 보면 확실합니다. 日本語(ABC)テキスト의 실측은 105.02704pt로, 전각 아홉 글자(9 × 9.24683pt)와 ABC의 21.8056pt를 그대로 더한 값이며 \xkanjiskip은 한 군데도 들어가지 않았습니다. 전각 괄호는 애초에 좌우 여백을 품은 자형이라 거기에 2.5pt를 더하면 눈에 띄게 넓어집니다. 그래서 괄호 쪽에서 금지하는 것입니다. 영문자와 숫자는 반각, 일본어 구두점과 괄호는 전각이라는 원고 관례를 지키는 데에는 겉모습만이 아니라 이 간격 조정이 제대로 듣는다는 실익이 있습니다. 반각 (를 일본어에 섞으면 \xspcode 쪽 규칙((는 1)이 작동해 전혀 다른 간격이 됩니다.
% (u)pLaTeX: inspect and change how xkanjiskip attaches to one character
\typeout{open paren = \the\inhibitxspcode`(} % 2 in jsarticle
\typeout{close paren = \the\inhibitxspcode`)} % 1 in jsarticle
\inhibitxspcode`(=3 % allow the gap on both sides after all
% LuaLaTeX: same idea, other name -- and NO space after the comma
\ltjsetparameter{jaxspmode={`(,preonly}}여기에 pLaTeX과 LuaLaTeX 사이에서 설정을 옮길 때의 함정이 있습니다. \inhibitxspcode의 1과 2는 LuaTeX-ja의 jaxspmode에서 번호가 반대입니다. 실측으로 확인됩니다. pTeX 쪽에서 한자 語의 \inhibitxspcode를 1로 하면 A語에서 공백이 사라지고(19.24684pt → 16.74684pt) 語A에는 남습니다. 그런데 LuaTeX-ja에서 같은 글자의 jaxspmode를 1로 하면 반대로 語A에서 사라지고(19.24713pt → 16.74713pt) A語에 남습니다. LuaTeX-ja의 소스를 보면 jaxspmode의 1은 preonly, 2는 postonly라는 이름이 붙어 있고 뜻은 "그 일본어 문자의 앞에만 허용/뒤에만 허용"입니다. pTeX의 1은 "뒤에만 허용"입니다. 말로는 같은 체계인데 숫자만 뒤바뀌어 있는 셈입니다. 설정을 기계적으로 복사하면 공백이 들어가는 쪽이 반대가 됩니다. 옮길 때는 숫자가 아니라 preonly / postonly라는 이름으로 적어 두는 편이 안전합니다. 다만 LuaTeX-ja 쪽에서는 쉼표 바로 뒤에 공백을 넣으면 안 됩니다. 공백이 있으면 TeX Live 2024는 ! Missing number, treated as zero.를 내고 그대로 0을 적용해, 한쪽이 아니라 양쪽 공백이 모두 사라집니다.
금칙 처리 — \prebreakpenalty와 \postbreakpenalty
금칙이란 특정 문자를 행 머리나 행 끝에 오지 못하게 하는 규칙입니다. 구두점 。、, 닫는 괄호 )」』, 작은 가나 ゃ ぁ는 행 머리에 올 수 없고, 여는 괄호 (「『는 행 끝에 올 수 없습니다. 어떤 문자가 어느 쪽을 꺼리는지는 일본어 폰트 메트릭(JFM)과 클래스가 정해 주며, TeX은 이를 금지가 아니라 페널티로 구현했습니다. 마침표 。나 닫는 괄호 ) 바로 앞에서 줄을 바꾸면 그 문자가 다음 행 머리에 떨어집니다. 그래서 jsarticle에서는 \prebreakpenalty。도 \prebreakpenalty)도 10000으로 설정되어 있습니다. 반대로 여는 괄호 ( 바로 뒤에서 끊으면 괄호가 행 끝에 남겨지므로 \postbreakpenalty(도 10000입니다. TeX의 관례에서 10000은 무한대 페널티, 곧 사실상의 금지입니다. 이렇게 만든 이점은 금칙을 단계적으로 다룰 수 있다는 데 있습니다. "절대 피하고 싶다"면 10000, "되도록 피하고 싶다"면 200처럼 쓸 수 있고, 줄바꿈 알고리즘이 다른 요소와 저울질해 줍니다. 서양 조판에는 금칙이라는 개념 자체가 없으므로, 이것은 pTeX이 TeX에 증설한 기구입니다.
% read the shipped values, then relax one of them
\typeout{full stop = \the\prebreakpenalty`。} % 10000
\typeout{open paren = \the\postbreakpenalty`(} % 10000
\prebreakpenalty`〜=200 % discourage, do not forbid
\jcharwidowpenalty=1000 % dislike a lone character on the last line
% the LuaTeX-ja spelling of the same three settings
\ltjsetparameter{prebreakpenalty={`〜, 200}}
\ltjsetparameter{jcharwidowpenalty=1000}페널티로 쓰인 덕분에 일본어 조판에는 서양어에 대응물이 없는 전용 항목까지 마련될 수 있었습니다. \jcharwidowpenalty가 그것으로, jsarticle에서도 ltjsarticle에서도 기본값은 500입니다. 이는 "문단의 마지막 행에 일본어 문자가 하나만 남는" 상황——서양 조판의 위도우에 해당합니다——을 꺼리는 가중치입니다. 500이라는 중간값인 것이 요점으로, 절대 금지가 아니라 "다른 행을 조금 죄어서라도 피하고 싶지만 판면을 망칠 정도는 아니다"라는 세기에 놓여 있습니다. 실무에서는 한 글자만 남는 문단을 만났을 때 원고를 고치기 전에 \jcharwidowpenalty=1000을 시도하는 것이 안전한 처치입니다. 그래도 해결되지 않으면 문장 자체를 한 글자 늘리거나 줄이는 편이 빠를 때도 있습니다.
세로쓰기를 어떻게 지정하는가 — tate 옵션과 plext
지금은 클래스 옵션으로 전환하는 것이 정석입니다. \documentclass[tate]{jlreq}는 uplatex에서도 lualatex에서도 통하고, \documentclass[tate]{ltjsarticle}는 LuaLaTeX에서 통합니다. pLaTeX 계열에는 세로쓰기 전용 클래스인 utarticle/utbook도 있고, 이것도 uplatex으로 그대로 컴파일됩니다. 세로쓰기가 TeX에 특별한 까닭은 행이 나아가는 방향과 글자가 쌓이는 방향이 뒤바뀌기 때문만이 아니라, 같은 문자가 다른 자형을 요구하기 때문입니다. 구두점도 괄호도 세로쓰기에서는 90도 돌린 위치와 방향으로 다시 놓아야 합니다. 그래서 pTeX에는 가로쓰기용(JY)과 세로쓰기용(JT)이라는 두 계통의 폰트 인코딩이 처음부터 마련되어 있고, \tate를 선언한 상자 안에서는 자동으로 JT 쪽이 쓰입니다. 서양 조판에는 대응하는 개념이 전혀 없는 대목입니다.
% vertical writing, three ways that all compile on TeX Live 2024
\documentclass[tate,uplatex]{jlreq} % uplatex; also works under lualatex
\documentclass[tate]{ltjsarticle} % lualatex
\documentclass[uplatex]{utarticle} % uplatex, dedicated vertical class
% tate-chu-yoko: a short horizontal run inside vertical text
平成\tatechuyoko{31}年 % jlreq only -- under uplatex or lualatex
平成\rensuji{31}年 % plext (pLaTeX) and lltjext (LuaTeX-ja)
% zw is the width of one full-width character, zh its height;
% \begin{minipage}<t>{10zw} sets a vertical box ten characters wide세로쓰기 안에서 두 자리 숫자나 단위를 바로 세워 가로로 배열하는 조판을 다테추요코(tate-chu-yoko) 라고 합니다. 표기는 경로마다 다릅니다. \tatechuyoko{31}을 정의하는 것은 jlreq뿐이며, jlreq를 쓰고 있다면 uplatex에서도 lualatex에서도 동작합니다. ltjsarticle에는 없으므로 거기서 \tatechuyoko라고 쓰면 ! Undefined control sequence.가 납니다. LuaTeX-ja 쪽 표기는 lltjext(또는 세로쓰기 전용 ltjtarticle・ltjtbook)가 주는 \rensuji{31}이고, pLaTeX 계열은 plext 패키지의 \rensuji{31}입니다. plext는 본래 세로쓰기를 위해 ASCII가 마련한 확장으로, \rensuji 외에 표・상자・조판 방향의 지정을 맡습니다. \begin{minipage}<t>{10zw}처럼 홑화살괄호로 방향을 명시할 수 있는 것이 plext의 얼굴이며, 세로쓰기 문서에 가로쓰기 도표를 끼워 넣을 때 이것이 필요합니다. LuaTeX-ja 쪽 대응물이 lltjext로, 이름도 인수 관례도 거의 옮겨 적은 수준입니다. 세로쓰기에서 막히면 먼저 "그 클래스에 tate 옵션이 있는가", 다음으로 "plext/lltjext를 불렀는가"를 확인하는 것이 빠릅니다.
루비(후리가나)와 권점 — 어느 패키지를 고를까
지금의 정석은 (u)pLaTeX이면 pxrubrica, LuaLaTeX이면 luatexja-ruby입니다. \ruby{...}{...}라는 표기는 공통이지만, pxrubrica는 JIS X 4051/JLReq가 정의하는 루비의 종류를 구별하고 둘 중 하나를 고르게 합니다. 세로 막대는 읽기를 한 글자씩 나누는 표시로, \ruby{漢字}{かん|じ}는 모노 루비(한자 한 글자마다 루비를 대응)입니다. 숙어 전체에 한 줄기 루비를 얹는 그룹 루비는 \ruby[g]{漢字}{かんじ}처럼 명시적으로 요청해야 하며, 막대도 옵션도 없이 두 글자 숙어에 루비를 달면 ! Package pxrubrica Error: Group count mismatch between the ruby and the body (2 <> 1).에서 멈춥니다. 권점(방점)도 같은 패키지의 \kenten{重要}로 붙일 수 있습니다. 아주 가볍게 끝내고 싶다면 jsclasses에 딸린 okumacro의 \ruby로도 충분합니다. 이쪽은 언제나 그룹 루비라 \ruby{漢字}{かんじ}가 그대로 통합니다. 주의할 점은 CTAN에 ruby라는 이름의 패키지도 따로 존재한다는 것으로, 이것은 Werner Lemberg의 CJK 패키지 쪽 ruby(TeX Live 2024 동봉판은 4.8.5)——출신이 다른 별개 계통입니다.
| 패키지 | 경로 | 무엇을 할 수 있는가 |
|---|---|---|
pxrubrica | pLaTeX / upLaTeX / LuaLaTeX / XeLaTeX | 모노 루비와 그룹 루비의 구별, 루비가 걸치는 방식 제어, \kenten에 의한 권점 |
luatexja-ruby | LuaLaTeX 전용 | LuaTeX-ja에 통합된 루비. 줄바꿈과 덜 간섭합니다 |
okumacro | pLaTeX / upLaTeX | jsclasses에 딸림. \ruby와 \kenten을 손쉽게. 분할 표기는 없습니다 |
ruby | CJK 패키지 계열(pdfLaTeX 등) | Werner Lemberg의 별개 계통. 일본어 TeX용이 아니므로 섞지 마세요 |
\usepackage{pxrubrica}
...
\ruby{漢字}{かん|じ} % mono ruby: the bar splits the reading per character
\ruby[g]{漢字}{かんじ} % group ruby: one run over the word -- [g] is required
\kenten{重要} % emphasis dots beside each character여기서 오류 없이 망가지는 사고를 하나 소개합니다. pxrubrica와 okumacro는 둘 다 \ruby를 정의하는데, 둘을 함께 불러도 경고조차 나오지 않습니다. 나중에 부른 쪽이 조용히 덮어쓸 뿐입니다. 실제로 pxrubrica 뒤에 okumacro를 부르고 \ruby{漢字}{かん|じ}를 조판하면, かん|じ의 세로 막대가 모노 루비의 구분자로 해석되지 않고 전각 세로선으로 그대로 인쇄됩니다(pxrubrica 단독이면 かん과 じ로 나뉘어 얹힙니다). 로그에는 아무것도 남지 않으므로 PDF를 눈으로 볼 때까지 알아챌 수 없습니다. \ruby를 정의하는 패키지는 하나만 부르세요. jsclasses를 쓰면서 okumacro를 부르고 있다면 pxrubrica로 바꿀 때 그 줄을 지우십시오.
JLReq란 무엇인가 — W3C 「일본어 조판 처리 요건」과 jlreq 클래스
JLReq는 W3C가 공개한 「일본어 조판 처리 요건(Requirements for Japanese Text Layout)」이라는 문서로, 일본어의 판면 설계・행 조판・금칙・루비・약물 처리를 구현자가 읽을 수 있는 형태로 체계적으로 적어 놓은 것입니다. 그전까지 일본어 조판 지식은 인쇄소의 경험지로 흩어져 있었으므로 이것이 공개된 의미는 컸고, 웹 브라우저부터 전자책 리더까지 같은 문서를 참조하게 되었습니다. jlreq 클래스는 이 문서를 LaTeX 위에서 구현한 것이며 작성자는 Noriyuki Abe입니다. TeX Live 2024 동봉판 클래스 파일 첫머리에는 참조한 판이 (based on JLReq 20200811)이라고 명시되어 있습니다. 즉 jlreq를 쓴다는 선택은 단지 "새 클래스를 고르는" 일이 아니라 판면의 근거를 공개 문서에 두는 일입니다.
실무상의 귀결은 이렇습니다. 세세한 조정을 직접 쓰지 않아도 됩니다. 세로쓰기는 tate 옵션 하나, \jlreqsetup으로 행 조판 파라미터를 이름으로 지정할 수 있고, 금칙과 루비의 기본값도 JLReq를 따른 값으로 들어 있습니다. 반대로 기존 jsarticle 문서를 그대로 jlreq로 바꾸면 \kanjiskip의 늘어나는 폭이 넓어져 행이 죄이는 방식이 달라지고 쪽수까지 움직일 수 있습니다. 새로 시작하는 문서라면 jlreq, 기존 원고의 겉모습을 지키고 싶다면 jsclasses 그대로가 온당한 선 긋기입니다. 어느 쪽을 고르든 이 페이지에서 본 \kanjiskip・\xkanjiskip・\prebreakpenalty・\jcharwidowpenalty는 같은 이름으로 존재하므로, 마지막 미세 조정은 언제나 직접 할 수 있습니다.