iReport JasperReport 浮水印 Jasper Pie

iReport + JasperReport產生PDF (實作篇)

劉正輝 Chenghui Liu 2021/09/28 14:00:00
11764

前言

    1. Jasper Report 是一個開源的Java報表引擎,它不像其他的報表工具,例如Crystal報表是基於Java的,冇有自己的表達式語法。JasperReports有提供豐富的內  容到屏幕上,到打印機,或轉換成PDF,HTML,XLS,RTF,ODT,CSV,TXT和XML文件的能力。因為它不是一個獨立的工具,它不能被安裝在其自身。相反,它是由包括它在應用程序的CLASSPATH庫嵌入到Java應用程序。

    2. JasperReports是一個Java類庫,而不是針對最終用戶,而是有針對性的對誰需要的報表功能添加到自己的應用程序的Java開發人員。

    3. JasperReports的特點

        一些主要的 JasperReport 的功能包括:

            □ 具有靈活的報表布局。

            □ 它可以用文字或圖形顯示數據。

            □ 開發人員可以通過多種方式提供數據。

            □ 它可以接受來自多個數據源的數據。

            □ 它可以生成水印(水印是這樣的方式被放置在主圖像的副圖像)。

            □ 它能夠導出報表到多種格式的。

 

內容簡介

    1. 工具安裝與開發依賴檔案說明

    2. 使用iReport製作.jrxml檔案

        ∟2.1 中文顯示問題注意事項

        ∟2.2 資料清單顯示設定

    3. 撰寫iReport + JasperReport程式內容

        ∟3.1 PDF顯示資料抓取&圓餅圖資料抓取

        ∟3.2 產生PDF檔案

        ∟3.3 產生圓餅圖

        ∟3.4 產生浮水印&將圓餅圖與浮水印置入PDF

    4. 製作結果

    5. 相關資源連結

 

1. 工具安裝與開發依賴檔案說明

   ◆ 開發工具:  Spring Tools 4.6.0 released

  Gradle依賴
 dependencies {

	//...上略 依照自身專案需求增減

	// jasperreports  dependencies

	implementation 'net.sf.jasperreports:jasperreports:6.17.0'

	implementation 'com.lowagie:itext:2.1.7'

  }

 

2. 使用iReport製作.jrxml檔案

   依照版面需求進行iReport版面設計,下圖為設計後範例, 詳細iReport設計操作方法請參考5. 相關資源連結

   ◆ 2.1 中文顯示問題注意事項

        關於第一次設定及使用的時,會發生中文無法正常顯示問題下述為解決方法之一,請於會顯示中文的物件依照下圖設定。

        1. Expression Class 設定為: java.lang.String

        2. Pdf Font name is now deprecated. 設定為: jasper/font/msjhbd001.ttf (ttf字體檔案(路徑請設定相對路徑) )

        3. Pdf Encoding 設定為: Identity-H (Unicode with horizontal writing)

        附圖.

 ◆ 2.2 資料清單顯示設定

         關於資料有多筆需要製作list狀況時,請依照下述步驟設定。

        2.2.1. 主要清單類資料或多筆資料想要依照資料數量產生陣列時主要設定分為兩部,資料的表頭以及資料的詳細內容。

              ● 資料的表頭: 使用Static Text即可

              ● 資料的內容: 使用Text Field即可  *會自動依照多筆資料進行往下增長

                 並且要顯示的一個欄位只需要放一個Text Field, 然後需要把該區塊的底部拉到與Text Field切齊  *圖2的 箭頭1與2處要與該資料列上下欄位切齊

圖1

圖2

 

 

