JasperReport 中踩過的坑

来源:https://www.cnblogs.com/penghongwei/archive/2018/08/13/9471801.html
-Advertisement-
Play Games

Mac Book Pro 10.13.6Jaspersoft Studio community version 6.6.9JDK 8 安裝 Jaspersoft Studio Jasper Report 分為專業版(收費)和社區版(免費),如果只是用來設計一些 基本的報表模板,社區版就足夠了。從這裡 ...


 

Mac Book Pro 10.13.6
Jaspersoft Studio community version 6.6.9
JDK 8

安裝 Jaspersoft Studio

Jasper Report 分為專業版(收費)和社區版(免費),如果只是用來設計一些 基本的報表模板,社區版就足夠了。從這裡可以下載,選擇 Jaspersoft Studio。

  jaspersoft-studio.png

安裝時可能遇到的問題

如果在安裝時一切順利,那麼可以直接跳過本小節。

  install-jasper-studio.png

如果安裝過程中遇到如上錯誤,可以嘗試一下 解決辦法:

  • Step 1

System Preferences -> Security & Privacy -> General

  security-privacy.png
  • Step 2

如果 Step 1 不能解決問題,打開 Terminal,執行如下命令:

# sudo bash
# xattr -cr "/Applications/TIBCO Jaspersoft Studio 6.6.0.app"

如果你對這個 xattr 感到好奇,可以執行 man xattr 讀一讀解釋。

理解 Report Band

Jasper Studio 內置了很多種 Band,不同的 Band 有不同的用途,也有些不同預設行為。除了 Background Band,其他的 Band 的總高度要小於或等於一頁的最大高度減去頂部和底部的邊距。

Title

預設只會出現在第一頁的最頂部。也可以像下麵這樣控制它顯示在單獨的一頁上。

  title-on-new-page.png

Page Header

頁眉預設會出現在每一頁的頂部。當 Title 和 Summary Band 設置為顯示在單獨的一頁上時,頁眉則不會顯示出來。

Detail

Detail Band 比較神奇,它能夠自動的迭代 DataSource 中的所有元素,如果 DataSource 為 null,則只有靜態文本會顯示出來。預設情況下,Main Dataset 的 Query 有多少條記錄,Detail Band 就會重覆多少遍。

更加詳細一點的解釋看這裡

Page Footer

頁腳預設會出現在每一頁的底部。

Last Page Footer

如果想在最後一頁的頁腳上放一些不同的內容,就可以考慮使用這個 Band。

Summary

預設會顯示在報表的最後,和 Title Band 一樣,也可以設置為顯示在單獨的一頁上。如果是基於統計的報表,一般會放一些計算出來的內容,比如:最大值,最小值,平均值,總值等。當然,根據實際需要,也可以放任意內容。

  summary-on-new-page.png

Background

顧名思義,這個一般就是放報表的背景的,比如水印之類的。它的最大高度可以和報表一頁的高度相同。

其他的 Band

Column HeaderGroup HeaderGroup FooterColumn FooterNo Data 這些 Band 根據名字也比較容易理解,具體的解釋在這篇文章中講解的比較詳細。

全局設置

在頁面的全局設置中可以設置頁面的大小,邊距,尺寸單位,頁面方向等。

  page-format.png

一個小建議是,如果設計稿是什麼計量單位,這裡就設置成什麼計量單位,然後把尺寸設置的和設計稿一樣,避免在設計模板的時候做繁瑣的尺寸換算。

關於頁面邊距需要註意的一點是,即使將上下左右都設置為 0,最終導出 PDF 列印出來,邊距也不見得是 0,因為印表機的設置中可能還會有預設的邊距設置。

  printer-settings.png

頁碼

在報表中,幾乎都會有顯示頁碼的需求,比如 Page x of y 這種。看似很簡單,並且 Jasper Studio 中已經提供預設好的組件。

  page-x-of-y.png

這個內置的組件是通過兩個 Text Field 實現的,如果對對齊的精度要求不高,直接使用這個組件也是挺方便的。其原理就是使用了兩次 $V{PAGE_NUMBER} 變數,只是設置不同的求值時機即可。對於每一頁的頁碼設置 Evaluation Time 為 Now,對於總頁碼設置為 Report

但是如果對精度要求非常高的話,這個方式就不太合適了,我們需要用一個 Text Field 來實現,具體的做法參考這裡

