LaTeX のファイルの文字コードをどうするか

LaTeX におけるソースファイル――文書ファイル（`*.tex`）や各種スタイルファイル（`*.sty`，`*.cls` など）――の文字コードには何が使えるか、また、何を使うべきか、という話。

## 何が使えるか：いきなり答え

| エンジン  | ASCII | JIS | EUC | SJIS | UTF-8N | UTF-8B | UTF-16| 出力 |
|:--|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| pdfLaTeX（inputenc に `utf8`）| ○ | × | × | × | ○ | × | × | UTF-8N |
| XeLaTeX                       | ○ | × | × | × | ○ | ○ | ○ | UTF-8N |
| LuaLaTeX                      | ○ | × | × | × | ○ | ○ | × | UTF-8N |
| (u)pLaTeX（`-kanji=jis`）     | ○ | ○ | ※1| ※1| ※1| ○ | × | JIS |
| (u)pLaTeX（`-kanji=euc`）     | ○ | ○ | ○ | × | × | ○ | × | EUC |
| (u)pLaTeX（`-kanji=sjis`）    | ○ | ○ | × | ○ | × | ○ | × | SJIS |
| (u)pLaTeX（`-kanji=utf8`）    | ○ | ○ | × | × | ○ | ○ | × | UTF-8N |

※「JIS」「EUC」「SJIS」はそれぞれ「ISO-2022-JP」「EUC-JP」「シフトJIS」を指す。
※「UTF-8N」「UTF-8B」はそれぞれ BOM 無と BOM 付の UTF-8 を指す。
※ BOM 無と BOM 付、LE と BE の組合せ、計 4 つの UTF-16 をまとめて「UTF-16」に示した。
※ (u)pLaTeX で文字コード自動判別が有効になっている場合は、JIS・EUC・SJIS・UTF-8 の中で自動判別される。
※1 “内部漢字コード”の設定に応じて、この3つの中のどれか1つが使える。詳しくは「詳細説明」の節を参照。

## 何が使えるか：詳細説明

