1.Apache FOP1.1の新機能
Apache FOP 1.1が、ついにリリースされました。私にとって一番の目玉は、暗号化鍵の鍵長が、RC4 40bit から,高強度のRC4 128bitを利用できるようになったことです。巷には、PDFのセキュリティを解除するソフトや、オンラインのサービスが展開されていますので、PDFのパスワードの管理はしっかりしたいところです。デフォルトでは、暗号鍵の長さは、40bitなので、明示的に指定しなければなりません。図 1. EncryptPDF.javaでは、42行目で高強度のRC4 128bitを使用するようにしています。また、38行目で、印刷の品質を設定しています。「Apache™ FOP: PDF encryption.」にて、レンダラのオプションで設定できる項目が記載されています。文書を開くパスワードで、PDFを開いた場合と、権限パスワードで、PDFを開いた場合で、PDFの文書プロパティの「PDFの文書に関する制限の概要」に違いがあります。
(SyntaxHighlighterを使用して、プログラムを掲載しています。ブラウザー上で、各々の図の右上のアイコンをマウスでクリックすることで、ソースを表示したり、クリップボードにコピーしたり、印刷したりできます。注意点として、「さらに記事を読み込む」のリンクで、この記事を読み込んだ場合に、SyntaxHighlighterによる表示が不正になる場合がありますので、恐れ入りますが、タイトルをクリックして再表示して下さい。)
図 1. EncryptPDF.java
package com.wordpress.okulejp.encryptpdf;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.xml.transform.Result;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.pdf.PDFEncryptionParams;
public class EncryptPDF {
public static void main(String[] args) {
OutputStream out = null;
try {
FopFactory fopFactory = FopFactory.newInstance();
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
fopFactory.setUserConfig(new File("fop.xconf"));
out = new FileOutputStream("src/com/wordpress/okulejp/encryptpdf/encrypt.pdf");
out = new BufferedOutputStream(out);
//セキュリティ設定。
PDFEncryptionParams encryptionParams = new PDFEncryptionParams();
//文書を開くパスワード設定。
encryptionParams.setUserPassword("user");
//権限パスワード設定。
encryptionParams.setOwnerPassword("owner");
//印刷可否設定。
encryptionParams.setAllowPrint(true);
//高解像度印刷可否設定。
encryptionParams.setAllowPrintHq(true);
//内容のコピー可否設定。
encryptionParams.setAllowCopyContent(false);
//暗号鍵の鍵長の指定。
encryptionParams.setEncryptionLengthInBits(128);
//レンダラのオプションを設定する。
foUserAgent.getRendererOptions().put("encryption-params", encryptionParams);
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
File fofile = new File("src/com/wordpress/okulejp/encryptpdf/encryptPDF.fo");
Result result = new SAXResult(fop.getDefaultHandler());
StreamSource streamSource = new StreamSource(fofile);
TransformerFactory trf = TransformerFactory.newInstance();
Transformer transformer = trf.newTransformer();
//PDF出力。
transformer.transform(streamSource, result);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException io) {
io.printStackTrace();
System.exit(1);
}
}
}
}
図 2. encryptPDF.fo
<?xml version="1.0" encoding="Shift_JIS"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="ja">
<fo:layout-master-set>
<fo:simple-page-master page-width="210mm" page-height="297mm" margin-top="0mm" margin-left="0mm"
margin-right="0mm" margin-bottom="0mm" master-name="PageMaster">
<fo:region-body margin-top="15mm" margin-left="15mm" margin-right="15mm" margin-bottom="15mm" />
<fo:region-before extent="15mm" />
<fo:region-after extent="15mm" />
<fo:region-start extent="15mm" />
<fo:region-end extent="15mm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="PageMaster">
<fo:flow flow-name="xsl-region-body">
<fo:block font-family="MS 明朝" font-size="xx-large">Hellow encrypted PDF</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
図 3. 暗号化したPDF(クリックするとPDFを表示します。権限パスワードは、「owner」です。文書を開くパスワードは、「user」です。)

