ヒント: 改ページとテーブルにXSL-FOを使う

キープ・プロパティとブラインド・テーブルで美しい体裁の文書を作成する

Comments

XSL Formatting Objects(XSL-FO)標準はXSL標準 (XSLTの方が有名ですが・・・) のうち、あまり知られていないものの一つです。XSL-FOは、XML文書からPDFやポストスクリプト・ファイルを作成するような作業では、非常に有用なのに知られていないのは残念なことです。一般的に、XSL-FOはXML文書のレイアウトと表示方法をコントロールします。ほとんどのユーザーがHTMLページは画面上の読み取りには適しているが、ハードコピーにはPDFの方 (そのためにXSL-FOがあります) が好ましいと思っています。

改ページのコントロール

XSL-FOの利点の一つは、文書のテキストを必要に応じて、複数のページに自動的に配置することです。ページが一杯になると、XSL-FOは自動的に次のページを挿入します。残念ながら、例えばセクションの見出しとセクションの内容の間で改ページするといったように、たまにアルゴリズムが誤った位置に改ページを挿入する場合があります。同様に図と図のラベルを分離したり、テーブルの見出しをテーブルの行から分離したりするかもしれません。

そのような問題の例を図1と図2に示します。図1ではセクションのタイトルがページの一番下にあり、残りのセクション本体が次ページになっています。

図1:セクション・タイトル直後の不適切な改ページ
セクション・タイトル直後の不適切な改ページ
セクション・タイトル直後の不適切な改ページ

図2では、画像のラベルが画像から分離されています。

図2:画像ラベル直後の不適切な改ページ
画像ラベル直後の不適切な改ページ
画像ラベル直後の不適切な改ページ

この問題は、XSL-FOレンダラが扱う各々のブロックが、互いにどのように関連付けられているかを知らないために起こります。タイトルがタイトルであることを知らないのです。解決策は、XSL-FOレンダラにブロックが互いに関連付けられていること、具体的には、ブロックが一緒のままであるべきと教えることです。このために標準では、キープおよびブレイクのカテゴリーで、幾つかのプロパティを定義しています。keep-with-previousプロパティとkeep-with-nextプロパティは、あるブロックが前のブロックと後ろのブロックのいずれと関連するのかを指定します。

これらのプロパティはwithin-linewithin-columnwithin-page構成要素に適用します。名前からも分かるように、これらの構成要素はブロックのグループ化レベルをコントロールします。通常、私はwithin-page構成要素を使用します。

許容値はauto (特に処理しない)、always (ブロックを常に同一ページ内に保持する) または整数です。整数は優先順位を指定します。幾つかのキープ・プロパティが競合した時に、最も大きい数字が優先されます。これらの値のうち、alwaysが最優先されます。

リスト1は、タイトルとセクションの間あるいはラベルと画像の間で、改ページしてしまうことを避けるためにキープ・プロパティを使ったスタイルシートからの抜粋です。XSL-FOではプロパティを属性として指定します。(完全なスタイルシートと関連するコードは、参考文献からダウンロードできます)

リスト1:キープ・プロパティ
<xsl:template match="doc:title" mode="keep">
   <fo:block font-family="Times Roman"
             font-size="12pt"
             space-after="0.6em"
keep-with-next.within-page="always">
      <xsl:apply-templates mode="keep"/>
   </fo:block>
</xsl:template>
<xsl:template match="doc:figure" mode="keep">
   <fo:block font-family="Times Roman"
             font-size="8pt"
             font-style="italic"
             space-after="0.5em">
      <xsl:value-of select="@label"/>
   </fo:block>
   <fo:block keep-with-previous.within-page="always">
      <fo:external-graphic src="url({@src})"
                           width="{@width}"
                           height="{@height}"/>
   </fo:block>
</xsl:template>

警告されてはいますが、FOP (Formatting Objects Processor :Apacheから提供されるオープン・ソースXSLプロセッサ) は、キープ・プロパティを完全にはインプリメントしていません。あなたがリスト1を使おうとしても、FOPはキープ・プロパティを無視しますので、依然として不適切な改ページが引き起こされる可能性があります。私の知る限りでは、RenderXから出ているXEPやアンテナハウスのXSL Formatter (参考文献を参照してください) などの市販のレンダラだけが、キープ・プロパティをインプリメントしています(少なくともこの記事の執筆時点では)。

FOPの代替手段

商用のインプリメントがより完全に標準をサポートしているのであれば、当分の間は市販品を購入することが、おそらくは最善の解決策でしょう。しかし、1サーバーあたり5,000USドル前後(制限付きのワークステーション・ライセンスならば、79USドル程度と安くなります)のライセンス・コストが掛かるため、全てのプロジェクトが独自にXSL-FOレンダラを購入する余裕があるとは限りません。これは予算が限定されるプロジェクトの初期段階において、正に当てはまるでしょう。

