2011年6月29日水曜日

emacs 23 (その5)

前回に unicode の合字を全部 OFF としましたが。JIS X 0213 には合字を用いないと unicode で表現できない文字が25文字ほどあります。今回はこれについての対応を考えます。

emacs 23.3 のデフォルトでは、これらの文字については JIS X 0213 から unicode(内部コード)への変換は行われていないようです。EUC-JIS-2004 などにより、これらの文字を読み込んだ場合には、unicode に変換されず外字的な扱いでそのまま内部に保管されます。逆に unicode でこれらの文字に相当する文字組み合わせがあった場合も EUC-JIS-2004 に変換することはできずセーブ不能な文字が含まれているとしてエラーにます。

これに対応するために Emacs の日本語環境には jisx0213-to-unicode, unicode-to-jisx0213 という変換テーブルがデフォルトで定義されいます。これを使えばうまくいくはず....

だったのですが....... この変換表 translate-region などでは、きちんと使用できるのですが、なぜか文字コードの translation-table に設定しても正常に動かないという問題があります。バグか仕様の変更に対応できていないか何かだと思われます。

ということで、このテーブルは個人環境で再定義してしまうことにしました。続きのところに書いておきます。



;;
;;  Re-defined jisx0213-to-unicode, unicode-to-jisx0213 tables
;;       which is in lisp/language/japanese.el.
;;
(let ((map
       ;; JISX0213-1 vs Unicode
       '((#x2477 . [#x304B #x309A])  ; Hiragana Ka with Semi-Voiced Mark
         (#x2478 . [#x304D #x309A])  ; Hiragana Ki with Semi-Voiced Mark
         (#x2479 . [#x304F #x309A])  ; Hiragana Ku with Semi-Voiced Mark
         (#x247a . [#x3051 #x309A])  ; Hiragana Ke with Semi-Voiced Mark
         (#x247b . [#x3053 #x309A])  ; Hiragana Ko with Semi-Voiced Mark
         (#x2577 . [#x30AB #x309A])  ; Katakana Ka with Semi-Voiced Mark
         (#x2578 . [#x30AD #x309A])  ; Katakana Ki with Semi-Voiced Mark
         (#x2579 . [#x30AF #x309A])  ; Katakana Ku with Semi-Voiced Mark
         (#x257a . [#x30B1 #x309A])  ; Katakana Ke with Semi-Voiced Mark
         (#x257b . [#x30B3 #x309A])  ; Katakana Ko with Semi-Voiced Mark
         (#x257c . [#x30BB #x309A])  ; Katakana Se with Semi-Voiced Mark
         (#x257d . [#x30C4 #x309A])  ; Katakana Tu with Semi-Voiced Mark
         (#x257e . [#x30C8 #x309A])  ; Katakana To with Semi-Voiced Mark
         (#x2678 . [#x31F7 #x309A])  ; Katakana Small Pu
         (#x2b44 . [#x00E6 #x0300])  ; Latin Small AE with Grave
         (#x2b48 . [#x0254 #x0300])  ; Latin Small Open O with Grave
         (#x2b49 . [#x0254 #x0301])  ; Latin Small Open O with Acute
         (#x2b4a . [#x028C #x0300])  ; Latin Small Turned V with Grave
         (#x2b4b . [#x028C #x0301])  ; Latin Small Turned V with Acute
         (#x2b4c . [#x0259 #x0300])  ; Latin Small Schwa with Grave
         (#x2b4d . [#x0259 #x0301])  ; Latin Small Schwa with Acute
         (#x2b4e . [#x025A #x0300])  ; Latin Small Schwa with Hook, Grave
         (#x2b4f . [#x025A #x0301])  ; Latin Small Schwa with Hook, Acute
         (#x2b65 . [#x02E9 #x02E5])  ; Ascending Sign
         (#x2b66 . [#x02E5 #x02E9]))); Descending Sign
      (table (make-translation-table))
      (table2 (make-translation-table)))
  (dolist (elt map)
    (let ((j0213-1 (decode-char 'japanese-jisx0213.2004-1 (car elt)))
          (comp_char (cdr elt))
          (indep_char (aref (cdr elt) 0)))
      (aset table j0213-1 (list (cons (vector j0213-1) comp_char)))
      (let ((old (aref table2 indep_char)))
        (aset table2 indep_char
              (if old
                  (append (list (cons comp_char j0213-1)) old)
                (list (cons comp_char j0213-1)
                      (cons (vector indep_char) indep_char)))))
      ))
  (set-char-table-extra-slot table2 1 2)
  (define-translation-table 'jisx0213-to-unicode table)
  (define-translation-table 'unicode-to-jisx0213 table2))

;;
;; coding system
;; utf-8-ja
;;
(define-coding-system 'utf-8-ja
  "UTF-8 for japanese (with JIS X 0213 compatibility)"
  :coding-type 'utf-8
  :mnemonic ?U
  :charset-list '(unicode japanese-jisx0213.2004-1 japanese-jisx0213-2)
  :mime-charset 'utf-8
  :decode-translation-table (get 'unicode-to-jisx0213 'translation-table)
  :encode-translation-table (get 'jisx0213-to-unicode 'translation-table)
)

;
;(define-coding-system-alias 'utf-8                   'utf-8-ja)

上記のテーブルをJIS X 0213系の文字コードの decode/encode-translation-table で使うことにより EUC-JIS-2004 をオープンして内部コードにする時に unicode の合字に変換したり、Shift-JIS-2004 でセーブする時 JIS X 0213 に戻すということができます。しかし、私はこのような利用はしないことにしました。

内部的にユニコードの合字で扱うとすると以下の問題が出ます。
  • 内部的に2文字扱いになるのでカーソル移動や削除等で見た目が1文字なのに2文字分キー操作が必要になる。で間違って合字の半分だけ削除したりしてしまう。
  • 声調記号とか前後の文字によって合字したりしなかったりの設定をする必要があるが、個々に設定するのは非常にめんどくさい。
  • 文字幅問題があるので、前回書いたように合字は全部OFFにしたい。
結局、色々と試行錯誤した結果、逆転の発想が非常にうまくいきました。日本語で作業している場合には utf-8 の decode-translation-table に unicode-to-jisx0213 を、encode-translation-table に jisx0213-to-unicode を設定します。こうすると以下のようになって良い感じになります。
  1. UTF-8 の文章をロードする際に JIS X 0213 の合字に相当する文字列があれば JIS X 0213 の文字として内部に持つことになります。これは euc-jis-2004 をロードしたのと同じ状態です。
  2. この状態だと内部的に1文字なので、削除や、移動や、文字幅で問題が出ません。
  3. terminal-coding-system が utf-8 の場合には画面に出力する際に、逆の変換をして UTF-8 の合字文字列に戻されるので正しく表示されるはず。EUC-JIS-2004 とかを読み込んだ場合にも、出力時に適切な UTF-8 に変換可能
  4. ファイルに UTF-8 でセーブする時も内部の JIS X 0213 の文字から UTF-8 の合字文字列に変換されるので同様に正しく動く。
上記のコードでは utf-8 以外に、utf-8-ja という別コードとして定義して使い分けできるようにしていますが、どうせ日本語と英語くらいしか使用しないのならば最後のコメントを外して utf-8 自体を置き換えた方が便利だと思います。

1 件のコメント:

  1. > なぜか文字コードの translation-table に設定しても正常に動かない
    という問題について、原因を調べて報告した所、半田さんが直してくださいました。
    http://lists.nongnu.org/archive/html/mule-ja/2016-05/msg00001.html
    その修正がいよいよ release 版に取り込まれ、emacs 26 からはやっと、本記事のような table の再定義は不要になりました。文字コードの translation table にそのまま設定できるようになっています。

    返信削除