XMLサーバ/Cocoon自由自在!
最終回 XML文書からPDFファイルを生成する


XMLからPDFを出力してみよう

簡単なXML文書のサンプル

 それでは簡単なサンプルで動作の確認をしてみましょう。取りあえず難しい解説は抜きにして、リスト1を${COCOON_HOME}/samples/hello-fo.xmlというファイル名で保存してください。

<?xml version="1.0" encoding="Shift_JIS"?>
<?cocoon-format type="text/xslfo"?>


<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <fo:layout-master-set>
    <fo:simple-page-master master-name="Master"
        margin-top="10mm"
        margin-bottom="10mm"
        margin-right="10mm"
        margin-left="15mm"
        page-width="210mm"
        page-height="297mm">
          <fo:region-body margin-top="5mm"
          margin-bottom="5mm"
          margin-right="5mm"
          margin-left="5mm" />
    </fo:simple-page-master>
  </fo:layout-master-set>


  <fo:page-sequence master-name="Master">
    <fo:flow flow-name="xsl-region-body">
      <fo:block font-size="20pt"
      font-family="MS Gothic">
        こんにちは!
      </fo:block>
    </fo:flow>
  </fo:page-sequence>


</fo:root>
リスト1 簡単なサンプル(hello-fo.xml

 サンプルとしてはおなじみですが「こんにちは」という文字列をPDFで出力するものです。tomcatを起動した後、以下のURLをオープンしてください。

http://localhost:8080/cocoon/hello-fo.xml

 ただし、Internet Explorer(IE)でオープンする場合は以下のURLを用います。

http://localhost:8080/cocoon/hello-fo.xml?dummy=hello-fo.pdf

 これは、半分おまじないと思っていただいていいのですが、IEにこれからオープンするURLのファイルがPDFだと指示するためのものです。画面1のような出力を確認できればOKです。

画面1 サンプルの出力例

XSL-FOの内容

 ここで簡単にXSL-FOがどんなものか見ておきましょう。見てお分かりのとおり、ルートタグは<fo:root>になります。その下に、ページマスタの設定(<fo:layout-master-set>)と、ページシーケンスの設定(<fo:page-sequence>)がきます。

 いずれも聞き慣れない言葉ですが、ページマスタはレイアウトに用いるページのひな型を定義するもの、ページシーケンスは定義したページのひな型に流し込む内容、すなわち実際の文章や図を定義するものと考えればよいでしょう。

 ページシーケンスの定義で<fo:flow>タグが出てきますが、これが実際に流し込む段落や表をまとめるものになります。<fo:block>で囲まれた内容が、実際に流し込まれる1つの段落になります。不正確を承知でいうなら、HTMLの<p>タグのようなものをイメージしていただいたらよいでしょう。

 この中でCocoonと関連が深いのは2行目にある以下のプロセッシング・インストラクションです。

<?cocoon-format type="text/xslfo"?>

 処理するXML文書にこのプロセッシングインストラクションが含まれる場合、Cocoonはその文書をXSL-FOであると認識し、FOPを用いてPDFを生成しようとします。ですからこの行がまさに、PDF出力を行う場合のポイントといえます。

【コラム 〜 おまじないの必要性】
 IEでファイルをオープンする際、なぜおまじないが必要なのでしょうか。これはIEの仕様なのですが、HTTPにより取得したファイルをどのように処理するか決める際に、ファイルのContent-Typeだけでなく拡張子に基づいて判断するためです。

 Webアプリケーションを構築するうえで、これはいろいろと厄介な問題のタネになります。対処法はいくつかありますが、一番手軽なのは、今回のようにURLにダミーのパラメータを追加し、見た目上URLに拡張子が含まれるようにする方法です。いまいちスマートではありませんが……。


注文リストをPDFで出力

 それでは恒例ですが、注文票のデータをPDFの帳票として出力してみましょう。元データをリスト2に掲載しておきます(スタイルシートの指定の部分のみ追加の形になっています)。

<?xml version="1.0" encoding="Shift_JIS" ?>
<?xml-stylesheet type="text/xsl" href="order-fo.xsl" ?>
<?cocoon-process type="xslt" ?>


<order>

  <item>
    <product>
      <code>X-200-001</code>
      <name>万能耳掻き</name>
      <price currency="YEN">300</price>
    </product>
    <quantity>1000</quantity>
  </item>


  <item>
    <product>
      <code>X-201-002</code>
      <name>鍼灸サンダル</name>
      <price currency="YEN">1200</price>
    </product>
    <quantity>500</quantity>
  </item>


  <item>
    <product>
      <code>X-200-005</code>
      <name>爪切りセット</name>
      <price currency="YEN">1200</price>
    </product>
    <quantity>2000</quantity>
  </item>


</order>
リスト2 注文データ(hello.xml)

 出力イメージとしては、画面2のようなものを目指すことにします。帳票ですからもう少しレイアウトに凝りたいところですが、サンプルということでここではシンプルなものにとどめてあります。

画面2 注文データをPDF帳票で出力

 まずは画面2のような出力を得るためのXSL-FO文書について確認してみましょう(リスト3)。そのうえで、元データから変換するためのスタイルシートについて見てみることにします。

<?xml version="1.0" encoding="Shift_JIS"?>
<?cocoon-format type="text/xslfo"?>


<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <fo:layout-master-set>
    <fo:simple-page-master master-name="page-master-0"
      margin-top="10mm"
      margin-bottom="10mm"
      margin-right="10mm"
      margin-left="15mm"
      page-width="210mm"
      page-height="297mm">
      <fo:region-body region-name="region-body-0"
        margin-top="5mm"
        margin-bottom="5mm"
        margin-right="5mm"
        margin-left="5mm" />
    </fo:simple-page-master>
  </fo:layout-master-set>


  <fo:page-sequence master-name="page-master-0">

    <fo:flow flow-name="region-body-0" font-family="MS Gothic">

      <fo:block font-size="20pt"
      text-align="center" >注文票</fo:block>


      <fo:block space-before="10mm"
      space-start="10mm">


        <fo:table font-size="12pt"
        border="solid 1px"
        padding-left="2mm"
        padding-right="2mm">


          <fo:table-column column-width="40mm" />
          <fo:table-column column-width="80mm" />
          <fo:table-column column-width="20mm" />
          <fo:table-column column-width="20mm" />


          <fo:table-header border-bottom="solid 0.1px">

            <fo:table-row>
              <fo:table-cell column-number="1">
                <fo:block>商品コード</fo:block>
              </fo:table-cell>
              <fo:table-cell column-number="2">
                <fo:block>商品名</fo:block>
              </fo:table-cell>
              <fo:table-cell column-number="3">
                <fo:block text-align="end">単価</fo:block>
              </fo:table-cell>
              <fo:table-cell column-number="4">
                <fo:block text-align="end">数量</fo:block>
              </fo:table-cell>
            </fo:table-row>
          </fo:table-header>


          <fo:table-body>

            <fo:table-row>
              <fo:table-cell column-number="1">
                 <fo:block>X-200-001</fo:block>
              </fo:table-cell>
              <fo:table-cell column-number="2">
                <fo:block>万能耳掻き</fo:block>
              </fo:table-cell>
              <fo:table-cell column-number="3">
                <fo:block text-align="end">300</fo:block>
              </fo:table-cell>
              <fo:table-cell column-number="4">
                <fo:block text-align="end">1000</fo:block>
              </fo:table-cell>
            </fo:table-row>


    :
    :
    :
          </fo:table-body>

        </fo:table>
      </fo:block>
    </fo:flow>
  </fo:page-sequence>
</fo:root>

リスト3 PDF帳票を出力するためのXSL-FO文書(order-fo-image.xml

 主だったポイントについて順に見ていきます。先ほども書いたとおり、<fo:layout-master-set>により、出力に用いるページのレイアウトのひな型を定義します。ここでは<fo:simple-page-master>を用いて、もっともシンプルなレイアウトを定義してあります。見てお分かりのとおり、page-widthおよびpage-height属性を用いてページのサイズを指定し、margin-topやmargin-bottomといった属性でページマージンを指定します。<fo:region-body>が、実際のコンテンツを収めるリージョン(枠)を定義しています。ここでは1つのリージョンしか定義していませんが、ほかにもヘッダやフッタなどに相当するリージョンを含めることもできます。

 ここでのポイントはページマスタおよびリージョンにそれぞれ、page-master-0、region-body-0という名前を付けているところです。以下の<fo:page-sequence>で実際に用いるページマスタの名前を指定し、その下の<fo:flow>でコンテンツを流し込むリージョンの名前を指定しているところに注意してください。

 実際のコンテンツは、<fo:block>や<fo:table>を用いて<fo:flow>の中に定義します。先ほども書いたように、<fo:block>はHTMLでいうところの<p>タグのようなものです。ここで初めて出てくるのは<fo:table>タグですが、これもHTMLの<table>のようなものだと考えてよいでしょう。<fo:row>でテーブルの行を定義し、<fo:header>および<fo:cell>で見出しやセルを定義するあたりは、HTMLのテーブル定義と似ていますね。少し違うところは、<fo:column>タグを用いてあらかじめ表のカラム構成を定義する必要があるところです。

 さて、リスト1のXMLデータをこの出力イメージに合うように変換するスタイルシートを用意しましょう(リスト4)。

<?xml version="1.0" encoding="Shift_JIS"?>

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">


  <xsl:output method="xml" encoding="Shift_JIS" />

    <xsl:template match="/">

      <xsl:processing-instruction name="cocoon-format">
        type="text/xslfo"
      </xsl:processing-instruction>


      <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

        <fo:layout-master-set>
          <fo:simple-page-master master-name="page-master-0"
          margin-top="10mm"
          margin-bottom="10mm"
          margin-right="10mm"
           margin-left="15mm"
          page-width="210mm"
          page-height="297mm">
            <fo:region-body region-name="region-body-0"
            margin-top="5mm"
            margin-bottom="5mm"
            margin-right="5mm"
            margin-left="5mm" />
          </fo:simple-page-master>
        </fo:layout-master-set>


        <fo:page-sequence master-name="page-master-0">
          <fo:flow flow-name="region-body-0" font-family="MS Gothic">
            <fo:block font-size="20pt"
            text-align="center" >注文票</fo:block>

                <fo:block space-before="10mm" space-start="10mm">

              <fo:table font-size="12pt"
              border="solid 1px"
              padding-left="2mm"
              padding-right="2mm">


                <fo:table-column column-width="40mm" />
                <fo:table-column column-width="80mm" />
                <fo:table-column column-width="20mm" />
                <fo:table-column column-width="20mm" />


                <fo:table-header border-bottom="solid 0.1px">

                  <fo:table-row>
                    <fo:table-cell column-number="1">
                      <fo:block>商品コード</fo:block>
                    </fo:table-cell>
                    <fo:table-cell column-number="2">
                      <fo:block>商品名</fo:block>
                    </fo:table-cell>
                    <fo:table-cell column-number="3">
                      <fo:block text-align="end">単価</fo:block>
                    </fo:table-cell>
                    <fo:table-cell column-number="4">
                      <fo:block text-align="end">数量</fo:block>
                    </fo:table-cell>
                  </fo:table-row>
                </fo:table-header>


                <fo:table-body>

                  <xsl:for-each select="/order/item">
                    <fo:table-row>
                      <fo:table-cell column-number="1">
                        <fo:block><xsl:value-of select="product/code" /></fo:block>
                      </fo:table-cell>
                      <fo:table-cell column-number="2">
                        <fo:block><xsl:value-of select="product/name" /></fo:block>
                      </fo:table-cell>
                      <fo:table-cell column-number="3">
                        <fo:block text-align="end"><xsl:value-of select="product/price" /></fo:block>
                     </fo:table-cell>
                     <fo:table-cell column-number="4">
                       <fo:block text-align="end"><xsl:value-of select="quantity" /></fo:block>
                    </fo:table-cell>
                  </fo:table-row>
                </xsl:for-each>


              </fo:table-body>
            </fo:table>

          </fo:block>
        </fo:flow>
      </fo:page-sequence>
    </fo:root>
  </xsl:template>
</xsl:stylesheet>

リスト4 注文データを変換するためのスタイルシート(order-fo.xsl

 スタイルシートの基本的な構造についてはいままで見てきたスタイルシートとほぼ同じです。全体の出力イメージを文書ルートに対するテンプレートとして定義し(<xsl:template>)、各行を<xsl:for-each>で出力しています。

 ここでのポイントは、<xsl:processing-instruction>を用いて、変換結果に以下のプロセッシング・インストラクションが含まれるようにしているところです。

<?cocoon-format type="text/xslfo"?>

 Cocoonは、処理したXML文書に自分自身(Cocoon)に対するプロセッシング・インストラクションが含まれていることを確認すると、その指示に従って、その出力結果を繰り返し処理します。実はこれが、Cocoonの内部動作の基本原理(Reactor-Model)なのです。

 動作を確認するために以下のURLをオープンします。

http://localhost:8080/cocoon/order-fo.xml

 IEを用いる場合は、以下のようにおまじないを忘れないように注意してください。

http://localhost:8080/cocoon/order-fo.xml?dummy=order-fo.pdf

 画面2のような帳票を確認できればOKです。

まとめ

 4回にわたり、Cocoonの使い方について解説してきた連載も今回で最後です。基本的なポイントに絞って解説してきましたが、XMLをさまざまな形で表現できるCocoonの実力は十分に分かっていただけたのではないでしょうか。

 今回は取り上げませんでしたが、XSPを用いた動的なページ生成や、Batikを用いたベクタグラフィクスの描画など、まだまだ魅力的な機能があります。今回の連載を足掛かりに、そういった機能の利用にもぜひ挑戦してみてください。

 それでは機会があればまたお会いしましょう。

【コラム 〜 横浜ベイキットと Baykit XML Server】
 今回紹介した「横浜ベイキット」は、XML関連のオープンソース・ソフトウェアを開発しているボランティア団体であり、XMLアプリケーションサーバ「BXS」はその代表作の1つです。

 BXSでは、横浜ベイキットオリジナルのスクリプト言語であるXiや、本連載でも紹介している日本語化されたCocoonなどが簡単に利用できるようになっており、XMLを用いたアプリケーション構築に力を発揮します。

 横浜ベイキットではBXS以外にもさまざまソフトウェアの開発作業を行っています。FOP 0.20.2とCocoon 1.8.2の組み合わせで簡単に日本語を用いるための改変作業などもその1つで、その成果はこの連載で紹介したとおりです。その活動に興味を持たれた方は、ぜひ横浜ベイキットのサイトをのぞいてみてください。

■関連記事
実践! XMLアプリケーションサーバ「BXS」


Index
XMLサーバ/Cocoon自由自在!
最終回 XML文書からPDFファイルを生成する
  PDFモジュールをCocoonに組み込む
XMLからPDFを出力してみよう

「XMLサーバ/Cocoon自由自在!」


XML & SOA フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日月間