設置中英文為不同的字體(Font Set)

相信用 Word 軟體進行過中英文排版的可能應對過這種需求,就是對一篇文章中的中英文需要設置不同 的字體,在 Word 中是有這個菜單項可以設置的,但是在 Jasper Report 中要實現這個需求就不那麼簡單了。

主要涉及到下麵這些步驟:

創建 Font Set

創建 Font Extension 和 Font Set 的步驟比較直觀,參考官方文檔即可。

在 Java 代碼中引用字體文件

如果伺服器所在的系統中沒有相應的字體,在 Jasper Report 嘗試生成 PDF 的時候就會報錯。需要以下幾步來解決:

  1. 首先需要從 Jasper Studio 中導出包含字體的 Jar 包,一次點擊 Window > Preferences > Jaspersoft Studio > Fonts,選中創建的 Font Set 和其包含的字體並點擊 Export

  2. 在導出 Font 到 Jar 包的對話框中寫上名稱並保存。比如 SampleFontSet.jar。這個 Jar 包和一般普通的 Jar 包不同,它包含了一些 Jaspersoft 需要的額外信息。

  3. 在代碼依賴中引入字體 Jar 包。

  jasper-font-set.png

將字體嵌入到 PDF 中

有人可能會懷疑將報表中用到的字體嵌入到導出的 PDF 中是否會帶來性能問題,畢竟一般漢字的字體文件體積都比較大,大幾十兆。在用 Japser 導出 PDF 的時候,只會嵌入用到的字體文件的子集,所以 幾乎不用擔心導出的 PDF 的體積。

測量 Text Field 的實際寬度(使用 Font Set )

如果在 Text Field 上設置的字體是自定義的 Font Set,那麼需要註意了,不是那麼容易能夠測量出真實的寬度。目前的解決辦法是先分離出文本中的漢字字元和非漢字字元,然後分別設置成相應的字體,再各自測量出寬度,最後相加即為文本所占的實際寬度。

  • 分離漢字字元和非漢字字元
    private static Pair<String, String> splitHanAndOthers(String str) {
        StringBuilder hanToken = new StringBuilder();
        StringBuilder othersToken = new StringBuilder();

        for (int i = 0; i < str.length(); ) {
            final int codePoint = str.codePointAt(i);

            if (UnicodeScript.of(codePoint) == UnicodeScript.HAN) {
                hanToken.append(str.charAt(i));
            } else {
                othersToken.append(str.charAt(i));
            }
            i += Character.charCount(codePoint);
        }

        return Pair.of(hanToken.toString(), othersToken.toString());
    }
  • 測量文本的實際寬度
    private static float getWidth(JRPrintText jrPrintText, String text, String fontName) {
        final DefaultJasperReportsContext instance = DefaultJasperReportsContext.getInstance();

        final JRDefaultStyleProvider defaultStyleProvider = jrPrintText.getDefaultStyleProvider();
        JRPrintText printText = new JRBasePrintText(defaultStyleProvider);
        printText.setText(text);
        printText.setFontName(fontName);
        printText.setFontSize(jrPrintText.getFontsize());

        final JRStyledText hanTextFullStyledText = printText.getFullStyledText(JRStyledTextAttributeSelector.getAllSelector(instance));
        final JRTextMeasurer measure = JRTextMeasurerUtil.getInstance(instance).createTextMeasurer(jrPrintText);
        final JRMeasuredText measuredText = measure.measure(hanTextFullStyledText, 0, 0, false);
        return measuredText.getTextWidth();
    }
  • 計算總寬度
    private float getTotalWidth(JRPrintText jrPrintText, String text) {
        final Pair<String, String> hanAndOthers = splitHanAndOthers(text);
        final String han = hanAndOthers.getLeft();
        final String others = hanAndOthers.getRight();

        final float hanWidth = getWidth(jrPrintText, han, "JianSong");
        final float othersWidth = getWidth(jrPrintText, others, "CorporateS");
        return hanWidth + othersWidth;
    }

Image

顯示圖片

  • 路徑問題

在 Jasper Studio 中添加一個 Image 組件,正確的設置一個圖片的路徑,點擊預覽就可以看到顯示出來的圖片了。但是細心的同學會發現在 .jrxml 模板文件中圖片的路徑是一個絕對路徑,這在實際生產過程中肯定是不行的。

