2012年2月Archives

Unicode 文字—十六進表記相互変換

昨夜,漢字の拡大表示の JavaScript 自作プログラム『老眼鏡』について書いた。その後,漢字の入力を,漢字そのものではなく,Unicode コードポイントでも可能とした。つまり,例えば森鷗外の「鷗」という文字を拡大表示したいとき,「鷗」の Unicode コードポイント U+9DD7 の 9dd7 を入力してもよいようにした。Enter キーを押しても実行されるようにも改修した。老眼鏡 Reading Glasses for the Aged からお使いください。もしブックマークに登録するならこのリンクをドラッグしてください。

かな漢字変換で出て来ない文字も,Unicode コードポイントがわかっていれば,これで表示してコピペして入力するという使い方もできる。牛丼の吉野家の「吉」は,商標では異体字の「𠮷」(上部が士ではなく土のキチ,いわゆる土吉。画面に文字が出てますか?)が本来のものだが,「𠮷」(U+20BB7)は Unicode CJK Unified Ideographs Extension B という拡張領域に定義されていて,ちょっと古いエディタ,ワープロでは扱えない文字である。最近のかな漢字変換ソフトウェアでも変換候補に出て来ないものが多いのではないだろうか。このように入力するにやっかいな文字があるとき,『漢字源』で Unicode を調べ『老眼鏡』にこれを入れれば,その文字が表示され,これをコピペして入力することができる。もちろん逆に,漢字を入力して Unicode コードポイントを調べることも可能である。文字は漢字だけでなく 4 オクテットまでの Unicode 文字なら何でも(キリル文字でも,古典ギリシア語複式アクセント文字でも,アラビア文字でも,サンスクリット文字でも,ハングルでも)OKのはずである。

20120224-jiten-yosi.png
『漢字源』で「𠮷」Unicode 値は 20BB7
20120224-tutiyosi.png
20bb7 を入力すると「𠮷」が表示される

さて,『老眼鏡』のために Unicode コードポイント十六進数と文字との間で変換を行う関数を二本書いた。unitohex 関数は引数に文字(「𠮷」)をとって,これを Unicode コードポイント十六進数文字列(「20BB7」)を返す。hextouni 関数はこの逆を行う。この二つの関数コードを以下に掲載しておく。こんなのはネットにごろごろ転がっていそうだが,適当なものがなかなか見当たらず,自分で書くことにした。

// -*- coding: utf-8; mode: js2; -*-
// Unicode 十六進 / 文字 相互変換
// - unitohex('文字'):        文字 -> Unicode 十六進コードポイント
// - hextouni('十六進文字列'): Unicode 十六進コードポイント -> 文字
// 2012 (c) coded by isao yasuda.
/*
UTF-8 符号化方式 (1-4 オクテットのみ)
Code point        octet1    octet2    octet3    octet4
-----------------+0123 4567+0123 4567+0123 4567+0123 4567 
U+0000-U+007F     0xxx xxxx
 (min)U+0000       000 0000
 (max)U+007F       111 1111
-----------------+0123 4567+0123 4567+0123 4567+0123 4567 
U+0080-U+07FF     110y yyyx 10xx xxxx
 (min)U+080          0 0010   00 0000
 (max)U+7FF          1 1111   11 1111
-----------------+0123 4567+0123 4567+0123 4567+0123 4567 
U+0800-U+FFFF     1110 yyyy 10yx xxxx 10xx xxxx
 (min)U+0800           0000   10 0000   00 0000
 (max)U+FFFF           1111   11 1111   11 1111
-----------------+0123 4567+0123 4567+0123 4567+0123 4567 
U+010000-U+1FFFFF 1111 0yyy 10yy xxxx 10xx xxxx 10xx xxxx
 (min)U+010000          000   01 0000   00 0000   00 0000
 (max)U+1FFFFF          111   11 1111   11 1111   11 1111
*/
// 文字 -> Unicode 十六進コードポイント変換
function unitohex(c) {
    // %xx%yy%zz 形式にエスケープする
    var w = encodeURI(c);
    if (w.length > 9) {// 4 オクテット以上の Unicode 文字
        // U+010000 - U+1FFFFF 拡張領域文字
        // UTF-8 デコード
        // 1. 符号化 UTF-8 ビットパターンの取得
        w = w.replace(/%/gi, "");
        var h = "";
        for (var i = 0; i < w.length; i++) {
            // 二進数文字列に変換
            var bstr = parseInt(w.charAt(i), 16).toString(2);
            // 4 bit 前ゼロ付加
            var dig0 = "";
            for (var j = 0; j < (4 - bstr.length); j++) {
                dig0 += "0";
            }
            h += dig0 + bstr;
        }
        // 2. Code Point 部の bit を抽出
        var uar = [];
        uar[0] = parseInt(h.charAt(5),2);
        uar[1] = parseInt(h.substring(6,8) + h.substring(10,12),2);
        uar[2] = parseInt(h.substring(12,16),2);
        uar[3] = parseInt(h.substring(18,22),2);
        uar[4] = parseInt(h.substring(22,24) + h.substring(26,28),2);
        uar[5] = parseInt(h.substr(28,4),2);
        // 3. Code Point digit (4 bit) 毎に十六進数文字列に変換
        w = "";
        if (uar[0] != 0) {// 0, 1 以外ありえない
            w = uar[0].toString(16);
        }
        for (var i = 1; i < uar.length; i++) {
            w += uar[i].toString(16);
        }
    } else {// 3 オクテット (エンコード形式で 9 文字) 以下
        // U+FFFF までの BMP 領域文字: オクテット毎に Latin1 と同じ扱いでよい
        w = (c.charCodeAt(0)).toString(16);
        var dt = 4 - w.length;
        for (var i = 0; i < dt; i++) {
            w = '0' + w;
        }
    }
    return w;
}
// Unicode 十六進コードポイント -> 文字 変換
function hextouni(cp) {
    // 入力チェック [0-9a-fA-F] 以外の入力を弾く]
    if (cp.match(/[^0-9a-fA-F]/)) {
        alert("入力に十六進数以外の文字が含まれています");
        return;
    }
    // 一文字ずつ bit 変換 (4 bit 境界)
    cp = cp.replace(/^0*/, ""); // 前ゼロサプレス
    var bits = "";
    for (var i = 0; i < cp.length; i++) {
        var bs = parseInt(cp.charAt(i), 16).toString(2);
        var dt = 4 - bs.length;
        for (var j = 0; j < dt; j++) {
            bs = '0' + bs;
        }
        bits += bs;
    }
    // 有効桁数で 1-4 オクテット符号の場合分け
    var wb = bits.replace(/^0*/g, "");
    var u8bits = "";
    // 各グループのビットパターンに埋め込み UTF-8 にエンコードする
    if (wb.length < 8) {
        // 1 octet Unicode U+0000-U+007F
        return unescape('%' + cp);
    } else {
        if (wb.length < 12) {
            // 2 octet Unicode U+0080-U+07FF
            bits = regbits(bits, 12);
            // 0123 4567+0123 4567
            // 110y yyyx 10xx xxxx
            u8bits = '110' + bits.substring(1,6) + 
                '10' + bits.substring(6,12);
        } else {
            if (wb.length < 17) {
                // 3 octet Unicode U+0800-U+FFFF
                bits = regbits(bits, 16);
                // 0123 4567+0123 4567+0123 4567
                // 1110 yyyy 10yx xxxx 10xx xxxx
                u8bits = '1110' + bits.substring(0,4) +
                    '10' + bits.substring(4,10) +
                    '10' + bits.substring(10,16);
            } else {
                if (wb.length < 22) {
                    // 4 octet Unicode U+010000-U+1FFFFF
                    bits = regbits(bits, 24);
                    // 0123 4567+0123 4567+0123 4567+0123 4567 
                    // 1111 0yyy 10yy xxxx 10xx xxxx 10xx xxxx
                    u8bits = '11110' + bits.substring(3,6) +
                        '10' + bits.substring(6,12) + '10' +
                        bits.substring(12,18) + '10' +
                        bits.substring(18,24);
                } else {
                    alert("5バイト以上のUnicode文字は未サポート");
                    return;
                }
            }
        }
    }
    // 4 bit 毎に十六進変換
    var u8hex = "";
    for (var i = 0; i < u8bits.length; i+=4) {
        u8hex += parseInt(u8bits.substring(i,i+4), 2).toString(16);
    }
    // 十六進文字 2 個毎に % を付加し,urlencode 形式に変換
    var u8enc = "";
    for (var i = 0; i < u8hex.length; i+=2) {
        u8enc += '%' + u8hex.substring(i,i+2);
    }
    // urldecode で文字に復元し返却
    return decodeURI(u8enc);
}
// bit 正規化 (4 bit 境界にする)
function regbits(bs, bc) {// bit 文字列,正規化数
    var dt = bc - bs.length;
    if (dt < 0) {// 仮に要求よりも短い場合そのまま返却
        return bs;
    }
    // 短い数だけ前0を付加する
    for (var i = 0; i < dt; i++) {
        bs = '0' + bs;
    }
    return bs;
}

