先日、Servlet + Apache FOP ネタ とか その続編 とか投稿したのですが、知り合いから「Jasper 良さげだよ」との情報をいただきまして。ググったら情報多い!主流はコッチだったか… と。
既に日本語の情報は十分にある気もするので、備忘録として、簡単な実施メモにまとめてみました。
JasperReports 概要
まず Jasper 系の PDF (帳票) 生成に関して、以下が理解の助けになりました。
- JasperReportsとiReportで帳票の常識を理解しよう (1/4)
- JavaでPDFのテンプレートを作成し、帳票作成を行う
- Jasperレポート作り方 ~iReport(基本編)~
- Jaspersoft StudioプラグインをEclipseにインストールする
以下は公式もしくはダウンロードサイト。
LGPL のようですが、一応はライセンス周りを確認。
Jaspersoft StudioプラグインをEclipseにインストール
iReportはjdk1.8に対応していない らしいので、今回は Jaspersoft Studio の Eclipse 用プラグインを利用してみます。
私の今のテスト環境 Eclipse 2020-09 で「Help -> Eclipse Marketplace」から Jaspersoft 6.16.0 をインストール。
ここで同意するライセンスは Eclipse Foundation Software User Agreement のみの模様。
インストール後は Eclipse が再起動するので、Welcome 画面に以下のような表示があればok。
後は新規作成メニューに追加された「Jasper Report」を新規作成すれば
テンプレートの選択画面が出てきて、
そして、こんな感じの帳票のデザイン画面が開きます。
開発環境の準備
1) お馴染みのデータ入力用のシンプルな html ページ
2) 今回の主役、PDF を自動生成する Servlet
3) 先ほどインストールした Jaspersoft Studioプラグインで作成したテンプレート
4) 開発時に必要な JasperReports Library 用 jar ファイル
5) Servlet 実行時に必要な JasperReports Library 用 jar ファイル
4) ですが Jaspersoft Studioプラグインをインストール時に Eclipse に登録されていますので、ビルドパスへライブラリを追加します。
5) もプラグインをインストールしてあればPC内にあるはずなので、探して WEB-INF/lib
下にコピーもしくはリンクします。MVN リポジトリ JasperReports Library から入手してもかまいません。なお ipaexm.jar
に関してはこの後で説明します。
フォント設定
こちら の情報をもとに 「IPAex明朝」フォントをダウンロードし設定します。
ここで大事なのは PDF Encoding
欄で Identity-H(Unicode with horizontal writing)
を選択することです。これを忘れると日本語が出力されません。
フォントの設定が終わったら、一覧メニューで「Export」を実行します。
jar ファイルの保存画面になりますので WEB-INF/lib
下に ipaexm.jar
というファイル名で保存します。これで開発環境で指定したフォント設定を、Servlet 実行環境に反映することができました。
PDF テンプレートを作成する
Jaspersoft Studioプラグインで PDF テンプレートを作成します。今回は以下のようにシンプルなもので試しましょう。
1) は単純にタイトルを表示しているだけです。Static Text
を配置して、適当なタイトルを入力し、さきほど設定した日本語フォントを指定します。
2) は Current Date
を配置しただけです。日本語ではありませんが、表示を統一するためフォントだけ指定します。
3) が動的に設定するテキストになり、最も大事な部分です。まず右の要素ツリーで MY_MESSAGE
という名称の独自パラメーターを追加します。
指定する名称と型(今回は文字列)は以下のような感じ。
次に Text Field
要素を追加します。表示内容を指定する Expression Editor では追加した独自パラメーターの MY_MESSAGE
を選択します。あ、フォント指定もお忘れなく。
さて、これでテンプレートは完成しました。以下はプレビューを実行した様子です。
保存した sample1.jrxml
ファイルは以下のような感じ。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.16.0.final using JasperReports Library version 6.16.0-48579d909b7943b64690c65c71e07e0b80981928 -->
<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="sample1" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="3fe7289f-d8d2-4a65-9b55-c9aa20be3391">
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="One Empty Record"/>
<parameter name="MY_MESSAGE" class="java.lang.String" isForPrompting="false"/>
<queryString>
<![CDATA[]]>
</queryString>
<background>
<band splitType="Stretch"/>
</background>
<title>
<band height="79" splitType="Stretch">
<staticText>
<reportElement x="80" y="20" width="400" height="30" uuid="b771d8a0-efcc-4c72-a8eb-f66f1b7bc644"/>
<textElement textAlignment="Center">
<font fontName="IPAex明朝" size="16"/>
</textElement>
<text><![CDATA[JasperReports PDF サンプル]]></text>
</staticText>
</band>
</title>
<pageHeader>
<band height="49" splitType="Stretch">
<textField pattern="MMMMM dd, yyyy">
<reportElement x="370" y="10" width="180" height="30" uuid="cf6034bc-e2c5-4db9-806d-e40ed1bc65a5"/>
<textElement textAlignment="Right">
<font fontName="IPAex明朝"/>
</textElement>
<textFieldExpression><![CDATA[new java.util.Date()]]></textFieldExpression>
</textField>
</band>
</pageHeader>
<columnHeader>
<band height="61" splitType="Stretch"/>
</columnHeader>
<detail>
<band height="125" splitType="Stretch">
<textField>
<reportElement x="0" y="10" width="550" height="100" uuid="0298349c-4284-4697-b6ad-792637b8e76e"/>
<textElement>
<font fontName="IPAex明朝"/>
</textElement>
<textFieldExpression><![CDATA[$P{MY_MESSAGE}.toString()]]></textFieldExpression>
</textField>
</band>
</detail>
<columnFooter>
<band height="45" splitType="Stretch"/>
</columnFooter>
<pageFooter>
<band height="54" splitType="Stretch"/>
</pageFooter>
<summary>
<band height="42" splitType="Stretch"/>
</summary>
</jasperReport>
保存した sample1.jrxml
ファイルを右クリックメニューからコンパイルし、sample1.jasper ファイルを生成しておきます。
入力用ページ
入力用ページは これまでと同じ ですので、説明は省きますね。フォーム部分は以下のような感じ。
<div class="container">
<h2>Simple JasperReports sample with Servlet</h2>
<form method="POST" action="/test03/pdfServlet">
<div class="form-group">
<label for="i_fname">Document format</label>
<input type="text" class="form-control" id="i_fname" name="i_fname" value="sample1">
<label for="i_body">Body mwssage</label>
<input type="text" class="form-control" id="i_body" name="i_body" value="Sample body text...">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
Servlet
さて、今回のコアとなる Servelt 部分ですが、実は過去の Apache FOP 版 とあまり変わらなかったりします。呼び出しているライブラリが異なるぐらい?実際のコードを見てもらったほうが理解し易いとおもいます。
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JasperRunManager;
@WebServlet("/pdfServlet")
public class pdfServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public pdfServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
request.setCharacterEncoding("utf-8");
String i_fname = request.getParameter("i_fname");
String i_body = request.getParameter("i_body");
File jasperFile = new File(getServletConfig().getServletContext().getRealPath("/" + i_fname + ".jasper"));
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("MY_MESSAGE", i_body);
byte[] bytes = JasperRunManager.runReportToPdf(jasperFile.getPath(), params, new JREmptyDataSource());
response.setContentType("application/pdf");
response.setContentLength(bytes.length);
response.setHeader("Content-Disposition", "inline");
ServletOutputStream out = response.getOutputStream();
out.write(bytes);
out.flush();
} catch (Exception ex) {
throw new ServletException(ex);
}
}
}
ポイントは params.put("MY_MESSAGE", i_body);
ですね。PDF テンプレートを作成した際に設定した独自パラメーターに対し、表示用の値をここで渡してあげるわけです。
今回はテンプレートをファイルから読み込んでいますが、DB 化については 続編のほう を参照してみてください。コンパイル後はバイナリ形式であることに留意すれば、ほぼ同じ考え方で対応できるとおもいます。
実行してみよう
さて、まずはいつものように index.html を開いてテキストを入力し、
「Submit」で入力したテキストが埋め込まれた PDF が表示されることを確認します。
うん、問題なさそうですね。日本語のタイトルも、今日の日付も表示されています。そして本文の部分には、フォームで入力した日本語を含んだテキストが、ちゃんと埋め込まれているのを確認できました。
生成された PDF について
今回のサンプルプログラムで生成した PDF ファイルを以下の URL に置きました。
いまのところ、以下の環境で表示の確認をしています。もし文字化けとかしちゃう環境などありましたら、コメントいただけると助かります!
- Google Chrome on Windows 10
- Edge on Windows 10 (IEで開いてもこちら起動する)
- Safari on Mac
- Safari on iPhone
- Kindle Fire HD (Silk ブラウザで表示するとKindleアプリが起動し表示)
ライセンス
この投稿に含まれる私の作成した全てのコードは Creative Commons Zero ライセンス とします。自由にお使いください。
おまけ:表示・非表示をコントロールする
PDFで表を生成するサンプルを作成したところ「データが無い場合、ヘッダだけの空の表はみっともない。この場合は表自体を非表示にできないか」という相談がありました。こんな場合に使えるのが、各要素にある Print When Expression
欄です。
ここに式を書いておくと、その式が true
の場合にその要素が表示されます。false
であれば非表示です。例えば今回のサンプルでメッセージの表示欄に以下のような式を設定してみると
10文字以下の短いメッセージを入力すると、PDFに表示されなくなります。
表に使用する場合は、表自体、もしくは表を含むセクション全体の Print When Expression
欄に、表示行数を参照する条件演算子を設定すると良さそうです。
Enjoy!
以上、Servlet を用いて Web サイトで、JasperReports を用いた PDF の動的生成を試してみました。これをベースに、いろいろ機能を追加して遊んでみてください。
Apache FOP も JasperReports も Servlet から使うぶんには、それほど違いがない気がします。でもテンプレートを作成する部分、JasperReports 用プラグインによる GUI ベースの設定は圧倒的に楽ですね。なので、JasperReports のほうを強く推したいとオモイマス。
それではまた!