FOPでキープ・プロパティを使用するための代替手段は利用可能ではあるものの、部分的に利用できるに過ぎません。FOPはテーブルの行のみでキープ・プロパティを認識します。限定されたサポートは、見出しとそれ以外の行から構成されるテーブルには便利ですが、完全なサポートが利用可能になるまでの間、提供される代替手段です。その解決策は、いわゆるブラインド・テーブル(もっぱらレイアウトのために導入された目に見えないテーブル)を使うことです。何等かのHTMLのコーディングをしたことがある方なら、レイアウトのために(例えば欄を分けて綺麗に配置する場合)テーブルを使うことに慣れているに違いありません。

リスト2は、FOPのブラインド・テーブルでキープ・プロパティの使用方法を示したスタイルシートの抜粋です。スタイルシートのこのバージョンは、不適切な改ページを防ぎます。doc:figureテンプレートは1行目にラベルを2行目に画像を配置したブラインド・テーブルを作成します。keep-with-previousプロパティが2行目に適用されています。

リスト2:ブラインド・テーブル
<xsl:template match="doc:figure" mode="blind">
   <fo:table table-layout="fixed" width="100%">
      <fo:table-column column-width="proportional-column-width(1)"/>
      <fo:table-body>
         <fo:table-row padding-bottom="0.5em">
            <fo:table-cell>
               <fo:block font-family="Times Roman"
                         font-style="italic"
                         font-size="8pt">
                  <xsl:value-of select="@label"/>
               </fo:block>
            </fo:table-cell>
         </fo:table-row>
         <fo:table-row keep-with-previous="always">
            <fo:table-cell>
               <fo:block>
                  <fo:external-graphic src="url({@src})"
                                       width="{@width}"
                                       height="{@height}"/>
                </fo:block>
            </fo:table-cell>
         </fo:table-row>
     </fo:table-body>
  </fo:table>
</xsl:template>
<xsl:template match="doc:section" mode="blind">
   <fo:table table-layout="fixed" width="100%">
      <fo:table-column column-number="1"/>
      <fo:table-body>
         <fo:table-row keep-with-next="always">
            <fo:table-cell>
               <xsl:apply-templates select="doc:title" mode="blind"/>
            </fo:table-cell>
         </fo:table-row>
         <fo:table-row>
            <fo:table-cell>
               <xsl:apply-templates select="*[2]" mode="blind"/>
            </fo:table-cell>
         </fo:table-row>
     </fo:table-body>
  </fo:table>
  <xsl:apply-templates select="*[position() > 2]" mode="blind"/>
</xsl:template>

doc:sectionテンプレートも類似しています。この場合も同様に、2行からなるブラインド・テーブルを作成しています。1行目にタイトルが入り、2行目に最初の段落が入ります。最初の段落を選択するには注意する必要があります。私は文章が増加した時の柔軟性を加味した位置を考慮して、その段落を選びます。

実際のところブラインド・テーブルは、セクションのタイトルより、図のラベルやテーブル・タイトルのためには最適です。ただし、FOPはセルの中間で改ページするのを嫌っているようです。つまり、ブラインド・テーブルに長い段落が含まれている場合、その段落を改ページしないようにすると言うことです。これは意図したより多くの改ページが、次々と引き起こされるかもしれないということを意味します。文書中のどこで、このテクニックを利用すべきかを、判断しなければなりません。

うまい方法としては、短い段落だけにブラインド・テーブルを使用するのがよいかもしれません。(パラグラフの長さはstring-length()関数で判断できます)

要約

XSL-FOのおかげで、XML文書をPDFに変換するのは難しいことではありません。またこのヒントが明示するように、XSL-FOを使用すれば、文書の最終レイアウトをいろいろ制御できるようになります。


ダウンロード可能なリソース


関連トピック

  • 本記事で使用しているソースコードはこちらからダウンロードできます。ダウンロードしたファイルには、「改ページを意識しない」、「キープ・プロパティを使用する」、「ブラインド・テーブルを使用する」の3種類の方法でPDFを作成するスタイルシートと確認用の文書ファイルが入っています。2番目の方法はFOPでは動作しません。
  • コードをテストするためには、Xalan-JavaやXSL-FOレンダラのようなXSLTプロセッサが必要となります。FOPはすぐに試せるオープン・ソースのXSL-FOレンダラですが、XSL-FO標準を完全にはインプリメントしませんので注意が必要です。標準にこだわるのなら、RenderX社のXEPやアンテナハウス社のXSL Formatterのような市販のレンダラを使うべきです。
  • XSL-FOの関連記事は、developerWorks のチュートリアルにあります。
  • 既にHTMLをご存知であれば、HTML to Formatting Objects (FO) conversion guideは直ぐにお役に立つでしょう(developerWorks 、2003年2月)。
  • developerWorks のXML zoneには、XMLに関する記事が多数掲載されています。
  • IBM WebSphere Studioでは、Javaと他の言語の両方で、XML開発を自動化するためのツール・スイートが提供されています。
  • XMLおよび関連テクノロジーのIBM認証開発者になる方法についてはこちらを参照してください。
static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=242468
ArticleTitle=ヒント: 改ページとテーブルにXSL-FOを使う
publish-date=06112003