UTF-8 の符号化方式は,コードポイントの領域によってオクテット(バイト)数が変化するので,その判定とビット列の取り扱いとがオペレーションの核である。JavaScript コードの先頭コメントに 1 〜 4 オクテットの符号化方式を整理してある。これまで Perl なんかではしょっちゅうこのオペレーションが必要だったので,サブルーチンを書いて使っていたが,JavaScript でははじめてだったので勉強になった。Perl だと pack 関数と ord 関数を用いて UTF-8 Unicode / 十六進数の間をラクに往来できるのだが,JavaScript だと馴れないためか苦労した。もう少し賢いやり方があるかも知れない。それでも encodeURI, decodeURI, unescape などの便利な関数のお世話になった。

久しぶりにトニー・グラハムの書いた『Unicode標準入門』を引っ張り出して UTF-8 符号化方式をおさらいした。最近,技術評論社から文字コードのよい解説書が出た。ともにアマゾンリンクをあげておきます。

Unicode標準入門
トニー・グラハム
関口正裕 監修
乾和志,海老塚徹 訳
翔泳社

老眼鏡 JavaScript

漢詩作成支援のために漢字字典を引きまくっていると,画数が多くてよく読めない字にしばしば遭遇する。おまけに年を取って老眼が進んでいるせいか,虫眼鏡で拡大しないと字のつくりがよく見えないのだ。学研『漢字源』には文字の Unicode コードポイントが収録されており,GNU Emacs テキストエディタ ucs-insert 関数を使って文字の入力は簡単にできる。でも入力で出て来た多画数文字を,いちいちエディタのフォントを大きくして確認するのもやってられない。

そこで Web で簡易に文字を拡大表示する JavaScript を拵えた。自虐も込めて『老眼鏡』という名前にした。入力した文字をただただ大きくするだけの,ホントつまらないツールだけど,Emacs から文字をコピってすぐ確認できるので自分としては大いに役に立っている。文字とサイズを指定すると,そのサイズで文字を表示するだけ。一応 Unicode コードポイントも出力する。こんな感じ。

20120223-rogankyo.png

誰が使うのかわかりませんが, ボタンから試すことが出来る。初期状態では,上の画像のように,このアプリを作るきっかけになった文字「蠶」(かいこ)が設定されている。

ソースコードをあげておく。使い方の説明はコードのはじめのあたりにしるしてある。つまらないアプリの割に 100 行以上のコードになっているのは,拡張領域を含む UTF-8 符号をデコードし Unicode 十六進コードポイントを組立てるのに手間取ったからに過ぎない。

