2010年2月7日日曜日

日本語ファイル名を持つファイルを含む zip ファイルを、utf-8 .tar.gz に変換する

 ZIPは古い圧縮形式です。ZIPではアーカイブのファイル名の文字コードは決まっておらず、世界各国でばらばらでした。日本ではZIPアーカイブに含まれるファイル名の文字コードは、ほぼ Shift_JIS(CP932)で統一されています。Windows XP に組み込まれている explorer も、日本では ZIPファイルのファイル名は Shift_JISとして扱います。

しかしながら、Linuxの unzip コマンドは、ZIPに含まれるファイル名を、ヨーロッパ系の文字コードと仮定して、勝手にUTF-8に直して展開します。そのため、多くの日本のZIPファイルはLinuxでは正しく扱うことができません。

これでは後々のトラブルを起こす原因になりかねないので、日本のzipファイルの中に入っている Shift_JISのファイル名を、すべて一括して、UTF-8のファイル名に変換して、tar.gz のアーカイブに変換することにしました。

標準の unzip を使っている限り、日本語を扱うことはできません。まずは、ファイル名の変換を抑制するための改造を行います。そのために、infozip のページ(http://sourceforge.jp/projects/sfnet_infozip/releases/)から、ソースコード "unzip60.tar.gz" をダウンロードします。


% tar xvfz unzip60.tar.gz
 % cd unzip60
 % cp unix/Makefile .


次に、fileio.c の、Ext_ASCII_TO_Native を呼び出している2箇所をコメントアウトします。そして、そのファイルをコンパイルし、インストールします。(デフォルトでは、/usr/local/bin/ にインストールされます。)

% make generic 
 % sudo make install

次に、/tmp ディレクトリに、専用のテンポラリディレクトリを作り(仮に /tmp/arc とします)、次のスクリプトを、パスに通しておきます。

#/bin/sh 
rm -rf /tmp/arc/*
dir=`dirname "$1"`
base=`basename "$1" .zip`
/usr/local/bin/unzip "$1" -d /tmp/arc/
convmv --notest -r -f sjis -t utf-8 /tmp/arc/*
tar cvfz "$dir/$base.tar.gz" --directory=/tmp/arc "."

このスクリプトを使うと、日本語ファイルの .zip ファイルを、コマンド一発で、UTF-8のファイル名を持つ .tar.gz に変換することができます。(ファイル名に空白が含まれる場合は、クォートに注意しましょう)。

2010年2月5日金曜日

Emacs で 青空文庫を読む。

今回は、Lookup に添付されている 青空文庫ビューアを使って、気軽に Emacs で、青空文庫を読む方法を紹介したいと思います。

前準備として、青空文庫の一式をダウンロードします。BitTorrent等で配布されている DVD 版(青空文庫・全)を利用するのが便利です。このDVDの「作家別フォルダ」には、各作品は作者ごとに分類され、ディレクトリ名は「作者名」、ファイル名は「作品名」で日本語ファイル名となっているため、diredで楽にブラウズできます。ただし、ディレクトリ名の末尾の作品数を示すカッコつきの数字は削った方が、DVD発行以降の作品を入れる際には便利です。(ファイル名の一括置換には、diredの新機能である wdired-change-to-wdired-mode を活用するのが便利です。)

「青空文庫・全」には追加となる「青空文庫・差分配信」もあります。ただし差分配信の方は、各作者フォルダ内の各作品フォルダ内に、各ファイルがzipで固められた状態で配布されています。これを一括して解き、かつそれらを日本語ファイル名を持つテキストファイルとして保存するためには、以下のようなコマンドを実行します。

$ for file in */*.zip;
 > unzip -p $file > `dirname $file`/`basename $file .zip`.txt

生成されたファイルを、既存の青空文庫の作家別フォルダにマージするためには、まず
cd 090305_txt
mv * ..<path_to>../作家別フォルダ
として、「青空文庫・全」と重複していない作家の作品を移動し(重複している作家の分はエラーとなりコピーされません)、次に、
for file in */*.txt
 > mv $file ..../作家別フォルダ/`dirname $file`
のようにして、残りの「青空文庫・全」と作家の重複している作品を移動します。

さらに作品の活用を目指すのならば、全てのテキストファイルを iconv(CP932用のパッチを当てた版を用意しておくと便利です)で UTF-8 に変換し、手持ちのパソコンの検索エンジンでインデックスを作っておくと、諺や言い回しからの検索もできるようになり、さらに楽しみ方が広がります。

Emacs 青空文庫ビューアを使うには、まず、M-x load-library aozora-view をしておき、青空文庫のテキストファイル(拡張子は必ず.txtでなければなりません)のバッファで、M-x aozora-view を実行します。すると、新しいバッファが作られ、そこに文字を調整した表示バッファが表示されます。ルビ位置の計算がややかかるので、2回目以降は素早く表示できるよう、キャッシュファイルを保存しておくことができます。



Emacsは縦書きはできません。小説を読む際に、どのフォントが読みやすいのか、様々な漢字とかなを組み合わせて試してみるのが良いでしょう。

これを簡単に行うために、以下のようなスクリプトを作ってみました。これを使うと、漢字・仮名・ローマ字において、様々なフォントや背景色を、キーボードの1キー入力で切り替えることができます。

(defun list-rotate-forward (ring)
  (let ((item (car ring))
        (last (last ring)))
    (setcdr last (list item))
    (cdr ring)))

(defun list-rotate-backward (ring)
  (let ((item (car (last ring)))
        (last2 (last ring 2)))
    (setcdr last2 nil)
    (cons item ring)))

;; color-theme を使う。
(require 'color-theme nil t)

(defvar color-theme-set (cdr (cdr (mapcar 'car color-themes))))

(defvar kana-font-set
  '("ヒラギノ明朝 Pro"
    "ヒラギノ丸ゴ Pro"
    "ヒラギノ角ゴ Pro"
    "PMinIWA-HW-Md" ;; "PMinIWA-Md" ← 非固定幅
    "小塚明朝 Pr6N"
    "小塚ゴシック Pr6N"
    "IPA明朝"
    "IPAゴシック"))

(defvar kanji-font-set
  (append kana-font-set '("Hanazono Mincho OT xProN")))

(defvar font-size-set '(12 16 20 28 40))

(defvar ascii-font-set
  '("Droid Sans Mono"
    "Inconsolata"
    "DejaVu Sans Mono"))

(defvar set-rotate-set
  '((ascii-font-set . "ASCII")
    (kana-font-set . "かな")
    (kanji-font-set . "漢字")
    (font-size-set . "フォントサイズ")
    (color-theme-set . "テーマ")))

(defun rotate-rotate-set-forward ()
  (interactive)
  (setq set-rotate-set (list-rotate-forward set-rotate-set))
  (message "Rotate Set = %s" (cdar set-rotate-set)))

(defun rotate-rotate-set-backward ()
  (interactive)
  (setq set-rotate-set (list-rotate-backward set-rotate-set))
  (message "Rotate Set = %s" (cdar set-rotate-set)))

(defun rotate-set-forward ()
  (interactive)
  (let ((set (caar set-rotate-set))
        (name (cdar set-rotate-set)))
    (set set (list-rotate-forward (eval set)))
    (message "%s=%s" name (car (eval set)))
    (update-current-settings)))

(defun rotate-set-backward ()
  (interactive)
  (let ((set (caar set-rotate-set))
        (name (cdar set-rotate-set)))
    (set set (list-rotate-backward (eval set)))
    (message "%s=%s" name (car (eval set)))
    (update-current-settings)))

(global-set-key "\M-\S-f" 'rotate-rotate-set-forward)
(global-set-key "\M-\S-b" 'rotate-rotate-set-backward)
(global-set-key "\M-\S-n" 'rotate-set-forward)
(global-set-key "\M-\S-p" 'rotate-set-backward)
;
;;; フォント幅テスト用の文字列。|
;;; 1234567890123456789012345678|
;;; 上記の二本の縦棒が揃えば、全角・半角が揃っている。(例:Inconsolata & 小塚・ヒラギノ書体)
(defun update-current-settings ()
  "自分の好みのフォントセットに変更する。"
  (interactive)
  (let ((sz (car font-size-set))
        (ascii (car ascii-font-set))
        (kanji (car kanji-font-set))
        (kana  (car kana-font-set))
        (color-theme (car color-theme-set)))
    (if (functionp color-theme) (funcall color-theme))
    ;; 下記以外のフォントは、自動選択に任せる。
    (set-fontset-font nil '(    #x0 .   #x6ff) (font-spec :size sz :family ascii)) ;; 日本語向き。
    (set-fontset-font nil '( #x1700 .  #x171f) (font-spec :family "Tagalog Stylized"))
    (set-fontset-font nil '( #x1720 .  #x1c4f) (font-spec :family "Code2000"))
    (set-fontset-font nil '( #x1720 .  #x1c4f) (font-spec :family "MPH 2B Damase") nil 'prepend)
    (set-fontset-font nil '( #x2000 .  #x33ff) (font-spec :family "HanaMin"))
    (set-fontset-font nil '( #x2000 .  #x33ff) (font-spec :family kana) nil 'prepend)
    (set-fontset-font nil '( #x3400 .  #x9fff) (font-spec :family kanji) nil 'prepend)
    (set-fontset-font nil '( #xa000 .  #xefff) (font-spec :family "MPH 2B Damase"))
    (set-fontset-font nil '( #xf100 .  #xf6ff) (font-spec :family "EUDC2"))
    (set-fontset-font nil '( #xff00 .  #xfffd) (font-spec :family kanji) nil)
    (set-fontset-font nil '(#x10000 . #x10fff) (font-spec :family "Code2001"))
    (set-fontset-font nil '(#x18000 . #x1ffff) (font-spec :family "Unicode Symbols"))
    (set-fontset-font nil '(#x2a700 . #x2fff0) (font-spec :family "HanaMin"))
    ))

上記のコードを .emacs.el に入れておくと、フォントサイズ/背景色/ASCII文字/漢字/かなを、一発で切り替えることができるようになります。まず、何を切り替えるかを、S-M-f, S-M-b で切り替えます。その後、S-M-n, S-M-b で、それぞれを個別に切り替えます。以下に、漢字/かなフォントを幾つか切り替えた例を示します。どの漢字フォントとひらがなフォントの組み合わせが読みやすいでしょうか?



IVSによる異体字表示機能

青空文庫ビューアは、OpenTypeの IVS 表示機能を用いて、書体を古風な漢字に切り替えることができます。そのためには、M-x aozora-view-use-ivs を実行します。

ただし現状では、IVS機能を使えるフォントは、小塚明朝 Pr6N シリーズなどごく一部のフォントに限られます。早くヒラギノ等の他のOpenTypeフォントでも IVS が使えるようになるといいですね。

以下は、小塚明朝フォントを使って、一部の漢字を古風にさせたものです。どの漢字が変化したかわかりますでしょうか?


さて、フォントを変えるだけではなく、今度は背景色や表示色も色々切り替えてみましょう。
一部のライブラリのフォント表示ルーチンは、アンチエイリアス表示をする際に、背景色と表示色のガンマ値を見ないものがあります。そのため、色を変えると見え方がかなり変わる場合があります。


サイズも色々変えてみると面白いかもしれません。それぞれ、12dot, 16dot, 28dot での表示です。

なお、青空文庫ビューアでは"b"ボタンを押す(aozora-view-bookmark)と「しおり」をはさみ、","ボタンを押す(aozora-view-restore-bookmark)と、「しおり」を元に戻します。この「しおり」情報は、aozora-view-bookmarks変数で保存されるので、複数のセッションでこの「しおり」情報を保持したい場合は、session.el をロード後、以下のような設定を行ってください。

(add-to-list 'session-globals-include 'aozora-view-bookmarks)