2.最後に
このブログ記事が、実際のシステム開発に役立った方は、ぜひいいねボタンを押してください。またコメントもよろしくお願いいたします。(コメントするのに電子メールアドレス、名前、ウェブサイトは必須ではありません。入力なしで匿名でコメント可能です。)
©中條勝徳 and okulejp.com, 2012.
1.はじめに
このブログにたどりついた、検索エンジンのキーワードを調べると、SVGを利用したApache FOPの使用方法について、知りたいと思われる読者がいると思われます。そのような理由で、今日は、簡単にSVGを利用したApache FOPの使用方法について、とても簡単に説明したいと思います。連携の仕方は、すごく簡単です。図 1は、SVGをXSL-FO文書に埋め込み場合です。15行目にある、<fo:instream-foreign-object>要素を使用します。子要素は、SVG形式そのものです。図 2は、XSL-FO文書の外部にあるSVGを利用する場合です。15行目にある、<fo:external-graphic>要素を使用します。また注意点としては、src属性で指定するURIは、XSL-FO文書からの相対パスではなく、プログラムから見た相対パスです。図 3のhellow_svg.svgは、XSL-FO文書で参照されるSVGです。図 4のhellow_svg.pdfは、出力結果です。
Apache FOPのバイナリ配布に含まれるサンプルプログラムに、SVGを利用したサンプルがあります。パスは、「fop-1.0-bin/fop-1.0/examples/fo/svg」です。
(SyntaxHighlighterを使用して、XSL-FO、XMLを掲載しています。ブラウザー上で、各々の図の右上のアイコンをマウスでクリックすることで、ソースを表示したり、クリップボードにコピーしたり、印刷したりできます。注意点として、「さらに記事を読み込む」のリンクで、この記事を読み込んだ場合に、SyntaxHighlighterによる表示が不正になる場合がありますので、恐れ入りますが、タイトルをクリックして再表示して下さい。)
図 1. hellow_svg_embedding.fo
<?xml version="1.0" encoding="UTF-8" ?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xml:lang="ja">
<fo:layout-master-set>
<fo:simple-page-master page-width="210mm" page-height="297mm" margin-top="0mm" margin-left="0mm" margin-right="0mm" margin-bottom="0mm" master-name="PageMaster">
<fo:region-body margin-top="0mm" margin-left="0mm" margin-right="0mm" margin-bottom="0mm"/>
<fo:region-before extent="0mm" />
<fo:region-after extent="0mm" />
<fo:region-start extent="0mm" />
<fo:region-end extent="0mm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="PageMaster">
<fo:flow flow-name="xsl-region-body">
<fo:block>
<fo:instream-foreign-object content-width="210mm" content-height="297mm">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"
width="210mm" height="297mm" font-size="20"
stroke-width="0.1mm" font-family="MS 明朝">
<text x="4.9mm" y="10.5mm" fill="black">Hellow SVG with FOP</text>
</svg>
</fo:instream-foreign-object>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
図 2. hellow_svg_external.fo
<?xml version="1.0" encoding="UTF-8" ?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xml:lang="ja">
<fo:layout-master-set>
<fo:simple-page-master page-width="210mm" page-height="297mm" margin-top="0mm" margin-left="0mm" margin-right="0mm" margin-bottom="0mm" master-name="PageMaster">
<fo:region-body margin-top="0mm" margin-left="0mm" margin-right="0mm" margin-bottom="0mm"/>
<fo:region-before extent="0mm" />
<fo:region-after extent="0mm" />
<fo:region-start extent="0mm" />
<fo:region-end extent="0mm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="PageMaster">
<fo:flow flow-name="xsl-region-body">
<fo:block>
<fo:external-graphic src="hellow_svg.svg"/>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
図 3. hellow_svg.svg
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"
width="210mm" height="297mm" font-size="20"
stroke-width="0.1mm" font-family="MS 明朝">
<text x="4.9mm" y="10.5mm" fill="black">Hellow SVG with FOP</text>
</svg>
図 4. hellow_svg.pdf

