HTML からフォーマット化オブジェクト (FO) への変換ガイド

ここに記載する XSLT テンプレートを使用して迅速に HTML 要素から FO へ、さらに FO から PDF へと変換してください

HTML 文書を PDF に変換するための手助けが必要ですか?このリファレンス・ガイドでは、よく使用されている 45 の HTML 要素を取り上げ、XSLT テンプレートを使って (XSL-FO 語彙の) フォーマット化オブジェクトに変換し、XSLT を使用して簡単に PDF に変換できるようにする方法を具体的な例で説明します。記載するサンプル・コードは、読者が Apache XML Project の FOP ツールを使用していることを前提としますが、これらの手法のほとんどは、他の XSL-FO ツールを使用する場合にも有効に機能します。

Doug Tidwell, Cyber Evangelist for developerWorks, IBM

Photo of Doug TidwellDoug Tidwell は、新しい技術によって問題を解決できるように人々を支援している developerWorks のサイバー・エバンジェリストです。彼はこれまで、世界中の何万人もの開発者に Web Services および XML について説明してきましたが、開発者の多くは居眠りすることなく彼の話を聞いています。彼は O'Reilly 出版の『XSLT』の著者であり、同じく O'Reilly 出版の『Programming Web Services with SOAP,』の共著者です。この 2 冊はどちらも、友人や家族に贈るには絶好のプレゼントになります。



2013年 11月 14日 (初版 2011年 8月 05日)

2013年 11月 15日 ― 著者によって記事が更新され、付属のスタイルシートが FOP バージョン 1.1 で動作するように更新されました。また、<fo:page-sequence> 要素と HTML <nobr> 要素の実装が変更されました (「<nobr> 改行なしのテキスト」セクションを参照)。このスタイルシートは、標準的な 3 つの要素 <fo:bookmark-tree><fo:bookmark><fo:bookmark-title> を使用して、PDF ファイル内にブックマークを生成します (「<h1><h6> 見出し」セクションの「ブックマークの生成」を参照)。「ダウンロード」セクションを参照し、スタイルシートの最新バージョンを入手してください。

2011年 8月 05日 ― 著者により、「<ul> 番号なしリスト」セクションの 2 番目に記載されたコードを 2 箇所更新するように要請がありました。その 2 箇所とは、2 つの fo:list-item-body 要素の内容を fo:block 要素でラップし、A Love Supreme<fo:block>A Love Supreme</fo:block> に変更すること、そして The Joshua Tree<fo:block>The Joshua Tree</fo:block> に変更することです。

私たちは画面上での体裁を気にして HTML ページを設計するものですが、これらの Web ページを印刷することに関しては一般に後回しにします。Web ページの印刷可能バージョンを作成するのに最適な手法は、XSLT と XSL-FO を使って PDF を生成することです。そのために使用できるツールには、オープンソースの XSLT プロセッサー、XSL-FO (XSL Formatting Objects) 語彙、そしてフォーマット化オブジェクト・エンジンがあります。XSL-FO および XSLT を扱う方法をすでに知っている読者にとって、このガイドは貴重な資料になります。このガイドでは最も一般的な HTML タグをひと通り取り上げ、それぞれのタグをフォーマット化オブジェクトに変換する方法を明示します (XSL-FO の基本的な使い方については、XSL-FO を話題にした developerWorks チュートリアルを試してください。これらのチュートリアルは「参考文献」で簡単に見つけられます)。

このガイドでは多数のサンプルを記載して、HTML 要素をそれに対応するフォーマット化オブジェクトに変換する XSLT スタイルシートを作成する方法を説明します。フォーマット化オブジェクトは、XSL-FO によってレンダリングされる文書の基本構成要素です。

このガイドで使用する XSLT テンプレートに関する前置きとして、ほぼすべてのテンプレートには以下のテキストが含まれています。

<xsl:apply-templates select="*|text()"/>

上記の要素は XSLT プロセッサーに対し、現在の要素のテキストだけでなく、すべての子要素も取得して変換するように指示します。この再帰的手法により、いかに複雑に HTML 要素同士がネストされているとしても、すべての HTML 要素が確実に処理されます。XSLT および XSL-FO の手法についての詳細は、このガイドの最後にある「参考文献」を参照してください。

サンプル・コードの背景情報

背景情報として、このリファレンス・ガイドで説明するすべての要素が含まれる HTML 文書、everything.html を表示することができます (この HTML ファイルを表示するには、x-xslfo2app-samples.zip をダウンロードしてください)。また、このガイドで取り上げるすべてのテンプレートだけでなく、関連するチュートリアルで説明している高度な手法の大部分が含まれる XSLT スタイルシート xhtml-to-xslfo.xsl (x-xslfo2app-samples.zip の中に見つかります) も表示することができます。このスタイルシートを HTML ファイルで使用するには、以下のコマンドを使用してください。

> fop -xml everything.html -xsl xhtml-to-xslfo.xsl -pdf everything.pdf

The command tells the FOP rendering engine to transform the XHTML file everything.html into formatting objects using the rules in xhtml-to-xslfo.xsl, then generate a PDF file named everything.pdf from those formatting objects.

このコマンドは FOP レンダリング・エンジンに対し、xhtml-to-xslfo.xsl のルールを使用して、XHTML ファイルである everything.html をフォーマット化オブジェクトに変換し、その後、それらのフォーマット化オブジェクトから everything.pdf という PDF ファイルを生成するように命令します。

以下に示すのは、この PDF ファイルのスクリーン・キャプチャーです。

 
everything.pdf サンプル・ファイルのスクリーン・キャプチャー

お望みであれば、everything.pdf (x-xslfo2app-samples.zip の中に見つかります) ファイルを表示することもできます。これにより、レターサイズの用紙に PDF が生成されます。A4 サイズの PDF ファイルを生成するには、上記コマンドの末尾に -param page-size a4 を追加します。


このガイドでは取り上げない HTML 要素

さまざまな理由により (そのほとんどは、もっともな理由です)、このガイドでは取り上げていない HTML 要素がいくつかあります。これらの HTML 要素を除外している第 1 の理由は、PDF ファイル (フォーマット化オブジェクト変換で最も一般的な生成結果) では意味をなさないためです。またいくつかの HTML 要素は、W3C で非推奨とされています。これも、この記事でその要素を除外している正当な理由となっています。これらの HTML 要素のなかには、読者の皆さんそれぞれの PDF のコンテキストのなかでは意味をなす要素もあるかもしれないので、その場合にはご意見をお聞かせください。このガイドに追加するのに正当な理由があれば、その HTML 要素の追加を検討したいと思います (このガイドの終わりにあるフィードバック用のフォームを使用して、皆さんのご意見をお寄せください)。


HTML 要素の変換ガイド

このガイドでは、多数の HTML 要素を XSL フォーマット化オブジェクトに変換する方法を説明します。このガイドをオンラインで表示している場合、以下に記載されたリンクのいずれかをクリックすると、その特定の要素に関する説明に直接ジャンプすることができます。それぞれの HTML 要素ごとに、要素の概要、対応するフォーマット化オブジェクト、HTML を XSL-FO に変換する XSLT テンプレートが記載されています。フォーマット化オブジェクトおよびテンプレートには、処理対象とする HTML と同じく単純明快なものもあれば、非常に込み入っているものもあります。

サンプル・コードではいつものことですが、扱っている事例に固有の選択を行う必要がありました。記載するサンプルでは一貫して、フォーマット化オブジェクトは最終的に PDF に変換するための媒介として使用することを前提としています。そのため、選択した値には無作為に選んだものも多少あるものの、ほとんどの値は、この一連の変換の最終結果である PDF ファイルに定義されたレイアウトに従って選択しました。このレイアウトは、私が developerWorks 用に書き上げた 2 つのチュートリアルで使用したのと同じものです。当然のことながら、皆さんがそれぞれのニーズに合わせてサンプルを適応させる際には、目的とするレイアウトになるような値に置き換えてください。この記事の PDF ファイルのレイアウトに従う必要はありません。

注意する点として、この記事では参照しやすいように、以下のようにアルファベット順で要素を取り上げますが、順に読み進めるには理想的な順序ではありません。例えば、表を作成するための HTML タグのほとんどは T で始まる要素のグループに分類されますが、アルファベット順でリストすると、このグループに title 要素が割り込んできます。


<a name="..."> 名前付きアンカー・ポイント

このガイドでは、3 種類のアンカー要素の変換について説明します。その 1 つは、このセクションで説明する名前付きアンカー・ポイントです。残りの 2 つは、名前付きアンカー参照と、単なるアンカー参照で、どちらもアルファベット順のガイドでは次の 2 つのセクションで説明することになります。3 種類すべてのアンカー要素を実際に変換するサンプル XSLT テンプレートは、3 番目のセクションに記載します。

名前付きアンカーは、<a name="xyz" /> のような形を取ります。この HTML 要素は通常、id が設定された <fo:block> 要素に変換されます。以下に、典型的な変換結果を記載します。

<fo:block id="xyz"/>

簡単に変換できるように見えますが、文書の編成によっては、問題が生じる場合もあります。例えば、前述のチュートリアルのサンプルで使用したスタイルに従うと、HTML の <h1> 要素をレンダリングするには、この見出しテキストの前に横罫線と改ページを挿入しなければなりません。けれども、この位置に改ページを挿入すると、名前付きアンカーで以下のような問題が生じます。

<a name="xslt"/>
<!-- A page break will be inserted here -->
<h1>Using XSLT style sheets</h1>

<h1> が新しいページから始まる場合、名前付きアンカーへのリンクを作成すると、そのリンクは意図に反してユーザーを前のページの終わりに移動させることになります。この状況に対処するには、プロセッサーに HTML 文書内の名前付きアンカーの後に続く要素を調べさせて、その要素が <h1> であれば、名前付きアンカーを無視させるようにします。この場合、無視された名前付きアンカーは、<h1> 要素の XSLTテンプレートで処理します。以下に、改ページの後に見出しが続く場合でも名前付きアンカーを処理するための XSLT ロジックを記載します。

