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;

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