2.注意事項
SVGを利用したデータ可視化のためのグラフ作成の記事で説明しましたが、XSL-FO文書を介して、SVGをPDFで出力する際、Apache FOPの制限で、フィルター処理を使用したSVGをPDFにすると正しく表示されませんので注意が必要です。FOPのバイナリ配布に含まれるサンプルプログラムの「ExampleSVG2PDF.java」を利用すれば、SVGを直接PDF化するので、フィルター処理のものもうまく処理されます。
3.総評
SVGには、ご存知のとおりいろいろな機能があります。SVGの機能を利用することで、既存の商用の帳票作成ツールでは、できないようなデザインを、自由自在にできます。Apache FOPの可能性と日本語設定の記事で紹介したとおり、コンビ請求書のようなデザインも可能です。
©中條勝徳 and okulejp.com, 2012.
1.はじめに
Apache FOPを利用するにあたり、Apache FOPのTIFF出力の方法 のブログで紹介したTIFF出力とPDF出力に使用したものと同じXSL-FOドキュメントを例に、実際のシステム開発で役立つ、Apache FOPの使用方法を説明したいと思います。Apache FOPを使用するために、帳票出力などに出力するデータは、XML形式のデータでなくてはなりません。しかし実際にXML形式で出力するとなると、DOMなどのXMLを扱うプログラムを利用しなければなりません。そのためのコストも余分にかかります。しかし、JAXBを利用することで、XMLを簡単に作成することができます。一般的な帳票の出力データ形式は、そんなに複雑なものではないはずです。ここでは、単一項目と、繰返し項目のデータを扱います。図 2. fax.xsdがそのためのXMLスキーマです。XMLスキーマは、XMLマスターベーシック試験より少し難しい程度で、この後にでてくるXSLTの部分に関しても、XMLマスターベーシック試験を取得している人なら難しくないはずです。
(SyntaxHighlighterを使用して、XMLスキーマ、XSLT、Java、XMLを掲載しています。ブラウザー上で、各々の図の右上のアイコンをマウスでクリックすることで、ソースを表示したり、クリップボードにコピーしたり、印刷したりできます。注意点として、「さらに記事を読み込む」のリンクで、この記事を読み込んだ場合に、SyntaxHighlighterによる表示が不正になる場合がありますので、恐れ入りますが、タイトルをクリックして再表示して下さい。)
2.帳票出力するまでの準備作業の流れ
- XSL-FO文書の実サンプルの作成。
- XMLの様式とXMLスキーマの作成。
- XSLTの作成。
- JAXBによるクラス生成。
- XMLとXSLTから、XSL-FOドキュメントを作成して、帳票を作成するJavaプログラムの作成。
XSL-FO文書の実サンプルを作成します。データのXMLの様式とXMLスキーマの作成をします。その後、XSL-FO文書の動的に変わる部分を、XSLTを使用して書き直します。XMLスキーマを元に、JAXBを使用してXMLとJavaオブジェクトをマッピングするクラスを作成します。生成されたクラスを使用してXMLデータを作成します。ここで示した流れ以外にもいろいろあると思いますが、これが一番わかりやすいと思います。
3.実際の出力結果
図 1. PDF出力(クリックするとPDFを表示します。)


