13 March 2010

(Perl) HTML::TreeBuilderでブール値属性が省略されて、XHTML準拠にならない件

HTML::TreeBuilderを使ってHTMLファイルを整形出力するときに、ブール値属性が省略されてしまい、XHTML準拠の出力にならない。

検証コード


use utf8; # スクリプト内utf8処理
binmode STDOUT,":utf8";
use HTML::TreeBuilder;
use HTML::Entities;
use Encode;
use Encode::Guess;

my $body = "";

# ファイルからHTMLデータを読み込む
open(fi, "< sample.html") or &sub_error_exit($!);
my $datBuffer = "";
while(read(fi, $datBuffer, 2048)){
$body .= $datBuffer;
}
close(fi);

# 文字コードをUTF-8に変換する
my $encode = guess_encoding($body, qw/ euc-jp shiftjis 7bit-jis /);
$body = decode($encode->name, $body) unless (utf8::is_utf8($body));

my $tree = HTML::TreeBuilder->new;
$tree->eof();

# HTMLソースコードを画面出力
print "<pre>\n";
print encode_entities($tree->as_HTML('<>&', "\t", {}), '<>&');
print "</pre>\n";

# ツリーを削除
$tree = $tree->delete;

で、このままだと次のように、W3C XHTML規格文書 『 4.5 属性最小化 』 および 『 C.10 ブール値属性 』 に定められている条件を満たせない。


<input checked name="CheckBox1" type="checkbox" />

ではなく、規格に沿うように次のようにしなければならない


<input checked="checked" name="CheckBox1" type="checkbox" />

で、解決方法。

属性名と属性値が同じ場合、HTML::Tagset モジュールソースコードで定義されている、次の部分のハッシュで指定されている属性名がブール値属性として認識されるようだ。


%boolean_attr = (
# TODO: make these all hashes
'area' => 'nohref',
'dir' => 'compact',
'dl' => 'compact',
'hr' => 'noshade',
'img' => 'ismap',
'input' => { 'checked' => 1, 'readonly' => 1, 'disabled' => 1 },
'menu' => 'compact',
'ol' => 'compact',
'option' => 'selected',
'select' => 'multiple',
'td' => 'nowrap',
'th' => 'nowrap',
'ul' => 'compact',
);

つまり、このハッシュを「上書き消去」してしまえば、(他に影響が出ないならば)問題解決である。

この方針に従って、実証コードを修正する。


use utf8; # スクリプト内utf8処理
binmode STDOUT,":utf8";
use HTML::TreeBuilder;
use HTML::Entities;
use Encode;
use Encode::Guess;
use HTML::Tagset ();

~ 略 ~

# 文字コードをUTF-8に変換する
my $encode = guess_encoding($body, qw/ euc-jp shiftjis 7bit-jis /);
$body = decode($encode->name, $body) unless (utf8::is_utf8($body));

my $tree = HTML::TreeBuilder->new;

# ブール値属性のハッシュ値を削除する
*HTML::Element::boolean_attr = \%HTML::Tagset::boolean_attr;
foreach my $key (keys %HTML::Element::boolean_attr){
delete $HTML::Element::boolean_attr{$key};
}


$tree->eof();

# HTMLソースコードを画面出力
print "<pre>\n";
print encode_entities($tree->as_HTML('<>&', "\t", {}), '<>&');
print "</pre>\n";

# ツリーを削除
$tree = $tree->delete;

赤で示した部分を追加している。 これで、とりあえずブール値属性が勝手に省略されないようになった。