因此,通過搜索找出了一個方法,可以解決靜態圖片的路徑問題。在 Image 組件的表達式中寫上這樣的表達式:

this.getClass().getResourceAsStream("/images/logo.png")
  image-expression.png

還有幾種其他的場景可能直接寫相對路徑也能工作,我沒有驗證過。

  • 傳參問題

其實,Image 組件的表達式中不僅支持寫圖片的路徑,也可以在代碼中將圖片讀入記憶體然後直接以 InputStream 的形式傳遞,不過要確保每個 Stream 只能被消費一次,不要復用同一個 InputStream,如果想要復用,考慮使用 Image 組件的 isUsingCache 屬性。不過 Jasper 支持的類型除此之外,還有好幾種:

* java.lang.String
* java.io.File
* java.net.URL
* java.io.InputStream
* java.awt.Image
* net.sf.jasperreports.engine.JRRenderable

例子可以參考這個

使用 InputStream 時可能遇到的問題

net.sf.jasperreports.engine.JRException: java.io.IOException: The byte array is not a recognized imageformat.
      at net.sf.jasperreports.engine.export.JRPdfExporter$InternalImageProcessor.processImageRetainShape(JRPdfExporter.java:1747)
      at net.sf.jasperreports.engine.export.JRPdfExporter$InternalImageProcessor.process(JRPdfExporter.java:1604)
      at net.sf.jasperreports.engine.export.JRPdfExporter$InternalImageProcessor.access$300(JRPdfExporter.java:1532)
      at net.sf.jasperreports.engine.export.JRPdfExporter.exportImage(JRPdfExporter.java:1472)
      at net.sf.jasperreports.engine.export.JRPdfExporter.exportElements(JRPdfExporter.java:1090)
      at net.sf.jasperreports.engine.export.JRPdfExporter.exportFrame(JRPdfExporter.java:3117)
      at net.sf.jasperreports.engine.export.JRPdfExporter.exportElements(JRPdfExporter.java:1098)
      at net.sf.jasperreports.engine.export.JRPdfExporter.exportPage(JRPdfExporter.java:1053)
      at net.sf.jasperreports.engine.export.JRPdfExporter.exportReportToStream(JRPdfExporter.java:917)
      at net.sf.jasperreports.engine.export.JRPdfExporter.exportReport(JRPdfExporter.java:537)
      at net.sf.jasperreports.engine.JasperExportManager.exportToPdfFile(JasperExportManager.java:155)
      at net.sf.jasperreports.engine.JasperExportManager.exportReportToPdfFile(JasperExportManager.java:503)
      at com.finger.hr.controllers.HRCreatePDFController$2.run(HRCreatePDFController.java:244)
Caused by: java.io.IOException: The byte array is not a recognized imageformat.
      at com.lowagie.text.Image.getInstance(Unknown Source)
      at net.sf.jasperreports.engine.export.JRPdfExporter$InternalImageProcessor.processImageRetainShape(JRPdfExporter.java:1742)
      ... 12 more

解決辦法很奇特,到現在我也不知道為什麼。

如果傳遞的參數是一個獨立的 InputStream 類型的欄位,就會有上述錯誤。 解決辦法就是把這個單獨的 欄位包到一個 Java 對象中(:D)。

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class PhotoDto {
    private InputStream photo;
}

List

List 顯示行號

不管是 List 還是 Table 如果只是想顯示簡單的行號,最簡單的辦法就是使用 $V{REPORT_COUNT}。 如果對行號還有一些比較複雜的計算邏輯,則最好在代碼中處理好,然後作為數據的一個欄位傳遞給 Jasper 直接渲染。

_THIS 大法

如果傳遞給 List 的(或者是 Table)數據是一個基本類型(這裡姑且把 String 也看做基本類型,其實只要是不包含屬性的對象都滿足條件)的數據的集合,那麼在引用單個數據的時候就得註意了,因為基本類型的數據不存在欄位一說,所以需要在 Field 的命名上做點文章。

比如說 dataSource 是 List<String>,那麼在定義 Field 的時候就沒辦引用對象的欄位名了,這時候我們只需要將欄位名定義為 _THIS 即可,在引用的地方寫 $F{_THIS}。更多解釋參考這裡

