JasperReportsでPDFを作成する
JavaでPDFを作成するJasperReportsの使い方について解説します。少し古いライブラリですが企業がバックについてますし、継続的にメンテナンスはされているようです。
JasperReportでPDFを作成するには、
- JasperReport Studioでテンプレートを作成する
- Javaプログラムでデータを流し込んでPDFを作成する
と言う手順を踏みます。JasperReport StudioではGUIからText、Imageといったパーツを配置しますが、裏ではjrxml形式のファイルを吐き出しています。このjrxmlをコンパイルした.jasperという拡張子のファイルをJavaのプログラムから読み込み、PDFを作成するというのが通常の手順です。
PDF出力のサンプルコードは次のようになります。大抵の場合はこれで事足りると思います。
// 入力パス、出力パス Path in = ... Path out = ... // .jasperを読み込む // // .jrxmlを直接読み込むこともできる // // JasperReport report = // JasperCompileManager.compileReport(in.toAbsolutePath().toString()); JasperReport report = (JasperReport) JRLoader.loadObject(in.toAbsolutePath().toFile()); // パラメータ、データソースの準備 Map<String, Object> parameters = new HashMap<>(); Collection<Map<String, ?>> source = new ArrayList<>(); ... // パラメータ、データソースの流し込み // ここでは JRMapCollectionDataSource を利用しているが、他にも JRBeanCollectionDataSource などがある。 // データソースが空の時は空の JRMapCollectionDataSource ではなく JREmptyDataSource を利用する。 // 空の JRMapCollectionDataSourc を利用すると JasperReports 6.7.0 では真っ白なPDFが作成され、エラーも発生しない問題があった。 JasperPrint print = JasperFillManager.fillReport(report, parameters, new JRMapCollectionDataSource(source)); // 書き出し JasperExportManager.exportReportToPdfFile(print, out.toAbsolutePath().toString());
テンプレートへ流し込むデータには、「パラメータ」と「データソース」の2種類があります。データソースはDetailセクションに流し込む繰り返し構造のデータで、CSVファイルをイメージすると良いです。パラメータは主にDetailセクション以外で使われる埋め込みデータです。
テンプレートの作成
まずは空のテンプレートを見ていきましょう。
次の画像はJasperReports Studioで作った空のテンプレートファイルです。テンプレートはTitle, Page Header, Column Header, Detail1, Column Footer, Page Footer, Summaryのセクションからなっています。Title, Page Headerなどはそのままの意味で、タイトルやヘッダーを表示するところです。Column Header, Detail1, Column Footerがデータを流し込んで、テーブルとして表示する場所です。
jrxmlの方も見てみましょう。Title,Page,Header...といった構造がjrxmlにも反映されていることがわかります。
<?xml version="1.0" encoding="UTF-8"?> <jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Blank_A4" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="86d21441-0199-44fa-8d1e-8b7170bc739f"> <queryString> <![CDATA[]]> </queryString> <background> <band splitType="Stretch"/> </background> <title> <!-- bandタグで各セクションの高さを指定できる --> <band height="79" splitType="Stretch"/> </title> <pageHeader> <band height="35" splitType="Stretch"/> </pageHeader> <columnHeader> <band height="61" splitType="Stretch"/> </columnHeader> <detail> <band height="125" splitType="Stretch"/> </detail> <columnFooter> <band height="45" splitType="Stretch"/> </columnFooter> <pageFooter> <band height="54" splitType="Stretch"/> </pageFooter> <summary> <band height="42" splitType="Stretch"/> </summary> </jasperReport>
各セクションは必須ではなく、不要であったら削除しても構いません。例えば何枚かに渡るレポートを作りたい時、最初の1枚は表紙になるかもしれません。その時はTitleセクションだけにして大丈夫です。
文字の埋め込み
雰囲気を掴むためにjrxmlを直接編集してみます。
文字の埋め込みは staticText
タグを使います。
<title> <band height="79" splitType="Stretch"> <staticText> <!-- reportElementタグで表示場所などを指定できる --> <reportElement x="0" y="0" width="100" height="50"/> <text><![CDATA[Invoice]]></text> </staticText> </band> </title>
独自のフォントを埋め込む場合には設定の追加が必要です。特に日本語を利用する場合は必須です。今回はIPAフォントを利用します。
まず、クラスパス上に jasperreports_extension.properties
というファイルを作成し、フォントの読み込みのための設定と、フォント情報を記述したxmlファイルへのパス(fonts/fonts.xml)を指定します。
net.sf.jasperreports.extension.registry.factory.fonts=net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory net.sf.jasperreports.extension.simple.font.families.font=fonts/fonts.xml
fonts.xmlは次のように記述します。
<?xml version="1.0" encoding="UTF-8"?> <fontFamilies> <fontFamily name="IPAexゴシック"> <normal>fonts/ipaexg.ttf</normal> <pdfEncoding>Identity-H</pdfEncoding> <pdfEmbedded>true</pdfEmbedded> </fontFamily> </fontFamilies>
その上で、jrxmlに textElement
タグでフォントの名前を指定します。これで日本語フォントが利用可能になります。
<title> <band height="79" splitType="Stretch"> <staticText> <!-- reportElementタグで表示場所などを指定できる --> <reportElement x="0" y="0" width="100" height="50"/> <textElement> <font fontName="IPAexゴシック" size="18"/> </textElement> <text><![CDATA[請求書]]></text> </staticText> </band> </title>
なおフォントの使用にあたってはライセンスに注意してください。
パラメータの埋め込み
パラメータの埋め込みは textField
タグを使います。textFieldExpression
として $P{パラメータ名}
を指定します。パラメータの名前はXMLの先頭で parameter
タグで宣言しておきます。
<?xml version="1.0" encoding="UTF-8"?> <!-- Created with Jaspersoft Studio version 6.6.0.final using JasperReports Library version 6.6.0 --> <jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Blank_A4_1" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="f7adae20-db6a-4207-ae6a-75eb4a16e40e"> <parameter name="title"/> <queryString> <![CDATA[]]> </queryString> <background> <band splitType="Stretch"/> </background> <title> <band height="79" splitType="Stretch"> <textField> <reportElement x="0" y="0" width="100" height="50"/> <textElement verticalAlignment="Middle" textAlignment="Center"> <font fontName="IPAexゴシック" size="14"/> </textElement> <textFieldExpression><![CDATA[$P{title}]]></textFieldExpression> </textField> </band> </title> <pageHeader> <band height="35" splitType="Stretch"/> </pageHeader> <columnHeader> <band height="61" splitType="Stretch"/> </columnHeader> <detail> <band height="125" splitType="Stretch"/> </detail> <columnFooter> <band height="45" splitType="Stretch"/> </columnFooter> <pageFooter> <band height="54" splitType="Stretch"/> </pageFooter> <summary> <band height="42" splitType="Stretch"/> </summary> </jasperReport>
テーブルの作成
次のようなテーブルを作成してみましょう。
columnHeaderに見出しの内容が、detailにテーブルの一行分の内容が入ります。
<columnHeader>{ここに見出しの内容が入る}</columnHeader> <detail>{ここにテーブルの一行分の内容が入る}</detail>
<?xml version="1.0" encoding="UTF-8"?> <!-- Created with Jaspersoft Studio version 6.6.0.final using JasperReports Library version 6.6.0 --> <jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Blank_A4" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="86d21441-0199-44fa-8d1e-8b7170bc739f"> <queryString> <![CDATA[]]> </queryString> <field name="fruit"/> <field name="price"/> <columnHeader> <band height="30" splitType="Stretch"> <staticText> <reportElement x="0" y="0" width="100" height="30"/> <textElement verticalAlignment="Middle" textAlignment="Center"> <font fontName="IPAexゴシック" size="14"/> </textElement> <text><![CDATA[商品]]></text> </staticText> <staticText> <reportElement x="100" y="0" width="100" height="30"/> <textElement verticalAlignment="Middle" textAlignment="Center"> <font fontName="IPAexゴシック" size="14"/> </textElement> <text><![CDATA[価格]]></text> </staticText> </band> </columnHeader> <detail> <band height="30" splitType="Stretch"> <textField isBlankWhenNull="true"> <reportElement x="0" y="0" width="100" height="30"/> <textElement verticalAlignment="Middle" textAlignment="Center"> <font fontName="IPAexゴシック" size="14"/> </textElement> <textFieldExpression><![CDATA[$F{fruit}]]></textFieldExpression> </textField> <textField isBlankWhenNull="true"> <reportElement x="100" y="0" width="100" height="30"/> <textElement verticalAlignment="Middle" textAlignment="Center"> <font fontName="IPAexゴシック" size="14"/> </textElement> <textFieldExpression><![CDATA[$F{price}]]></textFieldExpression> </textField> </band> </detail> </jasperReport>
テーブルに埋め込むデータは「パラメータ」ではなく主に「データソース」です。データソースはコレクション形式のデータで、そのコレクション内の一つの要素がテーブル一行分のデータとなります。コレクション内の要素はその中にいくつかのフィールドを持つことになります。 今回の例の場合、データソースは「fruit」、「price」というフィールドを持つ要素のコレクションです。
データソースの内容を埋め込むには、$F{フィールドの名前}
という記法を使います。フィールドの名前は field
タグであらかじめ宣言しておく必要があります。
これをレンダリングするJavaプログラムは次のようになります。
Path in = ...; Path out = ...; JasperReport report = (JasperReport) JRLoader.loadObject(in.toAbsolutePath().toFile()); Map<String, Object> parameters = new HashMap<>(); Collection<Map<String, ?>> source = new ArrayList<>(); Map<String, Object> row1 = new HashMap<>(); row1.put("fruit", "りんご"); row1.put("price", "100"); source.add(row1); Map<String, Object> row2 = new HashMap<>(); row2.put("fruit", "みかん"); row2.put("price", "50"); source.add(row2); Map<String, Object> row3 = new HashMap<>(); row3.put("fruit", "バナナ"); row3.put("price", "30"); source.add(row3); JasperPrint print = JasperFillManager.fillReport(report, parameters, new JRMapCollectionDataSource(source)); JasperExportManager.exportReportToPdfFile(print, out.toAbsolutePath().toString());
罫線や背景色の調整
reportElement
タグで背景色をつけたり、 box
タグで罫線を引いたりすることができます。
<columnHeader> <band height="30" splitType="Stretch"> <staticText> <reportElement x="0" y="0" width="100" height="30" mode="Opaque" backcolor="rgba(200, 200, 200, 0.5)"/> <box> <topPen lineWidth="1"/> <leftPen lineWidth="1"/> <bottomPen lineWidth="1"/> <rightPen lineWidth="1"/> </box> <textElement verticalAlignment="Middle" textAlignment="Center"> <font fontName="IPAexゴシック" size="14"/> </textElement> <text><![CDATA[商品]]></text> </staticText> <staticText> <reportElement x="100" y="0" width="100" height="30" mode="Opaque" backcolor="rgba(200, 200, 200, 0.5)"/> <box> <topPen lineWidth="1"/> <leftPen lineWidth="1"/> <bottomPen lineWidth="1"/> <rightPen lineWidth="1"/> </box> <textElement verticalAlignment="Middle" textAlignment="Center"> <font fontName="IPAexゴシック" size="14"/> </textElement> <text><![CDATA[価格]]></text> </staticText> </band> </columnHeader> <detail> <band height="30" splitType="Stretch"> <textField isBlankWhenNull="true"> <reportElement x="0" y="0" width="100" height="30"/> <box> <topPen lineWidth="0"/> <leftPen lineWidth="1"/> <bottomPen lineWidth="1"/> <rightPen lineWidth="1"/> </box> <textElement verticalAlignment="Middle" textAlignment="Center"> <font fontName="IPAexゴシック" size="14"/> </textElement> <textFieldExpression><![CDATA[$F{fruit}]]></textFieldExpression> </textField> <textField isBlankWhenNull="true"> <reportElement x="100" y="0" width="100" height="30"/> <box> <topPen lineWidth="0"/> <leftPen lineWidth="1"/> <bottomPen lineWidth="1"/> <rightPen lineWidth="1"/> </box> <textElement verticalAlignment="Middle" textAlignment="Center"> <font fontName="IPAexゴシック" size="14"/> </textElement> <textFieldExpression><![CDATA[$F{price}]]></textFieldExpression> </textField> </band> </detail>
とまあここまでくるとJasperReports StudioのGUIも使いながらデザインするのが楽だと思います。
画像、線、矩形表示などのタグもありますが、基本的な考えは変わらないので割愛します。 より詳しくはドキュメントのJasperReports Library Ultimate Guideを参照してください。
https://community.jaspersoft.com/documentation
JasperReportsはテーブルが1つのPDF、例えば見積書や請求書のような書類を作成するのに特化しているようにも思いましたが、SubReportという機能を利用することでより複雑なレイアウトにも対応できそうです。
IntelliJ IDEA を無料で使う方法
IntelliJ IDEA は Community Edition であれば無料で使えますが、 Ultimate Edition になると大体初期2万+維持費1万/年くらいの課金をする必要があります。
これをどう見るかは人によると思いますが、自分は Scala はほとんど Emacs で書いてしまって、たまに気分で IntelliJ IDEA を使うくらいのノリなので少し高く感じています。
しかし、実は Ultimate Edition をタダで使う方法が存在します。Open Source License というヤツです。
IntelliJ IDEA 14.x Open Source License
Open Source License はオープンソースのプロジェクトの開発に使用できるライセンスです。ライセンスの発行には審査が必要です。 条件は、
- プロジェクトのリーダー、または常にコミットしていること
- プロジェクトがオープンソースの定義を満たしていること
- 資金援助などを受けていないこと
- アクティブに開発されていること
- コミュニティがアクティブであること
- ウェブサイトを持っていること
- 定期的にリリースがされていること
と、一見厳しそうですが、コミュニティとかウェブサイトがどうとかは GitHub のリポジトリや issues がその役目を果たしますし、規模とかはあまり関係なく、真面目に開発してる感さえあれば大丈夫だと思います。
というわけで、今回は tototoshi/scala-csv で申請してライセンスを発行してもらえました。ライセンスの発行には大体数日必要らしいですが、以前 PhpStorm のライセンスを tototoshi/staticmock に対して発行してもらったときは1ヶ月くらいかかった(絶対忘れてただろ)ので気長に待ちましょう。
Open Source License の有効期間は1年で、更新のときは期限切れの直前にメールでお願いする必要があるようです。
というわけで、自信のない人もとりあえず申請してみればよいと思います。JetBrains もそれなりに儲けてそうだし皆さんがタダで使っても潰れないでしょう。
追記: はてぶコメントで補足してくれてる方がいますが、仕事には使えないので会社に Commercial License を買ってもらいましょう
Flyway は複数人での開発に向かないという誤解について
“データベースマイグレーションについて考えないといけないことや諦めないといけないことが結構あるでよ”
http://t.co/BYna6w5luj
期待して記事見たが、複数人開発時におけるバージョン番号の衝突について説明がなかったのが残念。紙面が限られてるししゃあない。
— 早すぎる最適化オジサン (@makotokuwata) 2014, 12月 29
flyway って、V1_ みたいな prefix をつけるけど、integer を increment するのって、ブランチきって平行で開発してる場合どうなるの?という気がしている
— tokuhirom (@tokuhirom) 2014, 7月 30
flywayのマイグレーションについて見てたけど、これ、バージョン番号をファイル名につける感じなのですかね。まだよく見てないが。Railsのマイグレーションと比べて複数人開発に強くないですねぇ。
— でこくん (@dekokun) 2013, 3月 26
というように、Flyway は複数人開発に向かないという噂をたまに聞くのですが、多分誤解です。
誤解が生まれるのは公式ドキュメントで Flyway のマイグレーションスクリプトのファイル名として、V1__Add_new_table.sql
みたいなファイル名が例に出されており、これが V2__
、V3__
のような名前をつける必要があるという印象を与えるためかと思われます。
ドキュメントを良く読むと、バージョンのルールは
- One or more numeric parts
- Separated by a dot (.) or an underscore (_)
- Underscores are replaced by dots at runtime
- Leading zeroes are ignored in each part
なので、もう少し柔軟です。だから rails のようにタイムスタンプベースのファイル名付けちゃえば良いです。
sql ├── V20150127114055__create_user_table.sql ├── V20150127114322__add_country_column.sql └── V20150127114323__add_age_column.sql
このようにタイムスタンプベースのバージョン付けを行うとブランチをマージしたときなどに順番が狂うという問題が発生しますが、 outOfOrder
という設定があるのでこいつを on にしてやれば OK です。
WEB+DB PRESS VOL.84 の Flyway の記事にもなかったので書いてみました。
Heroku で JDK のバージョンを指定する
Heroku でサポートされている JDK は 1.6, 1.7, 1.8 です。 今ではデフォルトは 1.8 ですが、古いアプリではどうやらそのまま 1.6 が使われているようです。
JDK のバージョンを指定したいときには system.properties というファイルを使います。
java.runtime.version=1.8
このファイルをコミットし、PATH を設定します。
APP_PATH=`heroku config:get PATH` heroku config:set PATH=/app/.jdk/bin:$APP_PATH
で、あとはこれを git push heroku master するだけで、JDK のバージョンアップができます。
% git push heroku master Fetching repository, done. Counting objects: 4, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 324 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) -----> Play 2.x - Scala app detected -----> Installing OpenJDK 1.8...done
参考: Updating Existing Java Apps to Use Java 7 | Heroku Dev Center
Google App Engineを試してみました。
Google Plugin for Eclipseいいですね。
ローカルでのテスト用にWebサーバ内蔵してるし、
デプロイもボタンひとつでやってくれる。
以下、メモ
- 開発用サーバの管理者ページは http://localhost:8888/_ah/admin
- 初めてGAEを使うときは携帯電話での認証が必要(iPhoneでもできた) https://appengine.google.com/permissions/smssend
- RDBは使用できない。BigTableを使っている。
- BigTableとのデータのやりとりはJDO