ごくごく簡単なXHTML 1.1のDTDの説明

XHTML 1.1はモジュール化され、他の名前空間(タグセット)からもそのモジュールを利用できるように設計されているため、DTD(文書型定義)の読み方が多少複雑になっています。XHTMLのモジュールには抽象モジュールが用意されているので、内容モデルなどについてはそちらを参照すれば把握できますが、せっかくなので、DTDそのものを読むための基礎知識も整理してみましょう(DTDの基本的な読み方は、ごくごく簡単なDTDの説明を参照してください)。

モジュール化DTDのパラメータ実体名のルール

モジュール化されたDTDを見てとっつきにくく感じる要因の一つは、ほとんどの定義が実体参照を用いてなされ、しかもその名前に.qnameだとか.classだとかの見慣れぬ接尾辞(拡張子)がついているところにありそうです。まず最初に、この実体参照の名前付けルールについて確認しておきましょう。XHTMLモジュール化仕様の付録D.1では、パラメータ実体を7つのカテゴリーに分け、それぞれに固有の接尾辞を付けるとしています。

パラメータ実体の接尾辞のルール
接尾辞カテゴリーと意味
.mod ひとつのDTDモジュールを示すときに用います。多くの場合、一つのファイルを構成します。
.module あるDTDモジュールを採用する(取り込む)かどうかをコントロールするキーワード(INCLUDEもしくはIGNORE)として用います。条件マーク区間の条件として利用します。
.qname 名前空間修飾された要素タイプ名を示します。全ての要素タイプ宣言はこの実体参照を用いて行われます。
.content 内容モデルを示します。要素タイプ宣言では、内容モデルをこの実体参照により表現します。
.class 要素タイプのグループ(クラス)を示します。HTML4でいうインライン要素、ブロックレベル要素などを表現するために用います。
.mix 異なるクラスから要素タイプを集めたグループを示します。
.attrib 属性リスト宣言で用いる、一つ以上の属性定義を示します。

モジュールと名前空間

XHTMLのモジュールは、XHTML 1.1やXHTML Basic以外にも、さまざまなマークアップ言語で使われることを前提にしています。その場合、それぞれのモジュール(のグループ)の名前空間を示す手段を、DTDや文書インスタンスで適切に指示できるだけの汎用性を備えなければなりません。このためDTDモジュールは、単純に(X)HTMLだけを想定するDTDに比べ、かなり複雑な仕組みを導入しています。

XHTMLに組み込む場合は、これらの機能はほとんど無効化され空白文字列になるので、最終的には無視してしまえばいいのですが、何段階にも仕掛けが施されていて目がくらむかも知れません。概要を示しておきましょう。

  1. まず、仕様書の付録C.2で示されているXHTML 1.1 Driverの先頭付近に次の実体宣言が出てきます。

    <!ENTITY % NS.prefixed "IGNORE" >
    <!ENTITY % XHTML.prefix "" >
    

    すなわち、NS(名前空間)の接頭辞に関係ある部分は使わない(IGNORE)で、またXHTML(の名前空間)の接頭辞も、空白文字列とするということで、XHTMLに関しては名前空間の接頭辞は原則用いません。要するに、通常は<p>とか<dt>と書くだけでよくて、<xhtml:p>とか<xhtml:dt>とする必要はないということです。なぜ最終的にそうなるのかは、以下のステップで確認します。

  2. XHTMLのモジュールとしての名前空間の扱いは、ドライバで上記の実体宣言後に読み込まれるフレームワーク・モジュール%xhtml-framework.mod;)で定義されています。ここのSection Aが、名前空間関連の枠組みを定義する部分です(flat.dtdでは440行目あたりから)。主要な部分を見てみましょう。

    <!ENTITY % NS.prefixed "IGNORE" >
    <!ENTITY % XHTML.prefixed "%NS.prefixed;" >
    <!ENTITY % XHTML.xmlns  "http://www.w3.org/1999/xhtml" >
    <!ENTITY % XHTML.prefix  "" >
    <![%XHTML.prefixed;[
    <!ENTITY % XHTML.pfx  "%XHTML.prefix;:" >
    ]]>
    <!ENTITY % XHTML.pfx  "" >
    

    1、4行目のNS.prefixedXHTML.prefixはドライバでも宣言されていました。異なるタグセットからXHTMLモジュールを利用する場合は、ドライバや文書インスタンスの文書型宣言で異なる宣言をすることにより、モジュールの定義を上書きし、名前空間接頭辞の使い方を変更できるわけです(XHTML 1.1の場合はいずれも同じ値なので影響はありません)。

    2行目のXHTML.prefixedが、モジュールごとの名前空間の扱いをコントロールするキーワードです。通常はNS.prefixedと同じであり、この場合はIGNOREとなります。5〜8行目でこのキーワードを使ってXHTML.pfxを定義しています。ここではマーク区間がIGNOREされるのでXHTML.pfxは "" となります(これは、以下に見るように、XHTML 1.1では名前空間接頭辞を用いないことを意味します)。

  3. このXHTML.pfxは、Section Bで各要素タイプの名前空間修飾名を定義するときに登場します。

    <!ENTITY % address.qname "%XHTML.pfx;address" >
    

    ここで、XHTML.pfxは空白文字列であることから、%address.qname;addressそのものであることが分かります。この先、各地で%element.qname;という実体参照が出てきますが、XHTML 1.1の場合は、全て従来のelementそのものと解釈できます(別の言語に組み込まれて、NS.prefixedXHTML.prefixが異なる値で宣言されていれば、もちろんこの実体の解釈も違ってきます)。

