HTML Tidyで整形

はじめに

このサイトはxtを使用してxmlからxhtmlに変換して公開している.ブラウザで見る分には支障ないのだが,ソースとしてみた場合,xtの作成するxhtmlは決して見やすいものではない.整形するためのperlスクリプトを作ってみたのだが,なかなかきれいには整形できない.HTML Tidy(以下tidy)はこの悩みを解消してくれそうだ.

インストール

Windowsで動くバイナリもあるが,普段cygwinを使っているのでcygwin版をインストールする.cygwinからsetup.exeをダウンロードして起動する.tidyはカテゴリTextの中にある.デフォルトではインストール対象から外れているので,インストール対象に変更する.後はsetup.exeがうまい具合にやってくれる.

コマンドラインで動かしてみる

トップページのindex.htmlをサンプルにして動かしてみた.index.htmlの文字コードに合わせてオプションに-utf8,インデントをしてほしいので-iをつけた.なお,-hでヘルプを見ることができる.

$ tidy -utf8 -i index.html
Info: Document content looks like HTML 3.2
No warnings or errors were found.
        

index.htmlはxhtml1.1なのだが,html3.2になってしまっている.xml(xhtml)に見てくれるように-xmlを指定した.

$ tidy -xml -utf8 -i index.html
No warnings or errors were found.
        

エラーにはなっていない.しかし,標準出力に出てくるはずの処理結果が出てこない.もっと簡単な別ファイルを作って試してみた.

$ cat sam.html
<html>
<head>
<title>sample</title>
</head>
<body>
this is sample
</body>
</html>

$ tidy -xml -i sam.html
No warnings or errors were found.

<html>
  <head>
    <title>sample</title>
  </head>
  <body>this is sample</body>
</html>
        

うまく動いている.

文字コードなのだろうか?試しに,imdkcvを使って文字コードをシフトJISにしてみた.

$ imdkcv -sn index.html|tidy -shiftjis -xml -i
Info: Doctype given is "-//W3C//DTD XHTML 1.1//EN"
No warnings or errors were found.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xml:lang="ja" xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type"
    content="text/html; charset=utf-8" />
    <meta http-equiv="Content-Script-Type"
    content="text/javascript" />
    <meta name="Generator"
    content="written by xyzzy. transformed by XT" />
    <meta name="wwwc" content="2003.7.13 歳時記・リンク更新" />
    <link rel="stylesheet" type="text/css"
    href="css/tabs_in_top.css" />
    <link rel="stylesheet" type="text/css" href="css/top.css" />
    <link rel="start" href="index.html" />

---以下省略---
        

うまくいった.

utf8とutf8nの違いなのだろうか.xmlファイルはutf8nで書いている.utf8だとxtがうまく動かなかったからだ.xtはutf8nならうまく動いた.xtが対応しているutf8はutf8ではなくutf8nのようだ.

tidyはutf8nではうまく動かない.tidyが対応しているutf8はutf8nではなくutf8のようだ.utf8nではうまく動かない.ややこしい.とりあえず,シフトJISに変換すると何とかなるので,良しとしよう.ビバ,シフトJIS.

preタグと改行

これで終わりかと思ったが,<pre>タグで囲った内容が一行おきに出てきてしまうことに気がついた.

$ imdkcv -sn _tidy.html|tidy -shiftjis -xml -i
No warnings or errors were found.

<?xml version="1.0"?>
<html>
  <head>
    <title>sample</title>
  </head>
  <body>this is sample
  <pre>

this is pre line1.

this is pre line2.

</pre></body>
</html>
        

imdkcvにオプション-aを指定して,改行コードを0x0a(lf)だけにしたら解決した.

$ imdkcv -sn -a _tidy.html|tidy -shiftjis -xml -i
No warnings or errors were found.

<?xml version="1.0"?>
<html>
  <head>
    <title>sample</title>
  </head>
  <body>this is sample
  <pre>
this is pre line1.
this is pre line2.
</pre></body>
</html>
        

できあがったファイルの改行コードは0x0aのはずなのだが,エディタ(xyzzy)で見ると,crlfになっている.よく分からない.実害がないのでとりあえず放置.

ant用タスクの修正

xmlからxhtmlへの変換はantを使って行っている.antで使うxmlファイルの該当部分にimdkcvとtidyを使った整形を追加する.パイプの使い方が分からないので,データの受渡しには一時ファイルを使っている.

