w3m で禁則もどき

w3m で禁則をしたいと思う人は多いらしい。

動機は人それぞれだと思うが、僕の場合はメールマガジンの発行であった。メールマガジンを作成するときに HTML 形式から整形できると非常に楽である。というのは、XML で元データを記述して、XSLT で HTML 変換しているからであった。元の XML データに XHTML タグが紛れ込んでいるので、いいかげんな整形ではテーブルの表示に困る。 あと、HTML でもテキストでも配信する、といった場合に HTML メールとテキストメールの2回作成しなくていい、とかいう副作用もある(これは利用するつもりはあんまりない)。

ということで、テキストブラウザを使って HTML 整形をしてやろうと思い立った。ブラウザの出力をファイルに書き出せばそのままテキスト整形されたメールマガジンの完成という寸法である。世に有名なテキストブラウザは lynx であるが、あまりこういう処理向きではない(できなくはないが)し、禁則もちゃんとできない。なら日本発のテキストブラウザ w3m ならできるかとおもったら、けっこういい感じなのだが、禁則だけがやっぱりちゃんとできないのであった。

インターネットで検索してみると、どうもテーブル整形と禁則のからみがあって難しい様子。むかし禁則がインプリされた時期もあるのだが、どうもブラウジング時のテキスト検索と相性が悪く、没になってしまったようだ。 僕の場合はメールマガジンなので、テキスト検索なんて関係ないわけで、ともかく禁則つかわせろという気分であったが、古い実装を使う気もしないし、近い将来に実装される気配もない。

そこで、Perl Script を書いてみた(いっつもこのパターンやな)。要するに、切ってほしくない部分の前後に <nobr> ... </nobr> を挿入するだけである。この nobr というタグ、この範囲内で自動改行させないという機能で、もとは古い Netscape の独自タグなのだが、w3m はこのタグに対応していて、これで囲ってやれば改行されない。結果的に、追い込み型(前の行の行末の文字を次の行の行頭にもってくる)禁則ができるという按配である。HTML4.01-的には、改行してほしくないところに &zwj; (zero width joiner) を入れたほうがいいはずなんだが、w3m では非対応の様子なので、だめでしょう。

作ったスクリプトは sjis のみ対応である。漢字の途中に <nobr> が挿入されないよう、あと一度挿入した <nobr> をもう一度変換しないように気を配っているので、なにやらわけわからなくなっているが、たいしたことはしていない。カッコの種類とかもやたら少ないが、用途に応じて適当に改変してほしい。

あとは、この程度の機能でも w3m が標準装備していてくれればいいのになー、と思うだけである。できれば tty で CSS 完全対応のブラウザにならないかなー。w3m がむりでも gecko エンジンの応用でさくっとできたりしないかなー。

#!/usr/bin/perl
# HTML に <nobr> 挿入して w3m で禁則もどき

# use Jcode;

while(<>)
{
# 漢字コード変換してもよし
#   $_ = new Jcode($_)->sjis;

# <nobr> ... </nobr> 挿入
    while( s/
        ^
            (
                (?: [\x80-\xff][\x40-\xff]
                |   [\x00-\x3b\x3d\x3f-\x7f]
                |   <(?:[^n]|n[^o]|no[^b]|nob[^r])[^<>]+)>
                |   <nobr>[^<>]+<\/nobr>
                )*
            )
            (
                [\x80-\xff][\x40-\xff]
                (?:。|、|)|」|』|】|\)|})
            )
        /$1<nobr>$2<\/nobr>/x) 
        { ; }
    while( s/
        ^
            (
                (?: [\x80-\xff][\x40-\xff]
                |   [\x00-\x3b\x3d\x3f-\x7f]
                |   <(?:[^n]|n[^o]|no[^b]|nob[^r])[^<>]+)>
                |   <nobr>[^<>]+<\/nobr>
                )*
            )
            (
                (?:「|(|{|『|【|{|\()
                [\x80-\xff][\x40-\xff]
            )
        /$1<nobr>$2<\/nobr>/x )
         { ; }
# <nobr> ... </nobr> がすでにある場合に伸ばす (ex. "<nobr>(株</nobr>)" → "<nobr>(株)</nobr>" )
    while( s/
        ^
            (
                (?: [\x80-\xff][\x40-\xff]
                |   [\x00-\x3b\x3d\x3f-\x7f]
                |   <[^<>]+>
                )*            )
            <\/nobr>
            (
                (?:。|、|)|」|』|】|\)|})
            )
        /$1$2<\/nobr>/x ) 
         { ; }
    while( s/
        ^
            (
                (?: [\x80-\xff][\x40-\xff]
                |   [\x00-\x3b\x3d\x3f-\x7f]
                |   <(?:[^n]|n[^o]|no[^b]|nob[^r])[^<>]+)>
                |   <nobr>[^<>]+<\/nobr>
                )*
            )
            (
                (?:「|(|{|『|【|{|\()
            )
            <nobr>
        /$1<nobr>$2/x )
         { ; }
    print;
}