〔補足〕

各要素タイプの名前空間修飾名を定義するパラメータ実体宣言は、それぞれのモジュール内にも記述されていて、たとえばaddress要素タイプについてはTextモジュールに

<!ENTITY % address.qname  "address" >

という部分があるのですが、上記のフレームワーク・モジュールSection Bの宣言が優先するため、これはXHTML 1.1の定義としては意味を持ちません。Textモジュールでこの実体宣言を見つけて「%address.qname;はaddressである」と考えるのは、結果的には同じことでも、DTDの読み方としては正しくないので注意してください(NS.prefixedXHTML.prefixによっては、%address.qname;の実体はxhtml:addressとなることもあり得ます)。

〔以上補足〕

〔参考〕名前空間接頭辞の扱いの変更:XHTML 1.1 plus MathML 2.0の例

*この節の説明はXHTML 1.1の範囲を超えるので、お急ぎの方、概略だけ読みたい方は先に進んでください

パラメータ実体を変更して名前空間の接頭辞をコントロールする方法を、MathMLを例に見てみましょう。MathMLをXHTML 1.1とともに使用するためのDTDが、MathML 2.0の仕様書で示されています。その中で、名前空間をコントロールする部分を少し抜粋します。

<!ENTITY % NS.prefixed     "IGNORE" >
<!ENTITY % MATHML.prefixed "%NS.prefixed;" >
<!ENTITY % MATHML.prefix   "m" >
<![%MATHML.prefixed;[
<!ENTITY % MATHML.pfx  "%MATHML.prefix;:" >
]]>
<!ENTITY % MATHML.pfx  "" >

<!ENTITY % mrow.qname      "%MATHML.pfx;mrow" >

このDTD(モジュール)では、MathMLの要素タイプ名は最後の行にあるように%MATHML.pfx;mrowという形で、%MATHML.pfx;部分が名前空間接頭辞に対応するパラメータ実体になっています(XHTMLの場合の%XHTML.pfx;に相当)。通常の状態では%MATHML.prefixed;がIGNOREなので、MathMLの要素もXHTMLの要素と同様、接頭辞なしで使用するようになってます。

(例)

<p>次の数式を見てください:</p>

<math>
 <mrow>
  <mi>a</mi>
  <mo>+</mo>
  <mi>b</mi>
 </mrow>
</math>

ここで、MathMLの要素だけは、XHTMLの要素と区別するために名前空間を明示したいとしましょう。ドライバもしくは文書インスタンスで次のような実体宣言を行います。

(例)

<!ENTITY % MATHML.prefixed "INCLUDE" >

これによってモジュール内の宣言(2行目)が上書きされ、MATHML.pfxは、5行目で示されるようにデフォルトの接頭辞である%MATHML.prefix;':'の組み合わせ、すなわちm:となります。その結果、%mrow.qname;m:mrowに、つまりMathMLの要素はm:という接頭辞付きで記述するようになります。

(例)

<p>次の数式を見てください:</p>

<m:math>
 <m:mrow>
  <m:mi>a</m:mi>
  <m:mo>+</m:mo>
  <m:mi>b</m:mi>
 </m:mrow>
</m:math>

この場合、ルート要素でxmlns:m="..."としてMathMLの名前空間を接頭辞m:に結びつけておく必要があります。

〔以上参考〕

ドライバと内容モデル・モジュール

XHTMLのモジュールを集めてひとつのDTDを構成するためには、どのモジュールを使うのかを示すドライバと、各モジュールで定義されている要素タイプの親子関係を示す内容モデル・モジュールが必要になります。XHTML1.1の場合は、仕様書の付録C.2がドライバ、C.3が内容モデル・モジュールになっています。文書インスタンスの文書型宣言で指定するDTDは、このドライバになります。

ドライバによるモジュールの組み込み

たとえば、Listモジュールを組み込むために、XHTML 1.1のドライバは次のような指定を行っています。

<!ENTITY % xhtml-list.module "INCLUDE" >
<![%xhtml-list.module;[
<!ENTITY % xhtml-list.mod
     PUBLIC "-//W3C//ELEMENTS XHTML Lists 1.0//EN"
     "http://www.w3.org/TR/xhtml-modularization/DTD/xhtml-list-1.mod" >
%xhtml-list.mod;]]>