<xsl:template match="a">
  <xsl:choose>
    <xsl:when test="@name">
      <xsl:if test="not(name(following-sibling::*[1]) = 'h1')">
        <fo:block line-height="0pt" space-after="0pt" 
          font-size="0pt" id="{@name}"/>
      </xsl:if>
    </xsl:when> 

following-sibling 軸を指定することによって、スタイルシート・プロセッサーは確実に、名前付きアンカーの後に続く要素をチェックすることになります。名前付きアンカーの後に続く最初の要素の名前が h1 でなければ、スタイルシート・プロセッサーは id を設定した <fo:block> を作成します。また、<fo:block> 要素が line-heightfont-sizespace-after の各プロパティーをゼロに設定していることにも注目してください。これは、可視にならないアンカー・ポイントをレンダリングして縦方向のスペースを無駄にすることがないようにするためです。


<a href="#..."> 名前付きアンカー参照

同じ文書内の別の宛先を参照するアンカー・タグを変換するには、そのタグを <fo:basic-link> 要素に変換し、同じ文書内の参照として internal-destination 属性を指定します。例えば、以下のアンカー要素があるとします。

For more information, see <a href="#chapter1">Chapter 1</a>.

このアンカー要素は、以下の XSL-FO マークアップに変換する必要があります。

For more information, see 
<fo:basic-link color="blue" internal-destination="chapter1">
  Chapter 1
</fo:basic-link>.