<target name="format">
  <tempfile property="sjis"/>
  <tempfile property="utf8"/>

  <!-- シフトJISに変換 -->
  <exec executable="imdkcv" output="${sjis}">
    <arg line="-sn -a '${html}'"/>
  </exec>
  <!-- 整形 -->
  <exec executable="tidy"> 
    <arg line="-xml -shiftjis -wrap 500 -im '${sjis}'"/>
  </exec>
  <!-- utf8nに変換 -->
  <exec executable="imdkcv" output="${utf8}">
    <arg line="-u8 '${sjis}'"/>
  </exec>
  <!-- 微調整 -->
  <exec executable="perl" output="${html}">
    <arg line="'${xsl.path}/format.pl' '${utf8}'"/>
  </exec>

  <delete file="${sjis}"/>
  <delete file="${utf8}"/>
</target>
        

xmlをxhtmlに変換するためのxmlファイルは,共通で使うbase.xmlと個別に用意したbuid.xmlを作成している.あんまり格好の良い物ではないが,付録として全リストをつけた.

おわりに

今回はtidyを整形にしか使っていない.これ以外にも様々な機能があるのだが,それについては,以下に挙げた参考サイトを参照してほしい.そちらのほうがずっと詳しい.

参考サイト

HTML Tidy Project Page
本家.Windowsで動くバイナリもあり.
プログラマー天国
プログラマーのためのHTML Tidy入門を読めば,一通り使えるようになれる.
cygwin
Windowsで動くunix環境.
いまでぃのホームページ
文字コード変換プログラムimdkcvがダウンロードできる.今回も大活躍.

付録

<!-- base.xml -->
<project name="xml2html" default="xslt" basedir=".">
  <property environment="env"/>
  <property name="xt.path" value="${env.PROGRAMFILES}/xt"/>
  <property name="xsl.path" value="${user.home}/My Documents/web/xsl"/>
  <property name="htmllint" value="${env.PROGRAMFILES}/htmllint/htmllint"/>

  <target name="up-to-date">
    <uptodate property="isIndexUpToDate" targetfile="${html}">
      <srcfiles dir="." includes="${xml}"/>
      <srcfiles dir="${xsl.path}" includes="${xslt} framework.xsl"/>
    </uptodate>
  </target>

  <target name="xslt" depends="up-to-date" unless="isIndexUpToDate">
    <java classname="com.jclark.xsl.sax.Driver" failonerror="true">
      <classpath>
        <pathelement path="${xt.path}/xt.jar;${xt.path}/lib/xp.jar;${xsl.path};."/>
        <pathelement path="${java.class.path}"/>
      </classpath>
      <arg line="${xml} '${xsl.path}/${xslt}' ${html}"/>
    </java>

    <antcall target="format"/>
    <antcall target="htmllint"/>
  </target>

  <target name="format">
    <tempfile property="sjis"/>
    <tempfile property="utf8"/>

    <exec executable="imdkcv" output="${sjis}">
      <arg line="-sn -a '${html}'"/>
    </exec>
    <exec executable="tidy" failonerror="true">
      <arg line="-xml -shiftjis -wrap 500 -im '${sjis}'"/>
    </exec>
    <exec executable="imdkcv" output="${utf8}">
      <arg line="-u8 '${sjis}'"/>
    </exec>
    <exec executable="perl" output="${html}">
      <arg line="'${xsl.path}/format.pl' ${utf8}"/>
    </exec>

    <delete file="${sjis}"/>
    <delete file="${utf8}"/>
  </target>
  
  <target name="htmllint">
    <tempfile property="lint.result"/>

    <exec executable="perl" output="${lint.result}">
      <arg line="'${htmllint}' ${html}"/>
    </exec>
    <exec executable="imdkcv">
      <arg line="-sn '${lint.result}'"/>
    </exec>

    <delete file="${lint.result}"/>
  </target>

  <target name="clean">
    <delete>
      <fileset dir="." includes="*.html"/>
    </delete>
  </target>
</project>

<!-- build.xml -->
<project name="xml2html" default="index" basedir=".">
  <property name="root" value="."/>

  <target name="index">
    <ant antfile="${root}/base.xml">
      <property name="xslt" value="top.xsl"/>
      <property name="xml"  value="index.xml"/>
      <property name="html" value="index.html"/>
    </ant>
  </target>

  <target name="all" depends="index"/>

  <target name="clean">
    <ant antfile="${root}/base.xml" target="clean"/>
  </target>
</project>