このページ冒頭のモジュール化DTDのパラメータ実体名のルールで紹介したように、.moduleという接尾辞をもつ実体は、モジュールを組み込むかどうかをコントロールするキーワードになります。回りくどいようですが、こうやっていったんモジュール組み込みのためのパラメータ実体を(INCLUDEとして)定義した上で、具体的なモジュール定義のあるファイル(URI)を.modという接尾辞をもつパラメータ実体として表し、その直後にその実体を参照する(ファイルの内容を取り込む)ことで、このモジュールをDTDに組み込んでいます。

モジュール内での要素タイプの宣言

ドライバを見ただけでは、どのモジュールを使うかということまでしか分かりません。具体的な要素タイプの宣言などは、組み込まれるモジュールそのものを調べる必要があります。このListモジュールでは、たとえばdt要素タイプが

<!ENTITY % dt.element  "INCLUDE" >
<![%dt.element;[
<!ENTITY % dt.content
     "( #PCDATA | %Inline.mix; )*"
>
<!ELEMENT %dt.qname;  %dt.content; >
]]>

と定義されます。.elementという接尾辞は上記の7つの分類には出てきませんでしたが、すぐ見てわかるように、その要素タイプを含めるかどうかを指定するキーワードとしてのパラメータ実体です。

6行目の要素タイプ宣言は、%dt.qname;というパラメータ実体が使われています。上で見たとおり、これはdtそのものですね。そしてdt要素タイプの内容モデルを示す%dt.content;は、3〜5行目で宣言されているパラメータ実体です。つまり、ここでの要素タイプ宣言は、実質的には

(例)

<!ELEMENT dt  "( #PCDATA | %Inline.mix; )*" >

ということになります。ここで登場する%Inline.mix;はListモジュール内では定義されておらず、内容モデル・モジュールを参照する必要があります。

内容モデル・モジュールの定義

モジュール化されたDTDでは、具体的なマークアップ言語(XHTML 1.1など)にどのモジュールが組み込まれるかは事前には分からないので、文書全体の内容モデルは「内容モデル・モジュール」として言語ごとに定義しなければなりません。XHTML 1.1の場合、このモジュールは仕様書の付録C.3に示されていますが、ドライバからは%xhtml-framework.mod;を通して%xhtml-model-1.mod;が呼び出されます。この中では、

<!ENTITY % InlStruct.class "%br.qname; | %span.qname;" >
<!ENTITY % InlPhras.class
     "| %em.qname; | %strong.qname; | %dfn.qname; | %code.qname;
      | %samp.qname; | %kbd.qname; | %var.qname; | %cite.qname;
      | %abbr.qname; | %acronym.qname; | %q.qname;" >
<!ENTITY % InlPres.class
     "| %tt.qname; | %i.qname; | %b.qname; | %big.qname;
      | %small.qname; | %sub.qname; | %sup.qname;" >
<!ENTITY % I18n.class "| %bdo.qname;" >

(中略)

<!ENTITY % Inline.class
     "%InlStruct.class;
      %InlPhras.class;
      %InlPres.class;
      %I18n.class;
      %Anchor.class;
      %InlSpecial.class;
      %InlForm.class;
      %Ruby.class;
      %Inline.extra;"
>
<!ENTITY % Inline.mix
     "%Inline.class;
      %Misc.class;"
>

というようなパラメータ実体の連鎖が定義され、「インライン要素」とは何であるかが示されるわけです(%em.qname;などの実体はemなど従来の要素タイプ名そのものであることを思い出してください)。

とりあえず読むには

XHTML 1.1に関して要素タイプfooの内容モデルや属性リストを調べるには、まずそのモジュール定義ファイルを開く必要があります。仕様書のアーカイブファイルには全てのモジュールを展開して一つのファイルにまとめたxhtml11-flat.dtdが含まれていますから、これを使うのもいいでしょう。

DTDの中で目的の要素タイプ宣言を見るためには、<!ELEMENT % foo.qname; ...という部分を探します。この直前で<!ENTITY % foo.content ...として定義されているのが、内容モデルです。属性リストは素直に<!ATTLIST % foo.qname; ...を読めば理解できるでしょう。

内容モデルに%bar.qname;というパラメータ実体があれば、それは要素タイプbarを意味します。.class.mixといった接尾辞を持つ実体は、通常「内容モデル・モジュール」で定義されるので、付録C.3を参照して、その定義の連鎖をたどりましょう。

複数のファイルを参照する必要があったり、見慣れぬ定義方法が採られているので最初はとまどうかも知れませんが、XHTML 1.1として扱う限りは、特殊なパラメータ実体もほとんど影響しないので、整理すれば従来のDTDと同じ読み方で十分理解可能です。