// -*- coding: utf-8; mode: js2; -*-
// 老眼鏡: 文字を拡大する
// - 表示したいブロックに以下を書いておく。
//     <script language="JavaScript">
//       magnifier('字', 100, 400);
//     </script>
//   引数: 初期値文字,文字の大きさ(px),表示枠サイズ幅(px) 
// 2012 (c) isao yasuda.
// 描画領域の作成/初期値表示
function magnifier(chr, size, width) {
    // 引数処理
    // size numeric check
    var msize   = 200;
    if (isNaN(size)) { }// 初期値を使う
    else { msize = size; }
    // width numeric check
    var mwidth  = 500;
    if (isNaN(width)) { }// 初期値を使う
    else { mwidth = width; }
    // 入力/出力エリアの描画
    document.write(
        // 全体枠
        '<div class="mag1" style="' +
            'width: ' + mwidth + 'px;">' +
            // 入力エリア
            '<div>' +
            '<table class="mag2"><tr><td>' +
            '文字:</td><td>' + 
            '<input id="magchr" type="text" size="4" maxlength="4"' +
            'value="' + chr + '" /></td><td>' + 
            'サイズ(px):</td><td>' + 
            '<input id="magsiz" type="text" size="3" maxlength="3"' + 
            'value="' + msize + '" /></td><td>' +
            '<input type="button" value="表 示"' +
            'onclick="magfocus(); magnifychar();" /> ' +
            '<input type="button" value="クリア"' +
            'onclick="magfocus(); magclear();" />' +
            '</td></tr></table></div>' +
            // 表示エリア
            '<div id="magdsp"><span style="font-size: ' + msize + 
            'px; line-height: 120%;">' + chr + '</span>' +
            '</div></div>');
    document.write('<div id="unic">Unicode <span class="code">U+' + 
                   unitohex(chr).toUpperCase() + '</span></div>');
    // 初期値文字の描画
    magnifychar();
    magfocus();
}
// 拡大文字の表示
function magnifychar() {
    var c = document.getElementById("magchr").value;
    if (c == "") {
        alert("文字が入力されていません");
        magfocus();
        return;
    }
    var size = document.getElementById("magsiz").value;
    // size numeric check
    if (isNaN(size)) {
        alert('文字サイズは数値(単位: px)でなければなりません');
        document.getElementById("magsiz").focus();
        return;
    }
    // 文字表示
    document.getElementById("magdsp").innerHTML 
        = '<span style="font-size: ' + size + 
        'px; line-height: 120%;">' + c + '</span>';
    document.getElementById("unic").innerHTML
        = 'Unicode <span class="code">' +
        'U+' + unitohex(c).toUpperCase() + '</span>';
}
// カーソルポイント
function magfocus() {
    document.getElementById("magchr").focus();
}
// 入力文字クリア
function magclear() {
    document.getElementById("magchr").value = ""; 
}
// Unicode 十六進コードポイント変換
function unitohex(c) {
    var w = encodeURI(c);
    if (w.length > 9) {// 4 オクテット以上の Unicode 文字
        // U+010000 - U+1FFFFF 拡張領域文字
        // UTF-8 デコード
        // 1. 符号化 UTF-8 ビットパターンの取得
        w = w.replace(/%/gi, "");
        var h = "";
        for (var i = 0; i < w.length; i++) {
            // 二進数文字列に変換
            var bstr = parseInt(w.charAt(i), 16).toString(2);
            // 4 bit 前ゼロ付加
            var dig0 = "";
            for (var j = 0; j < (4 - bstr.length); j++) {
                dig0 += "0";
            }
            h += dig0 + bstr;
        }
        // 2. Code Point 部の bit を抽出
        var uar = [];
        uar[0] = parseInt(h.charAt(5),2);
        uar[1] = parseInt(h.substring(6,8) + h.substring(10,12),2);
        uar[2] = parseInt(h.substring(12,16),2);
        uar[3] = parseInt(h.substring(18,22),2);
        uar[4] = parseInt(h.substring(22,24) + h.substring(26,28),2);
        uar[5] = parseInt(h.substr(28,4),2);
        // 3. Code Point digit (4 bit) 毎に十六進数文字列に変換
        w = "";
        if (uar[0] != 0) {// 0, 1 以外ありえない
            w = uar[0].toString(16);
        }
        for (var i = 1; i < uar.length; i++) {
            w += uar[i].toString(16);
        }
    } else {
        // U+FFFF までの BMP 領域文字
        w = (c.charCodeAt(0)).toString(16);
        var dt = 4 - w.length;
        for (var i = 0; i < dt; i++) {
            w = '0' + w;
        }
    }
    return w;
}

漢詩作成支援辞書追加

このお休み,漢詩作成支援プログラム(友人向け)の漢字 DB と詩語 DB のメンテナンスをしていた。ある方の好意でいただいた詩語集を SQLite3 データベースとして構築したとき,いくつか課題を残した。前回(記事『misima 漢詩・詩語集拡充』)では,詩語集の Excel データ CSV 変換で JIS 第三・第四水準文字の文字化けがあった。さらにこれら文字化け解消の過程で漢字 DB 収録文字が 60 ほど足りないことがわかった。また,私自身で準備した詩語データと入手詩語集データとの合体をキー重複排除程度で済ませたため,情報の欠落があった(キーは同じでもぶら下げた情報が異なるものが落ちてしまった)。旧字体エントリ追加でエントリの重複・過剰があった(旧字体変換で使用した misima 旧字・旧仮名遣い変換プログラムは戦前までの用字用語への変換も行う。このため本来の新字体—旧字体以外の変換も実行してしまった結果,余計なエントリができてしまっていた)。今回,これら課題をすべて始末した。この結果,漢字 DB は 7006 文字,詩語 DB は 82957件となった。詩語は旧字体エントリの整理などで少し減った。

旧字体エントリ追加に伴う重複・過剰の整理をしていて,やっかいな問題に遭遇した。旧字体エントリの追加というのは,例えば「如糸」という詩語エントリがあるとして,「糸」をその旧字体(正字体)である「絲」に置き換えた「如絲」というものもプログラムで自動的に生成し,新旧両字体で詩語検索ができるエントリ拡張を意図している。ところが,漢字字典によれば「糸」,「絲」の平仄は,それぞれ,仄入聲十二錫,上平聲四支で異なるのである。平仄が異なると韻字検索で齟齬が出る。韻字で「糸」を検索した場合にも「絲」がヒットしないといけないのに,平仄が違うと同韻字として「絲」を拾うことそのものが誤りになる。字体の問題なはずなのに,平仄の食い違いが何故起こるのか。

いわゆる「いと」の意味の漢字はもともとは「絲」(音:シ - 上平聲四支)が正しい文字である。「糸」(音:ベキ,ミャク - 仄入聲十二錫)は本来「蚕の繭からとった細い原糸」(『漢字源』第四版,学習研究社,2007 年)という特別な意味で用いられるものだったようである。ところが「絲」の略字として「糸」が流通し,一般的・概念的な「いと」も「糸」を用いるほうが普通になった。それで平仄の異なる文字が新字体—旧字体の対になってしまった。事情はこのようなもののようである。漢詩を為す現代人もおそらく「いと」を意図して「糸」の文字をつくるのではないか(?)。こう考えると,「糸」を「絲」と同じものとして扱うほうが音韻処理方式として妥当だろう。特殊よりも一般に合わせるほうが可用性が高いからである。