4.帳票出力項目について
帳票の出力項目は、単一出力では、「発行日」、「送信者会社名」、「送信者TEL」、「送信者FAX」、「宛先名」、「宛先TEL」、「宛先FAX」、「宛先郵便番号」、「合計金額」、「消費税」と「合計金額(税込)」の11項目。複数出力では、「商品名」、「価格」、「単価」と「金額」の4項目で、合計15項目です。
5.fax.xsdのXMLスキーマについて
送信者と宛先の内容は共通化できるので、使用するXMLの型は共通となっています。ただし、送信者の方には、郵便番号がないので、29行目で、minOccurs=”0″としています。複数出力の項目は、複数出力されるように、maxOccurs=”unbounded”としています。
図 2. fax.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema version="1.0" targetNamespace="https://okulejp.wordpress.com/fax"
xmlns:tns="https://okulejp.wordpress.com/fax" xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:element name="fax" type="tns:faxType"/>
<xs:complexType name="faxType">
<xs:sequence>
<xs:element ref="tns:issueDate"/>
<xs:element ref="tns:destination"/>
<xs:element ref="tns:sender"/>
<xs:element ref="tns:items"/>
<xs:element ref="tns:sum"/>
</xs:sequence>
</xs:complexType>
<xs:element name="issueDate" type="xs:string"/>
<xs:element name="destination" type="tns:company"/>
<xs:element name="sender" type="tns:company"/>
<xs:element name="items" type="tns:itemsType"/>
<xs:element name="sum" type="tns:sumType"/>
<xs:complexType name="company">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="tel" type="xs:string"/>
<xs:element name="fax" type="xs:string"/>
<xs:element name="postCode" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="itemsType">
<xs:sequence maxOccurs="unbounded">
<xs:element ref="tns:item"/>
</xs:sequence>
</xs:complexType>
<xs:element name="item" type="tns:itemType"/>
<xs:complexType name="itemType">
<xs:sequence>
<xs:element name="productName" type="xs:string"/>
<xs:element name="number" type="xs:int"/>
<xs:element name="unitPrice" type="xs:int"/>
<xs:element name="price" type="xs:int"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="sumType">
<xs:all>
<xs:element name="taxAmount" type="xs:int"/>
<xs:element name="consumptionTax" type="xs:int"/>
<xs:element name="tax-includedPrice" type="xs:int"/>
</xs:all>
</xs:complexType>
</xs:schema>
6.fax.xslのXSLTについて
XSLTにおける関数使用について、XSLTで使用できる関数は、バージョンが1.0なら、「XML Path Language (XPath) Version 1.0」の4 Core Function Libraryにあるもの、2.0なら「XQuery 1.0 and XPath 2.0 Functions and Operators(Second Edition)」にあるものが使用できます。
XSLTでXpathの関数を用いて、金額から5%かけて消費税を求めても良かったのですが、消費税が変わったときに、XSLTも変更しないといけないので、XMLデータから消費税を取得しました。XMLデータから消費税率を取得して、XSLTで消費税を計算する方法もあると思います。XMLデータで取得したものを出力するか、XSLTで計算したものを出力するかは、ケースバイケースだと思います。
47行目で、fo:table要素に、table-omit-footer-at-break=”true”を指定すると、フッターは繰り返されません。
81行目で、format-number(fax:sum/fax:consumptionTax, ‘#,###’)の’#,###’は、3桁ごとにカンマを表示するという意味です。
107行目で、fo:table-cell要素に、keep-together.within-column属性を追加して、その属性値にalwaysを指定しています。行が複数ページにまたがるのを防ぐためです。ない場合は、項番18の内容が複数ページに分割されます。
119行目で、ループが最終行の場合、fo:table-cell要素に、属性値が、last-pageのid属性を追加しています。これは、23行目のfo:page-number-citation要素が参照していて、最終ページ番号を取得しています。(Apache FOPが、実際の出力時に、商品詳細の最後の部分がどのページ番号になるのかを計算してくれます。)
市販の帳票作成ツールでは、表のソート機能がありますが、XSLTでも、xsl:sort要素を使用することでソートできます。102行目のxsl:for-eachの後に、xsl:sort要素を追加します。またxsl:sort要素を複数追加した場合は、ソートキーは複数になります。
図 3. fax.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:fax="https://okulejp.wordpress.com/fax">
<xsl:template match="/">
<xsl:apply-templates select="fax:fax" />
</xsl:template>
<xsl:template match="fax:fax">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xml:lang="ja">
<fo:layout-master-set>
<fo:simple-page-master page-height="297mm" page-width="210mm" margin-top="0mm" margin-left="0mm" margin-right="0mm" margin-bottom="0mm"
master-name="PageMaster">
<fo:region-body margin-top="10mm" margin-left="10mm" margin-right="10mm" margin-bottom="20mm" />
<fo:region-before extent="10mm" />
<fo:region-after extent="10mm" />
<fo:region-start extent="10mm" />
<fo:region-end extent="10mm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="PageMaster" font-family="MS ゴシック">
<fo:static-content flow-name="xsl-region-after">
<fo:block text-align="center" font-size="15pt">
( <fo:page-number />/<fo:page-number-citation ref-id="last-page" /> )
</fo:block>
</fo:static-content>
<fo:flow font-family="MS ゴシック" flow-name="xsl-region-body">
<fo:block-container absolute-position="absolute" top="0mm" left="130mm">
<fo:block font-size="11pt">発行日 <xsl:value-of select="fax:issueDate" /></fo:block>
</fo:block-container>
<fo:block text-align="center" space-before="2mm" border-after-style="solid" border-after-width="thin" font-size="xx-large">御請求書</fo:block>
<fo:block-container absolute-position="absolute" top="20mm" left="00mm">
<fo:block font-size="15pt">会社名 <xsl:value-of select="fax:destination/fax:name" /></fo:block>
<fo:block font-size="13pt">TEL <xsl:value-of select="fax:destination/fax:tel" /></fo:block>
<fo:block font-size="13pt">FAX <xsl:value-of select="fax:destination/fax:fax" /></fo:block>
<fo:block></fo:block>
</fo:block-container>
<fo:block-container absolute-position="absolute" top="25mm" left="150mm">
<fo:block font-size="14pt"><xsl:value-of select="fax:sendern/fax:name" /></fo:block>
<fo:block font-size="12pt">〒 <xsl:value-of select="fax:sender/fax:postCode" /></fo:block>
<fo:block font-size="12pt">TEL <xsl:value-of select="fax:sender/fax:tel" /></fo:block>
<fo:block font-size="12pt">FAX <xsl:value-of select="fax:sender/fax:fax" /></fo:block>
<fo:block></fo:block>
</fo:block-container>
<fo:block-container id="test" absolute-position="absolute" top="50mm" left="00mm">
<fo:block font-size="12pt">下記の通り、ご請求申し上げます。</fo:block>
</fo:block-container>
<fo:table table-layout="fixed" font-family="MS ゴシック" top="100%" width="20%" font-size="14pt" space-before="47mm" table-omit-footer-at-break="true">
<fo:table-column column-number="1" column-width="15mm" />
<fo:table-column column-number="2" column-width="80mm" />
<fo:table-column column-number="3" column-width="15mm" />
<fo:table-column column-number="4" column-width="40mm" />
<fo:table-column column-number="5" column-width="40mm" />
<fo:table-header background-color="gray">
<fo:table-row>
<fo:table-cell border-style="solid">
<fo:block text-align="center" margin-top="0.2em" margin-bottom="0.2em">項番</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid">
<fo:block text-align="center" margin-top="0.2em" margin-bottom="0.2em">商品名</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid">
<fo:block text-align="center" margin-top="0.2em" margin-bottom="0.2em">数量</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid">
<fo:block text-align="center" margin-top="0.2em" margin-bottom="0.2em">単価</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid">
<fo:block text-align="center" margin-top="0.2em" margin-bottom="0.2em">金額</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-footer>
<fo:table-row>
<fo:table-cell number-columns-spanned="3" number-rows-spanned="3">
<fo:block>備考</fo:block>
</fo:table-cell>
<fo:table-cell background-color="gray" padding="4pt" border-style="solid">
<fo:block font-weight="bold" text-align="center">合計</fo:block>
</fo:table-cell>
<fo:table-cell padding="4pt" border-style="solid">
<fo:block font-weight="bold" text-align="right"><xsl:value-of select="format-number(fax:sum/fax:taxAmount, '#,###')" /></fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell background-color="gray" padding="4pt" border-style="solid">
<fo:block font-weight="bold" text-align="center">消費税</fo:block>
</fo:table-cell>
<fo:table-cell padding="4pt" border-style="solid">
<fo:block font-weight="bold" text-align="right"><xsl:value-of select="format-number(fax:sum/fax:consumptionTax, '#,###')" /></fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell background-color="gray" padding="4pt" border-style="solid">
<fo:block font-weight="bold" text-align="center" font-family="MS Pゴシック">合計(税込)</fo:block>
</fo:table-cell>
<fo:table-cell padding="4pt" border-style="solid">
<fo:block font-weight="bold" text-align="right"><xsl:value-of select="format-number(fax:sum/fax:tax-includedPrice, '#,###')" /></fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-footer>
<fo:table-body>
<xsl:for-each select="fax:items/fax:item">
<fo:table-row>
<fo:table-cell border-style="solid" padding="0.4em">
<fo:block text-align="center"><xsl:value-of select="position()" /></fo:block>
</fo:table-cell>
<fo:table-cell keep-together.within-column="always" border-style="solid" padding="0.4em">
<fo:block>
<xsl:value-of select="fax:productName" />
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="0.4em">
<fo:block text-align="right"><xsl:value-of select="fax:number" /></fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="0.4em">
<fo:block text-align="right"><xsl:value-of select="format-number(fax:unitPrice, '#,###')" /></fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="0.4em">
<xsl:if test="position() = last()">
<xsl:attribute name="id">last-page</xsl:attribute>
</xsl:if>
<fo:block text-align="right"><xsl:value-of select="format-number(fax:price, '#,###')" /></fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
7.JAXBの使用方法
fax.xsdのスキーマを元に、バインディングコンパイラxjcを用いてJavaのクラスに生成します。コマンドラインから実行する方法と、Eclipseから実行する方法を示します。
●コマンドイランから実行する方法。スキーマファイル名を引数にしてxjcを呼び出すだけです。バインディングコンパイラxjcは、JDK 6以上に含まれています。
カレントディレクトリのcom/wordpress/okulejp/faxフォルダに7つのファイルが生成されました。
C:\Users\okule>"C:\Program Files (x86)\Java\jdk1.6.0_30\bin"\xjc C:\temp\jaxb\fax.xsd
parsing a schema...
compiling a schema...
com\wordpress\okulejp\fax\Company.java
com\wordpress\okulejp\fax\FaxType.java
com\wordpress\okulejp\fax\ItemType.java
com\wordpress\okulejp\fax\ItemsType.java
com\wordpress\okulejp\fax\ObjectFactory.java
com\wordpress\okulejp\fax\SumType.java
com\wordpress\okulejp\fax\package-info.java
C:\Users\okule>
●Eclipseから実行する方法。
図 4. fax.xsdを右クリックして、コンテキストメニューを表示させ、「Generate」→「JAXB Classes…」を選択します。

図 5. New JAXB Classes from schema設定画面が表示されたら、「Finish」ボタンを押下します。

8.Fax.javaについて
XMLの要素名とJavaの型名が対応しているので、DOMに比べてXMLを作成するのが簡単で、それでいてXMLスキーマ違反になりにくいです。
74から77行目で、XMLスキーマ検証を行っています。
クラスパスにXSLTプロセッサのプロバイダが、クラスパス上に複数ある場合に、使用するXSLTプロセッサを指定できます。88と90行目で、Xalanを使う場合と、Saxonを使う場合が示されています。詳細は、TransformerFactory.newInstance(String factoryClassName, ClassLoader classLoader) に載っています。
図 6. Fax.java
package com.wordpress.okulejp.fax;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.StringReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.transform.Result;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.XMLConstants;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
public class Fax {
public static void main(String[] args) {
OutputStream out = null;
try {
JAXBContext context = JAXBContext.newInstance("com.wordpress.okulejp.fax");
ObjectFactory ojb = new ObjectFactory();
//fax要素の作成
FaxType faxType = ojb.createFaxType();
JAXBElement<FaxType> fax = ojb.createFax(faxType);
//発行日
faxType.setIssueDate("平成24年08月12日");
//宛先
Company company = ojb.createCompany();
company.setName("株式会社A総務部御中");
company.setTel("03-3333-3333");
company.setFax("03-3333-3334");
faxType.setDestination(company);
//送信者
company = ojb.createCompany();
company.setName("株式会社B");
company.setTel("05-5555-5555");
company.setFax("05-5555-5556");
company.setPostCode("999-9999");
faxType.setSender(company);
//商品詳細
ItemType itemType = null;
ItemsType itemsType = ojb.createItemsType();
for(int i=0; i<22; i++) {
itemType = ojb.createItemType();
itemType.setProductName("ブルーレイレコーダー");
itemType.setNumber(1);
itemType.setUnitPrice(120000);
itemType.setPrice(120000);
itemsType.getItem().add(itemType);
}
faxType.setItems(itemsType);
itemsType.getItem().get(3).setProductName("ブルーレイレコーダー12345678901234567890");
itemsType.getItem().get(17).setProductName("ブルーレイレコーダー1234567890123456789012345678901234567890");
//金額合計
SumType sumType = ojb.createSumType();
sumType.setTaxAmount(2640000);
sumType.setConsumptionTax(132000);
sumType.setTaxIncludedPrice(2772000);
faxType.setSum(sumType);
// XMLスキーマ検証
Marshaller marshaller = context.createMarshaller();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(new File("src/com/wordpress/okulejp/fax/fax.xsd"));
marshaller.setSchema(schema);
// マーシャリング
StringWriter marshallerStringWriter = new StringWriter();
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(fax, marshallerStringWriter);
System.out.println(marshallerStringWriter.toString());
//TransformerFactoryクラスの実装を選択する。(明示しない場合は、プラットホームのデフォルトのものが使用される。)
//Xalanを使用する場合。
//TransformerFactory trf = TransformerFactory.newInstance("org.apache.xalan.processor.TransformerFactoryImpl", null);
//Saxonを使用する場合。Saxonは、XSLT2.0に対応。
//TransformerFactory trf = TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl", null);
//入力XMLとXSLTから、XSL-FOドキュメントを作成する。
TransformerFactory trf = TransformerFactory.newInstance();
Transformer transformer = trf.newTransformer(new StreamSource("src/com/wordpress/okulejp/fax/fax.xsl"));
java.util.Properties xmlProps = new java.util.Properties();
xmlProps.setProperty(OutputKeys.VERSION, "1.0");
xmlProps.setProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperties(xmlProps);
StringReader stringReader = new StringReader(marshallerStringWriter.toString());
StreamSource streamSource = new StreamSource(stringReader);
StringWriter foStringWriter = new StringWriter();
StreamResult streamResult = new StreamResult(foStringWriter);
transformer.transform(streamSource, streamResult);
System.out.println(foStringWriter.toString());
//XSL-FOドキュメントから、PDFを作成する。
FopFactory fopFactory = FopFactory.newInstance();
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
fopFactory.setUserConfig(new File("fop.xconf"));
out = new FileOutputStream("src/com/wordpress/okulejp/fax/fax.pdf");
out = new BufferedOutputStream(out);
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
Result result = new SAXResult(fop.getDefaultHandler());
stringReader = new StringReader(foStringWriter.toString());
streamSource = new StreamSource(stringReader);
transformer = trf.newTransformer();
transformer.transform(streamSource, result);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
} finally {
try {
if(out!=null) {
out.close();
}
} catch(IOException io) {
io.printStackTrace();
System.exit(1);
}
}
}
}
図 7. fax.xml(Fax.javaで作成されたXML。)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<fax xmlns="https://okulejp.wordpress.com/fax">
<issueDate>平成24年08月12日</issueDate>
<destination>
<name>株式会社A総務部御中</name>
<tel>03-3333-3333</tel>
<fax>03-3333-3334</fax>
</destination>
<sender>
<name>株式会社B</name>
<tel>05-5555-5555</tel>
<fax>05-5555-5556</fax>
<postCode>999-9999</postCode>
</sender>
<items>
<item>
<productName>ブルーレイレコーダー</productName>
<number>1</number>
<unitPrice>120000</unitPrice>
<price>120000</price>
</item>
<item>
<productName>ブルーレイレコーダー</productName>
<number>1</number>
<unitPrice>120000</unitPrice>
<price>120000</price>
</item>
:
:
省略
:
:
<item>
<productName>ブルーレイレコーダー</productName>
<number>1</number>
<unitPrice>120000</unitPrice>
<price>120000</price>
</item>
</items>
<sum>
<taxAmount>2640000</taxAmount>
<consumptionTax>132000</consumptionTax>
<tax-includedPrice>2772000</tax-includedPrice>
</sum>
</fax>
9.総評
fax.xsdから、JAXB Classesを生成すれば、XMLを意識することなく、Javaのsetterとgetterメソッドを用いて、XMLデータを作成するのがとても簡単なのがわかります。特に、DOMによるXMLを作成したことがある人なら、すごく簡単にXMLを作成できることが実感できると思います。固定様式の帳票を作成するときの入力XMLデータの様式は、一定または、ほぼ一定であると思うので、XMLスキーマファイルを難なく作成できると思います。JAXBをEclipse上で簡単に利用できるので、XMLバインドツールの敷居が低くなったと思います。またEclipseでは、XMLスキーマ、XSLT2.0のコード補完機能が利用できるので開発効率が上がります。それに加えて、XMLスキーマを選択して、マウスで右クリックしてコンテキストメニューを表示し、XMLファイルを生成することができます。またこのブログで掲載したとおり、fop.xsdを利用することで、Apache FOPのコード補完機能も利用できます。
10.参照
「Java SE 6完全攻略」第73回 JAXB その1
上記のサイトは、JAXBをより深く学ぶのに非常に役立ちます。いろいろなサイトを検索して探しましたが、一番わかりやすくて体系づけられて説明されています。連載は、「第80回 JAXB その8」まであります。
©中條勝徳 and okulejp.com, 2012.