Table

Dataset

Table 嵌套中的坑

Table 中嵌套 Table 可以實現類似合併單元格的效果,但是如果最外層的 Table 中一行的高度超過了頁面的最大高度就會直接報錯,而不是分頁。

Table 中實現分組效果

  • Merge Cell?impossible!

在 Table 組件中不能直接做合併單元格的操作,只能通過嵌套 Table 來實現類似的效果。

  • Table Cell 中的負數縮進

在使用 Jasper Studio 6.6.0 版的設計器的時候,在界面上設置坐標值的時候,會出現保存不成功的情況。只能通過直接修改模板源文件的方式設置負數坐標。感覺是 IDE 的 Bug,在後續版本應該修複了。

設置 right indent 為負數的時候,left indent 不能為 0,如果為 0 right indent 不會生效 :( (至今不知道為什麼 :boom:)

  • 利用 Table 組件的特性

如果有分組的需求,可以考慮使用 Table 的 Group Header 和 Group Footer 來顯示組名和小計之類的數據。

Text Field & Static Text

  • Strech

關於 Strech,在設計器里有一個屬性叫做 Strech With Overflow,當這個屬性被勾選後,表示當數據的長度超過組件的長度時會自動折行,相當於垂直方向上的 Overflow,而 Jasper 也不支持水平方向的 Overflow。

  • Pattern

在 Jasper 中可以很方便的設置一些特殊文本的顯示格式,比如: 日期,時間,貨幣,數字,百分比等。

  pattern.png

SubReport

  • Compile 問題

在 Jasper Studio 的設計器里可以通過直接引用 *.jasper 文件的方式,在主報表中引用子報表。但是如果在實際的產品代碼中這樣做,非常不利於對源碼的版本控制,因為這個 *.jasper 文件是一個經過編譯之後的臨時文件。

  subreport-expression.png

我們在生產代碼中可以先將子報表的模板預編譯好,然後直接以參數的形式傳遞給主報表(例如如下代碼將編譯好的 “checklist.jrxml” 子報表作為參數傳給主報表,然後在主報表中的引用表達式寫成$P{checklist})。

    String subreport = "checklist";
    JasperReport jasperReport = null;
    try (InputStream inputStream = getResourceAsStream(subreport + ".jrxml")) {
        jasperReport = JasperCompileManager.compileReport(inputStream);
    } catch (IOException | JRException e) {
        e.printStackTrace();
    }
    params.put(subreport, jasperReport);

Detail Band 重覆多次的問題

Detail Band 被渲染的次數是由 Main Dataset 中數據條數決定的,如果發現 Detail Band 被重覆渲染了多次,那麼請檢查是否添加了 Main Dataset 的 Query,並且查詢出來的數據不止一條。如果沒有要設置 Main Dataset 的場景,可以選擇 One Empty Record

頁面的多列排版

在頁面設置中的多列設置,只對 Detail Band,Column Header,Column Footer 有影響。

HTML Markup 的限制

對於 HTML Markup 的使用,其實沒有想象中的那麼美好,使用起來功能比較有限。只能支持文本格式相關的標簽和屬性,不支持佈局相關的標簽。所以想通過 CSS 實現 一些效果,基本上都行不通。

支持的 HTML 標簽如下:

  • b
  • u
  • i
  • font
  • sup
  • sub
  • li
  • br

這個列表是從源碼中 找出來的,並沒有什麼文檔(也有可能是我沒搜到)明確的列出來。 源碼參考這裡

Position Type 設置為 Float 中的誤區

一開始我不是特別理解這個 Float Position Type 的原理,走了很多彎路,經過很長時間的摸索才理解 Float Position Type 的用法。其實主要是因為 Jaser Studio 設計器中顯示出來的樣子容易誤導使用的人。

正常情況下,在設計模板的時候我們會把一些組件擺放的規規矩矩,整整齊齊的,看著都覺得舒服。Float 是相對於設計器里離它最近的元素漂移的,其相對位置在設計模板的階段就決定好了。而有的組件在設計器里展示的形狀比較大,如果要擺放整齊,需要 Float 的元素就會有比較大的相對距離出現。其實,這個時候我們不能相信我們眼睛看到的,也不能有強迫症,在設計器里。因為很多時候需要一些元素看上去是疊在一起的,渲染出來的效果反而是我們需要的。

  position-type-float.png

我們在實際項目中有一個模板設計出來是這樣的,是不是看著感覺很亂,但是要使用 Float 達到我們想要的效果,還必須要設計成這樣。

時間的時區問題

解決報表中時區問題很簡單,只需要給報表內置的一個變數傳入相應的時區就行了。

Map<String, Object> params = new HashMap<>();
params.put("REPORT_TIME_ZONE", TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));

