Hatena::ブログ(Diary)

マクロツイーター このページをアンテナに追加 RSSフィード Twitter

2012-05-27

hyperref で日本語しおりに endash を出す件について

アレの話なんだが。

"--"が hyperref で正しく扱われず,しおりが変になる

以下のように \texorpdfstring{texstring}{pdfstring} を使って pdfstring の方を8進数表記で書くことで回避可能です.

\section{\texorpdfstring{a--b}{a\055\055b}}
TeX Wiki - hyperref

例えば、次のような文書をコンパイル*1する。

\documentclass{jsarticle} % pLaTeX を使う場合
%\documentclass[uplatex]{jsarticle} % upLaTeX を使う場合
\usepackage[dvipdfmx,bookmarks=true,bookmarksnumbered=true]{hyperref}
\begin{document}
\section{enダッシュ--?\textendash!}
\end{document}

すると、DVI ファイルに記された節のしおりの文字列(これは最終的に PDF の文字列リテラルとして解釈される)は次のようになる。*2

1 enダッシュ\205?\205!

この中の和文文字は「内部漢字コード*3」(platex なら SJISEUC、uplatex なら UTF-8 *4)で符号化されている。「\205」は PDF の文法で符号値が 8 進数で 205(10 進数で 133)の文字を表す。PDF 中の文字列は既定では PDFDocEncoding という文字コードで解釈されることになっていて、その符号値 '205 の文字は endash(U+2013)である。

すなわち、(unicode オプション非指定時の*5)hyperref は、\textendash のような「文字命令」(LICR)を適切に PDFDocEncoding の(エスケープを用いた)PDF 文字列に変換する機能を有している。また hyperref は自前でリガチャ(-- → endash)を処理するが、この時も一旦「文字命令」(\textendash)を経由して PDFDocEncoding の文字列に変換している。従って、この文書の場合、endash は正しく変換されたことになる。しかし、「PDF 文字列は PDFDocEncoding で解釈される」ということは、和文文字(「内部漢字コード」で符号化されている)は正しく解釈されず文字化けが起こることになる。これは、「何も対策をしないと PDF のしおりの和文文字が化ける」というお馴染みの現象である。和文部分を正しく解釈させる方法は、DVI ドライバ毎に異なるが、dvipdfmx の場合、次のように「tounicode special」を出力すればよいことも知られている。*6

(内部漢字コードによって使い分ける)
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 であると想定している*7ので、この仕様の間で齟齬が起こっていることになる。

私は「文字命令」(LICR)は常に正しい文字を表すべきであると考えているので、この状況は対処が必要であると捉えている。以下のような方法を用いると tounicode 指定時に全体が正しく解釈される文字列を DVI に出力できる。

ASCII 以外の文字について)「文字命令」を「8 進エスケープ」に変換する代わりにその文字自体の和文文字トークンに変換する

例えば、\textsection であれば、これまでは \247 に変換していたところを和文文字の〈§〉に変換する。tounicode が効いていることを前提にすると、dvipdfmx はこれを U+00A7 と解釈する*8ことになる。

というわけで作ってみた。

基本的な使い方は、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 文書ができる。

f:id:zrbabbler:20120527182649p:image

なお、endash(U+2013)は JIS X 0208 にないため、pLaTeX ではそもそも PDF 文字列に出力できない。*9通常はそういう場合はその文字命令を無効(hyperref で警告が出る)としているが、例外的に endash のようにリガチャで使われる文字については(hyperref の処理を通すために)代替的な定義を与えている。endash は「--」に変換される。

% pLaTeX 文書
\documentclass{jsarticle}
\usepackage[dvipdfmx,bookmarks=true,bookmarksnumbered=true]{hyperref}
\usepackage{pxjahyper}
\begin{document}
\section{enダッシュ--?\textendash!}
\end{document}
f:id:zrbabbler:20120527182648p:image

なお、以上の 2 つの例で文書ファイルの漢字コードは設定(自動判定や -kanji オプション)と合致する限り何でもよい。

*1:(u)platex を 2 回実行する必要がある。

*2:「1」は節番号である。

*3:エンジンの内部で和文文字を表すのに用いられる漢字コードのこと。ソースファイルの漢字コードとは異なる場合がある。-kanji-internal で指定できるが、普通はシステムの既定のものから変更されない。

*4:実際は、upTeX では和文文字は Unicode の符号値そのものを用いて処理されていて、UTF-8 表現を用いているのではない。ただ、DVI に special を書き出す場合には UTF-8 が使われるようなので便宜的にその扱いとする。

*5pTeX 系を用いる場合は unicode オプションは役に立たないので、以降はこれを前提にする。

*6:この special は当然ながら hyperref の出力する special(ここに和文文字列が含まれる可能性がある)より DVI 中で前に置く必要がある。最近の hyperref は \AtBeginShipoutFirst(atbegshi パッケージ)を用いていてこれは LaTeX 標準の \AtBeginDvi による出力よりも先行される。従って、一般的に hyperref 読込前に \AtBeginShipoutFirst で出力する方法が行われている。

*7:というか、本来は全ての文字列を PdfDocEncoding として出力しているのであって、pTeX 系の和文文字は「考慮外」だから正しく扱えていないだけである、ともいえる。

*8:この時点では単なる「Unicode 文字列」を扱っているので、「和文」とか「全角」とかいう属性はもはや存在しない。

*9:out2uni を用いると JIS にない文字を pLaTeX でしおりに含められる。後の機会に説明する。

ttkttk 2012/05/27 22:58 (ASCII 以外の文字について)「文字命令」を「8 進エスケープ」に変換する代わりに「Unicode の \0xUUUU エスケープ」に変換する、にすればpTeX + dvipdfmx でもendashが通るんじゃないかな、と思います。以下、作業途中のもの。
PDF2UNI[0o030..0o037] = 0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc
PDF2UNI[0o200..0o207] = 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044
PDF2UNI[0o210..0o217] = 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018
PDF2UNI[0o220..0o227] = 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160
PDF2UNI[0o230..0o236] = 0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e
PDF2UNI[0o240 ] = 0x20ac

zrbabblerzrbabbler 2012/05/28 11:33 >「Unicode の \0xUUUU エスケープ」
これの dvipdfmx でのサポートは「一時期あったが廃止された」と思ってるのですが。実際さっき試したところでは失敗しているようです。

out2uni ではこの形式が解釈されます。実際に、pLaTeX で pxjahyper を out2uni というオプション付で読むと(基本漢字外は)この形式の出力をするようになります。

ttkttk 2012/05/28 23:06 \0xUUUU 形式は、一度入ったが削除された、とのこと了解しました。out2uni は、このケースで今も有用なのですね。
↓に関連コメントを見つけました。
http://oku.edu.mie-u.ac.jp/~okumura/texfaq/qa/50071.html

sysy 2012/07/02 16:58 質問したの私でした。ありがとうございました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/zrbabbler/20120527/1338111080