このように新字体—旧字体で平仄が異なる対をチェック・プログラムで洗い出し,『字源』(増補版,角川書店,1955 年)と『漢字源』でひとつひとつその差異の意味を調べた。糸の原料をもたらす「蚕」(かいこ)も,その旧字体「蠶」(正確にいえば「蠶」は異体字というべきだが,正字体は JIS にも Unicode にも登録されておらず,「蠶」が正字体に近い字体である)とで平仄が異なる(蚕:テン,仄上聲十六銑 — 蠶:サン・ゾン,下平聲十三覃)。ここで「蚕」は,字典によれば,「かいこ」の意味では「誤用」であって,もともとは「ミミズ」を意味する文字である。「虫」(音:キ・ケ,上聲五尾)—「蟲」(音:チュウ・ジュウ,上平聲一東)の関係もこのようなものである(「虫」は「蟲」の略字で,本来は「マムシ」の意である)。漢詩作成支援としては,誤用や略字用法よりも旧字体の用法が漢詩の本性に相応しいと思われるので,こうした新字体の平仄をすべて旧字体に合わせることにした。つまり,例えば「糸」の平仄属性としてこの字本来の仄入聲十二錫ではなく「絲」の上平聲四支を登録した。もちろん,ミミズやマムシの意で「蚕」,「虫」を使いたい方がいるかも知れないが,それくらいの学識をもった方なら私の漢詩作成支援の診断・出力結果によらず作詩は可能だろう。一応,こうした措置は漢字 DB の備考欄に書き加えたので,漢字 DB で確認するとわかるようになっている。

漢字の音韻も字典によって違うものがあったりして面白い。「辨」という字(「弁」の「わける・わかつ・わきまえる・けじめをつける」意での旧字体。ちなみに「しゃべりがうまい」意での「弁」の旧字体は「辯」である)は,『字源』:上聲十六銑・去聲十六諫,『漢字源』:上聲十六銑のみ,『漢語新辞典』(初版,大修館,2001 年):去聲十六諫のみ,となっている。このように,漢字に懲り出すと際限がない。

エピグラフ・ランダム出力 JavaScript

私は名言集のようなものが好きである。昔の作家は愛する詩人・作家の文言を,象徴的意味を担うエピグラフ(題銘)として,己の作品にしばしば掲げたものである。西洋の著述家には現在もこの伝統が生きている。私もエピグラフの伝統が好きである。でもって,当サイトにも各ページにエピグラフを掲げたいと思い,短い JavaScript プログラムをひとつ書いた。

名言集テキストデータベースから指定した数だけランダムに名言を選択して,出力関数をしるしたブログ箇所にエピグラフを出力する。コード epigraphs.js は以下のようなものである。

// -*- coding: utf-8; mode: javascript; -*-
// Show Epigraphs
function epigraphs(outnum) {// outnum: 出力個数
    // 名言集 配列
    // - 追加するときは以下の形式で。' と , に注意
    //     '<div id="epgr">エピグラフ</div>' +
    //     '<div id="epgrath">— 著者</div>',
    // - 出力様式は id: epgr, epgrath を css で調整する。
    var words = [
        '<div id="epgr">Sator arepo tenet opera rotas<br />' +
        '種蒔く者は収穫を保有し収穫は種蒔く人を保有する</div>' +
        '<div id="epgrath">— ラテン語の格言</div>',
        '<div id="epgr">Hello darkness, my old friend</div>' +
        '<div id="epgrath">— ポール・サイモン</div>',
        '<div lang="grc" id="epgr">πάντα ῥεῖ. 萬物は流轉す</div>' +
        '<div id="epgrath">— Herakleitos</div>',
        '<div id="epgr">夜熟睡しない人間は多かれ少なかれ罪を犯している</div>' +
        '<div id="epgrath">— モーリス・ブランショ</div>',
        // このようにカンマ区切りで,ずらずら,いろいろ,名言を書いておく
        '<div id="epgr">おい小池,そろそろだ</div>' +
        '<div id="epgrath">— 警視庁</div>'
    ];
    // 引数チェック: 数値以外は初期値 2 を設定
    var wlen = words.length; // 名言集個数
    var num = 2;
    if (outnum != null) {
        // 引数指定がある場合
        if (isNaN(outnum)) {
            // 数値でない場合デフォルト
        } else {
            if (outnum > wlen) {
                // 名言集個数以上もデフォルト
            } else {
                num = outnum;
            }
        }
    } // 引数指定がない場合デフォルト
    // 乱数でランダムに指定個数分出力する。
    var alrd = new Array(); // 既出リスト
    var rand = wlen;        // 初期値は起こりえない数値
    var flag = true;        // 出力可フラグ
    while (num--) {
        rand = (Math.floor(Math.random()*1000)) % wlen;
        for (var i = 0; i < alrd.length; i++) {
            if (rand == alrd[i]) {
                // 同じものが選択されたら再度乱数を取得
                num++; flag = false; break;
            }
        }
        if (flag) {       // 初出のもののみ出力
            document.write(words[rand]);
            alrd.push(rand);
        } else {
            flag = true;
        }
    }
}

乱数を取得して三桁の整数値にし,名言データ配列数で割った剰余で出力すべき名言をポイントする。これで実行する都度「ランダム」な選択を実現している。つまりいまの版では,名言集は 1000 個までということになる。あまりに名言の数が多いと読込み時間もバカにならなくなるだろうから,1000 を越えるようなら,名言集を SQLite3 にでも格納して,Servlet がテキストを供給するように書き直した方がよいかも知れない。

名言データベースは上のコードの words[ ] 配列の要素として定義してある。名言とは言っても,私の勝手な思いつきで拾ったものなので,「おい小池,そろそろだ」みたいな愚にもつかないものも含まれている。面白いと思う方は,ご自分で名言集を構築してこのスクリプトを活用していただきたい。私のコードでは,出力スタイルの ID を埋め込むようになっていて,スタイルシートで表示様式をコントロールする前提である。

使い方は,epigraphs.js をロードしておき,エピグラフを表示したいブロックに以下のようにマークアップしておく。ブログならヘッダーテンプレートに書いておくなどすればよい。

<script language="JavaScript">epigraphs(3);</script>

引数(パーレンの中)に出力したいエピグラフの数を指定する。デフォルトは 2。

いま,このブログの右上に出ていますよね? リロードするたびに文句が変るはずである。

MovableType5 移行

