JasperReportsでプリンタに直接帳票印刷する

# 概要
JasperReportsで、PDF等にファイル出力せずに直接プリンタに出力させる。
業務で上記のような変わった要件があったため、調査した結果のメモ。
ネットにいくつかサンプルコードは存在したが、バージョンが古く非推奨APIを使用していたため、6.8に対応するようにサンプルを作成した。

# 環境など
* JasperReports（6.8.0）
* Jaspersoft Studio（6.8.0）※テンプレート作成に使用
* Java（1.8）

# 帳票テンプレート
以下の様な架空の見積書を想定
![template.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/c6343603-25f2-2a50-1d8e-b9a8152336fa.png)

## 帳票パラメータ一覧
|パラメータ|キー|設定値例|
|---|---|---|
|得意先住所|CLIENT_ADDRESS|東京都中央区銀座１‐２３‐４５|
|得意先会社名|CLIENT_NAME|株式会社　○○○○|
|得意先担当者|CLIENT_PERSON|○○　○○|
|自社住所|CORP_ADDRESS|東京都新宿区西新宿１‐２‐３|
|自社社名|CORP_NAME|株式会社　××××|
|自社担当者|SALES_PERSON|××　××|
|見積No.|ESTIMATE_NO|No.20190601|
|作成日|CREATED_DATE|2019/06/03|
|有効期限|EXPIRATION_DATE|2019/06/14|
|納期|DELIVERY_DATE|2019/06/28|
|支払条件|PAYMENT_TERMS|月末締め翌月末支払い|
|備考|REMARKS|消費税は8%で計算しています。|

## 帳票フィールド一覧
|フィールド|キー|設定値例|
|---|---|---|
|商品名|item|商品AAAA|
|数量|quantity|10|
|単価|unit|200|
|金額|price|2000|

# サンプルコード
※あくまでもサンプルのため、パラメータ、フィールドの設定についてはコード内で直接設定しています。

```java:Main.java
public class DirectPrintSample {

	/** JasperReportテンプレートファイル */
	public static final String REPORT_TEMPLATE = "template/quotation.jrxml";

	/** 出力先プリンタ名 */
	public static final String PRINTER_NAME = "（出力先プリンタ名）";

	public static void main(String[] args) {

		// 帳票定義を初期化する
		JasperReport report = null;
		try {
			report = createReport();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JRException e) {
			e.printStackTrace();
		}

		// 文書を作成する（帳票定義に出力データをセットする）
		JasperPrint print = null;
		try {
			print = createPrint(report);
		} catch (JRException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			e.printStackTrace();
		}

		// プリンターを指定して帳票を直接印刷する
		try {
			exportPrint(print, PRINTER_NAME);
		} catch (JRException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 帳票定義を初期化する。
	 * @throws IOException
	 * @throws JRException
	 */
	public static JasperReport createReport() throws IOException, JRException {

		// 帳票レイアウトをコンパイルする
		try (InputStream is = DirectPrintSample.class.getClassLoader().getResourceAsStream(REPORT_TEMPLATE)) {
			return JasperCompileManager.compileReport(is);
		}
	}

	/**
	 * 帳票出力するデータをセットする。
	 * @param jasperReport
	 * @throws JRException
	 * @throws ParseException
	 */
	public static JasperPrint createPrint(JasperReport jasperReport) throws JRException, ParseException {

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");

		// パラメータセット
		Map<String, Object> parameters = new HashMap<>();
		parameters.put("CLIENT_ADDRESS", "東京都中央区銀座１‐２３‐４５");
		parameters.put("CLIENT_NAME", "株式会社　○○○○");
		parameters.put("CLIENT_PERSON", "○○　○○");
		parameters.put("CORP_ADDRESS", "東京都新宿区西新宿１‐２‐３");
		parameters.put("CORP_NAME", "株式会社　××××");
		parameters.put("SALES_PERSON", "××　××");
		parameters.put("ESTIMATE_NO", "No.20190001");
		parameters.put("CREATED_DATE", sdf.parse("2019/06/03"));
		parameters.put("EXPIRATION_DATE", sdf.parse("2019/06/14"));
		parameters.put("DELIVERY_DATE", sdf.parse("2019/06/28"));
		parameters.put("PAYMENT_TERMS", "月末締め翌月末支払い");
		parameters.put("REMARKS", "消費税は8%で計算しています。");

		// レコードセット
		List<Item> list = new ArrayList<>();
		list.add(new Item("商品AAAA", 10, 200, 2000));
		list.add(new Item("商品BBBB", 20, 600, 12000));
		list.add(new Item("商品CCCC", 100, 250, 25000));
		JRDataSource dataSource = new JRBeanCollectionDataSource(list);

		return JasperFillManager.fillReport(jasperReport, parameters, dataSource);
	}

	/***
	 * 帳票を直接印刷する。
	 * @param jasperPrint
	 * @param printerName
	 * @throws JRException
	 */
	private static void exportPrint(JasperPrint jasperPrint, String printerName) throws JRException {

		// プリンタ用出力クラスを生成する
		JRPrintServiceExporter exporter = new JRPrintServiceExporter();

		// 出力対象の文書を設定する
		exporter.setExporterInput(new SimpleExporterInput(jasperPrint));

		// 出力先印刷プリンタを指定する
		SimplePrintServiceExporterConfiguration conf = new SimplePrintServiceExporterConfiguration();
		HashPrintServiceAttributeSet printAttribute = new HashPrintServiceAttributeSet();
		printAttribute.add(new PrinterName(PRINTER_NAME, Locale.getDefault()));
		conf.setPrintServiceAttributeSet(printAttribute);
		exporter.setConfiguration(conf);

		// プリンタ出力を実行する
		exporter.exportReport();
	}

}
```
```java:Item.java
public class Item {

	private String item;
	private int quantity;
	private int unit;
	private int price;

	Item(String item, int quantity, int unit, int price) {
		this.item = item;
		this.quantity = quantity;
		this.unit = unit;
		this.price = price;
	}

	public String getItem() {
		return item;
	}

	public void setItem(String item) {
		this.item = item;
	}

	public int getQuantity() {
		return quantity;
	}

	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}

	public int getUnit() {
		return unit;
	}

	public void setUnit(int unit) {
		this.unit = unit;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}
}
```

# 出力結果
以下は出力先を Microsoft Print to PDF に設定して、PDF出力したもの。
![print.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/1410dcb2-71f7-0638-468b-e1861d40da22.png)


# 課題・懸念事項
* プリンタに直接出力するには、APサーバとプリンタが同一LAN上に存在する必要があるため、クラウドサーバ等に実装する場合はプリンタをIPsec等に対応させる必要がある。