3. 撰寫iReport + JasperReport程式內容

        3.1 PDF顯示資料抓取&圓餅圖資料抓取 *下為範例
			// 查詢PDF表頭資料
			List<PortfolioMainInfo> portfolioMainInfoList = porfolioSearchInterface.searchPortfolioMainList(portfolioPdf.getPortfolioId());
			if (portfolioMainInfoList.isEmpty()) {
				log.error("createJasperReport searchPortfolioMainList portfolioMainInfoList is not defined!");
			}
			// 查詢PDF明細資料
			List<PortfolioWeightsPdfInfo> portfolioWeightsPdfInfoList = porfolioSearchInterface.searchPortfolioWeightsList(portfolioPdf.getPortfolioId());
			if (portfolioWeightsPdfInfoList.isEmpty()) {
				log.error("createJasperReport searchPortfolioWeightsList portfolioWeightsPdfInfoList is not defined!");
			}

        3.2 產生PDF檔案

	/**
	 * 產生PDF類別
	 * @param portfolioMainInfoList TITLE資料
	 * @param portfolioWeightsPdfInfoList 詳細資料
	 * @param portfolioProposalPo 建議書資料
	 * @param portfolioPdf 總金額
	 */
	public JasperPrint createSamplePdfReport(List<PortfolioMainInfo> portfolioMainInfoList, List<PortfolioWeightsPdfInfo> portfolioWeightsPdfInfoList, 
			PortfolioProposalPo portfolioProposalPo, PortfolioPdfReqDto portfolioPdf) {
		log.info("PdfReportRepositoryImpI createSamplePdfReport.");
		Map map = new HashMap<String, Object>();
		List<ExampleInfo> modelList = new ArrayList();

		// 報表TITLE文字設定
		map.put("commodityName", portfolioMainInfoList.get(PortfolioConst.LIST_FIRST_INDEX).getPortfolioName());
		map.put("commodity", this.checkCommodity(portfolioMainInfoList));
		map.put("commodityDate", DateUtils.getFormatDate(DateUtils.parseDate(portfolioMainInfoList.get(PortfolioConst.LIST_FIRST_INDEX).getReleaseDate())));
		map.put("commodityMoney", new DecimalFormat(CommonPattern.DEFAULT_MONEY_FORMAT).format(portfolioPdf.getInvAmt()).toString());
		map.put("riskLeavel", portfolioMainInfoList.get(PortfolioConst.LIST_FIRST_INDEX).getRiskLevel());
		map.put("proposal", portfolioProposalPo != null ? portfolioProposalPo.getProposal() : "");

		// 資料LIST設定
		portfolioWeightsPdfInfoList.forEach(temp -> {
			ExampleInfo exampleInfo = new ExampleInfo();
			exampleInfo.setCommodityType(this.checkCommodityPoolType(temp.getCommodityPoolType()));
			exampleInfo.setCommodityPool(temp.getCommodityId());
			exampleInfo.setCommodityName(temp.getCommodityPoolName());
			exampleInfo.setInvestmentAmount("9999"); // 測試
			exampleInfo.setPrice("8888"); // 測試
			exampleInfo.setRate("7777"); // 測試
			exampleInfo.setUnits("6666"); // 測試
			exampleInfo.setCommodityWeight(temp.getCommodityWeight());
			modelList.add(exampleInfo);
		});
		try {
			// 讀取jrxml的InputStream
			JasperDesign design = JRXmlLoader.load(jrxml.getInputStream());
			// 讀取製作的jrxml檔案並轉換成compileReport
			JasperReport report = JasperCompileManager.compileReport(design);
			// 透過compile過的JasperReport製作JasperPrint
			JasperPrint jasperPrint = JasperFillManager.fillReport(report, map, new JRBeanCollectionDataSource(modelList));
			log.info("PdfReportRepositoryImpI JasperFillManager.fillReport({}, {}, {})", jrxml.getURL(), map, modelList);
			return jasperPrint;
		} catch (Exception e) {
			log.error("errorMsg: {}.", e.getMessage());
		} 
	}

        3.3 產生圓餅圖

            3.3.1 產生圓餅圖資料

                 透過資料庫抓取的詳細資料,製作圓餅圖所需物件DataSet

	/**
	 * 取得圓餅圖集合資料
	 */
	public Map<String, Double> getDataset(List<PortfolioWeightsPdfInfo> portfolioWeightsPdfInfoList) {
		Map tempMap = new HashMap();
		portfolioWeightsPdfInfoList.forEach( temp -> {
			tempMap.put(temp.getCommodityPoolName().toString(), Double.valueOf(temp.getCommodityWeight()));
		});
		return tempMap;
	}

            3.3.2 產生圓餅圖

	/** 
	 * 建立圓餅圖
	 */
	public JFreeChart createPiePort(Map<String, Double> dataset) {
		try {
			// 圓餅圖資料
			DefaultPieDataset pds = new DefaultPieDataset(); // jasperreports
			Set<Entry<String, Double>> set = dataset.entrySet();
			Iterator iterator = (Iterator) set.iterator();
			Entry entry = null;
			List<String> sectionPaint = new ArrayList();
			while (iterator.hasNext()) {
				entry = (Entry) iterator.next();
				pds.setValue(entry.getKey().toString(), Double.parseDouble(entry.getValue().toString()));
				sectionPaint.add(entry.getKey().toString());
			}
			// 創建JFreeChart
			JFreeChart chart = ChartFactory.createPieChart(StringUtil.EMPTY_STRING, pds, false, false, false);
			// 設置背景透明
			chart.setBackgroundPaint(null);
			// 取消背景框限
			chart.setBackgroundImageAlpha(0.0f);
			// 設定圓餅圖渲染
			this.setPieRender((PiePlot) chart.getPlot(), sectionPaint);
			return chart;
		} catch (Exception e) {
			log.error("PieRepositoryImpl createPiePort error! Msg: {}.",e.getMessage());
			return null;
		}
	}

	/**
	 * 設定圓餅圖渲染
	 * @param plot
	 */
	public void setPieRender(Plot plot, List<String> sectionPaint) {
        plot.setInsets(new RectangleInsets(10, 10, 5, 10));
        plot.setOutlinePaint(null);
        plot.setBackgroundAlpha(0.0f);
		PiePlot piePlot = (PiePlot) plot;
		piePlot.setInsets(new RectangleInsets(0, 0, 0, 0));
		// 圓形
		piePlot.setCircular(true);
		// 取得圓餅圖預設填充顏色
		List<Color> colorList = setRenderColor(sectionPaint.size());
		
		for (int i = 0; i < sectionPaint.size() ; i++) {
			piePlot.setSectionPaint(sectionPaint.get(i), colorList.get(i));
		}

		// 设置背景透明度
		piePlot.setBackgroundAlpha(0.0f);
		piePlot.setLabelFont(
				new Font(JasperConst.FONT_DEAULT_PATH + JasperConst.PDF_FONT_MSJHBD_TTF, Font.PLAIN, 12));
		// 標籤空隙
		piePlot.setLabelGap(0.01);
		piePlot.setInteriorGap(0.05D);
		piePlot.setBackgroundAlpha(0.0f);
		// 圖形形狀
		piePlot.setLegendItemShape(new Rectangle(5, 5));
		piePlot.setIgnoreNullValues(true);
		// 去掉背景色
		piePlot.setLabelBackgroundPaint(null);
		// 去掉陰影
		piePlot.setLabelShadowPaint(null);
		// 去掉邊框
		piePlot.setLabelOutlinePaint(null);
		piePlot.setShadowPaint(null);
		// 0:category 1:value:2 :percentage 標籤數據
		piePlot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}\r\n{2}"));
	}

        3.4 產生浮水印&將圓餅圖與浮水印置入PDF

	/**
	 * 加入浮水印與圓餅圖
	 * 
	 * @param documentBytes
	 * @return
	 * @throws Exception
	 * @throws IOException
	 * @throws DocumentException
	 */
	public byte[] setDocumentWithWaterMarkAndPieImage(byte[] documentBytes, JFreeChart chart) {
		log.debug("PdfReportRepositoryImpI setDocumentWithWaterMarkAndPieImage({}, {}).", documentBytes, chart);
		float x, y;
		PdfContentByte over = null;
		Rectangle pagesize;
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		PdfGState gs1 = new PdfGState();

		try {
			PdfReader reader = new PdfReader(documentBytes);
			int n = reader.getNumberOfPages();
			PdfStamper stamper = new PdfStamper(reader, outputStream);
			// 樣式設定
			BaseFont base = BaseFont.createFont(JasperConst.FONT_DEAULT_PATH + JasperConst.PDF_FONT_MSJHBD_TTF, JasperConst.PDF_FONT_IDENTITY_H, BaseFont.EMBEDDED);
			Font waterMarkFont = new Font(base, 60);
			// 浮水印: 昕力大學 投資平台
			Phrase waterMark = new Phrase(JasperConst.WATER_MARK, waterMarkFont);
			// 浮水印: 系統日期
			waterMarkFont.setSize(35);
			Phrase systemDate = new Phrase(DateUtils.getFormatSystemDate(), waterMarkFont);
			// 套明度設定: 淺灰色
			gs1.setFillOpacity(0.06f);
			
			for (int i = 1; i <= n; i++) {
				pagesize = reader.getPageSizeWithRotation(i);
				over = stamper.getOverContent(i);
				over.saveState();
				// 設定樣式
				over.setGState(gs1);
				// 加入文字, 文字置中
				x = (pagesize.getLeft() + pagesize.getRight()) / 2;
				y = (pagesize.getTop() + pagesize.getBottom()) / 2;
				// 文字45度, 置中
				ColumnText.showTextAligned(over, Element.ALIGN_CENTER, waterMark, x, y, 45);
				ColumnText.showTextAligned(over, Element.ALIGN_CENTER, systemDate, x * 1.1f, y * 0.9f, 45);
				over.restoreState();
			}
			// PDF檔案插入圖片
			this.addImage(stamper, reader, chart, over);
			stamper.close();
			reader.close();
		} catch (Exception e) {
			log.error("setDocumentWithWaterMarkAndPieImage error! msg: {}.", e.getMessage());
		}
		return outputStream.toByteArray();
	}
	
	/**
	 * PDF檔案插入圖片
	 * @param stamper
	 * @param reader
	 * @return
	 */
	public void addImage(PdfStamper stamper, PdfReader reader, JFreeChart chart, PdfContentByte over) {
		log.debug("PdfReportRepositoryImpI addImage({}, {}, {}).", stamper, reader, chart, over);
		float x, y;
		Rectangle pagesize;
		PdfGState gs1 = new PdfGState();

		try {
			int n = reader.getNumberOfPages();
			// 套明度設定: 淺灰色
			gs1.setFillOpacity(1.0f);
			ByteArrayOutputStream outStream = new ByteArrayOutputStream();
			ImageIO.write(chart.createBufferedImage(300, 300), "PNG", outStream);
			Image image = Image.getInstance(outStream.toByteArray());
			outStream.close();
			pagesize = reader.getPageSizeWithRotation(1);
			over = stamper.getOverContent(1);
			// 設定圖片寬高
			image.scaleToFit(250, 250);
			image.setTransparency(new int[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
			over.setGState(gs1);
			x = (pagesize.getLeft() + pagesize.getRight()) / 2;
			y = (pagesize.getTop() + pagesize.getBottom()) / 2;
			// 設定圖片位置
			x = x + 150;
			y = y - 75;
			image.setAbsolutePosition(x , y);// 左邊距、底邊距
			// 圖片
			over.addImage(image);
			over.saveState();
		} catch (Exception e) {
			log.error("addImage error! msg: {}.", e.getMessage());
		}
	}

 

4. 製作結果

   

 

5. 相關資源連結

    5.1 STS4下載: https://spring.io/tools

    5.2 iReport-5.6.0下載: https://zh-tw.osdn.net/projects/sfnet_ireport/downloads/iReport/iReport-5.6.0/iReport-5.6.0.zip/

    5.3 依賴檔下載: https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports

    5.4 iReport 環境設定參考: https://www.tpisoftware.com/tpu/articleDetails/1563

劉正輝 Chenghui Liu