通過代碼定位元素坐標(終極大法)

如果在 Jasper Studio 中無法實現組件的動態定位,基本上也就只有這麼一條路可以走了。如果這條路也實現不了,要麼改需求,要麼換掉 Jasper Report 解決方案。

參考鏈接

  1. https://community.jaspersoft.com/documentation/tibco-jaspersoft-studio-user-guide/v71/getting-started-jaspersoft-studio-0
  2. https://community.jaspersoft.com/documentation/tibco-jaspersoft-studio-user-guide/v630/understanding-bands
  3. https://community.jaspersoft.com/wiki/report-structure-jaspersoft-studio
  4. https://communities.ca.com/blogs/rob.ensinger/2015/12/28/how-to-single-textbox-page-x-of-y-page-counts-in-jaspersoft-reports
  5. https://stackoverflow.com/questions/10673263/show-page-x-of-y-using-a-single-text-field
  6. https://community.jaspersoft.com/documentation/tibco-jaspersoft-studio-user-guide/v630/working-font-extensions
  7. https://stackoverflow.com/questions/3623420/image-expression-url-in-jasper-reports
  8. https://stackoverflow.com/questions/11949333/passing-the-list-of-primitive-type-objects-as-datasource-for-subreport
  9. https://stackoverflow.com/questions/27903072/print-liststring-each-element-in-new-field
  10. https://stackoverflow.com/questions/11949333/passing-the-list-of-primitive-type-objects-as-datasource-for-subreport

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 近幾年的的微服務概念大火特火,隨之框架也變得大火起來,尤其是spring boot,可能是因為spring cloud火起來的原因 搞得沉寂多年的dubbo也開始更新變得火起來。 說起微服務對於不瞭解整個系統架構歷史的小伙伴可能有些迷惑,怎麼就突然一下子就微服務了,有點摸不著頭腦,到底咋回事那?聽我 ...
  • 《劍指offer》: 首先熟悉一下java自帶的進位之間轉換的api: /*java中進行二進位,八進位,十六進位,十進位間進行相互轉換十進位轉成十六進位:Integer.toHexString(int i)十進位轉成八進位Integer.toOctalString(int i)十進位轉成二進位In ...
  • 問題:通常會遇到這樣一個問題,就是svn伺服器不能以SVN 協議訪問,而只能用https協議訪問。 主要原因是svn服務端集成了apache的安裝包,安裝後自動配置成了http協議訪問。如果需要svn協議訪問,則需要重新架設svn版本管理伺服器,不能集成apache,使用純svn安裝包即 “ sub ...
  • zendstudio 10.0破解版,新建完項目後,首先修改項目的編碼方式,統一改成utf-8 (選中項目,再右鍵properties:Text file encoding)。修改字體大小。 apache 安裝:httpd-2.2.17-win32,安裝成功後訪問apache伺服器:http://1 ...
  • 1. Java 中File類的createNewFile()與createTempFile()的區別 最近,在看代碼時看到了一個方法, File.createTempFile() ,由此聯想到File.createNewFile() 方法,一時間不知道兩者到底有什麼區別,感覺都是創建新文件嘛,後來查 ...
  • You can do this using a JLabel, but an alternative would be to style a JButton. That way, you don't have to worry about accessibility and can just fir ...
  • String examplejsPrefix = "example"; String examplejsSuffix = "js"; String examplejs = examplejsPrefix + "." + examplejsSuffix; try { // save it as a t ...
  • 在程式運行時,發生了期望之外的情況,它阻止了程式按照程式員的預期正常執行,這就是異常。 對於異常,Java提供了優秀的解決辦法:異常處理機制。常處理機制能讓程式在異常發生時,按照代碼的預先設定的異常處理邏輯,針對性地處理異常,讓程式盡最大可能恢復正常並繼續執行,且保持代碼的清晰。 Java中的異常可 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...