HTML アンカー要素に href 属性が設定されている場合には、それがハッシュ・マーク (#) で始まっているかどうかを調べてください。ハッシュ・マークで始まっている場合には、その href 属性を <fo:basic-link>internal-destination 属性として使用します。属性の値を使用するには、ハッシュ・マークを削除しなければなりません。それには、XSLT の substring() 関数を使用します。内部リンクを処理するためにもう 1 つ必要なことは、参照先のセクションに <fo:page-number-citation> を追加することです。

以下に、この変換を行う XSLT テンプレートの一例を記載します。

<xsl:template match="a">
  <xsl:choose>
    <xsl:when test="@name">
    ... The previous entry covered named anchors ...
    </xsl:when>
    <xsl:when test="@href">
      <fo:basic-link color="blue">
        <xsl:choose>
          <xsl:when test="starts-with(@href, '#')">
            <xsl:attribute name="internal-destination">
              <xsl:value-of select="substring(@href, 2)"/>
            </xsl:attribute>
          </xsl:when> 
          <xsl:otherwise>
            ... Handle external links here ...
          </xsl:otherwise>
        </xsl:choose>
        <xsl:apply-templates select="*|text()"/>
      </fo:basic-link>
      <xsl:if test="starts-with(@href, '#')">
        <xsl:text> on page </xsl:text>
        <fo:page-number-citation ref-id="{substring(@href, 2)}"/>
      </xsl:if>
    </xsl:when>
  </xsl:choose>
</xsl:template>

<fo:page-number-citation> 要素は、以下のようなリンクがレンダリングされることを意味します。

For more information, see Chapter 1 on page 73.

<a href="..."> アンカー参照

最後に説明するリンクのタイプは、URI への参照です。これらの参照をチュートリアルのサンプル PDF ファイルにレンダリングするには、<fo:basic-link>external-destination 属性を使用します。例えば、以下のアンカー要素があるとします。

<a href="http://www.ibm.com/developerWorks/">
  IBM's developerWorks Web site
</a>

上記の要素は、以下のマークアップに変換することになります。

<fo:basic-link color="blue" 
    external-destination="http://www.ibm.com/developerWorks/">
  IBM's developerWorks Web site
</fo:basic-link>

以下に、これまで説明した 3 種類すべてのアンカー要素に対処する完全な XSLT テンプレートを記載します。

<xsl:template match="a">
  <xsl:choose>
    <xsl:when test="@name">
      <xsl:if test="not(name(following-sibling::*[1]) = 'h1')">
        <fo:block line-height="0" space-after="0pt" 
          font-size="0pt" id="{@name}"/>
      </xsl:if>
    </xsl:when>
    <xsl:when test="@href">
      <fo:basic-link color="blue">
        <xsl:choose>
          <xsl:when test="starts-with(@href, '#')">
            <xsl:attribute name="internal-destination">
              <xsl:value-of select="substring(@href, 2)"/>
            </xsl:attribute>
          </xsl:when>
          <xsl:otherwise>
            <xsl:attribute name="external-destination">
              <xsl:value-of select="@href"/>
            </xsl:attribute>
          </xsl:otherwise>
        </xsl:choose>
        <xsl:apply-templates select="*|text()"/>
      </fo:basic-link>
      <xsl:if test="starts-with(@href, '#')">
        <xsl:text> on page </xsl:text>
        <fo:page-number-citation ref-id="{substring(@href, 2)}"/>
      </xsl:if>
    </xsl:when>
  </xsl:choose>
</xsl:template>

<address> 連絡先

あまり使われることのない、この HTML 要素は連絡先を定義します。ただし、典型的な連絡先のコンポーネント (電話番号、E メール・アドレス、住所など) は、<address> 要素内では識別されません。<address> 要素は通常、以下のように使用されます。

<address>
  Mrs. Mary Backstayge
  <br />
  283 First Avenue
  <br />
  Skunk Haven, MA  02718
</address>

上記のサンプルで注目すべき点は、<address> 内では <br> 要素を使用して改行を表していることです。これに相当する XSL-FO マークアップは、以下のようになります。

<fo:block>Mrs. Mary Backstayge<fo:block> </fo:block>
283 First Avenue<fo:block> </fo:block>
Skunk Haven, MA  02718</fo:block>

<address> に対応する XSLT テンプレートは非常に単純で、<address> 要素を <fo:block> 要素に変換してから、そこに含まれるテキストとその他すべての要素を処理すればよいのです。そのためのテンプレートを以下に記載します。

<xsl:template match="address">
  <fo:block>
    <xsl:apply-templates select="*|text()"/>
  </fo:block>
</xsl:template>

<b> 太字テキスト

太字要素を変換するのは至って簡単で、単に font-weight="bold" 属性を設定した <fo:inline> 要素に変換すればよいのです。以下に太字要素の一例を記載します。

<p>Jackdaws <b>love</b> my big sphinx of quartz.</p>

このコンテンツをレンダリングするには、<fo:block> および <fo:inline> という基本的な XSL-FO 要素を使用します。

<fo:block>
  Jackdaws <fo:inline font-weight="bold">love</fo:inline>
  my big sphinx of quartz.
</fo:block>

XSL-FO の基礎知識として、<fo:block> 要素を使用すると常に改行が発生しますが、<fo:inline> 要素では改行は発生しないという点を覚えておいてください。したがって、<b> 要素のコンテンツをレンダリングするには <fo:inline> を使用します。以下に記載する単純な XSLT テンプレートがレンダリングに対処します。

<xsl:template match="b">
  <fo:inline font-weight="bold">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<xsl:apply-templates> 要素の select 属性は、<b> 要素のテキストだけでなく、そこに含まれているすべての子要素のテキストも選択することに注目してください。例えば、上記のマークアップが <p>Jackdaws <b><i>love</i></b> ... であるとしたら、<b> の子要素を選択すると、太字要素と斜体要素の両方が処理されることになります。


<big> サイズを大きくしたテキスト

<big> はめったに使用されない HTML 要素です。この要素のなかに含まれるテキストは、周りのテキストよりも多少大きくなります。以下に、この要素の使用例を示します。

<p>Jackdaws <big>love</big> my big sphinx of quartz. </p>

このように簡単に変換できる理由は、XSL-FO のチュートリアルで FO プロセッサーとして使用した FOP ツールが、font-size プロパティーの値としてパーセントの値をサポートするようになったからです (以前はパーセントの値がサポートされているとは限りませんでした)。<big> 要素をレンダリングするのに妥当な値として、font-size は 120% に設定します。

<xsl:template match="big">
  <fo:inline font-size="120%">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

フォントの相対サイズを使用するということは、複数の <big> 要素を互いにネストさせていくと、変換によってレンダリングされるテキストが、HTML のネストされた <big> 要素と同じように、次第に大きくなることを意味します。上記にサンプルとして記載した段落の場合、以下のようなフォーマット化オブジェクトが生成されます。

<fo:block>Jackdaws 
<fo:inline font-size="120%">love</fo:inline> my big
sphinx of quartz. </fo:block>

もちろん、<big> 要素を処理するサンプル・テンプレートは、フォント・サイズをさらに大きくしたり、色を変えたりするなど、望みどおりに変更することができます。


<blockquote> ブロック単位の引用

この例では、ブロック単位の引用文を処理するために、左右に 1.5 センチのインデントを付けた 1 つの段落としてレンダリングします。そのために使用するのは、<fo:block> 要素の start-indent および end-indent 属性です。以下に、<blockquote> 要素の例を記載します。

<blockquote>
  When in the Course of human events, it becomes necessary for one people 
  to dissolve the political bands which have connected them with another, 
  and to assume among the powers of the earth, the separate and equal 
  station to which the Laws of Nature and of Nature's God entitle them, 
  a decent respect to the opinions of mankind requires that they should 
  declare the causes which impel them to the separation.
</blockquote>

この引用文を左右両側にインデントを付けた段落にするには、以下の XSL-FO マークアップを使用します。

<fo:block start-indent="1.5cm" end-indent="1.5cm">
  When in the Course of human events, it becomes necessary for one people 
  to dissolve the political bands which have connected them with another, 
  ...
</fo:block>

<blockquote> 要素を変換するテンプレートは以下のとおりです。

<xsl:template match="blockquote">
  <fo:block start-indent="1.5cm" end-indent="1.5cm">
    <xsl:apply-templates select="*|text()"/>
  </fo:block>
</xsl:template>

当然、インデント属性の値を変更すれば、引用のレイアウトを変更することができます。


<body> 文書の本体

XSL-FO で HTML の <body> 要素に相当する要素は、<fo:flow flow-name="xsl-region-body"> です。ここに記載するサンプル・コードでは、HTML 文書と XSLT スタイルシートとの対応関係を維持するために、<body> 要素を処理して対応する XSL-FO 要素を生成します。サンプル文書の <fo:flow flow-name="xsl-region-body"> 要素には、以下の 6 つの項目が含まれています。

  • 文書のタイトル (<head> 内の HTML title 要素)
  • developerWorks loves you! という元気が出るメッセージ
  • developerWorks の URL
  • 目次
  • 文書に含まれるコンテンツ全体
  • 文書の最後のページを識別するための id

当然、これらの項目の大部分を組み込むと、文書には意図したレイアウトが使用されることになります。XSLT テンプレートを変更すれば、別のレイアウトを作成したり (例えば、タイトル・ページを設けるなど)、複数のスタイルシートを使って同じ情報を複数のフォーマットでレンダリングしたりすることもできます。以下に、このテンプレート全体を記載します。

<xsl:template match="body">
  <fo:flow flow-name="xsl-region-body">
    <!-- Item 1 -->
    <xsl:apply-templates select="/html/head/title"/>

    <!-- Item 2 -->
    <fo:block space-after="12pt" line-height="17pt" 
      font-size="14pt" text-align="center">
      developerWorks loves you!
    </fo:block>

    <!-- Item 3 -->
    <fo:block space-after="24pt" line-height="17pt" 
      font-size="14pt" text-align="center" font-weight="bold" 
      font-family="monospace">
      ibm.com/developerWorks
    </fo:block>

    <!-- Item 4 -->
    <xsl:call-template name="toc"/>

    <!-- Item 5 -->
    <xsl:apply-templates select="*|text()"/>

    <!-- Item 6 -->
    <fo:block id="TheVeryLastPage" font-size="0pt"
      line-height="0pt" space-after="0pt"/>
  </fo:flow>
</xsl:template>

<br> 改行

ご存知のとおり、<fo:break> は改行するための要素です。したがって、<br> 要素を処理するには、<fo:break> 要素を別の要素内に組み込めばよいのです。一例として、以下のマークアップがあるとします。

<p>My mistress' eyes are nothing like the sun,
  <br/>Coral is far more red than her lips red.
  <br/>If snow be white, why then her breasts be dun,
  <br/>If hairs be wires, black wires grow on her head. 
  ...
</p>

上記のマークアップを XSL-FO 要素に変換すると、以下のような結果になります。

<fo:block>
  My mistress' eyes are nothing like the sun, 
  <fo:block> </fo:block>
  Coral is far more red than her lips red. 
  <fo:block> </fo:block>
  If snow be white, why then her breasts be dun,
  <fo:block> </fo:block>
  If hairs be wires, black wires grow on her head.
</fo:block>

以下は、<br> 要素を変換する XSLT テンプレートです。

<xsl:template match="br">
  <fo:block> </fo:block>
</xsl:template>

<caption> 表のキャプション・テキスト

<caption> 要素は、表のキャプションを作成するために使用されます。XSL-FO で表のキャプションを表現するのは <fo:table-caption> 要素です。このガイドでは <fo:table-caption> 要素と <fo:table-and-caption> 要素について説明しますが、現在のところ、FOP ではこれらの要素をサポートしていません。この制約はすぐに是正されるべきです。

この要素の変換には、多少厄介な点が 1 つあります。それは、HTML では <caption> 要素は任意の要素のすぐ上の行に挿入できますが、XSL-FO では <fo:table-and-caption> 要素の中に含めなければならないことです。したがって、XSL-FO での構造は以下のようになります。

<table-and-caption>
  <table-caption>
  <table>
</table-and-caption>

以下に、<caption> 要素を変換する単純な XSLT テンプレートを記載します。

<xsl:template match="caption">
  <fo:table-caption>
    <xsl:apply-templates select="*|text()"/>
  </fo:table-caption>
</xsl:template>

HTML の <caption> 要素を変換する際に考えられる唯一の問題は、処理対象の HTML の <table> 内にこの要素がない可能性があること、そして <table> 内の最初の要素として使用されていない可能性があることです。これらの問題に対処するように意図された <table> 要素のテンプレートを以下に記載します。

<xsl:template match="table">
  <fo:table-and-caption>
    <xsl:choice>
      <xsl:when test=".//caption">
        <xsl:apply-templates select=".//caption[1]">
      </xsl:when>
      <xsl:when test="preceding-sibling::caption">
        <xsl:apply-templates select="preceding-sibling::caption"/>
      </xsl:when>
    </xsl:choice>
    <fo:table>
      <xsl:apply-templates select="*[not(name()='caption')]"/>
    </fo:table>
  </fo:table-and-caption>
</xsl:template>

このテンプレートは、最初に <table> 要素に含まれる <caption> 要素の有無をチェックします。要素がある場合、テンプレートは最初の要素を選択して処理します (これにより、プロセッサーにはその他すべての <caption> 要素を無視させます)。表の内部に <caption> が存在しない場合には、プロセッサーは表の前にある最初の要素を調べます。その要素が <caption> であれば、それが表のキャプションであると推測することができます。最後に面倒な点として挙げられるのは、実際の <table> そのものを処理するときには、<caption> 要素を除くすべての要素に対して <xsl:apply-templates> を使用しなければならないことです。


<center> 中央揃えのテキスト

中央揃えのテキストを処理するには、<fo:block> 要素の text-align="center" 属性を使用します。例えば、以下のマークアップがあるとします。

<center>
  Table of Contents
</center>

このマークアップは、以下の XSL-FO 要素に変換することができます。

<fo:block text-align="center">
  Table of Contents
</fo:block>

この変換を行う XSLT テンプレートは以下のとおりです。

<xsl:template match="center">
  <fo:block text-align="center">
    <xsl:apply-templates select="*|text()"/>
  </fo:block>
</xsl:template>

<cite> 出典

<cite> 要素は一般に斜体のテキストでレンダリングされます。少し複雑なケースでは、<i> 要素に含まれる <cite> 要素を周りの斜体のテキストと区別できるように、通常のテキストとしてレンダリングする XSLT テンプレートを作成することもできます。以下に、サンプル・マークアップを記載します。

<p>
  When she was little, my daughter loved it when I read 
  <cite>Goodnight Moon</cite> to her.
  <i>But <cite>Harold and the Purple Crayon</cite> 
  was her favorite.</i>
</p>

このマークアップをレンダリングするには、XSL-FO の <fo:inline> 要素を使用します。

<fo:block>
  When she was little, my daughter loved it when I read
  <fo:inline font-style="italic">Goodnight Moon</fo:inline> to her.
  <fo:inline font-style="italic">But
    <fo:inline font-style="normal">Harold and the Purple Crayon</fo:inline>
    was her favorite.
  </fo:inline>
</fo:block>

斜体のフレーズに含まれる <cite> 要素を処理する場合は、変換する前に、その要素の親を調べてください。

<xsl:template match="cite">
  <xsl:choose>
    <xsl:when test="parent::i">
      <fo:inline font-style="normal">
        <xsl:apply-templates select="*|text()"/>
      </fo:inline>
    </xsl:when>
    <xsl:otherwise>
      <fo:inline font-style="italic">
        <xsl:apply-templates select="*|text()"/>
      </fo:inline>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<cite> 要素の親が <i> 要素であれば、font-stylenormal に変更し、そうでなければ italic に変更します。この手法により、<cite> 要素と <i> 要素が互いにネストされていても、この 2 つの要素の組み合わせを適切に処理することができます。


<code> サンプル・コード

<code> 要素をレンダリングするには、等幅フォントを使用する必要があります。ご想像のとおり、そのために使用するのは <fo:inline> 要素です。以下に、サンプル・マークアップを記載します。

<p>If you're a Java programmer, an easy way to break a string apart 
is with the <code>java.util.StringTokenizer</code> class.</p>

テキストのこの部分を適切にレンダリングするためには、font-family="monospace" 属性を設定した <fo:inline> 要素を使用します。

<fo:block>If you're a Java programmer, an easy way to 
  break a string apart is with the 
  <fo:inline font-family="monospace">java.util.StringTokenizer</fo:inline>
  class.
</fo:block>

<code> 要素を変換する XSLT テンプレートは以下のとおりです。

<xsl:template match="code">
  <fo:inline font-family="monospace">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<dl><dt>、および <dd> 定義リスト

番号付きリスト (<ol>) や番号なしリスト (<ul>) に比べると、定義リストはそれほど一般的に使用されてはいませんが、用語や選択肢のリストを定義する場合には定義リストが大いに役立ちます。以下の定義リストでは、パラメーターの選択肢を定義しています。

<p>There are four valid values for the 
  <code>text-align</code> parameter:</p>
<dl>
  <dt>start</dt>
  <dd>
    The text is aligned at the start of the paragraph, normally 
    the left side.  
  </dd>
  <dt>middle</dt>
  <dd>The text is aligned at the middle of the paragraph.</dd>
  <dt>end</dt>
  <dd>
    The text is aligned at the end of the paragraph, normally 
    the right side. 
  </dd>
  <dt>justify</dt>
  <dd>The text is aligned at both the start and end of 
    the paragraph.</dd>
</dl>

定義リストに一般的なフォーマットは、1 つの行に用語 (<dt> 要素) を太字で配置し、それ以降のインデントを付けた行にその用語の定義を記載することです。以下に、<dl><dt>、および <dd> の各テンプレートを示します。

<xsl:template match="dl">
  <xsl:apply-templates select="*"/>
</xsl:template>

<xsl:template match="dt">
  <fo:block font-weight="bold" space-after="2pt"
      keep-with-next="always">
    <xsl:apply-templates select="*|text()"/>
  </fo:block>
</xsl:template>

<xsl:template match="dd">
  <fo:block start-indent="1cm">
    <xsl:attribute name="space-after">
      <xsl:choose>
        <xsl:when test="name(following::*[1]) = 'dd'">
          <xsl:text>3pt</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>12pt</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
    <xsl:apply-templates select="*|text()"/>
  </fo:block>
</xsl:template>

<dd> 要素のテンプレートでは、プロセッサーが次の要素も <dd> 要素であるかどうかを調べます。これに該当する場合、現行の <dt> 要素には 1 つの用語に対して複数の定義があることになるため、プロセッサーは現行の定義の後に 3 ポイントの縦方向の余白を設けます。そうでない場合には、プロセッサーは 12 ポイントの縦方向の余白を設定します。もう 1 つ注意する点として、このテンプレートは <fo:block> 要素の keep-with-next プロパティーを使用していますが、FOP はこのプロパティーを必ず適切に処理するとは限りません。


<em> 強調表示テキスト

ほとんどのブラウザーは強調表示するテキストを斜体でレンダリングします。したがって、<em> 要素は、単純に <font-style="italic"> 属性を設定した <fo:inline> 要素に変換することができます。まずは、変換対象とするマークアップを以下に記載します。

<p>You <em>must<> disconnect the power supply before 
you open the product housing.  </p>

上記のマークアップは、以下の FO にレンダリングすることになります。

<fo:block>
  You <fo:inline font-style="italic">must</fo:inline> disconnect
  the power supply before you open the product housing.
</fo:block>

この場合の XSLT テンプレートは、以下のようになります。

<xsl:template match="em">
  <fo:inline font-style="italic">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<font color="..."> テキストの色の変更

これから、colorfacesize という 3 つの属性を使用して <font> 要素を XSL-FO 要素に変換する方法を説明します。まず、color 属性には、16 進 RGB 値 (x33cc99 など) を使用することも、XSL-FO 仕様で定義している以下の 16 色の名前のいずれかを使用することもできます。

XSL-FO 仕様で定義されている 16 色の名前
aquablackbluefuchsia
graygreenlimemaroon
navyolivepurplered
silvertealwhiteyellow

以下の XSLT テンプレートは、色の値が使用されている場合に FOP がその値を処理できることを前提とします。デフォルトの色は black です。

<xsl:template match="font">
  <xsl:variable name="color">
    <xsl:choose>
      <xsl:when test="@color">
        <xsl:value-of select="@color"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>black</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  ...
  <fo:inline font-size="{$size}" font-family="{$face}"
    color="{$color}">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

上記では、XSLT テンプレートが color 属性の値を格納する変数を作成していることに注目してください。テンプレートは colorfacesize のそれぞれに対応する変数を作成してから、これらの変数の値を使用して <fo:inline> 要素を作成します。


<font face="..."> テキストのフォントの変更

<font> 要素の face 属性は、XSL-FO の font-family 属性に対応しますが、これには問題点が 1 つあります。それは、FOP ツールは限られた数のフォントしかサポートしていないことです。以下に、font-family 属性に有効な値を記載します。

  • serif
  • sans-serif
  • monospace
  • Courier, Courier-Bold, Courier-BoldOblique, または Courier-Oblique
  • Helvetica, Helvetica-Bold, Helvetica-BoldOblique, または Helvetica-Oblique
  • Symbol
  • Times-Roman, Times-Bold, Times-BoldItalic, または Times-Italic

以下の XSLT テンプレートは、上記のテキストの色の要素でも行ったように、書体の変数を作成して、デフォルトの書体を sans-serif に設定します。

<xsl:template match="font">
  <xsl:variable name="face">
    <xsl:choose>
      <xsl:when test="@face">
        <xsl:value-of select="@face"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>sans-serif</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  ...
  <fo:inline font-size="{$size}" font-family="{$face}"
    color="{$color}">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

さらに、FOP ツールには Adobe Type 1 フォントおよび Truetype フォントを XML フォント・メトリック・ファイルに変換するための手段もあります。XML フォント・メトリック・ファイルは、FOP が上記に記載した以外のフォントでテキストをレンダリングする際に使用します。詳細については、FOP のドキュメントを参照してください。


<font size="..."> テキストのサイズの変更

<font> 要素の size 属性は、XSL-FO の font-size 属性に対応します。HTML の size 属性を処理するためのロジックは、以下のとおりです。

  • size 属性に pt ストリングが含まれている場合 (例えば、size="24pt" となっている場合)、その値をそのまま使用します。
  • size 属性がプラス記号またはマイナス記号で始まっている場合 (例えば、size="+2" または size="-1" となっている場合)、フォントの相対サイズを使用します。例えば、+1 はフォントの相対サイズ 110% に対応します (これは無作為に選んだ値なので、自由に変更してください)。
  • size 属性が 1 から 7 までの値の場合、フォントを任意のサイズに設定します (これも同じく無作為に選んだ値なので、自由に変更してください)。
  • 上記のいずれにも当てはまらない場合は、フォント・サイズを 12pt に設定します。

以下に、<font> 要素の完全な XSLT テンプレートを記載します。このテンプレートは前述の変数を初期化した後、これらの変数を使用して <fo:inline> 要素を作成します。

<xsl:template match="font">
  <xsl:variable name="color">
    <xsl:choose>
      <xsl:when test="@color">
        <xsl:value-of select="@color"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>black</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="face">
    <xsl:choose>
      <xsl:when test="@face">
        <xsl:value-of select="@face"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>sans-serif</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="size">
    <xsl:choose>
      <xsl:when test="@size">
        <xsl:choose>
          <xsl:when test="contains(@size, 'pt')">
            <xsl:text>@size</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '+1'">
            <xsl:text>110%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '+2'">
            <xsl:text>120%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '+3'">
            <xsl:text>130%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '+4'">
            <xsl:text>140%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '+5'">
            <xsl:text>150%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '+6'">
            <xsl:text>175%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '+7'">
            <xsl:text>200%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '-1'">
            <xsl:text>90%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '-2'">
            <xsl:text>80%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '-3'">
            <xsl:text>70%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '-4'">
            <xsl:text>60%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '-5'">
            <xsl:text>50%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '-6'">
            <xsl:text>40%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '-7'">
            <xsl:text>30%</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '1'">
            <xsl:text>8pt</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '2'">
            <xsl:text>10pt</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '3'">
            <xsl:text>12pt</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '4'">
            <xsl:text>14pt</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '5'">
            <xsl:text>18pt</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '6'">
            <xsl:text>24pt</xsl:text>
          </xsl:when>
          <xsl:when test="@size = '7'">
            <xsl:text>36pt</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>12pt</xsl:text>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise> 
        <xsl:text>12pt</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <fo:inline font-size="{$size}" font-family="{$face}"
    color="{$color}">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<h1><h6> 見出し

見出しタグを変換するのは比較的簡単で、各タグを <fo:block> 要素内に組み込み、フォント、フォント・サイズ、そしてその他の属性を見出しレベルに応じて変更するという方法を使用します。サンプル・レイアウトでは、最上位レベルの見出しが特に目立って表示されるように、<h1> テキストの前に改ページと横罫線を入れています。このレイアウトでは、以下のフォーマット設定を使用しました。

HTML タグフォント・サイズ行の高さ下の余白備考
<h1>28pt32pt22ptテキストの前に改ページおよび横罫線を追加
<h2>24pt28pt18ptなし
<h3>21pt24pt14ptなし
<h4>18pt21pt12ptなし
<h5>16pt19pt12pt下線付きテキスト
<h6>14pt17pt12pt下線付き斜体テキスト

以下は、サンプルの見出し要素です。

<h1>Sample text from Henry Fielding's <cite>Tom Jones</cite></h1>

<h2><b>Book I.</b>  Containing as Much of the Birth of the Foundling 
  as Is Necessary or Proper to Acquaint the Reader with in the 
  Beginning of This History</h2>

<h3><b>Chapter VII.</b>  Containing Such Grave Matter, That the Reader
  Cannot Laugh Once Through the Whole Chapter, Unless Peradventure He 
  Should Laugh at the Author</h3>

これらの要素は、以下のフォーマット化オブジェクトに変換されます。

<fo:block break-before="page">
  <fo:leader leader-pattern="rule"/>
</fo:block>
<fo:block font-family="serif" space-after="22pt" keep-with-next="always" 
    line-height="32pt" font-size="28pt" id="tomjones">
  Sample text from Henry Fielding's 
    <fo:inline font-style="italic">Tom Jones</fo:inline>
</fo:block>

<fo:block font-family="serif" space-after="18pt" keep-with-next="always" 
    line-height="28pt" font-size="24pt" id="N10017">
  <fo:inline font-weight="bold">Book I.</fo:inline>  
  Containing as Much of the Birth of the Foundling 
  as Is Necessary or Proper to Acquaint the Reader with in the 
  Beginning of This History
</fo:block>

<fo:block font-family="serif" space-after="14pt" keep-with-next="always" 
    line-height="24pt" font-size="21pt" id="N1001C">
  <fo:inline font-weight="bold">Chapter VII.</fo:inline>  
  Containing Such Grave Matter, That the Reader
  Cannot Laugh Once Through the Whole Chapter, Unless Peradventure He 
  Should Laugh at the Author
</fo:block>

<h1> 要素のテキストの前に挿入される改ページにより、名前付きアンカーと <h1> 要素を一緒に使用すると、事態がややこしくなります。<h1> 要素の XSLT テンプレートが、この要素の前にある要素をチェックし、それが名前付きアンカーであるかどうかを調べるのは、そのためです。以下に、<h1> 要素と <h6> 要素のテンプレートを記載します。

<xsl:template match="h1">
  <fo:block break-before="page">
    <fo:leader leader-pattern="rule"/>
  </fo:block> 
  <fo:block font-size="28pt" line-height="32pt"
      keep-with-next="always"
      space-after="22pt" font-family="serif">
    <xsl:attribute name="id">
      <xsl:choose>
        <xsl:when test="@id">
          <xsl:value-of select="@id"/>
        </xsl:when>
        <xsl:when test="name(preceding-sibling::*[1]) = 'a' and
                         preceding-sibling::*[1][@name]">
          <xsl:value-of select="preceding-sibling::*[1]/@name"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="generate-id()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
    <xsl:apply-templates select="*|text()"/>
  </fo:block>
</xsl:template>

<xsl:template match="h6">
  <fo:block font-size="14pt" line-height="17pt"
      keep-with-next="always" space-after="12pt"
      font-family="serif" font-style="italic"
      text-decoration="underline">
    <xsl:attribute name="id">
      <xsl:choose>
        <xsl:when test="@id">
          <xsl:value-of select="@id"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="generate-id()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
    <xsl:apply-templates select="*|text()"/>
  </fo:block>
</xsl:template>

最後に付け加えておく注意点として、見出しはブックマークや目次に使用されるため、リンク・ポイントとして役立ちます。このことから、各見出しに id が設定されていることを確実にしてください。ある特定の見出し要素にすでに id 属性が設定されている場合には、その属性を使用し、設定されていない場合には、XSLT の generate-id() 関数を使用して新しく id 属性を作成してください。

<xsl:attribute name="id">
  <xsl:choose>
    <xsl:when test="@id">
      <xsl:value-of select="@id"/>
    </xsl:when>
    <xsl:otherwise>
     <xsl:value-of select="generate-id()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:attribute>

ブックマークの生成

PDF ファイルは、文書の異なる部分にブックマークの階層ツリーを含めることができます。これらブックマークのツリーは、XSL-FO の 3 つの要素 <fo:bookmark-tree><fo:bookmark><fo:bookmark-title> を使用して、見出し要素から自動的に生成することができます。この HTML マークアップが以下のとおりであるとします。

              <h1>Sample text from Henry Fielding's Tom Jones</h1>
              <h2>Book I. Containing as Much of the Birth...</h2>
              <h3>Chapter VII. Containing Such Grave Matter...</h3>

スタイルシートは、以下の XSL-FO マークアップを生成します。

              <fo:bookmark-tree>
              <fo:bookmark starting-state="hide" internal-destination="tomjones">
              <fo:bookmark-title>
              Sample text from Henry Fielding's Tom Jones
              </fo:bookmark-title>
              <fo:bookmark starting-state="hide" internal-destination="N10017">
              <fo:bookmark-title>
              Book I.  Containing as Much of the Birth...
              </fo:bookmark-title>
              <fo:bookmark starting-state="hide" internal-destination="N1001C">
              <fo:bookmark-title>
              Chapter VII.  Containing Such Grave Matter...
              </fo:bookmark-title>
              </fo:bookmark>
              </fo:bookmark>
              </fo:bookmark>
              </fo:bookmark-tree>

その結果は、これら 3 つのネストされたブックマークになります。

図 1.
PDF ファイル内のブックマークのスクリーン・キャプチャー

詳細は、スタイルシート内の generate-bookmarks テンプレートを参照してください。


<hr> 横罫線

XSL-FO には、横罫線に対応するように意図された <fo:leader> という特殊な要素があります。以下の HTML マークアップを見てください。

<p>Here's a short paragraph.</p>
<hr/>
<p>Here's another paragraph, following a horizontal rule.</p>

上記のコンテンツをレンダリングするのは、以下の XSL-FO マークアップです。

<fo:block>
  Here's a short paragraph.
</fo:block>
<fo:block>
  <fo:leader leader-pattern="rule"/>
</fo:block>
<fo:block>
  Here's another paragraph, following a horizontal rule.
</fo:block>

<hr> 要素を処理するための XSLT テンプレートは、以下のように非常に単純です。

<xsl:template match="hr">
  <fo:block>
    <fo:leader leader-pattern="rule"/>
  </fo:block>
</xsl:template>

最後に付け加えておく注意点として、leader-pattern 属性は、目次で一般的に使われる dots の値、そして空白のエリアを作成する space もサポートします。


<i> 斜体テキスト

XSL-FO で HTML の <i> 要素を表現するには、要素のテキストを <fo:inline> に入れて、font-style="italic" 属性を指定するだけのことです。以下に、前に記載した <b> 要素のサンプル・コードと非常によく似たサンプル・コードを記載します。

<p>Jackdaws <i>love</i> my big sphinx of quartz.</p>

このコンテンツをレンダリングするには、<fo:block> および <fo:inline> という基本的な XSL-FO 要素を使用します。

<fo:block>
  Jackdaws <fo:inline font-style="italic">love</fo:inline>
  my big sphinx of quartz.
</fo:block>

<fo:block> 要素を使用すると、常に改行を伴いますが、<fo:inline> 要素では改行を伴うことはありません。したがって、<i> 要素のコンテンツをレンダリングするには <fo:inline> を使用してください。そのための XSLT テンプレートは、以下のように単純なものとなります。

<xsl:template match="i">
  <fo:inline font-style="italic">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<img> 埋め込み画像

<img> 要素は XSL-FO <fo:external-graphic> 要素に直接対応します。<img><fo:external-graphic> に変換する際の厄介な問題は、FOP ツールは width="200" のような HTML 属性をサポートしていないことです。width および height 属性には、例えば width="200px" のように、測定単位を含めなければなりません。このことから、<fo:external-graphic> 要素を生成するプロセッサーには、HTML の値を調べさせる XSLT テンプレートをセットアップする必要があります。そのためのテンプレートは以下のとおりです。

<xsl:template match="img">
  <fo:block space-after="12pt">
    <fo:external-graphic src="{@src}">
      <xsl:if test="@width">
        <xsl:attribute name="width">
          <xsl:choose>
           <xsl:when test="contains(@width, 'px')">
              <xsl:value-of select="@width"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="concat(@width, 'px')"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:attribute>
      </xsl:if>
      <xsl:if test="@height">
        <xsl:attribute name="height">
          <xsl:choose>
           <xsl:when test="contains(@height, 'px')">
              <xsl:value-of select="@height"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="concat(@height, 'px')"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:attribute>
      </xsl:if>
    </fo:external-graphic>
  </fo:block>
</xsl:template>

テンプレートは src 属性をそのまま使用し、プロセッサーが width 属性と height 属性に必要な測定単位を追加することに注目してください。


<kbd> キーボード入力

HTML の <kbd> 要素で示されるキーボード入力は、一般に多少サイズの大きな等幅フォントで表現されます。以下に HTML の一例を記載します。

<p>
  An easy way to delete a directory and all its contents (including
  all subdirectories) is with the <kbd>rd /s</kbd> command.
</p>

このサンプル HTML は、以下の XSL-FO 要素で表現します。

<fo:block>
  An easy way to delete a directory and all its contents (including
  all subdirectories) is with the 
  <fo:inline font-family="monospace" font-size="110%">rd /s</fo:inline>
  command.
</fo:block>

この変換は、以下の単純な XSLT テンプレートが対処します。

<xsl:template match="kbd">
  <fo:inline font-family="monospace" font-size="110%">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<li> リストの項目

リストの項目は、段落とほぼ同じように扱われますが、若干違うところがあります。これらの違いはリスト項目の親 (<li> 要素が <ol> 要素または <ul> 要素のどちらに含まれているのか) によって変わってくるため、リスト項目についての詳細は、<ol> 要素および <ul> 要素のセクションを参照してください。


<nobr> 改行なしのテキスト

HTML の <nobr> 要素を使用すると、<nobr> 要素のなかにどれだけのコンテンツが含まれているかに関わらず、ブラウザーは行の折り返しをしません。例えば、以下の HTML マークアップがあるとします。

<p>
  On the Windows 2000 command line, you can use the ampersand (&) to 
  combine several commands into one statement. 
  <nobr>
    pushd d:\projects\xslfo & del *.fo & rebuild.bat & popd
  </nobr>
  is an example of this technique.
</p>

上記のコンテンツをフォーマット化オブジェクトでレンダリングするには、wrap-option="no-wrap" 属性を指定した <fo:inline> 要素を使用します。残念ながら、FOP 1.1 ではこの要素を正しく処理しないため、代わりに <fo:block> を使用することができます。

<fo:block>
  On the Windows 2000 command line, you can use the ampersand (&) 
  to combine several commands into one statement.  
  <fo:inline wrap-option="no-wrap">pushd d:\projects\xslfo &
  del *.fo & rebuild.bat & popd </fo:inline> is an 
  example of this technique.
</fo:block>

この変換を行う XSLT テンプレートは、以下のように簡潔です。

<xsl:template match="nobr">
  <fo:inline wrap-option="no-wrap">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

良いニュースは、ほとんどの <nobr> 要素は単独で使用されることです。そのため、自分が望む結果がページ上に生成されます。FOP 以外の XSL-FO レンダリング・エンジンを使用する場合は、<fo-inline> を使用するようにスタイルシートを変更してみてください。おそらく、うまく行きます。


<ol> 番号付きリスト

XSL-FO 語彙には、番号付きリストを含め、リスト専用の要素が揃っています。以下は、HTML リストの一例です。

<p>A few of my favorite albums</p>
<ol>
  <li>A Love Supreme</li>
  <li>Remain in Light</li>
  <li>London Calling</li>
  <li>The Indestructible Beat of Soweto</li>
  <li>The Joshua Tree</li>
</ol>

リストの項目を XSL-FO で表現するには、複数の要素を使用する必要があります。具体的に言うと、<fo:list-block><fo:list-item><fo:list-item-label>、および <fo:list-item-body> 要素です。これらの要素は、以下のように使用します。

  • <fo:list-block> 要素には、リスト全体を含めます。
  • <fo:list-item> 要素のそれぞれに、1 つのリスト項目を含めます。この要素のなかには<fo:list-item-label> および <list-item-body> 要素がネストされます。
  • <fo:list-item-label> は、コンテンツの隣に表示されるラベルです。番号付きリストの場合、<fo:list-item-label> 要素には例えば 10. というテキストが含まれます。
  • <fo:list-item-body> には、リスト項目の実際のコンテンツを含めます。

上記の HTML マークアップをフォーマット化オブジェクトに変換すると、以下のようになります。

<fo:block>A few of my favorite albums</fo:block>
<fo:list-block provisional-distance-between-starts="0.75cm"
  provisional-label-separation="0.5cm" space-after="12pt"
  start-indent="1cm">
  <fo:list-item>
    <fo:list-item-label end-indent="label-end()">
      <fo:block>1. </fo:block>
    </fo:list-item-label>
    <fo:list-item-body start-indent="body-start()">
      <fo:block>
        A Love Supreme
      </fo:block>
    </fo:list-item-body>
  </fo:list-item>
   ...
  <!-- other list items appear here -->
   ...
</fo:list-block>

この変換を処理する XSLT テンプレートを以下に記載します。テンプレートに必要とされる処理内容は、以下のとおりです。

  • 別のリスト内に表示されるリストの場合には、リストの下に余白を挿入しないこと。別のリストに含まれるリストでなければ、リストの下に 12 ポイントの縦方向の余白を挿入します。
  • 別のリスト内に含まれるリストではない場合、1 cm のインデントを付けること。リストが別のリストに含まれる場合には、追加されるリストごとに、インデントの値 1 cm1.25 cm を加算します。例えば、深さのレベルが 3 のリストには、3.5 cm のインデントを付けることになります。
  • <ol> 要素に start 属性が指定されている場合、リスト項目には、この属性に指定された値から番号を付けます。この属性が指定されていない場合は、XSLT の position() 関数を使用してリスト項目に番号を付けます。
  • <ol> 要素に type 属性が指定されている場合、その値を使用して各リスト項目の番号のフォーマット (ローマ数字、アラビア数字、または英字) を判断します。それには、<xsl:number> 要素の format 属性を使用します。

この箇条書きしたルールを適用して <ol> 項目および <li> 項目を変換する XSLT テンプレートを以下に記載します。

<xsl:template match="ol">
  <fo:list-block provisional-distance-between-starts="1cm"
    provisional-label-separation="0.5cm">
    <xsl:attribute name="space-after">
      <xsl:choose>
       <xsl:when test="ancestor::ul or ancestor::ol">
          <xsl:text>0pt</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>12pt</xsl:text>
          </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
    <xsl:attribute name="start-indent">
      <xsl:variable name="ancestors">
        <xsl:choose>
         <xsl:when test="count(ancestor::ol) or count(ancestor::ul)">
            <xsl:value-of select="1 + 
                                  (count(ancestor::ol) + 
                                   count(ancestor::ul)) * 
                                  1.25"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>1</xsl:text>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      <xsl:value-of select="concat($ancestors, 'cm')"/>
    </xsl:attribute>
    <xsl:apply-templates select="*"/>
  </fo:list-block>
</xsl:template>

<xsl:template match="ol/li">
  <fo:list-item>
    <fo:list-item-label end-indent="label-end()">
      <fo:block>
        <xsl:variable name="value-attr">
          <xsl:choose>
            <xsl:when test="../@start">
              <xsl:number value="position() + ../@start - 1"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:number value="position()"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>
        <xsl:choose>
          <xsl:when test="../@type='i'">
            <xsl:number value="$value-attr" format="i. "/>
          </xsl:when>
          <xsl:when test="../@type='I'">
            <xsl:number value="$value-attr" format="I. "/>
          </xsl:when>
          <xsl:when test="../@type='a'">
            <xsl:number value="$value-attr" format="a. "/>
          </xsl:when>
          <xsl:when test="../@type='A'">
            <xsl:number value="$value-attr" format="A. "/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:number value="$value-attr" format="1. "/>
          </xsl:otherwise>
        </xsl:choose>
      </fo:block>
    </fo:list-item-label>
    <fo:list-item-body start-indent="body-start()">
      <fo:block>
        <xsl:apply-templates select="*|text()"/>
      </fo:block>
    </fo:list-item-body>
  </fo:list-item>
</xsl:template>

上記の 2 つのテンプレートが、<ol> 要素と <li> 要素を適切な XSL-FO 要素に変換します。


<p> 段落

HTML の段落要素を <fo:block> 要素に変換するのは簡単です。例えば、以下の段落があるとします。

<p>When in the Course of human events, it becomes necessary 
  for one people to dissolve the political bonds which have connected 
  them with another, and to assume among the powers of the earth, 
  the separate and equal station to which the Laws of Nature and 
  of Nature's God entitle them, a decent respect to the opinions 
  of mankind requires that they should declare the causes which 
  impel them to the separation.</p>

上記の段落は、以下の XSL-FO マークアップに変換されます。

<fo:block font-size="12pt" line-height="15pt
  space-after="12pt">When in the Course of human events, it becomes 
  necessary for one people to dissolve the political bonds which 
  ...
</fo:block>

この変換を処理するのは、以下の単純な XSLT テンプレートです。

<xsl:template match="p">
  <fo:block font-size="12pt" line-height="15pt"
    space-after="12pt">
    <xsl:apply-templates select="*|text()"/>
  </fo:block>
</xsl:template>

このテンプレートは、font-sizeline-height、および space-after プロパティーのデフォルト値を設定して実装されています。当然、これらの値は必要に応じて変更することができます。


<pre> フォーマット設定済みテキスト

<pre> 要素には厄介な点がいくつかあります。それは、この要素に含まれるすべてのホワイト・スペースを維持しなければならないこと、そして XSL-FO エンジンが行う自動ワード・ラップを無効にしなければならないことです。慣例では、<pre> 要素のコンテンツについても等幅フォントで表示します。以下に抜粋したのは <pre> 要素のサンプルです。

<pre>
public static void main(String args[])
{
  System.out.println("Hello, world!");
}
</pre>

このサンプル <pre> 要素を適切に処理するためには、以下の XSL-FO マークアップに変換しなければなりません。

<fo:block font-family="monospace" 
  white-space-collapse="false"
  wrap-option="no-wrap">
public static void main(String args[])
{
  System.out.println("Hello, world!");
}
</fo:block>

この変換を処理する XSLT テンプレートは単純なものです。

<xsl:template match="pre">
  <fo:block font-family="monospace"
    white-space-collapse="false"
    wrap-option="no-wrap">
    <xsl:apply-templates select="*|text()"/>
  </fo:block>
</xsl:template>

<samp> サンプル・テキスト

<samp> 要素は、多少サイズの大きな等幅フォントでレンダリングされるのが通常です。<samp> が使用されることはめったにありませんが、この要素は簡単にフォーマット化オブジェクトに変換することができます。以下に、サンプル <samp> 要素を記載します。

<p>The <samp>DOCTYPE</samp> keyword lets you
  refer to a DTD from your XML source document.</p>

この要素をフォーマット化オブジェクトに変換する XSLT テンプレートは簡単明瞭です。

<xsl:template match="samp">
  <fo:inline font-family="monospace"
      font-size="110%">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<small> 書体を小さくしたテキスト

<small> 要素も簡単に変換できる要素です。お察しのとおり、この要素はやや小さなサイズのフォントでレンダリングされます。<big> 要素のテンプレートでは、font-size の値を 20 パーセント大きくしてテキストをレンダリングしたので、<small> は 20 パーセント縮小されたテキストとして定義するのが妥当でしょう。XSLT テンプレートは、以下のように単純なものになります。

<xsl:template match="small">
  <fo:inline font-size="80%">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<p>The Lakers' chances for a fourth straight title are <small>slim</small>.</p> という段落があるとすると、このテンプレートによって以下のマークアップが生成されます。

<fo:block font-size="12pt" line-height="15pt" space-after="12pt">
  The Lakers' chances for a fourth straight title are
  <fo:inline font-size="80%">slim</fo:inline>.
</fo:block>

<big> 要素の場合と同じく、複数の <small> 要素を互いにネストさせると、テキストは次第に小さくなっていきます。


<strike> テキストの取り消し線

HTML の <strike> 要素は、text-decoration="line-through" プロパティーを指定した <fo:inline> 要素を作成するだけで実装することができます。テキストの取り消し線は、文書から削除されたセクションを強調表示する場合に役立ちます。以下に、一例を記載します。

<p>The underline property
  <strike>is not currently supported by FOP.</strike>
  is now supported by FOP.</p>

この段落をレンダリングすると、テキストがどのように変更されているのかが明らかになります。<strike> の XSLT テンプレートは簡潔なものです。

<xsl:template match="strike">
  <fo:inline text-decoration="line-through">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

text-decoration プロパティーのキーワードは、肯定形と否定形の両方があることに注意してください。何らかの理由で、line-through が有効になった長いセクションのなかに line-through を無効にした短いセクションのテキストを含める必要がある場合には、text-decoration="no-line-through" プロパティーを指定した <fo:inline> 要素を作成することで対処することができます。また、このプロパティーに複数の値を指定することも可能です。例えば、text-decoration="line-through underline" プロパティーを指定すると、取り消し線と下線の両方が適用されます。


<strong> 特に強調されたテキスト

一般に、<strong> 要素は太字でレンダリングされます。以下に、この変換を行う単純な XSLT テンプレートを記載します。

<xsl:template match="strong">
  <fo:inline font-weight="bold">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<sub> 下付き文字のテキスト

下付き文字のテキストを処理するには、XSL-FO の vertical-align プロパティーを使用して、テキストのベースラインを変更します。また、フォント・サイズも小さくするのが通常です。以下に HTML の一例を記載します。

<p>When I'm thirsty, nothing beats a cold 
  glass of H<sub>2</sub>O.</p>

XSLT テンプレートは以下のようになります。

<xsl:template match="sub">
  <fo:inline vertical-align="sub"
        font-size="75%">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<sup> 上付き文字のテキスト

下付き文字のテキストと同じく、上付き文字のテキストを処理する場合にも XSL-FO の vertical-align プロパティーを使用します。以下はサンプル HTML です。

<p>Einstein's famous e=mc<sup>2</sup>
  is an equation that changed the world. </p>

XSLT テンプレートは以下のようになります。

<xsl:template match="sup">
  <fo:inline vertical-align="super"
        font-size="75%">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<table> 表のタグ

HTML の <table> 要素を処理するときの最大の難問は、XSL-FO 表の列数とこれらの列の幅を決定することです。FOP では、表に含まれる列ごとに <fo:table-column> 要素を指定しなければなりません。列の処理が完了したら、あとは表内のすべての要素の XSLT テンプレートを呼び出すだけです。以下に、cols を処理する XSLT テンプレートを記載します。

<table cols="200 100pt" border="1">

この cols 属性は、以下のマークアップに変換しなければなりません。

<fo:table-column column-width="200pt"/>
<fo:table-column column-width="100pt"/>

この変換を有効にするためには、末尾再帰として知られる一般的な XSLT 手法を使用する必要があります。それには、属性の値から最初の単語を取得して <fo:table-column> 要素に変換した後、テンプレート自身を呼び出して属性の値の残りの部分を処理する、名前付きテンプレートを作成します。このテンプレートによって、最終的に属性全体が処理されます。<table> 要素の XSLT テンプレートは、以下のようになります。

<xsl:template match="table">
  <fo:table table-layout="fixed">
    <xsl:choose>
     <xsl:when test="@cols">
        <xsl:call-template name="build-columns">
          <xsl:with-param name="cols" 
            select="concat(@cols, ' ')"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <fo:table-column column-width="200pt"/>
      </xsl:otherwise>
    </xsl:choose>
    <fo:table-body>
      <xsl:apply-templates select="*"/>
    </fo:table-body>
  </fo:table>
</xsl:template>

プロセッサーは、cols 属性が設定されている場合には build-columns テンプレートを呼び出し、設定されていない場合には単一の <fo:table-column> 要素を作成します。また、<fo:table> 要素にtable-layout="fixed" プロパティーが指定されている点にも注目してください。現在、FOP はこの属性が設定されていないと、警告メッセージを発行します。

build-columns テンプレートを以下に記載します。

<xsl:template name="build-columns">
  <xsl:param name="cols"/>
  
  <xsl:if test="string-length(normalize-space($cols))">
    <xsl:variable name="next-col">
      <xsl:value-of select="substring-before($cols, ' ')"/>
    </xsl:variable>
    <xsl:variable name="remaining-cols">
      <xsl:value-of select="substring-after($cols, ' ')"/>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="contains($next-col, 'pt')">
        <fo:table-column column-width="{$next-col}"/>
      </xsl:when>
      <xsl:when test="number($next-col) > 0">
        <fo:table-column column-width="{concat($next-col, 'pt')}"/>
      </xsl:when>
      <xsl:otherwise>
        <fo:table-column column-width="50pt"/>
      </xsl:otherwise>
    </xsl:choose>
    
    <xsl:call-template name="build-columns">
      <xsl:with-param name="cols" select="concat($remaining-cols, ' ')"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

このテンプレートはまず、$cols パラメーターの前後にある空白を削除してから (これを行うのは、XSLT の normalize-space() 関数です)、$cols パラメーターの長さを調べるように指示します。次に、$cols の値を 2 つに分割し、最初のスペースの前にあるサブストリングと、最初のスペースの後にある残りの部分に分けます (プロセッサーはこのテンプレートを <table> 要素のテンプレートから呼び出すときに、必ず 1 つ以上のスペースが含まれるように、値の最後にスペースを追加することに注意してください)。

$cols パラメーターが 2 つに分割された後、以下のようにして最初の部分が処理されます。

  • 値に pt というストリングが含まれる場合は、200pt のような値が含まれていることを想定して、<fo:table-column> 要素を生成します。
  • 値が数値の場合は、値の終わりにストリング pt を追加してから、<fo:table-column> 要素を生成します。値を数値に変換するには、XSLT の number() 関数を使用します。この関数は、値が数値でなければ (例えば、number(xyz))、ストリング NaN (Not a Number) を返します。
  • 上記のルールがいずれも当てはまらない場合は、column-width50pt に設定した <fo:table-column> 要素を新規に作成します。

プロセッサーが $cols パラメーターの最初の部分を処理し終えると、名前付きテンプレートは $cols パラメーターの残りの部分で自身を呼び出し、値の終わりにスペースを追加して、少なくとも 1 つのスペースが含まれるようにします。


<td> 表のセル

HTML の <td> 要素は、XSL-FO の <fo:external-graphic> 要素とかなり対応しています。デフォルトとして、テンプレートは表のセルの上下左右に 3 ポイントの余白を設定します。処理しなければならない HTML 属性は、colspanrowspan、および align の 3 つだけです。また、border 属性の処理方法も限られています。

colspan および rowspan 属性は、それぞれ XSL-FO の number-columns-spanned 属性number-rows-spanned 属性に直接対応するので、処理するのは難しくありません。

HTML の align 属性は XSL-FO の text-align 属性に対応しますが、属性に使われる値のセットは異なります。HTML の align 属性値 leftcenterrightjustify は、XSL-FO の text-align 属性では startcenterendjustify の値にそれぞれ対応します。もう 1 つの厄介な点は、配置の値が現れるのが HTML の <td><tr><thead><table> 要素のどれかは決まっていないため、値が見つかるまで上位の要素をすべてチェックしなければならないことです (text-align プロパティーが設定される要素は、<fo:table-cell> ではなく、<fo:block> であることに注意してください)。

<td><tr><thead>、または <table> 要素に border="1" 属性が設定されている場合には、表の該当するセルの周りに、XSL-FO の border-style="solid" border-color="black" border-width="1pt" という 3 つのプロパティーを使用した枠を描画します。

以下に完全な XSLT テンプレートを記載します。このテンプレートの大部分は、どの XSL-FO のプロパティーを使用するかを決定する <xsl:choose> 要素で構成されています。

<xsl:template match="td">
   <fo:table-cell 
    padding-start="3pt" padding-end="3pt"
    padding-before="3pt" padding-after="3pt">
    <xsl:if test="@colspan">
      <xsl:attribute name="number-columns-spanned">
        <xsl:value-of select="@colspan"/>
      </xsl:attribute>
    </xsl:if>
    <xsl:if test="@rowspan">
      <xsl:attribute name="number-rows-spanned">
        <xsl:value-of select="@rowspan"/>
      </xsl:attribute>
    </xsl:if>
    <xsl:if test="@border='1' or 
                  ancestor::tr[@border='1'] or
                  ancestor::thead[@border='1'] or
                  ancestor::table[@border='1']">
      <xsl:attribute name="border-style">
        <xsl:text>solid</xsl:text>
      </xsl:attribute>
      <xsl:attribute name="border-color">
        <xsl:text>black</xsl:text>
      </xsl:attribute>
      <xsl:attribute name="border-width">
        <xsl:text>1pt</xsl:text>
      </xsl:attribute>
    </xsl:if>
    <xsl:variable name="align">
      <xsl:choose>
        <xsl:when test="@align">
          <xsl:choose>
            <xsl:when test="@align='center'">
              <xsl:text>center</xsl:text>
            </xsl:when>
            <xsl:when test="@align='right'">
              <xsl:text>end</xsl:text>
            </xsl:when>
            <xsl:when test="@align='justify'">
              <xsl:text>justify</xsl:text>
            </xsl:when>
            <xsl:otherwise>
              <xsl:text>start</xsl:text>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:when test="ancestor::tr[@align]">
          <xsl:choose>
            <xsl:when test="ancestor::tr/@align='center'">
              <xsl:text>center</xsl:text>
            </xsl:when>
            <xsl:when test="ancestor::tr/@align='right'">
              <xsl:text>end</xsl:text>
            </xsl:when>
            <xsl:when test="ancestor::tr/@align='justify'">
              <xsl:text>justify</xsl:text>
            </xsl:when>
            <xsl:otherwise>
              <xsl:text>start</xsl:text>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:when test="ancestor::thead">
          <xsl:text>center</xsl:text>
        </xsl:when>
        <xsl:when test="ancestor::table[@align]">
          <xsl:choose>
            <xsl:when test="ancestor::table/@align='center'">
              <xsl:text>center</xsl:text>
            </xsl:when>
            <xsl:when test="ancestor::table/@align='right'">
              <xsl:text>end</xsl:text>
            </xsl:when>
            <xsl:when test="ancestor::table/@align='justify'">
              <xsl:text>justify</xsl:text>
            </xsl:when>
            <xsl:otherwise>
              <xsl:text>start</xsl:text>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>start</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
   <fo:block text-align="{$align}">
      <xsl:apply-templates select="*|text()"/>
    </fo:block>
  </fo:table-cell>
</xsl:template>

<tfoot> 表のフッター

めったに使用されませんが、<tfoot> は表のフッターを作成する要素です。この要素には表の行 (<tr>) 要素がいくつも含まれ、そのそれぞれに、表のセルがいくつか含まれます。この要素を処理するには、単に <tfoot> 要素に含まれる <tr> 要素のXSLT テンプレートを呼び出すだけです。

<xsl:template match="tfoot">
<xsl:apply-templates select="tr"/>
</xsl:template>

<th> 表の見出し内のセル

HTML の <th> 要素には、表の見出しのセルが含まれます。これを処理するには、セルの周囲に (<td> 要素のデフォルトに指定した値と同じ) 3 ポイントのパディングを設定した <fo:table-cell> を作成します。この <fo:table-cell> 要素に、太字のテキストを中央揃えした <fo:block> 要素を含めます。テンプレートは、この要素の上位要素に border="1" 属性が指定されているかどうかをチェックします。該当する上位要素が 1 つでもあれば、それに応じて枠線に関する XSL-FO のプロパティーを設定します。以下に、XSLT テンプレート全体を記載します。

<xsl:template match="th">
  <fo:table-cell
    padding-start="3pt" padding-end="3pt"
    padding-before="3pt" padding-after="3pt">
    <xsl:if test="@border='1' or 
                  ancestor::tr[@border='1'] or
                  ancestor::table[@border='1']">
      <xsl:attribute name="border-style">
        <xsl:text>solid</xsl:text>
      </xsl:attribute>
      <xsl:attribute name="border-color">
        <xsl:text>black</xsl:text>
      </xsl:attribute>
      <xsl:attribute name="border-width">
        <xsl:text>1pt</xsl:text>
      </xsl:attribute>
    </xsl:if>
    <fo:block font-weight="bold" text-align="center">
      <xsl:apply-templates select="*|text()"/>
    </fo:block>
  </fo:table-cell>
</xsl:template>

<thead> 表の見出し

ほとんど使われることのない <thead> 要素は、<tfoot> 要素とまったく同じように処理します。以下に、単純な XSLT テンプレートを記載します。

<xsl:template match="thead">
   <xsl:apply-templates select="tr"/>
</xsl:template>

<title> 文書のタイトル

このガイドの説明では一貫して、最終的には FO 文書を PDF ファイルに変換することを前提としています。したがって、PDF ファイルのレイアウトに従わなければなりません。チュートリアルのサンプルで規定した PDF のレイアウトを実現するには、文書のタイトル (<html> 要素に含まれる <head> 要素内の <title> 要素) をページの最上部に大きく、太字のスタイルを使って中央揃えで表示します。このようなタイトルにするための XSLT テンプレートは以下のとおりです。

<xsl:template match="title">
  <fo:block space-after="18pt" line-height="27pt" 
    font-size="24pt" font-weight="bold" text-align="center">
    <xsl:apply-templates select="*|text()"/>
  </fo:block>
</xsl:template>

<tr> 表の行

HTML の <tr> 要素は、XSL-FO の <fo:table-row> 要素に直接対応しています。表を処理するための作業のほとんどは <td> 要素のテンプレート内で行われるため、<fo:table-row> を作成して、HTML の <tr> 要素に含まれるすべての要素の XSLT テンプレートを呼び出すだけで変換することができます。以下に、この単純なテンプレートを記載します。

<xsl:template match="tr">
<fo:table-row>
    <xsl:apply-templates select="*|text()"/>
  </fo:table-row>
</xsl:template>

<tt> 等幅テキスト

等幅テキストは、等幅フォントでレンダリングされます。この XSLT テンプレートは以下のとおりです。

<xsl:template match="tt">
  <fo:inline font-family="monospace">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

<u> 下線付きテキスト

下線付きテキストをレンダリングするには、XSL-FO の text-decoration プロパティーを使用します。以下に、簡潔なサンプル HTML を記載します。

<p>When typewriters ruled the earth, 
  <u>underlining</u> was the most 
  common way to highlight text.</p>

このサンプルを XSL-FO に変換するには、text-decoration="underline" プロパティーを指定した <fo:inline> 要素を使用します。

<xsl:template match="u">
  <fo:inline text-decoration="underline">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

text-decoration プロパティーのキーワードは、肯定形と否定形の両方があることに注意してください。何らかの理由で、underline が有効になった長いセクションのなかに underline を無効にした短いセクションのテキストを含める必要がある場合には、text-decoration="no-underline" プロパティーを指定した <fo:inline> 要素を作成することで対処することができます。また、このプロパティーに複数の値を指定することも可能です。例えば、text-decoration="underline line-through" プロパティーを指定すると、下線と取り消し線の両方が適用されます。


<ul> 番号なしリスト

<fo:list-item-label> を使用すると、すべてのリスト項目にブレット (箇条書き用の黒丸) が付けられるため、番号なしリストは番号付きリストや定義リストよりも簡単に処理することができます。以下に、サンプルの HTML リストを記載します。

<p>A few of my favorite albums</p>
<ul>
  <li>A Love Supreme</li>
  <li>Remain in Light</li>
  <li>London Calling</li>
  <li>The Indestructible Beat of Soweto</li>
  <li>The Joshua Tree</li>
</ul>

使用する XSL-FO のリスト要素は、<fo:list-block><fo:list-item><fo:list-item-label>、および <fo:list-item-body> です。上記の HTML リストを XSL-FO に変換すると、以下のようになります。

<fo:block>A few of my favorite albums</fo:block>
<fo:list-block provisional-distance-between-starts="0.2cm"
  provisional-label-separation="0.5cm"
  space-after="12pt" start-indent="1cm">
  <fo:list-item>
    <fo:list-item-label end-indent="label-end()">
      <fo:block>•</fo:block>
    </fo:list-item-label>
    <fo:list-item-body start-indent="body-start()">
      <fo:block>A Love Supreme</fo:block>
    </fo:list-item-body>
  </fo:list-item>
  ...
  <fo:list-item>
    <fo:list-item-label end-indent="label-end()">
      <fo:block>•</fo:block>
    </fo:list-item-label>
    <fo:list-item-body start-indent="body-start()">
      <fo:block>The Joshua Tree</fo:block>
    </fo:list-item-body>
  </fo:list-item>
</fo:list-block>

テンプレートでは、ブレット文字として Unicode エンティティー • を使用します。

<ul> 項目と <li> 項目をフォーマット化オブジェクトに変換する XSLT テンプレートは、以下のルールに従って <ul> 要素を処理します。

  • 別のリスト内に表示されるリストの場合には、リストの下に余白を挿入しません。
  • 別のリスト内に含まれるリストではない場合、1 cm のインデントを付けるようにします。リストが別のリストに含まれる場合には、追加されるリストごとに、インデントの値 1 cm1.25 cm を加算します。例えば、深さのレベルが 3 のリストには、3.5 cm のインデントを付けることになります。

この XSLT テンプレートは以下のとおりです。

<xsl:template match="ul">
  <fo:list-block provisional-distance-between-starts="1cm"
    provisional-label-separation="0.5cm">
    <xsl:attribute name="space-after">
      <xsl:choose>
     <xsl:when test="ancestor::ul or ancestor::ol">
          <xsl:text>0pt</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>12pt</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
    <xsl:attribute name="start-indent">
      <xsl:variable name="ancestors">
        <xsl:choose>
       <xsl:when test="count(ancestor::ol) or count(ancestor::ul)">
            <xsl:value-of select="1 + 
                                  (count(ancestor::ol) + 
                                   count(ancestor::ul)) * 
                                  1.25"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>1</xsl:text>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      <xsl:value-of select="concat($ancestors, 'cm')"/>
    </xsl:attribute>
    <xsl:apply-templates select="*"/>
  </fo:list-block>
</xsl:template>

<xsl:template match="ul/li">
  <fo:list-item>
    <fo:list-item-label end-indent="label-end()">
      <fo:block>•</fo:block>
    </fo:list-item-label>
    <fo:list-item-body start-indent="body-start()">
      <fo:block>
        <xsl:apply-templates select="*|text()"/>
      </fo:block>
    </fo:list-item-body>
  </fo:list-item>
</xsl:template>

<var> 変数名

変数名は一般に、斜体の等幅フォントでレンダリングします。それには、XSL-FO の font-family および font-style プロパティーを使用します。以下に、簡単なサンプルを記載します。

<p>To run the FOP program, you must make sure
  your <var>classpath</var> variable
  is set correctly.</p>

この変換を処理する XSLT テンプレートは以下のとおりです。

<xsl:template match="var">
  <fo:inline font-style="italic"
      font-family="monospace">
    <xsl:apply-templates select="*|text()"/>
  </fo:inline>
</xsl:template>

ダウンロード

内容ファイル名サイズ
Example files (html, xsl, fo, pdf)x-xslfo2app-samples.zip45KB

参考文献

学ぶために

  • このリファレンス・ガイドに関連するチュートリアルで、XSL-FO の使用方法を学んでください。「XSL Formatting Objects (XSL-FO) basics」(developerWorks、2003年2月) では XSL-FO の基本を学べます。「XSL-FO advanced techniques」 (developerWorks、2003年2月) では、複雑な文書、リスト、表、相互参照リンクをフォーマット化する方法に加え、HTML 要素をフォーマット化オブジェクトに変換する方法を説明しています。
  • W3C のサイトで XSL-FO 仕様 (全 500 ページ強) を調べてください。
  • XSL-FO 全体の詳細については、W3C のスタイル・ページにアクセスしてください。
  • XSL-FO の別のアプリケーションを調べるには、developerWorks の記事「XSL-FO を使って文書を印刷用に加工する」(Rodolfo Raya 著、2001年11月) を読んでください。この記事では、著者が XSL-FO を使って Java 言語のアプリケーションから印刷可能なデータベース・レポートを生成する方法を説明しています。
  • New to XML: XML を学ぶために必要なリソースを入手してください。
  • developerWorks の XML ゾーン: DTD、スキーマ、XSLT など、XML 分野でのスキルを磨くために必要なリソースを見つけてください。広範な技術に関する記事、ヒント、チュートリアル、標準、そして IBM Redbooks については、XML 技術文書一覧を参照してください。
  • IBM の XML 認定技術者: XML および関連技術において IBM 認定技術者になる方法を調べてください。
  • developerWorks テクニカル・イベント: これらのセッションで最新の技術情報を入手してください。
  • Twitter での developerWorks: 今すぐ登録して developerWorks のツイートをフォローしてください。
  • developerWorks podcast: ソフトウェア開発者向けの興味深いインタビューやディスカッションを聴いてください。
  • developerWorks オンデマンド・デモ: 初心者向けの製品のインストールおよびセットアップから熟練開発者向けの高度な機能に至るまで、さまざまに揃ったデモを見てください。

製品や技術を入手するために

  • このガイドの例は、2012年 10月 20日にリリースされた FOP バージョン 1.1 で動作するように作られています。Apache XML Project の FOP パッケージをダウンロードしてください。
  • Rational Application Developer for WebSphere Software について調べてください。J2EE アプリケーションをビルド、テスト、そしてデプロイするために簡単に使用できるこの統合開発環境では、DTD およびスキーマから XML 文書を生成することもできます。
  • IBM 製品の評価版: DB2、Lotus、Rational、Tivoli、および WebSphere のアプリケーション開発ツールとミドルウェア製品を体験するには、評価版をダウンロードしてみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=749030
ArticleTitle=HTML からフォーマット化オブジェクト (FO) への変換ガイド
publish-date=11142013