アレの話なんだが。
"--"が hyperref で正しく扱われず,しおりが変になる
以下のように
\texorpdfstring{texstring}{pdfstring}
を使って pdfstring の方を8進数表記で書くことで回避可能です.\section{\texorpdfstring{a--b}{a\055\055b}}
\documentclass{jsarticle} % pLaTeX を使う場合 %\documentclass[uplatex]{jsarticle} % upLaTeX を使う場合 \usepackage[dvipdfmx,bookmarks=true,bookmarksnumbered=true]{hyperref} \begin{document} \section{enダッシュ--?\textendash!} \end{document}
すると、DVI ファイルに記された節のしおりの文字列(これは最終的に PDF の文字列リテラルとして解釈される)は次のようになる。((「1
」は節番号である。))
1 enダッシュ\205?\205!
この中の和文文字は「内部漢字コード((エンジンの内部で和文文字を表すのに用いられる漢字コードのこと。ソースファイルの漢字コードとは異なる場合がある。-kanji-internal
で指定できるが、普通はシステムの既定のものから変更されない。))」(platex なら SJIS か EUC、uplatex なら UTF-8 *2)で符号化されている。「\205
」は PDF の文法で符号値が 8 進数で 205(10 進数で 133)の文字を表す。PDF 中の文字列は既定では PDFDocEncoding という文字コードで解釈されることになっていて、その符号値 '205 の文字は endash(U+2013)である。
すなわち、(unicode オプション非指定時の*3)hyperref は、\textendash
のような「文字命令」(LICR)を適切に PDFDocEncoding の(エスケープを用いた)PDF 文字列に変換する機能を有している。また hyperref は自前でリガチャ(--
→ endash)を処理するが、この時も一旦「文字命令」(\textendash
)を経由して PDFDocEncoding の文字列に変換している。従って、この文書の場合、endash は正しく変換されたことになる。しかし、「PDF 文字列は PDFDocEncoding で解釈される」ということは、和文文字(「内部漢字コード」で符号化されている)は正しく解釈されず文字化けが起こることになる。これは、「何も対策をしないと PDF のしおりの和文文字が化ける」というお馴染みの現象である。和文部分を正しく解釈させる方法は、DVI ドライバ毎に異なるが、dvipdfmx の場合、次のように「tounicode special」を出力すればよいことも知られている。((この special は当然ながら hyperref の出力する special(ここに和文文字列が含まれる可能性がある)より DVI 中で前に置く必要がある。最近の hyperref は \AtBeginShipoutFirst
(atbegshi パッケージ)を用いていてこれは LaTeX 標準の \AtBeginDvi
による出力よりも先行される。従って、一般的に hyperref 読込前に \AtBeginShipoutFirst
で出力する方法が行われている。))
(内部漢字コードによって使い分ける) pdf:tounicode EUC-UCS2 ← EUCの場合 pdf:tounicode 90ms-RKSJ-UCS2 ← SJISの場合 pdf:tounicode UTF8-UCS2 ← UTF-8の場合
これで、8 進エスケープ(\205
等)以外の部分は指定の漢字コードで解釈されるので、例えば内部漢字コードが EUC で、EUC で「enダッシュ」と書かれた場合は、“pdf:tounicode EUC-UCS2
”を予め指定しておくと、「enダッシュ」いう文字列と解釈される。
さて、問題なのは、8 進エスケープの部分である。TeX Forum の投稿で述べられている解説に従うと、dvipdfmx は tounicode が指定されている場合は、8 進エスケープの部分も「指定した文字コードで符号化されたバイト列」であると思って解釈するようである。つまり「\205?\205!
」は〈85 3F 85 21〉であり、これを内部漢字コードで解釈しようとする。この結果は当然 endash にならない(そもそも endash は JIS X 0208 にない)。普通は文字列は単一の方式で符号化されていて、数値でのエスケープは直接バイトを置くことの代替と考えるなら、この dvipdfmx の挙動は理に適っているだろう。一方で、hyperref は 8 進エスケープを PdfDocEncoding であると想定している*4ので、この仕様の間で齟齬が起こっていることになる。
私は「文字命令」(LICR)は常に正しい文字を表すべきであると考えているので、この状況は対処が必要であると捉えている。以下のような方法を用いると tounicode 指定時に全体が正しく解釈される文字列を DVI に出力できる。
(ASCII 以外の文字について)「文字命令」を「8 進エスケープ」に変換する代わりにその文字自体の和文文字トークンに変換する
例えば、\textsection
であれば、これまでは \247
に変換していたところを和文文字の〈§〉に変換する。tounicode が効いていることを前提にすると、dvipdfmx はこれを U+00A7 と解釈する*5ことになる。
というわけで作ってみた。
- PXjahyper パッケージ(github/zr-tex8r)
基本的な使い方は、hyperref の後に pxjahyper パッケージを読み込むだけである。これで、内部漢字コードに応じて適切な tounicode special が出力されるし、また PDF 文字列中で「文字命令」が正常に機能する。
% upLaTeX 文書 \documentclass[uplatex]{jsarticle} \usepackage[dvipdfmx,bookmarks=true,bookmarksnumbered=true]{hyperref} \usepackage{pxjahyper} \begin{document} \section{enダッシュ--?\textendash!} \end{document}
この文書を uplatex 2 回 → dvipdfmx で変換して、次のように正しいしおりをもつ PDF 文書ができる。
なお、endash(U+2013)は JIS X 0208 にないため、pLaTeX ではそもそも PDF 文字列に出力できない。*6通常はそういう場合はその文字命令を無効(hyperref で警告が出る)としているが、例外的に endash のようにリガチャで使われる文字については(hyperref の処理を通すために)代替的な定義を与えている。endash は「--
」に変換される。
% pLaTeX 文書 \documentclass{jsarticle} \usepackage[dvipdfmx,bookmarks=true,bookmarksnumbered=true]{hyperref} \usepackage{pxjahyper} \begin{document} \section{enダッシュ--?\textendash!} \end{document}
なお、以上の 2 つの例で文書ファイルの漢字コードは設定(自動判定や -kanji
オプション)と合致する限り何でもよい。
*2:実際は、upTeX では和文文字は Unicode の符号値そのものを用いて処理されていて、UTF-8 表現を用いているのではない。ただ、DVI に special を書き出す場合には UTF-8 が使われるようなので便宜的にその扱いとする。
*3:pTeX 系を用いる場合は unicode オプションは役に立たないので、以降はこれを前提にする。
*4:というか、本来は全ての文字列を PdfDocEncoding として出力しているのであって、pTeX 系の和文文字は「考慮外」だから正しく扱えていないだけである、ともいえる。
*5:この時点では単なる「Unicode 文字列」を扱っているので、「和文」とか「全角」とかいう属性はもはや存在しない。