このお休み,國芳展に出かける以外はうちでずっと MovableType ブログ管理システム(以下 MT)のバージョンアップ作業をやっていた。これまで Ver. 4 を使っていたんだけど,記事数が 1000 を越えてからカテゴリアーカイブが更新されないなどの不具合が出て,記事を追加するたびに再構築するなどの面倒な対処をさせられていた。でも MT 版更新はデザインの移行がしち面倒くさくてならない。それでも,小さい面倒を毎回強いられるより,MT 5.12 へのバージョンアップという本格的な面倒を一回だけ受け容れることにした。

せっかくなのでデザインを新しくすることにした。Photoshop と Illustrator を用いてバナーやロゴなどの素材を自作した。MT4 のウィジェット設定をいちいち画面でコピーしてテキストファイルに格納して,これらを MT5 のデザイン設定に反映した。スタイルシートの調整がもっとも手間を要した。MT では,あるスタイル要素がいろんなスタイルシートで定義されているので,思うような効果が出ないとき悪さをしている定義を探すのがたいへんである。もっとラクな移行方法があるんだろうけど,MT をきちんと勉強するなんて気持ちはまったく起きない。

過去のブログ記事を MT4 環境でエクスポートし,Emacs や sed で必要な編集を施した。6 年も運用していると,引用などの表示様式に統一がなくなってしまったので,少しは改善しようと思ったのである。ついでに,先日インストールした SyntaxHighlighter をプログラム・コードの部分に全面的に適用した。

この過程で SyntaxHighlighter に Emacs-lisp,LaTeX,コマンドライン用のスクリプトがないのが気に入らなくなり,自作した。言語の追加は正規表現の扱い以外は極めて容易である。Emacs-lisp と LaTeX 用を以下に掲載しておく(コマンドライン用は Bash 用のものを少し訂正しただけなので割愛)。lisp 用は funcs 等の変数に指定したものが色づけされる。LaTeX 用はコメント行,コントロールシーケンスのみが色づけされる設定である。lisp 用,LaTeX 用のマークアップは brush に対してそれぞれ lisplatex を指定すればよい。

/**
 * SyntaxHighlighter Emacs-lisp
 */
;(function()
  { 
      typeof(require) != 'undefined' ?
          SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
 
      function Brush()
      {
          var funcs     = 'lambda list progn mapcar car cdr reverse ' +
                          'member append format';
          var keywords  = 'let while unless cond if eq t nil defvar ' +
                          'dotimes setq listp numberp not equal';
          var macros    = 'loop when dolist dotimes defun';
          var operators = '> < + - = * / %';
 
          this.regexList = [
              { regex: SyntaxHighlighter.regexLib.doubleQuotedString, 
                       css: 'string' },
              { regex: new RegExp('&\\w+;', 'g'), css: 'plain' },
              { regex: new RegExp(';.*', 'g'), css: 'comments' },
              { regex: new RegExp('\'(\\w|-)+', 'g'), css: 'variable' },
              { regex: new RegExp(this.getKeywords(keywords), 'gm'), 
                       css: 'keyword' },
              { regex: new RegExp(this.getKeywords(macros), 'gm'),
                       css: 'keyword' },
              { regex: new RegExp(this.getKeywords(funcs), 'gm'), 
                       css: 'functions' }
          ];
      };
   
      Brush.prototype = new SyntaxHighlighter.Highlighter();
      Brush.aliases   = ['lisp'];
      SyntaxHighlighter.brushes.Emacs = Brush;
      typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
  })();
/**
 * SyntaxHighlighter LaTeX
 */
;(function()
  { 
      typeof(require) != 'undefined' ? 
          SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
 
      function Brush()
      {
          this.regexList = [
              { regex: new RegExp('%.*','gm'),  css: 'comments' },
              { regex: new RegExp('\\\\\\w*','gm'), css: 'keyword' }
          ];
      };
   
      Brush.prototype = new SyntaxHighlighter.Highlighter();
      Brush.aliases   = ['latex', 'tex'];
      SyntaxHighlighter.brushes.Latex = Brush;
      typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
  })();

