pTeX を UTF-8 入力に対応させる試み(4) †
UTF-8対応(3) からは、まずライブラリ部分を、kpathsea から独立させました。
そして ttk 氏の
upTeX, upLaTeX − 内部unicode版 pTeX, pLaTeX の実装実験
とのすり合わせを行います。
ptexenc †
libkanji は、pTeX のあちこちに出てくる漢字コード変換に関するルーチンを、
ライブラリとして独立させたものです。
独立とは言っても kpathsea ライブラリへのパッチとして実現していました。
U*NIX につきものの autoconf を一から書かずに、手抜きをしていたのですが、
諸事情からやはり独立させたほうがよいだろうということで、
ptexenc という名前の単独のライブラリとしました。
ptetex3 を使っていただくと以下の作業は自動で行われますが、
念のため説明しておきます。
ptetex3 の archive 以下にある
ptexenc-0.88.tar.gz を、kpathsea と同じ高さになるよう展開して使います。
以下のようにディレクトリ名からバージョン番号を取り払ってしまったほうが
楽に作業できるでしょう。
tetex-src-3.0/texk/kpathsea
tetex-src-3.0/texk/ptexenc
tetex-src-3.0/texk/web2c/ptex
tetex-src-3.0/texk/web2c/ptex/jmpost
tetex-src-3.0/texk/mendexk
ptex や mendex には、同じく archive 以下の hoge-utf8.patch を当てておきます。
そして tetex-src-3.0/texk/configure の 6584 行あたりに一行追加すると、
pTeX をまとめて make できるようになります。
fmt の生成も teTeX の枠組を用いるので、意識しなくて大丈夫です。
PKGS='
ptexenc web2c/ptex web2c/ptex/jmpost mendexk ←追加
afm2pl bibtex8 cjkutils ....
ディフォルトの文字コードは ./configure に
"--with-ptexenc=UTF8" のようなオプションで指定します。
"--enable-kanji-iconv" で iconv を使うようになりますが、
Linux 以外ではちゃんと動く環境は少ないようです。
upTeX 対応 †
upTeX は ttk 氏による pTeX の拡張で、拙作の UTF-8 対応とは異なり、
内部処理も含めた Unicode 対応です。
TeX ソースに書いた文字は基本的にすべて upTeX で処理可能です。
Unicode と言っても、UTF-8 とか UTF-16 のように、
いろいろな表現法(エンコーディング)があります。
upTeX では、今まで JIS で扱われていた部分で、
細かなエンコーディングの使い分けが発生しています。
| pTeX | upTeX | 備考 |
---|
和文 | U+0080 〜U+FFFF | U+10000 〜U+10FFFF |
入出力バッファ | EUC/SJIS (2byte) | UTF-8 (2〜3byte) | UTF-8 (4byte) | 1byte部分は ASCII に相当し、欧文処理へ |
内部処理 | EUC/SJIS (16bit) | UTF-32変 (5+24bit) | 変数 (16 or 32bit) の一部のビットが文字コードに割り当てられている ちなみに欧文なら 4+8=12bit |
DVI, vf | JIS (2byte) | UCS-2 (2byte) | UTF-32 (=UCS-4) (3byte) | UTF-32 (本来 4byte) の上位 1byte の 0 は省略 |
TFM | -- | upTeXでは、U+FFFF超は漢字のみを想定 |
PS | UTF-16 (4byte) | dvipsやdvipdfmxのCMap等で使う |
代表的な文字が Unicode のどの部分にあるのかまとめてみます。
通常我々の使う文字のほとんどは U+FFFF までに収まっています。
文字の範囲 | UTF-8表現 | UTF-16表現 | 代表的な文字 |
---|
U+0000〜U+007F | 1byte | 2byte | ASCII の 7bit 文字 |
U+0080〜U+07FF | 2byte | Latin-1、ギリシャ、キリル文字 |
U+0800〜U+FFFF | 3byte | JIS漢字の第1,2水準すべて JIS漢字の第3,4水準の一部 |
U+10000〜U+10FFFF | 4byte | 4byte | JIS漢字の第3,4水準の一部 |
UTF-8 などの特長をまとめます。
| 一文字のバイト数 | 上限 | 備考 |
---|
UTF-8 | 1〜4byteの可変 | U+10FFFF | アルファベットに限れば普通の ASCII と同じ、左記はUnicodeの場合 |
UCS-2 | 2byteの固定 | U+FFFF | 範囲内では UTF-16, UTF-32/UCS-4の下位2byte と同じ |
UTF-16 | 2,4byteの可変 | U+10FFFF | U+FFFFの範囲内は UCS-2 と同じ、同範囲超はサロゲートペア |
UTF-32 | 4byteの固定 | U+10FFFF | 範囲内では UCS-4 と同じ、最上位部 1byte は必ず 0 |
UCS-4 | 4byteの固定 | 0x7FFFFFFF | 0x10FFFF以下では UTF-32 と同じ |
Unicodeスカラ値 コードポイント | U+10FFFF | 上記のような符号化表現ではなくて整数値 |
- UTF-32 とは言っても、DVI に書き込むときは、上位 2byte が 0 なら
下位 2byte しか書き込まず、上位 1byte が 0 なら下位 3byte しか書き込まない
工夫がされてるので、
サイズ的に不利になるわけではないようです。
前者の場合の下位 2byte は、UCS-2 と同じ表現になります。
- TFM には UCS-2 で書くため U+FFFF より上の文字は表現できませんが、
ひっくるめて漢字の扱いにすればよいので、
表現できなくても不都合はないと思われます。
- ちなみに U+2xxxx には CJK Extension-B など、漢字が沢山入っています。そのあたりを狙っています。 U+1xxxx に定義されている文字にも面白そうなものもあるのですが、 jfm 風の組み方をすることはないでしょうから、無視しちゃいました。(ttk)
- 個人的な感想としては、
想定される利用範囲では、UTF-32 も UCS-4 も同じ働きしかしてないように思うので、
どちらかというと UCS-4 と書いてしまったほうが理解しやすいような気がします。
UCS-2 は UCS-4 の下位 2byte という雰囲気が出てますし。
- UTF-32 と UCS-4 との違いは、上限の違いだそうです(参考)。どっちにしろ32bitでは無いから正しい UTF-32/UCS-4 ではないのですが、どちらかというと UTF-32 の方が近いように思います。(ttk)
- むしろ「Unicodeスカラー値を 2〜3byte で表現したもの」、と言った方が適切だったかもしれません。(ttk)
- なるほど、「Unicodeスカラー値」というのはわかりやすくてよいと思います。(土村)
- UTF-8という呼び名は Unicode も ISO/IEC10646 も同じ呼び名で呼ぶそうです。 ISO/IEC10646 なら上限 0x7FFFFFFF, 1〜6byte可変長です。(ttk)
- 内部処理の変数の大きさは、C言語上では、TeX/pTeX: 16bit, Omega/upTeX: 32bit という理解でいいんじゃないかなと思います。詳細は web2c/texmfmem.h のコメント欄をご参考に。(ttk)
使われている関数をまとめると以下のようになります。
以前は from/toJIS() となっていたものが、ほぼ from/toDVI() に置き換わりました。
(文字列で書かれた JIS コードを処理する部分では fromJIS() が使われています。
表には含めていません。)
ツール | 入力 ファイル | 内部 バッファ | 出力ファイル | 使用関数 |
---|
入力系 | 出力系 |
---|
ptex | 全 | 初期固定 | J/U (DVI) | 全 | input_line2() fromBUFF() | toDVI() putc2() |
---|
pltotf | 全 | E/S/U | J/U (tfm) | input_line2() fromBUFF() | toDVI() |
---|
tftopl | J/U (tfm) | E/S/U | 全 | fromDVI() | toBUFF() putc2() |
---|
pdvitype | J/U (DVI) | E/S/U | 全 | fromDVI() | toBUFF() putc2() |
---|
jbibtex | 全 | E/U | 全 | input_line2() | putc2() |
---|
mendex | 全 | EUC固定 | 全 | input_line2() | putc2() |
---|
jmpost | 全 | E/S/U | J/U (PS) | 全 | input_line2() fromBUFF() | toDVI() putc2() |
---|
pdvitomp | J/U (DVI) | E/S/U | 全 | fromDVI() | toBUFF() putc2() |
---|
- pTeX で JIS だった部分は upTeX では UCS-2/UTF-16/UTF-32(2〜3byte) のいずれかに、EUC/SJIS(8bit2byte) だった部分は UTF-8 に、EUC/SJIS(16bit) だった部分は Unicodeコードポイント(24bit) になります。(ttk)
ご意見をどうぞ †
- 松本さんの convert-euc.txt や dvipdfmx 用に書く special コマンドを鑑みれば,PDF のしおりに使われる文字コードは UCS-2 であると推測されます (UTF-8 に変換しないのはなぜなのか,は未調査.pdfmarks Reference には,UCS-2 の文言はなくて,Unicode と書かれているだけで,かつ 4 byte 表記を勧めているように見受けられます). 最終的な出力として PDF が多く用いられるようであれば,考慮しておくとよいかもしれません. -- kuroky
- 詳細はよくわかりませんが、BOM も付けてあったような。別エンコードと思った方がよさそうな印象なので、今の段階で考慮する意味は薄いように思います。 -- 土村
- BOM, pdfmark Ref. によると FEFF を先頭に付けるようです.つまり UCS-2BE ということでしょうか.pdfmark Ref. を見る限りはUTF-32BE (UCS-4BE) を求めていると思われますが・・・(???). -- kuroky
- FEFF(つまりは BOM)を出力する必要があるとすれば、その続きに UTF-32 <=> UCS-4 のような変換を行うことは難しくないはず。ですから bookmark のことまで配慮して upTeX のエンコードを検討する必要はないでしょう。upTeX が PDF を直接出力するわけでもありませんし。 -- 土村
- upTeX本体がテキストで出すべきところへの出力 (log, aux) などはすべて UTF-8 で、それ以外はありえないと思っています。 special に書き込まれるデータのコードはマクロで制御できる/しているのでしょうか? いずれにせよ、 pTeX の場合と同様、マクロ、dviware、外部プログラムのどこかで解決すべき問題です。それはさておき、従来 SJIS/EUC だった方法が UTF-8 に換わるだけなので、簡単に出来ると思います。ちなみに dvipdfmx を使うコースの場合は UTF8-UCS2 というものを dvipdfmxの開発元 から拾ってきたら、日本語部分は出来ました。"dvipdfmx bookmark UTF8-UCS2" でググるとやり方が多数引っかかります。ほとんど中国語
ですがなんとなく分かります。-- ttk
- それから、「UTF-8 に変換しないのはなぜなのか」については、試してみたら "(8bit多byte)" で書くと distiller は UTF-8 と解釈してくれず別のエンコーディングと思うようですね。 (Latin-1 かあるいは PostScript 標準エンコーディングかどちらかか?)-- ttk
- 井上さんの解説に、ちょっと気になるものがありました。マクロでしおりの Unicode 化もできるみたいです。漢字に対応するunicode番号(UCS2)を得るスタイルファイルjis2uni.sty(Hyperref対応) -- 土村
- ↑マクロの処理でも可能なような気がしていましたが、すでに公開されたものがあるのですね。そうではなくて、dviware内部での処理ですが、hyperref + dvipdfmx のコースの UTF8→UCS2/UTF16 への変換を行うコードを xdvipdfmx から拾ってきて uptex-0.07 に入れてみました。BMPのCJKの文字は上手くbookmarkにできているみたいです。-- ttk
- オリジナル pTeX で SJIS/EUC と呼んでいるものの中に、 8bit1〜2byte 可変長の部分と 16bit 固定長の部分があり、相互の変換の HILO(), Hi(), Lo() なども実は立派なコード変換だという風に理解すると、コード変換の回数は pTeX と upTeX で一緒です。そのあたりを BtoINTENC() などにしてあります。pTeX で JIS だった部分は、 UCS-2/UTF-16/UTF-32もどき の使い分けになっていますが、U+FFFF 超をどうするかの違いだけなので、一緒といえば一緒です。-- ttk
- 表を大胆に書き換えました。間違いがあれば修正お願いします。
参考までに、U+10FFFF 超の文字が来たら、
upTeX ではどのように扱うのか教えていだけるとありがたいです。 -- 土村
- 上限の処理は、まだちゃんと考えていません。現状は KANJI_code の最大が 0x11FFFFになっているので、それ以上はエラーになるかまともに動かないと思います。 U+10FFFF 超 0x11FFFF 以下の一部分はギリシャ文字など欧文 TeX の領域 (4+8bit) とバッティングしてしまう部分 (U+0080〜U+0FFF) の回避先に使っています。したがって \char などでそのコード値の文字のつもりで入力するとギリシャ文字などに化けます。この仕様は、場当たり的なもので、将来的には欧文 TeX の catcode の 4bit を上の方に移し、ギリシャ文字などを正規の Unicode の位置に戻すつもりです……が、いつ実現できるかは不明です。いずれにせよ、Unicode の上限はカバーできても、ISO/IEC10646 の上限までは到底無理ですし、サポートする意義も見出せないので、U+10FFFF 超はエラーにする方針のつもりです。
表の中で昨日入出力バッファの EUC/SJIS (2byte) と内部処理の EUC/SJIS (16bit) とは、“別物”であるという意識のもとでわざわざ書き分けたのですが、意図は伝わらなかったでしょうか。前者では欧文 8bit との混在は不可能ですが、後者では欧文 8bit+catcode 4bit との重畳が可能になっています。それを利用して pTeX+babel が成り立っているわけです。また、内部処理に使えるコードの範囲を決めている cs_token_flag の値は、欧文 TeX では 12bit, Omega では 20 bit という風であり、8bit × n byte という考え方になじみません。revert合戦になるのは本意ではないのでコメントにとどめることにします。-- ttk
- bit の意味は理解できておらず失礼致しました。cs_token_flag をキーワードに勉強しなおします。ASCII 文字の回避先がなぜ 21bit の微妙なところにしてあるのか、ようやく理解できそうです。 -- 土村
- 表にちょっと書き加えてみました。厳密さが要求される表ではないと思いますが、それでも押さえておくべき点だと私が思うものを入れてみました。-- ttk
- ありがとうございます。書きたいと思っていたことを書いていただきました。 -- 土村
- 「内部処理も含めた本格的な Unicode 対応」というとちょっと誤解を受けるかもしれません。CJKに関しては、まあ、当たっているのですが、(非英語)欧文は「CJK化されないように」もできるようにしただけです。アラビヤ語やヘブライ語などは、全然駄目なんじゃないかと思います。Omegaとくっつける日が来れば、本当に本格的なUnicode対応と呼んでもいいかも知れません。 -- ttk
- "本格的な" は消しておきました。ところで、"Unicode スカラー値" という言葉は、検索しても用例が少ないようです。半面、"コードポイント" は用例がたくさんあります。この二つに意味の違いはあるのでしょうか。 -- 土村
- お手数かけて済みませんでした。“Unicode スカラ値”なら少し多いようですね。Unicode.orgの解説によると、サロゲートの片割れを入れない(Unicode scalar value)か入れる(code point)かの差みたいです。-- ttk