※TL;DR（[スキップ](#どれを使うべきか)）

### pdfLaTeX

  * pdfTeX はいわゆる「8 ビットクリーン」なソフトウェアのように振舞う[^7]。
    つまり入力のファイルをそのままバイト列として処理する。
    昔の Perl や C 言語で書かれたソフトウェアと同様である。
  * 従って、（Perl や C 言語のように）TeX プログラム側で 8 ビットの（ASCII 互換の）文字コードをデコードして扱うことができる。
    それを実装したのが inputenc パッケージである。
  * プレアンブルで以下の指定を行うとUTF-8 のファイルが入力できる。

    ```latex
    \usepackage[utf8]{inputenc}
    ```

    詳しくは以下の記事を参照されたい。
      * [UTF-8 で欧文文字を入力する ～inputenc パッケージ～](http://qiita.com/zr_tex8r/items/b40ca3478e4fe14868e5)
  * TeX コードで処理するので、ファイル先頭の BOM が勝手に無視されることにはならない。特に、大本の文書ファイルの先頭の BOM は“inputenc を読む前に”現れることになるので対処ができない[^18]。

[^7]: 実は pdfTeX には 8 ビットの文字コードをデコードする機構（ecnTeX）を持っているのだが、LaTeX ではこれは使っていない。
[^18]: ただし、一部の TeX 環境（W32TeX とか）ではファイル先頭にある BOM のバイト列を無視する機能が備わっている。

### XeLaTeX

  * XeTeX エンジンの既定では、UTF-8 と UTF-16 の全ての変種を自動判別して受け付ける。
      * 文字コード指定のない XML と同様。
  * 出力テキストファイルの文字コードは常に UTF-8 となる。
  * 実は XeTeX エンジンには入力ファイルの文字コードを指定する機能（`\XeTeXdefaultencoding` プリミティブ）があり、それを使うと SJIS などの他の文字コードも扱える。
      * 参照：[きょうの XeTeX (3) : \XeTeXinputencoding, \XeTeXdefaultencoding](http://d.hatena.ne.jp/zrbabbler/20151030/1446222091)
      * しかし LaTeX レベルではその機能はサポートされていない。だから XeLaTeX では結局 UTF-8 か UTF-16 しか扱えない。
      * LaTeX では「エンジンが出力したファイルを自分で読み込む」という処理が多用される（例えば `*.aux` ファイル）。
        ユーザが別の文字コードを使うことを許してしまうと、出力は常に UTF-8 であるため、結局異なる文字コードが混在してしまって上手くいかないのである。
  * もちろん、UTF-16 なテキストファイルを作ろうとする正気な奴なんていないので、**「XeLaTeX 使うなら文字コードは UTF-8 だよね、常考」**という雰囲気になっている。

### LuaLaTeX

  * LuaTeX エンジンでは入力・出力ともに UTF-8 のみをサポートする（入力は BOM 付でもよい）。
    単純明快。
  * 「入力テキストファイルに（Lua で実装された）フィルタを適用する」という機能があるので、他の文字コードをサポートすることは原理的には可能である。
  * でも**「LuaLaTeX 使うなら文字コードは UTF-8 だよね、常考」**という雰囲気になっている。

### pLaTeX

  * pTeX エンジンは日本語文字コード（JIS、EUC、SJIS、UTF-8）を扱う機能を持っている。
    テキストファイルの入出力の文字コードはコマンド起動時に `--kanji` オプションで指定する。
      * この文字コードの既定値は TeX 環境によって異なる。
        Windows では SJIS であることが多い。
        昔の Unix 系 OS では EUC が多かった[^1]。
        それ以外では UTF-8 になっているだろう。
  * 実は、入力については多少の柔軟性がある。
      * 先頭に「UTF-8 の BOM」がある場合は `--kanji` の指定に関わらず常に UTF-8 と見なす。
      * JIS のファイルは常に正常に入力できる[^2]。
      * `--kanji=jis` の場合、JIS の他に <del>EUC</del> <ins>EUC と SJIS の何れか（「内部表現の文字コード」と同じもの）</ins>も入力できる。
  * 従って、ファイルの文字コードを JIS または BOM 付 UTF-8 の何れかにしておくと、それはどんな設定の pLaTeX でも読み込めることになる。
  * W32TeX の pTeX エンジンは入力ファイルの自動判別機能を持っていて、既定で有効になっている[^8]。
    この機能が有効である場合、入力については JIS・EUC・SJIS・UTF-8 の中で自動判別される。
      * つまり、入力については `--kanji` の指定が効かない。
        出力については `--kanji` の指定に従う。
      * JIS と BOM 付 UTF-8 は常に正常に読み込める、というのは変わらない。

#### 入出力ファイル以外の話

※高度な話題なので初級者は無視してよい。

  * 入出力のテキストファイル以外の文字コードは、これとは別扱いである。
      * 「和文の内部表現の文字コード」は EUC と SJIS の何れか[^3]で、入出力の文字コード（`--kanji`）とは必ずしも一致しない。
        例えば、`--kanji=utf8` の場合でも「内部表現の文字コード」は“UTF-8”ではない（従って、“JIS 外字”は扱えない）。
      * 和文 TFM の文字コードは JIS[^4]である[^5]。
        なので、DVI の文字出力命令でも JIS の値が使われる。
        また、フォントマップファイルの CMap 指定での変換元の文字コードも JIS である。
      * DVI の special 命令の文字列が和文を含む場合、その文字コードは「内部表現の文字コード」に等しい[^6]。

[^1]: pTeX エンジンが古くて UTF-8 に対応してなかったため。
[^2]: JIS でエンコードされたバイト列は 7 ビットであって他の 3 つの文字コードと明らかに区別ができるため。実際には、他の文字コードのファイルの途中に JIS の文字列が混ざっていても全て正常に読み込まれる。
[^3]: 「内部表現の文字コード」は `--kanji-internal` オプションで指定できる。`-kanji=sjis` が既定である環境では `--kanji-internal=sjis` が既定、それ以外では `--kanji-internal=euc` が既定である。
[^4]: 正確には「ISO 2022 での GL 表現の 2 バイトをビッグエンディアンで解釈した 16 ビットの整数」。一般に“フォントの文字コード”は単一の整数値であってバイト列ではない。
[^5]: 余談だが、pLaTeX で標準的に用いられる和文 TFM の名前は jis(.tfm) であるが、この“jis”は和文組版規則の規格である JIS X 4051 を指していて、JIS 符号系や JIS エンコーディングとは無関係である。upLaTeX の和文 TFM は文字コードが Unicode であるが、名前は upjisr-h(.tfm) のように“jis”を含んでいる。
[^6]: dvipdfmx の ToUnicode 指定（`pdf:tounicode` 命令）はこれを規準にするのが正しい。
[^8]: `--guess-inputenc`／`--no-guess-inputenc` でこの機能を有効／無効にできる。

### upLaTeX

  * 実は、入出力の文字コードの扱いについては pLaTeX と全く同じである。
      * つまり `--kanji` オプション指定で文字コードを指定できる。
      * 既定の文字コードは Windows を含めて全ての環境で UTF-8 であろう。
      * W32TeX では upTeX についても自動判別機能があり、しかも既定で有効になっていることに注意。

#### 入出力ファイル以外の話

※初級者は無視してよい。

  * 入出力以外の文字コードについては……。
      * 「内部表現の文字コード」は（既定では[^10]）Unicode[^11] である。
      * 和文 TFM の文字コードは Unicode。
        DVI の文字出力命令や CMap の変換元の文字コードも Unicode。
      * DVI の special 命令の和文の文字コードは UTF-8。

[^10]: 一応 `--kanji-internal` で変更が可能。upTeX をわざわざ「内部 Unicode」で無くしても何も嬉しくないのだが。
[^11]: 正確には「Unicode スカラー値」（UTF-8 ではない）。以後、“文字コードは Unicode”といった場合はこの意味で使う。

## どれを使うべきか

  * 自分だけが使うファイルについては、“使える”範囲で好きな文字コードを使えば良いと思う。
  * スタイルファイルを公開して配布する場合は**文字コードの選択を慎重に行う**べきである。
      * すなわち、そのファイルの使用が想定されるエンジンの（できる限り）全てで使用できる文字コードを選ぶべき。
      * **pLaTeX 用、または pLaTeX／upLaTeX 共用の場合は、JIS か BOM 付 UTF-8 にすることが推奨される。**
      * BOM 付 UTF-8 なら pdfLaTeX 以外の全てで通用する。
      * BOM 無 UTF-8 なら 「入出力が UTF-8 でない pLaTeX」以外の全てで通用する。
      * 特別な理由がない限り、**SJIS や EUC での配布は行ってはいけない**。
  * TeX Live ではテキストファイルの文字コードを原則的に BOM 無 UTF-8 に統一していることにも留意が必要。
      * つまり、オリジナルが別の文字コードであっても BOM 無 UTF-8 に変換して収録する。
      * ただし、「(u)pLaTeX 専用」のファイルに限って JIS を採用している。
        例えば jsclasses のクラスファイルは TeX Live でも CTAN でも JIS になっている。
  * JIS と BOM 付 UTF-8 以外を用いる場合、文字コードの自動判定が行われうることにも留意が必要。
      * ファイルの先頭に適当な日本語文字を含むコメントを入れておくと、判定を誤ってしまう確率を減らせる。
  * 最も安全で確実なのは中身を全て ASCII 文字にすることである。
    例えば、[pxrubrica パッケージ](http://qiita.com/zr_tex8r/items/42466cbcbeb670a3a2dc)は、和文文字列の変換処理などを行っているが、ファイル（pxrubrica.sty）は ASCII 文字のみを含んでいる。

## 補足：古い pLaTeX の場合

大昔の pTeX エンジンでは UTF-8 がサポートされていなかった。その場合の状況を先と同じ形の表で表すと以下のようになる。要するに、UTF-8 が使えないこと以外は今の pTeX と同じである。

| エンジン  | ASCII | JIS | EUC | SJIS | UTF-8N | UTF-8B | UTF-16| 出力 |
|:--|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| pLaTeX（`-kanji=jis`）     | ○ | ○ | ○ | × | × | × | × | JIS |
| pLaTeX（`-kanji=euc`）     | ○ | ○ | ○ | × | × | × | × | EUC |
| pLaTeX（`-kanji=sjis`）    | ○ | ○ | × | ○ | × | × | × | SJIS |

JIS で書かれたスタイルファイルは大昔の pLaTeX でも今の (u)pLaTeX でも常に正しく読み込めることがわかる。

## 補足：“UTF-8-MAC”とかはどうか

これについて考えるには、まず「UTF-8-MAC」というモノについて正しく理解する必要がある。

  * [UTF-8-MAC なんていう文字コードはありません](http://tama-san.com/utf-8-mac_1/)
  * [UTF-8-MAC は文字コードかな…](http://tama-san.com/utf-8-mac_2/)

そういうわけで、ここでは「Unicode[^20] の各種の正規化方式[^21]について TeX エンジンはどう取り扱うか」について解説する。要するに、

>   Mac のアプリに出てきたファイル名をコピペしたかなんかで、LaTeX ソースに「か+“結合濁点(U+3099)”」のような「分解された形（NFD）の濁点仮名文字」が含まれていたらどうなるか

という話である。

[^20]: 正規化方式（NFC か、NFD か、…）と文字符号化方式（UTF-8 か、UTF-16BE か、…）は全く別の（直交する）概念なので、ここでの“Unicode”は UTF-8 と UTF-16 の両方を指す。まあ、UTF-16 のテキストファイルなんて（以下略）
[^21]: 厳密に言うと、各種の“正規化方式”でなくて各種の“文字の表現方法”とでもいうべきもの。普通に（IME を使って）日本語の文章を入力した場合、一般にはそれは NFC にも NFD にもなっていない。

### 全般

原則としては、TeX エンジンが**勝手に正規化を行うことはない**。つまり、「が(U+304C)」と〈か+結合濁点〉 は TeX 言語上では別の文字列（トークン列）として扱われる。この点は多くのプログラミング言語と同じである。（というか、無暗に正規化が行われると[互換漢字が吹っ飛んで](http://d.hatena.ne.jp/NAOI/20130116/1358319668)しまう。）

Unicode が扱えるエンジンでは、Unicode の文字列が“出力”されることになるが、その場合でも「〈が〉を出力する」と「〈か+結合濁点〉を出力する」は飽くまで違うものと扱われる。それぞれの場合に“何が出力されるか”はエンジンやフォントの種類に依存することになる。XeLaTeX や LuaLaTeX で OpenType フォントを使っていて、フォントが“濁点の合成に対応している”ならば、通常は「〈か+結合濁点〉を出力」した場合でも正しい〈が〉の文字（グリフ）が出力される。一方、少し古い upLaTeX（の和文）は“合成”が全くできないので、「〈か+結合濁点〉を出力」するとおかしな結果になる。

### XeLaTeX

  * 実は XeTeX エンジンには入力ソース文字列に正規化を指定するための機能（`\XeTeXinputnormalization` プリミティブ）が存在する。
      * 参照：[きょうの XeTeX (2) : \XeTeXinputnormalization](http://d.hatena.ne.jp/zrbabbler/20151021/1445432229)
      * 今のところ LaTeX のインタフェースは用意されていない。しかし無理やり使っても特に異常は起きないと思う。
      * もちろん、これを使うと互換漢字が[“Lost in N11n”](https://twitter.com/afternormalize)になる。
  * なお、実際に試した限りでは、XeLaTeX で「〈か+結合濁点〉を出力」させると、（フォントが“合成に対応”していなくても）常に〈が〉の文字（グリフ）が出力されるようである。他の濁点・半濁点付きの仮名についても同様である。この辺りの仕組は自分はよく解っていない。<br>
  * 参照：[NFD な濁点仮名を XeLaTeX できる謎](http://d.hatena.ne.jp/zrbabbler/20151024/1445652536)

### pLaTeX

  * pLaTeX エンジンでは例外的に、**濁点・半濁点付きの仮名に限って NFC への正規化が行われる**。つまりソースに〈か+結合濁点〉があってもそれは〈が〉として読み込まれる。これは pTeX が JIS X 0208 ベースのため結合濁点・半濁点を単独の“文字”として扱えないからである。
      * つまり NFD の形の仮名文字が混ざっても大丈夫である。
      * 参照：[NFD な濁点仮名を pLaTeX できる話](http://d.hatena.ne.jp/zrbabbler/20151006/1444134044)

### upLaTeX

  * 新しい[^25]upLaTeX エンジンでは pLaTeX と同様に、**濁点・半濁点付きの仮名に限って NFC への正規化が行われる**。
  * 古い upLaTeX ではこの正規化が行われない。そのうえ“出力処理での合成”もできないので、結局不正な出力になってしまう。
      * upLaTeX では結合文字を一応は文字として扱えるので対応されなかったのだと思う。しかし、意図せずに NFD の形の仮名が混入すると、それを“自動的に”[^22]正規化するのは困難である。

[^22]: つまり、混入した部分を `\normalizeKana{...}` みたいな命令に入れることをせずに、ということ。
[^25]: W32TeX では 2015/10/26 以降。TeX Live では 2016 以降（予定）。