今回のブログ・デザイン変更でいちばん困ったのは,背景色変更に伴う画像の扱いである。背景色はこれまで白(RGB で示すと #ffffff)だったのを明灰色(#e8e5e1)に変えたのだが,すでにゴマンとある画像が白の背景色を前提としているためにドロップシャドウなどが白抜きになっていて,これを明灰色の背景に配置すると白の枠が大いに目立って醜いことこの上ない。例えばこんな感じになってしまう。

sample

この対策のために何百とある画像を作り直すのは現実的でない。背景色になじむ枠画像と重ね合わせる調整をスタイルシートで行うことなどを考えたが,ブラウザによっては配置がずれたりしてさらに醜くなるかも知れず,うまくない。結局,ImageMagick によって色の置換を行うことにした。

ImageMagick には convert ユーティリティが付属していて,各種画像変換が可能である。

convert -fill '#rgb-after' -opaque '#rgb-pre' prefile afterfile

とやると ファイル prefile の RGB 値 rgb-preの色を rgb-after に置き換えて afterfile に出力してくれるのである。しかも -fuzz nn% オプションを付加すれば,指定した度合いでファジーに中間色を置き換えてくれる。いくつかパーセンテージを変えて試したところ,-fuzz 10% くらいがよい感じであった。そこで,tcsh コマンドライン上で以下のようにして画像を一括変換した。もちろん,エクスポートしたブログ記事テキストの画像ファイル名拡張子を .png に置換しておかなければならない。

% foreach i (*.jpg)
foreach? set bn=`basename $i .jpg`
foreach? convert -fuzz 10% -fill '#e8e5e1' -opaque '#ffffff' $bn.jpg $bn.png
foreach? end
%

これで上の画像は以下のとおりとなり,背景色に対してごく自然になじむようになった。UNIX のコマンドライン・ユーティリティの文化は偉大である。ただし,白にごく近い色への変更でないとこの方法は通用しない。また,当然ながら画像の #ffffff の白がすべて #e8e5e1 に置き換わるので純白の画面がおとなしくなってしまうだけでなく,文字を含む画像では少し字が瘠せてしまうので,手放しでよいというわけではない。それでも私の目的には充分であった。Web に掲載する画像にドロップシャドウを付ける場合は,白抜きの部分をアルファチャンネル(透過)にして PNG で作成しておくべき — 今回の教訓。

sample

エクスポート・ブログ記事テキストの編集が終わったら,新 MT5 環境でインポートし,再構築して,過去の記事の取り込みは完了した。MT5 にバージョンアップして,1000 を超過した記事のカテゴリ処理もうまく行くようになっただけでなく,再構築がかなり高速になった。

没後150年 歌川国芳展

『没後150年 歌川国芳展』に行って来た。六本木・森美術館。前々から観たいと思いつつ,最終日の今日,なんとか繰り出すことになった。バレーの練習とバレンタインデーのチョコ作りとで忙しい娘は悩んでいたが,国芳観たいってことで私と出かけることになった。

20120212-kuniyoshi-1.png

森美術館は六本木ヒルズ 52 階にある。午后二時過ぎに家を出て 2:50 には森美術館の前に立っていたんだけど,最終日だからか長蛇の列。切符購入,エレベータ移動,さらに入場で一時間を費やした。場内も人だかりでゆっくり眺めて歩くという雰囲気ではなかった。

この展覧会は前期・後期で展示物が入れ代わるタイプで,國芳といえば頭に浮かぶ『相馬の古内裏』は前期の目玉になっていたらしくこの目で拝むことはならなかった。でも,『平知盛亡霊の図』,『通俗水滸伝豪傑百八人之一個』,『讃岐院眷属をして為朝をすくふ図』などの武者絵,関三十郎・瀬川菊之丞・中村芝翫の役者絵,美人子ども十二ヶ月の美人画,絵師の愛した猫・人体をモチーフにした戯画,などなどお目当ての作品をわんさか観ることが出来た。絵を少し習った娘も「この青と緑の中間色,あれいいよね。今日は中味が濃かった」と満足したようである。

次は『國芳もやう正札附現金男 野晒悟助』。髑髏模様の袈裟を羽織った鯔背な侠客を描いている。髑髏を猫で形作った騙し絵を身に纏っているなんてマンガの世界である。

20120212-kuniyoshi-4.png

図録,絵はがきなどを買って帰って来た。

20120212-kuniyoshi-2.png

20120212-kuniyoshi-3.png

二月十日

Facebook を眺めていたら,ロシアのある有名な女流詩人の, Поздравляю нас с днем рождения Александра Сергеевича! なる書き込みに目がとまった。「アレクサンドル・セルゲーエヴィチの誕生日を祝いましょう」。

アレクサンドル・セルゲーエヴィチとは,ロシア文学畑の人々にとっては,ロシア最高の詩人プーシキンのことにほかならない。2 月 10 日は,そうそう,プーシキンが 1837 年,決闘で亡くなった日(ただし,これは現在行われているグレゴリオ暦での話で,帝政ロシア時代の露暦,ユリウス暦では 1 月 29 日であった)である。誕生日?

別の書き込みでやっと合点がいった。2 月 10 日は二十世紀ソヴィエトの大詩人ボリス・パステルナークの誕生日でもある。彼の生誕が奇しくもプーシキンの命日と重なっているので,パステルナークを新しいアレクサンドル・セルゲーエヴィチに喩えたのである。なるほど。

Поздравляю нас с днем рождения Александра Сергеевича!

SyntaxHighlighter 3.0

プログラム・コードを Web ページに掲載する際,コードを pre タグでマークアップするのが一般的ではないだろうか。SyntaxHighlighter を使うと,C/C++ や Java などの予約語をカラー表示したり,行番号を付加したり,重要な行を強調したりできる。これを使用しているプログラマのブログを目にするのは稀ではない。私も使ってみることにした。

いま現在の SyntaxHighlighter 最新バージョンは 3.0.83。ダウンロード・ページから syntaxhighlighter_3.1.0.83.zip を取得して,ローカル計算機で解凍する。解凍してできたディレクトリ下にある compass, scripts, styles, src のディレクトリを Web ドキュメントルート下にコピーすれば,使い始めることができる。ここでは DocumentRoot/sh にインストールしたものとする。

使い方を簡単に説明すると以下のとおりである。SyntaxHighlighter - Installation に書かれているので,きちんとした仕様が知りたい方はそちらを参照のこと。

  1. プログラム・コードを掲載したい HTML(以下「対象ページ」)のヘッダ部に shCore.jsshCore.css を読み込むよう指定する。
  2. 出力様式のタイプがテーマ Theme として管理されており,好みのものを選択して,そのスタイルシートを対象ページに指定する。一覧は SyntaxHighlighter - Themes にある。とくにこだわりがなければ標準 Theme: shThemeDefault.css を指定しておけばよい。私は Eclipse Theme について表示色等カスタマイズしたものを shThemeSite.css として保存して使うことにした。
  3. 対象ページで掲載したいプログラム・コードの言語に応じて SyntaxHighlighter の JavaScript を選択し,それらを対象ページに指定する。JavaScript の一覧は SyntaxHighlighter - Bundled Brushes にある。

上記までで対象ページのヘッダ部は以下のようなものとなる。ここではシェルスクリプト,XML(HTML 含む),Java,C/C++,Perl,JavaScript を扱うものとした。これは SyntaxHighlighter の出力例でもある。

  <link type="text/css" rel="stylesheet" href="/sh/styles/shCore.css" />
  <link type="text/css" rel="stylesheet" href="/sh/styles/shThemeDefault.css" />
  <script type="text/javascript" src="/sh/scripts/shCore.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushBash.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushXml.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushJava.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushCpp.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushPerl.js"></script>
  <script type="text/javascript" src="/sh/scripts/shBrushJScript.js"></script>

プログラム・コードは pre タグでマークアップする。class オプションに SyntaxHighlighter のコンフィギュレーションを指定する。扱う言語種別,行番号表示や開始番号,タブのサイズ,ハイライトすべき行番号,ツールバー表示有無,等である。SyntaxHighlighter - Configuration に指定可能なオプションの説明がある。以下は,Perl 言語コード,開始行番号 1,タブ・サイズ 4,ハイライト行なし,の指定である。title に題名を設定できる。

<pre class="brush: perl; first-line: 1; tab-size: 4; highlight: null;" 
     title="xlstocsv.pl">

first-line: 1; は行番号の開始を 1 とするということである。行番号を出力したくない場合,gutter: false; を指定する。個別に行をハイライトしたいなら,highlight: [2, 4, 9]; のようにすればよい。プログラム・コードのマークアップが済んだら,最後に以下の JavaScript コードを書いておく。おまじないのようなものである。同一ページ内にいくつも SyntaxHighlighter 使用 pre タグを書いていても,最後に一度だけ発行すればよい。

<script type="text/javascript">SyntaxHighlighter.all()</script>

昨日作成した Excel 一括 CSV 出力の Perl コードは,以下のように表示される。

#!/usr/bin/perl -w
# -*- coding: utf-8; mode: cperl; -*-
# Excel to CSV
use strict;
use utf8;
use Spreadsheet::ParseExcel;
use Spreadsheet::ParseExcel::FmtJapan;
binmode STDOUT, ":utf8"; binmode STDERR, ":utf8";
($#ARGV < 0) && die "Usage: $0 xls-file\n";
my $fnm = $ARGV[0]; # Excel file name
my $ct = 0;         # 出力行数
# Excel object
my $xls = Spreadsheet::ParseExcel->new();
my $fmt = Spreadsheet::ParseExcel::FmtJapan->new(); # 日本語あり
my $bko = $xls->parse($fnm, $fmt); # Excel Book object
my @wsa = $bko->worksheets();      # Worksheet object 配列
utf8::decode($fnm);
# ワークシート毎の処理
foreach my $ws (@wsa) {
    my $wsnm  = $ws->{"Name"};
    my $mxrow = $ws->{"MaxRow"};
    my $mxcol = $ws->{"MaxCol"};
    print STDERR "*** $0 $fnm - sheet: $wsnm start.\n";
    if ((defined $mxrow) && (defined $mxcol)) {
        # 行毎の処理
        for (my $row = 0; $row <= $mxrow; $row++) {
            my $line = "";
            # 列毎の処理
            for (my $col = 0; $col <= $mxcol; $col++) {
                # セル値をセット
                my $cell = $ws->get_cell($row, $col);
                $line .= $cell->value() if (defined $cell);
                $line .= ",";
            }
            print "$line\n";
            $ct++;
        }
    }
}
print STDERR "*** $0 from $fnm wrote $ct lines\n";

ただし,オリジナルの shBrushPerl.js では # 文字があるとそれ以降がすべてコメントとみなされてしまうので,$#ARGV など配列末添字番号変数の表示がおかしくなるので,上掲では少し訂正したものを用いた。53 行目の正規表現の先頭に [^\$] を追加し,以下のとおりに訂正した。

this.regexList = [
	{ regex: new RegExp('[^\$]#[^!].*$', 'gm'),	css: 'comments' },
	{ regex: new RegExp('^\\s*#!.*$', 'gm'),	css: 'preprocessor' },

ちょっと使ってみて制約があった。XML のタグのように < > で囲まれたテキストがあるとき,開始タグに対して終了タグがないと,終了タグが最後に強制的に付加されてしまう仕様のようである。空要素タグ < /> の場合も例外ではない。たとえば C/C++ の #include <stdio.h> の行をそのまま記述すると,終了タグを探しても当然ないので,ソースコード表示の最後に </stdio.h> なるゴミが出てしまうのである。私はまだ SyntaxHighlighter を使い込んでいないのでなんとも判断できないが,< > については,これまでどおり実体参照指定(&lt; &gt;)で記述した方がよさそうである。

Excel ワークシートの一括 CSV 変換

漢詩詩語集を処理するに際して,Excel ファイルを CSV に一括変換するツール『Excel 一括 CSV』だと,JIS X 0212 のいわゆる JIS 第三・第四水準の文字の出力が「?」に化けてしまった。これをなんとかしたいと思い別のツールをいろいろ漁ってみたが,これというものが見当たらなかった。UNIX 系システムでの Excel ファイルのテキスト変換では,xlhtml というプログラムが昔から有名であるが,これとて日本語の扱いに関しては不十分なところがある。この調査の過程で,Windows でなくても動作する Perl Excel ファイルハンドリング・モジュール Spreadsheet::ParseExcel の存在を知り,これを使って一括変換ツールを自作することにした。このモジュールは標準ではないので,コマンドラインから cpan -i Spreadsheet::ParseExcel としてインストールする。

Excel は古いバージョンで作成したものだと,ファイル内の日本語文字コードが Shift_JIS なのか CP932 なのか UCS2 なのかよくわからない。おまけに私の目的として,出力すべきファイルは UTF-8 エンコードでなければならない。Spreadsheet::ParseExcelSpreadsheet::ParseExcel::FmtJapan という日本語処理のための下位モジュールを備えている。これらを併用すれば,日本語文字コードに煩わされずにすみ,JIS X 0212 文字もうまく処理でき,かつ UTF-8 で出力できることがわかった。Spreadsheet::ParseExcel のマニュアル(perldoc Spreadsheet::ParseExcel で閲覧できる)に API 解説とサンプルコードがあるので,参照いただきたい。

今回作成した Excel ワークシート一括 CSV 変換プログラム xlstocsv.pl を以下に掲げる。こんなに短いコードでできてしまうのなら,最初から自分の手を動かすべきであった。同じ課題をもつ方は必ずいらっしゃると思うので,以下のコードをコピって活用いただきたい。もちろん無保証,無サポートである。使い方は,コマンドラインで xlstocsv.pl 対象Excelファイル とする。引数に Excel ファイルを一つ指定する。すべてのワークシートが単一 CSV テキストストリームとして STDOUT に書き出される。ファイルに格納したいのならリダイレクトする。

#!/usr/bin/perl -w
# -*- coding: utf-8; mode: cperl; -*-
# Excel to CSV
use strict;
use utf8;
use Spreadsheet::ParseExcel;
use Spreadsheet::ParseExcel::FmtJapan;
binmode STDOUT, ":utf8"; binmode STDERR, ":utf8";
($#ARGV < 0) && die "Usage: $0 xls-file\n";
my $fnm = $ARGV[0]; # Excel file name
my $ct = 0;         # 出力行数
# Excel object
my $xls = Spreadsheet::ParseExcel->new();
my $fmt = Spreadsheet::ParseExcel::FmtJapan->new(); # 日本語あり
my $bko = $xls->parse($fnm, $fmt); # Excel Book object
my @wsa = $bko->worksheets();      # Worksheet object 配列
utf8::decode($fnm);
# ワークシート毎の処理
foreach my $ws (@wsa) {
    my $wsnm  = $ws->{"Name"};
    my $mxrow = $ws->{"MaxRow"};
    my $mxcol = $ws->{"MaxCol"};
    print STDERR "*** $0 $fnm - sheet: $wsnm start.\n";
    if ((defined $mxrow) && (defined $mxcol)) {
        # 行毎の処理
        for (my $row = 0; $row <= $mxrow; $row++) {
            my $line = "";
            # 列毎の処理
            for (my $col = 0; $col <= $mxcol; $col++) {
                # セル値をセット
                my $cell = $ws->get_cell($row, $col);
                $line .= $cell->value() if (defined $cell);
                $line .= ",";
            }
            print "$line\n";
            $ct++;
        }
    }
}
print STDERR "*** $0 from $fnm wrote $ct lines\n";

もしワークシート毎にファイルをばらしたいのなら,foreach my $ws (@wsa) のブロック(ワークシート単位のループ)部分の始めと終わりに独自ファイル処理(「ファイル名+シート名」でファイルハンドルを割り当てる等)を入れればよい。また,このコードは CSV をカンマ区切り形式とし,かつセルの内容に半角カンマが含まれない前提である。もし半角カンマが含まれてもよいようにするなら,得られたセル値を出力時にダブルクォーテーションマークで括る,等の処置を施していただきたい。

これで,一つの Excel ファイルに夥しいワークシートがあろうが,いちいち Excel を開いて CVS として保存しなくても,コマンドラインから一発で CSV 変換ができるようになった。おまけに FreeBSD でも Linux でも Mac OS X でも動くのである。もちろん Windows でも動くし,Excel がインストールされていなくても Excel ファイルを処理出来る,というメリットがある。

私の課題 — いただいた詩語集の処理に関しては,71 の Excel ファイルそれぞれに何十ものワークシートがあって,Excel をひとつひとつ開いてワークシート毎に CSV エクスポートするのはとてもやっていられないが,xlstocsv.pl を使えば次のコマンド操作だけで二字詩語,三字詩語の CSV(2c.csv, 3c.csv)をまとめることができた。

% foreach i (2c-xls/*.xls)
foreach? set BS=`basename $i .xls`
foreach? (xlstocsv $i > 2c-$BS.csv) >& 2c-$BS.err
foreach? end
% foreach i (3c-xls/*.xls)
foreach? set BS=`basename $i .xls`
foreach? (xlstocsv $i > 3c-$BS.csv) >& 3c-$BS.err
foreach? end
% env LC_ALL=C sort 2c-*.csv | uniq > 2c.csv
% env LC_ALL=C sort 3c-*.csv | uniq > 3c.csv

xlstocsv.pl は引数に Excel ファイルを一つしか指定できないが,複数の Excel ファイルを扱いたいなら,上の tcsh 操作のようにループで回せばよい。次のシェルスクリプトは,カレントディレクトリにあるすべての Excel ファイルを,「xls と同じファイルベース名+拡張子 csv」という名の CSV ファイルに書き出す。

#!/bin/sh
for i in *.xls
do
    BS=`basename $i .xls`
    xlstocsv.pl $BS.xls > $BS.csv
done

misima 漢詩・詩語集拡充

misima 漢詩作成支援の詩語検索機能で参照する詩語データベースを大幅に拡充した。ここのところ,この漢詩プログラム改造に取り組んでいた延長で,詩語のデータベース拡充のための元ネタがないか探していたら,今月 2 日,ある方から個人的に集めた詩語集をいただいた。入手した Microsoft Excel 形式のファイルを CSV に変換して行数を確認すると,なんと 8 万行もある。長年の労苦が偲ばれる素晴らしい成果をいただいた次第である。これは早速データベースにして,提供してくれた方に使ってもらわないとバチが当たる。昨夜と今日のお休み一日かけて,これに取り組んだのである。

いくつものシートに分けられた Excel データを CSV データに変換するのに,『Excel 一括 CSV』を使わせてもらった。妻の Windows 7 で変換したのち,Mac OS X にコピーして,ここからいくつかプログラムを書いて既存のデータベースにマージした。主な作業手順は以下のとおり。

  • 文字コードを UTF-8 に変換(nkf 使用)。
  • CSV データをフォーマット編集(新規作成ツール)
  • 平仄付加(既存ツール)
  • 詩語を旧字体変換して増幅(既存ツール)
  • 新旧両字体で検索してもヒットするよう韻字カラム調整(新規作成ツール)
  • 重複排除(既存ツール)
  • SQLite3 import
  • FreeBSD サーバ Tomcat 環境への辞書・プログラムのデプロイ(配備・インストール)
  • 読み,意味,出典も詩語検索の結果に出力する処理(JavaScript)も追加した。旧字体変換を入れたので,韻字の検索で新旧字体のどちらを指定してもヒットするようデータ構造,クエリを変えた。例えば,韻字が「仏」・平仄「○-●」の検索で,「銅佛」が出て来るようにした。
     

    20120204-sigo.png

    これまでの詩語データベースは,ネットで漁った『唐詩選』の漢詩データ 470 首程度を Perl HTML::TreeBuilder パーサで処理して二字,三字の詩語に分解し,この結果得た約 4000 語程度しか蓄積してなかった。これがいまや二字 33601 語,三字 53657 語,計 87258 語にまで拡張された。まだまだ使い勝手としては疑問が残るけれども,詩語ブラウザとしてはそれなりの規模が備わったのではないかと思う。

    今回作業で使用した『Excel 一括 CSV』ツールでは,残念ながら JIS 第三・第四水準の漢字が文字化けしてしまった。データベースにはこれらは含めなかった。これらを反映するのが次なる課題である。

    Moon Calendar

    Profile

    ISAO YASUDA。システムエンジニア。神奈川県在住。昭和 30 年代を懐かしむオヤジ。ロシアに興味があります。
    [more], [About our site]

    Notice

    この文書はフィクションであり,実在する個人,団体等とは一切関係ありません。

    R-18 指定サイトです。そのうち「18 歳以上ですか」の認証を入れる予定です。

    文書の記述内容は無保証です。不適切な表現があればコメントにてご指摘ください。

    コメント,トラックバックは,現在,運用を停止しています。ご意見等ありましたら isao@yasuda.homeip.net 宛電子メールにてお願いします。

    Links

    Entries

    About this archive

    Entries at 2012年2月 in chronological order.

    Previous: 2012年1月

    Next: 2012年3月

    Recent Entries in Main Index.
    All Entries in Archive Index.

    May 2012

    Sun Mon Tue Wed Thu Fri Sat
        1 2 3 4 5
    6 7 8 9 10 11 12
    13 14 15 16 17 18 19
    20 21 22 23 24 25 26
    27 28 29 30 31    

    Emacs: Monthly Archives

    Powered by Movable Type 5.12 Powered by FreeBSD 8.2-RELEASE
    blog counter