先日、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」を新規作成すれば
テンプレートの選択画面が出てきて、
そして、こんな感じの帳票のデザイン画面が開きます。
開発環境の準備
-
お馴染みのデータ入力用のシンプルな html ページ
-
今回の主役、PDF を自動生成する Servlet
-
先ほどインストールした Jaspersoft Studioプラグインで作成したテンプレート
-
開発時に必要な JasperReports Library 用 jar ファイル
-
Servlet 実行時に必要な JasperReports Library 用 jar ファイル
-
ですが Jaspersoft Studioプラグインをインストール時に Eclipse に登録されていますので、ビルドパスへライブラリを追加します。
-
もプラグインをインストールしてあれば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 テンプレートを作成します。今回は以下のようにシンプルなもので試しましょう。
-
は単純にタイトルを表示しているだけです。
Static Text
を配置して、適当なタイトルを入力し、さきほど設定した日本語フォントを指定します。
-
が動的に設定するテキストになり、最も大事な部分です。まず右の要素ツリーで
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 のほうを強く推したいとオモイマス。
それではまた!