Jasper Report 教程

1    简介

JasperReport是一个强 大、灵活的报表生成工具,能够展示丰富的页面内容,并将之转换成PDF,HTML,或者XML格式。最重要的是它是开源的,这给我们带来很大方便,但是文档却要钱,让人不爽。不过人总要生存,再说,做这么一个好东西,用户总不能一点代价也不付(虽然对于中国普通程序原来说太贵了点)。它还有一个相关的开源 工程—IReport,这是一个图形化的辅助工具,因为JasperReport仅提供了可使用的类库而未提供更好的开发工具,IReport的出现解决 了这一难题。它们配合使用将会更大程度的提高效率。

该库完全由Java写成,可以用于在各种Java应用程序,包括J2EE,Web应用程序中生成动态内容。它的主要目的是辅助生成面向页面的(page oriented),准备付诸打印的文档。

JasperReport借由定义于 XML文档中的report design进行数据组织。这些数据可能来自不同的数据源,包括关系型数据库,collections,java对象数组。通过实现简单的接口,用户可以 将report library插入到订制好的数据源中,在以后我们将提到有关内容。

其实这是一份JasperReport Ultimate Guide的简单翻译以及我的理解和例子。在最后,我将描述一个我正在做的工程,将其中用到的相关信息贡献出来。我这么做是因为当我在学这个类库的时候苦于很少有相关的中文文档,诱惑语焉不详,希望其他人不再受苦。这个文档将分几次贴出来,与原文档的章节相对应。这份文档的Word形式将在全部完成之后放在我的公开邮箱中与各位共享。我的EMail是jxuedi@gmail.com有什么意见或想法请与我联系。

闲言少叙,进入正题。

2      API概览


上图为一个生成报表并打印(导出)的全过程。我将会把涉及到的重要的类进行一一说明。

Class net.sf.jasper.engine.design.JasperDesign

这是一个未经加工的报表实例,供JasperReport Library使用。这个类可以在JasperReport类库内置的XML解析器对XML report design进行解析处理之后得到。如果你的程序不想对直接XML文件进行操作,在例子noxmldesign中有不使用XML设计文件而动态生成这个类 的方法。我们稍稍看看这个例子:

import 略

public classNoXmlDesignApp

{

private staticJasperDesign getJasperDesign() throws JRException

{

//JasperDesign定义JasperDesign的头信息

             JasperDesign jasperDesign = new JasperDesign();

             jasperDesign.setName("NoXmlDesignReport");

             .剩余略

             

             //Fonts定义使用到的字体

             JRDesignStyle normalStyle = new JRDesignStyle();

             normalStyle.setName("Arial_Normal");

                           

             //Parameters 定义Parameters的内容—这个内容以后会提到

             JRDesignParameter parameter = new JRDesignParameter();

             parameter.setName("ReportTitle");

             parameter.setValueClass(java.lang.String.class);

             jasperDesign.addParameter(parameter);

 

             parameter = new JRDesignParameter();

             parameter.setName("OrderByClause");

             parameter.setValueClass(java.lang.String.class);

             jasperDesign.addParameter(parameter);

 

             //Query 定义查询

             JRDesignQuery query = new JRDesignQuery();

             query.setText("SELECT * FROM Address $P!{OrderByClause}");

             jasperDesign.setQuery(query);

 

             //Fields

             JRDesignField field = new JRDesignField();

             field.setName("Id");

             field.setValueClass(java.lang.Integer.class);

             jasperDesign.addField(field);

 

             //Variables 定义变量

             JRDesignVariable variable = new JRDesignVariable();

             variable.setName("CityNumber");

             variable.setValueClass(java.lang.Integer.class);

             variable.setResetType(JRVariable.RESET_TYPE_GROUP);

 

             //Groups 定义组

             group.setMinHeightToStartNewPage(60);

             expression = new JRDesignExpression();

             //余下定义一个文档的其他内容,这里省略

             

             return jasperDesign;

      }

从getJasperDesign()方法我们可以看出,这个应用程序并没有从XML文件里面将report design提取出来在生成JasperDesign类,而是直接利用JasperDesign提供的函数生成了一个报表设计。这样做的原因是基于灵活性 的考虑,你可以在程序中随时动态生成报表,而不需要去从硬盘或网络中读取XML设计文件。但通常我不这么做,因为比较麻烦,而且要对 JasperReport的每个元素都非常熟悉才行。

Class net.sf.jasper.engine.JasperReport

这个类的实例包含了一个经过编译的report design对象。生成它的时机是对报表编译之后,但尚未对其填入数据的时候。编译过程中,JasperReport需要生成一个临时的类文件,用以保存 report expression,如变量表达式,文本,图像表达式,组表达式等等。这个临时的Java Source File是被动态编译的,编译器使用的是JDK中用来执行应用程序的编译器类(compiler class)。如果 tools.jar不在classpath中,编译过程将采用javac.exe来进行后台编译。编译后所得的字节码保存在JasperReport类 中,用来在执行期装填数据(filling thereport with data)和给表达式赋值(evaluate various reportexpression)。

Class net.sf.jasper.engine.JasperCompileManager

这是一个上面提到的与编译有关的类。 利用它提供的一些方法,你将有能力编译从本地硬盘或一个Input Stream获得的XML report;还可以通过传给JasperCompileManager一个JasperDesign类,来对内存中的report design进行编译—功能很强大。

Class net.sf.jasper.engine.JasperPrint

当一个报表已经装填好数据之后,这个 文档就以JasperPrint类的实例出现。这个类可以直接用JasperReport内置的viewer进行查看,也可以序列化到硬盘以备后用,或者 发送到网上去。这个类的实例是报表装填过程后的产物,它可以被JasperReport类库中的导出方法导出成各种流行的格式—PDF,HTML,XML 等等。

Interface net.sf.jasper.engine.JRDataSource

这个类与报表的数据源有关。只要能够恰当的实现他的一些接口,用户就可以在报表中使用各种数据源,在报表装填的时候由报表引擎负责对数据进行解释和获取。当报表装填的时候,报表引擎都会在后台生成或提供一个该接口的实例。

Class net.sf.jasper.engine.JRResultSetDataSource

这是一个JRDataSource的缺省实现,因为很多报表数据都来源于关系数据库,所以JasperReport缺省包含了这个外覆(wrap)了java.sql.ResultSet对象的实现。

这个类可以用来包裹(wrap)用以对报表进行装填的、已经载入的结果集,也可以被报表引擎用来包裹通过JDBC执行完查询后所得的数据----非常有用。

Class net.sf.jasper.engine.data.JRTableModelDataSource

顾名思义,这个类用于包裹java.swing.table.TableModel类中的数据,它也是实现了JRDataSource接口,用于在Java Swing程序中使用已经被载入到table中的数据来生成报表。

Class net.sf.jasper.engine.JREmptyDataSource

这是JRDataSouce接口的最简单实现,这个类用在不需要显示数据源数据而从参数中获取数据的报表和仅需要知道数据源中的实际行数(number of virtual rows)的报表中。

JasperReport自带的例子:fonts,images,shapes和unicode中使用这个类对报表进行装填,来模拟没有任何record的数据源,这时所有的field都为null。例如:

JasperRunManager.runReportToPdfFile(fileName,null, new JREmptyDataSource());

Class net.sf.jasper.engine.JasperFillManager

这个类用来实现报表的数据装填。这个类提供了很多方法来接受各种类型的report design--可以是一个对象,一个文件,或一个输入流。它的输出结果也是多样的:file,Object,outputStream。

report的装填引擎需要接收一个可以从中获取数据和value的数据源作为报表参数。参数值(Parameters value)通常使用Java.util.Map来提供,里面包含的KEY是报表的参数名。

数据源可以通过两种方式提供,这取决于你的解决方案:

通常情况下,用户应该提供一个JRDataSource对象,例如我前面提到的那些。

但是大多数的报表都是采用关系数据库 中的值来装填数据,所以JasperReport拥有一个内置的缺省行为—让用户在报表设计的时候提供一个SQL查询。在运行期,这个查询将被执行以用来 从数据库中获取要装填的数据。在这种情况下,JasperReport仅需要一个java.sql.Connection对象来取代通常的数据对象。JasperReport需要这个连接对象来连接数据库管理系统并执行查询操作。

在查询结束之后,JasperReport将自动生成一个JRResultSetDataSource,并将它返回给报表装填过程。

Class net.sf.jasper.engine.JRAbstractScriptlet

这个类同样用于报表装填期间,用户可以自己定义一些代码,并由报表引擎在装填过程中执行。这些用户代码可以处理报表数据操作,或在一些定义好的时刻执行,例如page,列,或组的分割处。

Class net.sf.jasper.engine.JRDefaultScriptlet

这是一个非常方便的JRAbstractScriptlet的子类。通常情况下你应该选择继承这个类。

Class net.sf.jasper.engine.JasperPrintManager

这个类用户提供打印方法,用户可以将整个文档或部分文档传递给它,也可以选择是否显示打印Dialog,这在他的API文档中可以找到,这里不再赘述。

Class net.sf.jasper.engine.JasperExportManager

顾名思义,这个类负责文档的导出。这个类的具体信息详见API文档。非常明显和清除,没什么好解释的,Just use it即可。

Class net.sf.jasper.engine.JasperRunManager

有时,我们仅仅需要构造一个流行的文档格式,例如PDF,或HTML,而不需要将装填过程后生成的JasperPrint对象保存到硬盘或其他中间媒体上。这时,可以使用这个类来直接将装填过程生成的文档导出到所需的格式。

Class net.sf.jasper.view.JRViewer

这是一个基于Swing的应用程序,你可以将它视为一个独立组件,用来进行打印预览。用户可以继承这个类,来构造满足自身要求的预览程序。

Class net.sf.jasper.view.JasperViewer

这个类更像是使用JRViewer的教学组件,它演示了一个Swing应用程序如何装在并显示报表。

Class net.sf.jasper.view.JasperDesignViewer

这个类用于报表的设计期间,用来预览报表模版。它仅作为一个开发工具存在于类库中。

Class net.sf.jasper.engine.util.JRLoader

装载器用于报表生成的各个主要阶段—编译,装填等等。用户和引擎都可以利用这个类来装载所需的序列化对象如file,URLs,intput stream等等。这个类最令人感兴趣的函数当属loadOnjectFromLocation(String location)。当用户使用这个类从指定地点装载对象的时候,该函数将首先将location解释为一个合法的URL,如果解析失败,函数将认为所提 供的location是硬盘上的一个文件名,并将试图读取它。如果在指定地点没找到文件,它将通过classpath定位一个相应于该location的 资源,所有努力失败之后,将抛出异常。

3      主要的任务和过程

这一节我们将看到对你的XML报表设计进行分析,编译,装填数据,预览结果和导出到其他格式的过程。

3.1   XML解析

JasperReport使用SAX2.0 API对XML文件进行解析。然而,这并不是必须的,用于可以在执行其自行决定使用哪一种XML解析器。

JasperReport使用 org.xml.sax.helpers.XMLReaderFactory类的createXMLReader()来获得解析器实例。在这种情况下,就 像在SAX2.0文档中说的那样,在运行期,把Java系统属性org.xml.sax.driver(这是属性的key)的值(value)设定为 SAXdriver类的全限定名是必要的。用户可以通过两种方法做到这一点,我稍后将解释全部两种方法。如果你想使用不同的SAX2.0XML解析器,你需要指定相应的解析器类的名字。

设置系统属性的第一种方法是在你启动Java虚拟机的时候,在命令行使用-D开关:java –Dorg.xml.sax.driver=org.apache.serces.parsers.SAXParsermySAXApp sample.xml

在JasperReport提供的所有例子中,都采用ANT构建工具来执行不同的任务。我们通过使用内置的<java> task中的<sysproperty>元素来提供这一系统属性:

<syspropertykey=”org.xml.sax.driver” value=”org.apache.xerces.parsers.SAXParser”/>

第二种设置系统属性的方法是使用java.lang.System.setProperty(Stringkey, String value)

System.setProperty(“org.xml.sax.driver”,”org.apache.xerces.parsers.SAXParser”);

Jsp/compile.jsp和web-inf/class/servlets/CompileServlet.java文件提供了这方面的例子。

注:对于第二种方法,我要说些题外 话。有关于JVM的系统属性(我们可以通过System.out.println(System.getProperty(“PropertyKey”) 来查看),可以在运行期像上面说所得那样用System.setProperty(“propertyKey”,”propertyValue”);来进 行设置。但是一旦JVM已经启动之后,其内建的系统属性,如user.dir,就不能再被更改。奇怪的是我们仍可以用 System.setProperty()方法对其进行设置,而在用System.out.println(System.getProperty())方法进行查看的时候发现,其值已经更改为我们设置的值,但事实上我们设置的值不会起任何作用。所以对于内置的属性,我们只能通过-D开关在JVM执行之前 进行设置。对于org.xml.sax.driver,由于它不是系统内建属性,所以仍然可以在JVM启动之后加以设置。更详细的信息可以参考王森的 〈Java深度历险〉。

3.2   编译报表设计(Report Designs)

为了深成一个报表,用户需要首先生成 报表的设计(report’s design),生成方法或采用直接编辑XML文件,或通过程序生成一个 net.sf.jasper.engine.design.JasperDesign对象。本文中,我将主要采用编辑XML文件的方法,因为这种方法在目 前是使用JasperReport类库的最好的方法,并且我们有机会更好的了解类库的行为。

先前提到过,XML报表设计是JasperReport用来生成报表的初级材料(raw meterial)。这是因为XML中的内容需要被编译并载入到JasperDesign对象中,这些对象将在报表引擎向其中填入数据之前经过编译过程。

注意:大多数时候,报表的编译被划归 为开发时期的工作。你需要编译你的应用程序报表设计,就像你编译你的Java源文件一样。在部署的时候,你必须将编译好的报表,连同应用程序一起安装到要 部署的平台上去。这是因为在大多数情况下报表设计都是静态的,很少用应用程序需要提供给用户在执行期编译的,需要动态生成的报表。

报表编译过程的主要目的是生成并装载 含有所有报表表达式(report expression)的类的字节码。这个动态生成的类将会被用来在装填数据,并给所有报表表达式求值(evaluate)的时候使用。具体例子是,如果 你用IReport生成一个报表名字叫SimpleSheetTest,它的XML设计文件名叫SimpleSheetTest.jrxml,同时和它在 同一目录下IReport会自动生成一个文件名为SimpleSheetTest.java,里面主要是一些报表元素,如 Field,Parameters,Variables的定义,以及一些求值表达式。当然,像上面提到的,这个文件在你直接使用JasperReport API的时候是看不到的,因为它是在执行期生成的一个Class。要想看到它的办法是:在IDE(JBuilder,Eclipse)中单步执行程序,在报表打印的阶段,你将能跟踪到这个类,它的名字就是“你的报表名.java”,按上面的例子就是SimpleSheetTest.java,这和 IReport是一致的。当然也可以像下面说的那样,到生成这个类的临时目录里找到它。

在这个类生成过程之前,JasperReport引擎需要验证报表设计的一致性(consistency),哪怕存在一处验证检查失败都不会继续运行下面的工作。在下面的章节,我将会展示报表设计验证成功之后的状况。

对于这个包含了所有报表表达式(reportexpressions)的类的字节码,我们至少需要关心三个方面的内容:

l       临时工作目录(temporary working directory)

l       Java 编译器的使用

l       Classpath

 

为了能够编译Java源文件,这个文件必须被创建并且被保存到磁盘上。Java编译过程的输出是一个.class文件,这个包含所有报表表达式的类在这个工作目录里被创建并编译,这也是为什么JasperReport需要访问这个临时目录的原因。当报表的编译过程结束之后,这些临时的类文件将被自动删除,而生成的字节码将保存在 net.sf.jasper.engine.JasperReport对象中。如果需要的话,这个类可以将自己序列化(serialized itself)并保存到磁盘上。这就是IReport的做法。

缺省情况下,这个临时工作目录就是启 动JVM时的当前目录,这却取决于JVM的系统属性user.dir。通过更改系统属性jasper.report.compile.temp,用户可以很容易更改这个工作目录。在Web环境下,特别是当你不想让含有启动WebServer的批处理文件的目录和报表编译过程的临时工作目录混在一起的时候,修改这个属性就可以了。

上面提到的第二个方面涉及用来编译报 表表达式类的Java编译器。首先,报表引擎将试图使用sun.tools.javac.Main类来编译Java源文件。这个类包含在 tools.jar中,当且仅当这个jar文件在JDK安装目录下的bin/目录中,或在classpath中 时,sun.tools.javac.Main才能正常使用。

如果JasperReport不能成功装载sun.tools.javac.Main文件,程序将动态执行java编译过程,就像我们通常用命令行那样,使用JDK安装目录下的bin/目录 下的javac.exe。这就是为什么将JDK安装目录/lib/下的tools.jar文件copy到JasperReport工程的lib/目录下是一个可选的操作(optional operation)。如果tools.jar不在classpath中,JasperReport将显示错误信息并继续上面提到的操作。

当编译Java源文件的时候,最重要的事情莫过于classpath。如果Java编译器不能在指定的classpath中找到它试图编译的所有相关类的源文件,则整个过程将失败并停止,错 误信息将在控制台显示出来。同样的事情也将发生在JasperReport试图编译报表表达式类的时候。所以,在runtime为编译过程提供正确的 classpath是非常重要的。例如,我们我们需要确认在classpath中,我们提供了在报表表达式中可能用到的类(custom class)。

在这个方面也有一个缺省的行为。如果 没有为编译report class特殊指定classpath,引擎将会使用系统属性java.class.path的值来确定当前的JVM classpath。如果你指定了系统属性jasper.reports.compile.class.path的值,你可以用你定义的classpath来覆盖缺省行为。

大多数情况下,编译一个report只需要简单的调用JasperReport类库中的JasperCompileManager.compileReport(myXmlFileName);即可。调用之后将生成编译好的report design并存储在.jasper文件中,这个文件将会保存在和提供XML report design文件相同的目录中。

3.3   Report Design预览

JasperReport类库并没有 提供高级的GUI工具来辅助进行设计工作。但是目前已经有至少4个project试图提供这样的工具。然而,JasperReport本身提供了一个很有 用的可视化组件来帮助报表设计者在编译的时候预览报表设计(其实不如直接用IReport方便)。

net.sf.jasper.view.JasperDesigner是一个基于Swing的Java应用程序,它可以载入并显示XML形式或编译后的报表设计。尽管它不是一个复杂的GUI应用程序,缺乏像拖拽可视化报表元素这样的高级功能,但是它仍然是一个有用的工具(instrument)。所有JasperReport工程提供的例子都利用了这个报表查看器(report viewer)。

如果你已经安装了ANT(别告诉我你不知道什么是ANT),想要查看一个简单的报表设计(JasperReport工程所带例子),你只需要到相应的文件夹下输入如下命令:

〉antviewDesignXML   或者   〉ant viewDesign

如果你没安装ANT,要达到上面的效果就不是很容易,因为JasperReport本身需要一些其他辅助的jar包(在JasperReport安装目录/lib下),在运行的时候,你需要 把这些jar包都包含到你的classpath里面,并且正确设计系统属性,如上面提到的org.xml.sax.driver。我可以展示一下在 windows下的例子:

>java-classpath ./;../../../lib/commons-digester.jar;

../../../lib/commons-beanutils.jar;../../../lib/commons-collections.jar;

../../../lib/xerces.jar;../../../lib/jasperreports.jar

-Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser

dori.jasper.view.JasperDesignViewer-XML -FFirstJasper.xml

很麻烦吧?还是赶快弄个ANT吧。下面是预览之后的结果(其实用IReport更好)


3.4报表装填(Filling Report)

报表装填(report filling)过程是JasperReport library最重要的功能。它体现了这个软件最主要的目的(mainobjective),因为这一过程可以自由的操作数据集(data set),以便可以产生高质量的文档。有3种材料需要装填过程中作为输入提供给JasperReport:

l       report design(reporttemplet)

l       参数(parameters)

l       数据源(data source)

这一过程的输出通常是一个单一的最终要被查看,打印或导出到其他格式的文档。

要进行这一过程,我们需要采用net.sf.jasper.engine.JasperFillManager类。这个类提供了一些方法来让我们装填报表设计(report design),report design的来源可以是本地磁盘,输入流,或者直接就是一个已存在于内存中的net.sf.jasper.engine.JasperReport类。 输出的产生是于输入类型相对应的,也就是说,如果JasperFillManager接到一个report design的文件名,装填结束后生成的report将会是一个放在磁盘上的文件;如果JasperFillManager收到的是一个输入流,则生成的 report将会被写道一个输出流中。

有些时候,这些JasperFillManager提供的方法不能满足某些特定的应用的要求,例如可能有人希望他的reportdesign被作为从classpath中得到的资源,并且输出的报表作为一个文件存放在一个指定的磁盘目录下。遇到这种情况时,开发人员需要考虑在将报表设计传递给报表装填过程之前,用net.sf.jasper.engine.util.JRLoader类来装载report design对象。这样,他们就能获得像报表名这样的reportdesign属性,于是开发者就能生成最终文档的名字(construct the name of theresulting document),并将它存放到所需的位置上。

在现实中,有许多报表装填的情境(scenarios),而装填管理器仅试图覆盖其中最常被使用到的部分。然而对于想要自己定制装填过程的人来说,只要采用上面所说的方法,任何开发者都可以达到满意的结果。

报表参数通常作为java.util.Map的value提供给装填管理器,参数名为其键值(key)。

作为装填过程所需的第三种资源—数据源,有如下两种情况:

通常,引擎需要处理net.sf.jasper.engine.JRDataSource接口的一个实例,通过这个实例,引擎可以在装填过程中获取所需数据。 JasperFillManager提供的方法支持所有的JRDataSource对象(这是一个Interface,上面一章提到过它的常用实现)。

然而,这个管理器还提供一些接受java.sql.Connection对象作为参数的方法集,来取代所需的数据源对象。这是因为在很多情况下,报表生成所需的数据都来源于某个关系型数据库中的表(table)。

在报表中,用户可以提供SQL查询语句来从数据库中取回报表数据(report data)。在执行期,engine唯一需要做的是获得JDBC connection对象,并使用它来连接想要连接的数据库,执行SQL查询并取回报表数据。在后台,引擎将使用一个特殊的JRDataSource对 象,但是它对于调用它的程序来说是透明的。

JasperReport工程提供了 相关的例子,它们采用HSQL数据库服务器(在工程文件中,有一个相应的文件夹),要运行这些例子你需要首先启动该服务器,方法是:在/demo /hsqldb目录下输入如下命令:>ant 或者 >ant runServer

没装ANT就麻烦点:>java -classpath ./;../../lib/hsqldb.jar org.hsqldb.Server

一下代码片断显示了query例子是如何装填数据的:

//Preparing parameters

Map parameters = new HashMap();

parameters.put("ReportTitle", "AddressReport");

parameters.put("FilterClause", "'Boston','Chicago', 'Oslo'");

parameters.put("OrderClause","City");

//Invoking the filling process

JasperFillManager.fillReportToFile(fileName, parameters,getConnection());

3.5   查看报表(Viewing Reports

报表填充阶段的输出通常是一个JasperPrint对象,如果把它保存在磁盘上,通常以一个.jrprint文件的形式存在。JasperReport拥有一个内置的查看器,用来查 看用内置的XML导出器(XML exporter)获得的XML格式的报表文件。这个查看器就是以前提到过的net.sf.jasper.niew.JRViewer—一个基于 Swing的应用程序组件,用户可以通过继承这个类来定制自己所需的查看器。JasperReport工程中自带的例子webapp中,你可以阅读 JRViewerPlus类的代码来获取进一步内容。

注意:JasperViewer更像是一个教人们如何使用JRViewer组件的演示程序,这里要注意一点,当你调用JasperViewer的viewReport()方法来显示报表时,如果你关闭了预览Frame,整个应用程序将会随之结束,因为这个函数最后调用了System.exit(0);你可以通过继承这个类,并重新在你的Viewer里注册java.awt.event.WindowListener来避免这一情况的发生。

3.6   打印报表

JasperReport类库的主要 目标,就是生成可打印的文档。而且多数应用程序生成的报表都是需要落实(或打印)到纸张上。我们可以用net.sf.jasper.engine.JasperPrintManager来打印JasperReport生成的文档。当然,报表也同样可以在被导出到其他格式如PDF,HTML之后再被打印。通过JasperPrintManager提供的方法,我们可以打印整个文档,打印单个文档或打印某一范 围内的文档,可以显示打印对话框也可以不显示。下面的例子演示了不显示对话框,打印整个文档的方法:JasperPrintManager.printReport(myReport,false);

这个例子显示了如何打印5-11页的文档,同时显示打印对话框:net.sf.jasper.engine.JasperPrintManager.printPages(myReport,4,10,true);

3.7   导出报表

在一些应用程序环境下,将JasperReport生成的文档从其特有的格式导出到其他更为流行的格式如PDF,HTML是非常有用的。这样一来,其他人就可以在没有安装JasperReport的情况下查看这些报表,特别是当这些文档要通过网络发送出去的时候。

JasperReport提供了 JasperExportManager类来支持此项功能。这项功能将会在以后不断加入对新的格式的支持。目前,JasperReport主要支持导出 PDF,HTML和XML类型的文档,下面是导出的代码片 断:JasperExportManager.exortReportToHtmlFile(myReport);

注意:想要将自己的报表导出到其他格式的用户,需要实现JRExporter的接口,或继承相应的JRAbstractExporter类。

3.8   对象的载入和保存

当使用JasperReport的时候,你经常会与序列化的对象,如以编译的报表设计,或已生成的报表打交道。有时,你需要手动载入从不同的source如input stream或你用类库核心功能(lib’s corefunctionality)产生的序列化类。JasperReport提供了两个特殊的工具类来提供上述操作的能力,这些类通常供报表引擎自己使用:

net.sf.jasper.engine.util.JRLoader

net.sf.jasper.engine.util.JRSaver

       第一个类提供了一些方法让我们能够从不同类型的数据源如文件,URL,inputstream和classpath里面获取序列化对象。最令人感兴趣的方法是loadObjectFormLocation(String)。它已经在上一章中介绍过了,这里不再赘述。

与上面的对象载入工具相反的部分是JRSaver类,它可以帮助程序员将自己的类序列化之后存放到本地磁盘或通过Output Stream发送到网络上去。

有时,开发人员可能想要载入已经生成 好的report,或最终的已经被导出到XML格式的JasperReport文档,这与上面所说的直接load序列化对象有所不同。这时,我们需要载入的是将载入的XML内容进行编译,并生成JasperPrint对象,而并非仅仅是载入序列化对象。这时,我们可以通过 net.sf.jasper.engine.xml.JRPrintXmlLoader类的一些静态方法,通过编译从XML文件中读取的内容构建出一个位 于内存中的文档对象。

4      报表设计(Report Designs

报表设计体现了一个模版,JasperReport引擎利用这个模版将同台生成的内容传递给打印机,屏幕或Web。存储在数据库中的数据在报表装填的过程过被组织起来,根据已有的报表设计来获得可以进行打印的,面向页面的(page oriented)文档。

总而言之,一个报表设计包含了所有的结构相关信息和将数据提供给报表所涉及的各个方面。这些信息涉及将在文档中显示出来的各种text或图像元素的位置和内容,自定义计算(custom calculation),数据组织,报表生成时的数据操作,等等。

报表设计通常都定义在一个拥有特殊格 式的XML文档中,并且在被填充数据之前要经历JasperReport的编译过程,有关于这个XML文档的详细信息我们将在以后说明。然而 JasperReport也允许用户通过JasperReport提供的API构造in-memory报表对象,例程noxmldesign就是很好的例 子,但是我们通常不这么用。

4.1   DTD Reference

当使用XML文件进行报表设计的时候,JasperReport将使用内置的DTD文件来验证其受到的XML内容的有效性。如果XML验证通过,则说明所提供的报表设计符合 JasperReport所需要的XML结构和语法规则,其引擎能够生成经过编译的report design。

有效的XML文档总是在验证时指向 JasperReport的内部DTD文件。如果没有提供DTD文档的引用,报表的编译过程将会突然结束。这对所有人来说都是一个负担,因为DTD引用通常是相同的,并且这些引用可能会简单的被从以前的报表设计中copy过来。在一开始,你需要将这个引用从给定的例子中copy过来。

正如以前说的一样,报表引擎仅能识别 指向其内部DTD文件的的引用。你不能随便从类库的源文件中将那些DTD文件copy到别的地方,再在你的报表设计文件中文件中指向你copy的那些 DTD文件。如果你想那样做的话,你将需要调整类库中某些类,包括net.sf.jasper.engine.xml.JRXmlDigester类的某些代码。如果你遇到像引擎无法找到其内部的DTD文件而导致的无法载入资源的问题,请确定你已经在使用外部DTD文件之前排除了所有可能发生的情况。遇到 这样的问题是不太可能的,因为资源载入机制会随着时间不断改进。JasperReport只有两种合法的XML报表设计的DTD引用,他们是:

<!DOCTYPEjasperReport PUBLIC “-//JasperReports//DTD Report Design//EN” “http://JasperReports.sourceforge.net/dtds/jasperreport.dtd”>或者

<!DOCTYPEjasperReport PUBLIC “-//JasperReports//DTD Report Design//EN”“http://www.jasperreports.com/dtds/jasperreport.dtd”>

XML报表的root元素是<jasperReport>,下面是一个普通带JasperReport的样子:

<?xmlversion=”1.0”>

<!DOCTYPEjasperReport PUBLIC “-//JasperReports//DTD Report Design//EN” “http://JasperReports.sourceforge.net/dtds/jasperreport.dtd”>

<jasperReportsname=”name_of_the_report”…>

</jasperReports>

4.2   XML编码

当要生成不同语言的XML报表设计的时候,在XMl文件的首部的编码属性需要特别关注一下。缺省情况下,如果这个属性的值没被订制,则XML解析器将会使用“UTF-8”作为XML文件的编码格式。这一点是非常重要的,因为报表设计通常包含了静态的本地化text。对于大多数西欧语言来说,ISO-8859-1编码,也就是我们常说的 LATIN1将会很好的处理如法语中重音符号的显示问题。

<?xmlversion=”1.0” encoding=”ISO-8859-1”>

在编辑XML文件的时候,要找到某种特殊语言的编码类型,你可以查看XML document.FIXME

4.3   报表属性

我们上面已经看 到,<jasperReport>斯XML报表设计的根元素。这一节我将介绍报表设计对象的Property的细节以及这些属性所对应的 XML attributes(为避免混淆,我将不提供Property和Attribute的中文而直接使用英文)。

1

Report Name

每一个报表都必须有一个名字。这个名 字是相当重要的,因为类库需要它来生成文件,尤其是当编译,装填,导出报表的默认行为被使用的时候,这个名的作用更为重要。这个名字是以<jasperReport>元素的name attribute的形式提供的,并且是强制必须填写。

Column Count(列数)

JasperReport允许生成每页的列数超过一列的报表,正如下面的图片,展示了拥有两列的报表:

1

默认情况下,报表引擎生成每页一列的报表。

Print Order(打印顺序)

对于拥有超过一列的报表,为其提供列将被以什么顺序填充是很重要的。你可以使用<jasperReport>的printOrder attribute来进行设置。有如下两种情况:

n       Vertical Filling:这个选项将导致列是自顶向下被填充(printOrder=”Vertical”)

n       Horzontal:列将自左向右被填充(printOrder=”Horizontal”)

缺省设置将是printOrder=Vertical

Page Size(页面大小)

有两个attribute是用来提供要生成的文档大小的:pageWith,pageHeight。像所有其他显示元素位置和尺寸的attribute一样,这两个attribute 是以像素为单位的。JasperReport采用Java默认的每英寸72点的设置。这意味着pageWith=”595”将大约是8.26英寸,这大概 是A4纸的尺寸。

默认的纸张大小是A4纸:pageWith=”595” pageHeight=”842”

Page Orientation(默认设置为Portrait)

orientation属性用 来设置文档打印格式是“Portrait”还是“Landscape”。JasperReport允许用户在从“Portrait”切换到 “Landscape”的时候调整液面的宽度和高度。我们先看一个例子:我们假定要生成一个A4纸的报表,采用“Protrait”格式。

pageWidth=”595”pageHeight=”842” orientation=”Portrait”

如果我们决定用A4纸的“Landscape”布局,首先要调整相应的页面宽度和高度:

pageWidth=”842”pageHeight=”595” orientation=”Landscape”

这是因为JasperReport需要确切知道它所要绘制的报表页的宽度和高度,而不只看我们提供的orientation属性,至少在报表装填的时候是这样。 orientation属性仅在报表打印时有用,来通知打印机或某些exporters页面的orientation设置。

Page Margins(页边距)

一旦页面大小确定下来,用户就可以在生成报表的时候设定报表的边距。有四个属性来完成这项工作:topMargin,LeftMargin,bottomMargin和rightMargin。缺省的设置是上下边距20像素,左右边距20像素。

Column Size andSpacing(列宽和列间距)

一个报表可能含有多列, 我们可以通过上面提到的columnCount属性得到报表列数。JasperReport需要知道列的宽度和列间距的大小。有两个属性用于这项工作:columnWidth和columnSpacing。当我们对报表设计进行编译的时候,编译器会对这项设置进行有效性检查(validation check)--看列的宽度和列间距是否符合给定的页面宽度和页边距。因为缺省的列数为一,所以缺省的列间距为0像素,并且缺省的列宽等于页面宽度减去左 右边距所得的值。在上面A4的例子中,列宽即为555像素。

Empty Data Source Behavior

有时我们提供给我们的报表的数据源可能会没有任何record。whenNoDataType属性可以让你选择当所提供的数据源中没有数据的时候入和察看生成的报表。如下有三种不同的可能性,你可以任选其一:

l              Empty Document:生成的报表不含有页面(no page in it)。当你试图装载这样的文档(whenNoDataType=”NoPages”)的时候,Viewer 可能会抛出一个错误。

l              Blank page:表示生成的报表将仅含有一个空白页。(whenNoDataType=”BlankPage”)

l              All sections displayed:除了detail部分的其他部分将在生成的文档中显示出来。(whenNoDataType=”AllSectionNoDetail”)。

缺省的设置是whenNoDataType=”NoPages”。

             Title and Summary Sections Placement(标题和摘要的放置)

如果你想让title部分和summary部分在单独的一页里显示,你所需要做的事情就是让下面的一个或两个属性的值为“true”:isTitleNewPage,isSummaryNewPage。这两个属性缺省情况下为false。

注意:即使你选择了在最后一页的剩余部分显示summary,如果列数超过一列,并且第二列已经在最后一页出现的时候(没试过,等有机会试验一下),新的一页将会被自动生成。

Scriptlet Class

scirptletClass 属性用于设置用于当前报表的scriptlet类的名字。在以后我会对Scriptlet进行详细讨论。如果你没为这个属性提供任何值,报表引擎将会使用 net.sf.jasper.engine.JRDefaultScriptlet的实例。

5      报表数据(Report Date

当我们谈到报表装填过程的时候,有三样东西需要作为输入提供给报表引擎:报表设计(report design),参数值(parameter values)和报表的数据源(data source)。

在先前的章节,我们已经看到了有关报 表设计的某些方面,现在我们要更加详细的关注其他两方面的内容:参数(parameter)和报表数据源。他们描绘了报表引擎在装填报表过程中所用到的仅 有的数据来源。像你用其他报表工具所希望的一样,这些数据将会根据报表设计中的定义的模版(template)被组织起来,并被用来生成准备打印的、面向 页面的文档。

5.1   表达式(expressions

表达式是JasperReport的一个非常有用的特性。他们可以用来声明执行各种执行各种计算(calculations)的报表变量(report variables),进行报表的数据组织,定制报表文本字段(textfield)的内容或者进一步定制报表对象的appearance。

所有报表表达式基本上都是Java表达式,他们可以以特殊的语法引用报表参数(parameter),报表字段(field)和报表变量(variable)。在XML报表设计中,有一些用 于定义表达式的元 素:<variableExpression>,<initalValueExpression>,<groupExpression>,<printWhenExpression>,<imageExpression>,<textFieldExpression>等等

因为所有的JasperReport表达式都是真正(real)的Java表达式,只要你用完整的类名(包括包名)来引用这些表达式,你就可以在任何class中使用他们。当你编译报表和装填数据的时候,你应该确定你在报表表达式中使用的类已经写入了classpath。

报表参数引用是通过$P{}序列引入的,例如:

<textFieldExpression>

     $P{ReportTitle}

</textFieldExpression>

这个例子假定我们在报表设计中有一个名为ReportTitle的报表参数,这个参数是一个java.lang.String类。当报表进行装填的时候,文本字段将会显示这个参数的值。

为了在一个表达式中使用报表字段,字段名必须放在$F{}的括号中。例如,如果我们想要在一个文本字段中显示两个数据源字段的连接值(concatenatedvalues),我们可以定义如下表达式:

<textFieldExpression>

     $F{FirstName} + “ “ + $F{LastName}

</textFieldExpression>

表达式可以更加复杂:

<textFieldExpression>

     $F{FirstName} + “ “ + $F{LastName} +“ was hired on “ +

   (newSimpleDateFormat(“MM/DD/YYYY”)).format($F{HireDate)) + “.”

</textFieldExpression>

正如你所看到的一样,参数、字段和变 量引用都是通过用JasperReport的特殊语法从一个真正地Java对象中引入的。(实际上是一个JREvaluator对象,该对象是在运行其生 成的动态对象,并不能在本地磁盘上见到它的身影,不过如果你使用iReport的话,你就可以在生成报表文件的同一目录下看到它的本来面目)

5.2   参数(Parameters

Parameters是传递给报表装填操作 的对象引用。这些参数主要作用于把那些不能从报表数据源中获得的数据传给报表引擎。例如,我们可能要把执行报表装填过程的用户名字专递给报表引擎,如果我们想让它显示在报表上或者在我们想在报表的title上动态的改变它,我们就可以以参数的形式传给报表引擎。


我们可以用如下的方式定义参数:

<parametername=”ReportTitle” class=”java.lang.String”>

<parametername=”MaxOrderId” class=”java.lang.Integer”>

<parametername=”SummaryImage” class=”java.awt.Image”>

这里所提供的报表参数值可以被用到各种报表表达式中,在报表SQL查询中,甚至可以用到报表的scriptlet类里。下面是构成参数定义的全部组件(XML元素):

参数名

name属性是一个强制属性。JasperReport的命名习惯和Java语言的命名习惯是类似的,这意味着参数名应该是一个单词,其中不含特殊字符(如分号)。

参数类型

报表参数的第二个强制属性是提供参数值的类型名。这个类型属性可以使任意的值,只要这个类型的名字在报表编译期和装填期能在classpath中找到即可。

Prompting for Parameter values

在GUI引用程序中,建立一个报表参数集,让用户在执行装填过程之前输入某些应用程序需要用户输入的报表参数是很有用的。可选参数isForPropting参数用来声明是否显示提示信息让用户输入某些参数。下面的例子中,我声明了一个文本参数,当需要用户输入参数值的时候,这个文本参数用来在一个已定制的对话框中描述需要用户输入什么样的 参数。

<parameter name="Comments"class="java.lang.String" isForPrompting="true">

<parameterDesciption>

<![CDATA[

Please type herethe report comments if any

]]>

</parameterDesciption>

</parameter>

注意:相信大家都知道<![CDATA[内容]]>表示“内容”将不被XML解析器解析,也就是说,你可以在“内容”里加入XML的特殊字符,如>,<等等。

参数的默认值(parameter default value

通常参数值都是使用 java.util.Map对象传给装填过程的,其中参数名作为Key。这样,你就不用每次都为每个参数提供一个value了—可以批量放到Map对象里 一起传给装填管理器。如果你没有为参数提供一个value,引擎就认为它是null。但是如果你为它提供了一个默认值,则引擎将在你没提供这个参数值的情况下使用这个默认值。如果你在装填时没提供数据,下面的java.util.Date将在装填是被引擎使用来表示当天日期:

<parametername="MyDate" class="java.util.Date">

<defaultValueExpression>

newjava.util.Date()

</defaultValueExpression>

</parameter>

在参数的默认表达式中,我们可以使用这个与定义的报表参数。

5.2.1 内置的报表参数

每一个报表设计中都含有一些与定义的报表参数,这些内置的参数的描述如下:

REPORT_PARAMETERS_MAP

这是一个内置的参数,这个参数总是指向一个java.util.Map对象,该对象保存了用户调用报表装填过程时传递给报表引擎的用户定的参数。

REPORT_CONNECTION

这个报表参数指向一个java.sql.Connection对象,这个对象被提供给报表引擎用来通过JDBC来执行SQL报表查询。将master报表使用的JDBCConnection对象传递给subreport是非常有用的,有关这方面信息请查看subreport例子

REPORT_DATASOURCE

在报表装填的时候,我们可以或者直接由应用程序中提供,或由报表引擎从所提供的JDBC Connection在后台create而获得一个数据源。这个内置的参数允许我们在报表表达式中或scriptlet中访问报表数据源,而不论我们为什么要这么做。

REPORT_SCRIPTLET

即使报表不使用scriptlet,这个内置的参数仍将指向一个net.sf.jasper.engine.JRAbstracStriptlet实例,该实例实际是一个net.sf.jasper.engine.JRDefaultScriptlet对象。

但是当使用scriptlet时,报表装填过程所生成的这个指向scriptlet类实例的引用允许我们调用其中的某些特殊函数,使用或控制scriptlet对象在装填过程中已经准备好的数据。在scriptlet例子中你可以看到更详细的使用过程。

5.3   Data Source(数据源)

在进行报表装填的时 候,JasperReport引擎迭代的从用户提供的数据源中提取record,并根据报表设计所提供的模版生成报表的各个部分(section)。通常情况下,引擎需要接收一个net.sf.jasper.engine.JRDataSource对象作为报表数据源。但是像我们即将看到的那样,当报表数据存储于关系型数据库的时候,JasperReport有了让用户提供一个JDBC链接对象来替代通常的数据源对象的特性。JRDataSource接口 非常简单,如果我们想要实现它只需要实现下面两个方法:

public Boolean next() throw JRException;

public ObjectgetFieldValue(JRField jrField) throw JRException

在报表装填的时候,next()方法将被报表引擎调用,迭代的从数据源中获取数据。第二个方法用来为每个在当前数据源记录(data source record)中的报表字段(report field)提供value。

       应当知道,从数据源取得数据的唯一方法是使用reportfield。 一个数据源对象更像是一个二维表,表中含有数据。这个二维表的行是一条一条的record,而每一列都映射为一个report field。所以我们可以在report表达式中使用数据源。JasperReport提供了一些缺省的JRDataSource实现,我们来具体看一下:

Classnet.sf.jasper.engine.JRResultSetDataSource

这是一个非常有用的缺省实现,因为他外覆(wrap)了java.sql.ResultSet对象。由于多数报表的生成都采用关系数据库中存储的数据,所以这个类是被使用得最为广泛的数据源对象。然而在以下的两种情况下您可以不必在装填过程中自己生成这个对象:

如果你选择在你的报表中用SQL查询来获得在关系数据库的某个table中的数据,报表引擎将会通过执行给定的SQL查询并且将返回的java.sql.ResultSet外覆为一个net.sf.jasper.engine.JRResultSetDataSource实例来执行这项操作。引擎唯一需要的是一个 java.sql.Connection对象来执行查询操作。这时你可以提供connection对象来作为通用数据源对象(usual data source object)。例子有:jasper,scriptlet,subreport和query。

当然你可以在应用程序中即JasperReport之外执行SQL查询。这样的话,你可以手动的外覆java.sql.ResultSet,再调用报表装填过程之前实例化这个数据 源对象。当使用这种类型的数据源的时候,你需要为在result set中的每一列生命一个report field。report field的名字和类型必须和列的名字和类型匹配。

Classnet.sf.jasper.engine.JREcptyDataSource

这个类主要用于当生成报表的数据不是 来自数据源,而是来自参数或重要的仅是数据源中virtual records的数量的时候。例子fonts,images,shapes和unicode都使用了这个类来装填报表,来模拟数据源中没有一条记录,所有字段都为null的情况。

Classnet.sf.jasper.engine.data.JRTableModelDataSource

这个JRDataSource接口的缺省实现外覆了javax.swing.table.TableModel对象,它可以用在JavaSwing应用程序中从已经显示到屏幕上的table中得数据来生成报表。--我喜欢。

通常有两种方法来使用这种数据源:

通常,为了要从中取得数据,你需要为 javax.swing.table.TableModel对象的每一列生命一个reportfield。但是有些情况下会出现问题,比如report field的命名需要遵照Java命名规范来声明变量,而table的列名则不需要。幸运的是,你仍然可以通过列的索引而不是它的名字来将report field与列进行映射。例如,一个列名为“ProduceDescription”不可能被映射到名为“Produce Description”的report field上,因为report field名中含有空格,这将引起一个编译错误。但是如果你知道这个列示table model对象的第三列(index=2),那么你就可以命名相应的字段“COLUMN_2”并无误地使用这一列的数据。例子有:datasource

Classnet.sf.jasper.engine.data.JRBeanArrayDataSource

这个类外覆了一个JavaBeans数组,并且通过反射来获取report field的值。在这种数据源中,一个JavaBean对象描述了一条记录。如果我们有一个名为“ProductDescription”的report field,在获取这个字段的值的时候,程序将会试图通过反射机制调用一个当前JavaBeans对象中]名为 getProductDescription()的方法。对于boolean字段,当调用get前缀的属性不能返回其属性值的时候,程序将会试图使用is前缀的方法来获得属性值。

Classnet.sf.jasper.engine.data.JRBeanCollectionDataSource

这个类和上一个类非常类似,它也是使用反射机制和JavaBean命名规范,但是它外覆了一个java.util.Collection对象而不是一个JavaBean对象数组。在datasource例子中你可以看到进一步的用法。

5.4   报表查询(Report Query

为了要为报表装填数据,我们需要为报 表引擎提供所需的数据,或者至少告诉它怎样去获取数据。JasperReport通常需要接受一个net.sf.jasper.engine.JRDataSource对象作为报表的数据源,同时作为更为强大的功能,JasperReport能直接用 JDBC从关系数据库总获取数据。类库允许用户在他们的报表设计中提供SQL查询以便可以自运行期从数据库中提取数据。要做到这一点,你只需要在装填的时 候为装填管理器的fillReport()方法提供一个java.sql.Connection而不是JRDataSource对象即可。

在报表中,可以使用<queryStrng>元素来引入查询。如果这个元素存在,则出现在报表参数声明之后,报表field之前。


如下是一个SQL查询的例子:

<queryString><![CDATA[SELECT* FROM Orders]]></queryString>

为了更好的定制从数据库中取回的数据集(data set),一个重要的方面是在报表查询字符串中报表参数的使用(use of report parameters)。在查询中,这些参数可能会像动态过滤器(dynamic filter)一样工作,它们用特殊的语法被引入进来为报表提供数据,很像reportexpression。

如下有两种在查询中的使用参数的方法:

1.     像通常的java.sql.PreparedStatement的参数那样使用,用如下语法:

<queryString>

<!CDATA[

SELECT * FROMOrders WHERE OrderID <= $P{MaxOrderID} ORDER BY ShipCountry

]]>

</queryString>

2.     有时,我们需要使用参数来动态更改SQL查询的某些部分,或将整个SQL查询作为参数提供给装填过程。在这种情况下,语法稍微有些不同,向下面的例子,注意!”

     <queryString>

<!CDATA[

SELECT * FROM $P!{MyTable}ORDER BY $P!{OrderByClause}

]]>

</queryString>

在这个例子中,这个引入了参数值得特殊的语法确定了我们为这些参数所提供的值将会替代查询中的参数引用($P!{}的内容)。这些参数将被传给使用java.sql.PrepqredStatement对象的数据库服务器。

事实上,报表引擎首先处理$P!{}参数引用,通过使用他们的值来获取最重的SQL查询,并且仅当这件事完成之后,引擎才会将剩下的普通的$P{}参数引用传递给usualIN parameters。--实际上就是嵌套查询啦。

第二种用于SQL查询的参数引用允许你在运行期传递整个SQL查询语句:

<queryString>$P!{MySQLQuery}</queryString>

注意:你不能在参数值中再加入参数引用,也就是说,参数引用不能嵌套使用。

更详细的信息可以参看工程所带的例程:jasper,subreport,scriptlet,webapp以及最有学习价值的query

5.5   字段(Field

报表字段是从数据源到报表设计的数据映射的唯一途径。你可以应用报表字段在report表达式中使用数据源中的数据,或获得所需的输出。一旦定义了报表字段,你需要在报表填充的时候确认你所提供的数据源中提供了所有你声明的fields。

例如,如果你使用JRResultSetDataSource作为数据源,你需要确定在SQL查询结束之后所获得的ResultSet中的column包含你生命的全部fields。相应的列名和字段名必须完全相同,并且其数据类型也必须完全一致。


这是一个字段声明的语法,字段的声明需要对应于数据库表中的某一字段。我们来考虑一个Employee的例子,有如下表结构(structure):


报表字段需要像下面这样定义:

<fieldname="EmployeeID" class="java.lang.Integer"/>

<fieldname="LastName" class="java.lang.String"/>

<fieldname="FirstName" class="java.lang.String"/>

<fieldname="HireDate" class="java.util.Date"/>

如果你定义的字段不能和ResultSet中的某一列相对应,则在运行期将会抛出异常(如果你用IReport,在编译时也会抛出异常)。然而执行SQL查询之后返回的结果集中包含的列不需要与报表字段一一对应(即列可以多于字段,只是在显示的时候不显示出来罢了)。

字段名(Field Name

<Field>元素的name属性是强制属性(即不能省略),你可以在表达式中通过它来引用该字段。字段名必须是一个单词,且不含有特殊字符,如点和分号。

字段类型(Field Class

这个属性描述了字段值的类型,缺省的类型是java.lang.String,还有其他可选类型如:

java.lang.Object

java.lang.Boolean

java.lang.Byte

java.util.Date

java.sql.Timestamp

java.lang.Double

java.lang.Float

java.lang.Integer

java.io.InputStream

java.lang.Long

java.lang.Short

java.math.BigDecimal

如果某些数据源含有一些自定义的类型,则该类型所对应的字段应该声明为java.lang.Object。但是与参数定义不同的是,你只能选择上述列表中的类型名。

字段描述(Field Description

当实现一个自定义数据源的时候跟,这 一伴随着某个字段的附加的文本块是很有用的。你可以在字段描述中保存一个key或其他任何信息以便于在运行期从自定义数据源中取回字段值。通过使用可选 的<fieldDescription>元素,你就可以轻松的越过字段名的约束(field naming conventions),在从数据源取数据的时候,使用字段描述而不是字段名来获取相应数据。

<fieldname="PersonName" class="java.lang.String"isForPrompting="true">

<fieldDesciption>PERSONNAME</fieldDesciption>

</field>

5.6   变量(Variables

报表变量是建立在报表表达式之上的一些特殊对象。他们是用表达式定义的,用来执行某些运算,来简化报表中频繁出现或使用某些项目(如页号,页码,某些列的累加和等等)。下面是它的定义语法:


可以看到,在这个语法中,一个变量可以引用其他变量,当且仅当被引用的变量已经在报表中被定义过。所以,在报表设计中,变量定义的顺序非常重要。

变量名(Variable Name

与字段名和参数名一样,<variable>元素的name属性是强制属性,报表引擎允许在报表表达式中通过这个这个名字来引用变量。变量名的命名规则与参数和字段的规则相同。

变量类型(Variable Class

每一个变量都有其类型,缺省为java.lang.String,然而只要你所选择的类型可以在classpath中找到,你就可以在报表编译期和填充期声明任何类型的报表变量。

重置类型(Reset Type

报表变量的值可以在每一次迭代 (iteration)中被改变,但也可以在装填过程中的某一特定的时间(specified moments)通过它的初始的value表达式恢复其初始值。这一行为是由resetType属性控制的,这一属性规定了报表装填过程中当报表变量在何 时需要重新进行初始化(或恢复到初始值)。该元素有五种选项值:

n       No Reset:变量将不会使用其initial value expression对自身进行初始化,而将仅报表从变量表达式中所求得的值(resetType=”None”)。

n       Report Level Reset:变量将在报表填充过程的起始阶段使用其初始化表达式初始化一次(resetType=”Report”)。

n       Page Level Reset:变量将在每一页的起始时被重新初始化(resetType=”Page”)。

n       Colunm Level Reset:变量将在每个新列的开始被初始化(resetType=”Column”)

n       Group Level Reset:变量将在每次resetGroup属性提供的break的地方被重新初始化(resetType=”Group”)。

缺省的属性为resetType=”Report”

Reset Group

如果存在的话,resetGroup属性包含了报表的组的名字并且仅与resetType=”Gropu”的resetType属性相关联。

5.6.1 运算(Calculations

像以上所提到的,变量能执行内置的计算类型(build-in types of calculation)。下面是<variable>元素的calculation属性的所有可能的值:

CalculationNothing

这是变量执行的缺省计算。这意味着变量值在数据源的每次迭代时被重新计算(这里的计算只是简单的通过变量表达式给变量赋值)。

Calculation Count

在每次数据源的迭代的时候(注:这里和之前我所说的数据源的迭代都是指:例如,在报表装填的时候,引擎执行SQL查询返回的ResultSet中含有若干记录,每次迭代都是指从ResultSet中获得一条记录,并将这条记录的值赋给已经声明的字段),一个计数(count)变量将会把用主表达式(main expression)计算所得的非空(not null)值累加起来。计数变量(Count variables)必须是一个数字类型的变量,但是它的主表达式可以是一个非数字型表达式(non-numeric expression),因为报表引擎不关心表达式类型,而仅仅将这些非空的返回值得数目累加起来—就好像一个累加器一样。

只有变量的初始值表达式必须是数字型的,并且需要和变量的类型相同,因为这个值将会在初始化的时候被直接赋给变量。

Calculation Sum

如果你选择了这种类型的计算,报表引擎将会把变量主表达式的返回值累加到一起,但需要注意的是变量必须是数字类型的。我们不能计算一个java.lang.String或java.util.Date类型的报表变量。

CalculationAverage

对于数据源中的每一条记录,报表引擎 可以计算通过对变量表达式求值所得的一些列结果的平均值(series of values obtained by evaluating the variable’s expression)。这种类型的计算的也只能是数字类型的变量。

为了完成计算平均值的操作,报表引擎 将在后台了创建一个helper报表变量来计算values的累加和(sum),并用它来计算这些value的均值。这个helper sum variables的命名规则是在对应的变量名后面加上“_SUM”前缀。例如,如果你声明了一个数字类型的变量,名为 MyAverageVariable,报表引擎将会建立一个MyVerageVariable_SUM的变量来帮助计算均值。如果你需要的话,你可以在其他的报表表达式中使用这个helper变量,就像你使用其他你声明的变量一样。

为了计算均值,报表引擎同时也需要一 个计数变量(count variable)。但是对于“Report”,“Page”和“Column”的resettype,引擎将使用我们在下一节即将看到的内置的计数变量。对于resetType=“Group”的情况,引擎将在后台建立一个helper计数变 量,这个变量的名字是在原来的均值变量名后面加上一个“_COUNT”后缀。

Calculation Lowestand Highest

对于每一个数据源记录来说,如果你想在一系列从赋值表达式中获得得最大或最小的value,你就需要选择这种类型的计算。

CalculationStandardDeviation(标准偏差) andVariance(方差)

在一些特殊的报表中,你可能会需要执 行一些高级的数字运算,而JasperReport已经内置了对的一些经过变量表达式赋值所得到的value的标准偏差和方差进行计算的算法。和前面计算均值的方法一样,报表引擎首先建立一些helper变量来获得相应于当前一系列值的计数和累加和,这些变量命名方式和上面一样,这里不再赘述。

Calculation System

这种类型的计算被用在当你不希望报表引擎干预你的变量运算的时候,这意味着你将自己计算变量的值。对于这种类型的计算,报表引擎唯一可以做的就是从数据源的一个迭代到另一个迭代的过程中将你已经计算好的value保存起来(conserve)。

例子:下面是一个是变量声明的简单例子,这个变量将为所有名为“Quantity”的数字型报表字段求和。

<variablename="QuantitySum" class="java.lang.Double"calculation="Sum">

<variableExpression>$F{Quantity}</variableExpression>

</variable>

如果我们想求得每一页的这个字段的总合,则应该如下定义:

<variablename="QuantitySum" class="java.lang.Double"resetType="Page"

calculation="Sum">

<variableExpression>$F{Quantity}</variableExpression>

<initialValueExpression>newDouble(0)</initialValueExpression>

</variable>

在上面的例子中,我们的页累加变量将会在每一新页的开始时被置为0。

5.6.2 内置的报表变量(Build-in Report Variables

JasperReport提供了一些内置的系统变量可以直接用于表达式中:

VariablePAGE_NUMBER

这个变量保存当前的页号,在报表装填结束的时候,这个变量就保存着最终文档的总页数。所以要在JasperReport的文本字段中显示页号和总页数你都可以用它。

VariableCOLUMN_NUMBER

这个变量将记录当前的列号。

VariableREPORT_COUNT

当数据源迭代结束之后,这个变量里将包含被处理的数据record的总数。

VariablePAGE_COUNT

这个变量纪录当前页中处理的record的数目。

VariableCOLUMN_COUNT

这个变量纪录生成当前列时所处理的record的数目。

VariableGroupName_COUNT

当我们声明一个group时,引擎将会自动建立一个用户计算构成当前group的记录数的计数变量,即在组和组的rupture之间所处理的纪录的数目。(注意,这里的GroupName是泛指组名,即任意一个组的名字)


 

至此,JasperReport的大致功能选项就都介绍完了,余下的部分如报表的各个组成部分,Scriptlet,报表元素(文本,图形元素--线,矩形,图片等),要么在 JasperReport所给例子中才能有更好的理解,要么你只要用一下IReport就一目了然了(比如报表的各个组成部分在IReport中都是可视化的,文本,图形元素也都是可拖拽到报表上的,非常容易弄懂),至于高级部分SubReport的用法,这片文档也说得比较少,如果你需要的话就看看 IReport的文档吧。不过我还是在下面列出了所有的英文原文,共有兴趣的朋友或我自己在遇到某些细节的时候备用。J

6 Report Sections

When building areport design we need to define the content and the layout of its sections. Theentire

structure of thereport design is based on the following sections: <title>, <pageHeader>,

<columnHeader>, <groupHeader>, <detail>, <groupFooter>, <columnFoter>,

<pageFooter>, <summary>.

Sections areportions of the report template that have a specified height and width and cancontain

report elementslike lines, rectangles, images or text fields. Those sections are filledrepeatedly at report

generating timeand make up the final document that is being produced. When declaring thecontent

and layout of areport section, in an XML report design, we use the generic element <band>.

XML Syntax

<!ELEMENT band(printWhenExpression?, (line | rectangle | image | staticText

| textField | subreport| elementGroup)*)>

<!ATTLIST band

height NMTOKEN"0"

<!ELEMENTprintWhenExpression (#PCDATA)>

Report sectionsare sometimes referred as report bands and represent a feature that almost allreport

tools have and usein the same way.

Band Height

The attributeheight available in a report band declaration specifies the height in pixelsfor that

particular bandand is very important in the overall report design.

The elementscontained by a certain report band should always fit the band's dimensions, toavoid

potential badresults when generating the reports. The engine issues a warning if it findselements

outside the bandborders, when compiling report designs.

Skipping Bands

All the reportsections allow you to define a report expression that will be evaluated atruntime in order

to decide if thatparticular section should be generated or skipped, when producing the document.

This expression isintroduced by the <printWhenExpression> that is available in any <band>

element of the XMLreport design and should always return a java.lang.Boolean object or null.

TheJasperReports Ultimate Guide

Page 39

6.1 Main Report Sections

A minimal reportdesign can contain no report section at all, because each one of them isoptional. But

such a minimalreport design won't produce very interesting documents.

XML Syntax

<!ELEMENT title(band?)>

<!ELEMENTpageHeader (band?)>

<!ELEMENTcolumnHeader (band?)>

<!ELEMENTdetail (band?)>

<!ELEMENTcolumnFooter (band?)>

<!ELEMENTpageFooter (band?)>

<!ELEMENTsummary (band?)>

So let's take acloser look at each report section and see how it behaves.

Title

This is the firstsection of the report. It is generated only once during the report fillingprocess and

makes it for thebeginning of the resulting document.

Being the firstsection of the report means that it will precede even the page header section.Those who

want to have thepage header printed somehow before the title section will have to copy theelements

present on thepage header also at the beginning of the title section. They could suppress theactual

page header on thefirst page using the <printWhenExpression>, based on the PAGE_NUMBER

report variable.

As we have alreadyseen in the 4.3 Report Properties paragraph,the title section could be followed by

a page break, ifthe attribute isTitleNewPage is set to "true".

Page Header

This sectionappears at the top of each page in the generated document.

Column Header

This sectionappears at the top of each column in the generated document.

Detail

For each record inthe data source, the engine will try to generate this section.

Column Footer

This sectionappears at the bottom of each column in the generated document. It neverstretches

downward toacquire the content of its containing text fields and will always remain ofdeclared fixed

height.

TheJasperReports Ultimate Guide

Page 40

Page Footer

This sectionappears at the bottom of each page in the generated document. Just like thecolumn footer

section above, thepage footer never stretches downwards to acquire the content of its containingtext

fields and willalways remain of declared fixed height.

Summary

This section isgenerated only once per report and appears at the end of the generateddocument, but is

not necessarilythe last section being generated.

That's because insome cases, the column footer or/and page footer of the last page can followit.

As mentioned inthe 4.3 Report Properties paragraph, thesummary section can start a new page of its

own, by settingthe isSummaryNewPage attribute to "true". Even if this attribute remains false, the

summary sectionalways starts a new page if it does not fit on the remaining space of the lastpage or if

the report hasmore than one column and on the last page it has already started a secondcolumn.

If the main reportsections that we have seen here are not sufficient for what you need, maybe you

should considerintroducing supplementary sections like group headers and group footers.

We are now goingto see how to group data on the report.

6.2 Data Grouping

Groups represent aflexible way to organize data on a report. A report group is represented bysequence

of consecutiverecords in the data source that have something in common, like the value of acertain

report field forexample.

A report group has3 components:

_ groupexpression;

_ group headersection;

_ group footersection.

The value of theassociated group expression is what makes group records stick tighter. Thisvalue is

the thing thatthey have in common. When the value of the group expression changes during the

iteration throughthe data source at report filling time, a group rupture occurs and thecorresponding

group sections <groupFooter> and <groupHeader> are inserted in the resulting document.

We can have asmany groups as we want on a report. The order of groups declared in a reportdesign is

important becausegroups contain each other. One group contains the following group and so on.And

when a largergroup encounters a rupture, all subsequent groups are reinitialized.

Data groupingworks as expected only when the records in the data source are already

orderedaccordingly to the group expressions used in the report.

For example, ifyou want to group some products by country and city of the manufacturer,

the engine expectsto find the records in the data source already ordered by country and city.

If not, you shouldexpect to find records belonging to a specific country or city in different

parts of theresulting document, because JasperReports does not sort the data source foryou,

before using it.

TheJasperReports Ultimate Guide

Page 41

XML Syntax

<!ELEMENT group(groupExpression?, groupHeader?, groupFooter?)>

<!ATTLIST group

name NMTOKEN #REQUIRED

isStartNewColumn(true | false) "false"

isStartNewPage(true | false) "false"

isResetPageNumber(true | false) "false"

isReprintHeaderOnEachPage(true | false) "false"

minHeightToStartNewPageNMTOKEN "0"

<!ELEMENTgroupExpression (#PCDATA)>

<!ELEMENTgroupHeader (band?)>

<!ELEMENTgroupFooter (band?)>

Group Name

The nameunequivocally identifies the group and can be used in other XML attributes,when you want

to refer aparticular report group. The name of a group is mandatory and obeys the samenaming

convention that wementioned for the report parameters, fields and report variables.

Starting NewPage/Column When Group Breaks

Sometimes isuseful to introduce a page or column break when a new group starts, probablybecause

that particulargroup is more important and should start on a page or column of its own.

To instruct theengine to start a new page or column for a certain group, instead of going toprint it on

the remainingspace at the bottom of the page or column, you have to set to "true" either the

isStartNewPage or isStartNewColumn attribute.

Those twoattributes are the only settings in the entire library that let you voluntary

introduce pagebreaks. In all other situation, the reporting engine introduces page breaks

automatically, ifit needs to.

However, in somereport designs, you probably want to introduce page breaks on purpose,

because someparticular report section of yours is larger than one page. You can achievethat

by introducingspecial dummy groups as you can see in the Tips & Tricks section of the

freely availabledocumentation, published on the JasperReports site.

However, if youdon't want to consistently introduce page or column breaks for a particulargroup, but

you rather do thatonly if the remaining space at the bottom of the page or column is too small,you

should considerusing the minHeightToStartNewPage attribute.

This attributespecifies the minimum amount of remaining vertical space required so that thegroup

does not start anew page of its own. It is measured in pixels.

Resetting PageNumber

If required,report groups have the power to reset the built-in report variable whichcontains the current

page number(variable PAGE_NUMBER). This could be achieved by setting the isResetPageNumber

attribute to "true".

TheJasperReports Ultimate Guide

Page 42

Group Header

This section isthe one that marks the start of a new group in the resulting document, and itis inserted

in the documentevery time the value of the group expression changes during the iterationthrough the

data source.

Group Footer

Every time areport group changes, the engine adds the corresponding group footer sectionbefore

starting the newgroup or when the report ends.

Check the providedsamples like jasper, datasource or query, to see how report groups can be used.

TheJasperReports Ultimate Guide

Page 43

7 Scriptlets

All the datadisplayed on a report comes from the report parameters and from the reportfields. This

data can beprocessed using the report variables and their expressions.

There are specificmoments in time when variable processing occurs. Some variables are initialized

according to theirreset type when the report starts, or when a page or column break

is encountered, or

when a groupchanges. Furthermore, variables are evaluated every time new data is fetchedfrom the

data source (forevery row).

But only simplevariable expressions cannot always implement complex functionality. This iswhere

scriptlets intervene.Scriptlets are sequences of Java code that are executed every time a reportevent

occurs. Throughscriptlets, users have the possibility to affect the values stored by thereport variables.

Since scriptletswork mainly with report variables, is important to h

have full controlover the exact

moment thescriptlet is executed.

JasperReportsallows the execution of custom Java code BEFORE or AFTER it initializes thereport

variablesaccording to their reset type: Report, Page, Column or Group.

In order to makeuse of this functionality, users only have to create a scriptlet class byextending one of

the following twoclasses:

dori.jasper.engine.JRAbstractScriptlet

dori.jasper.engine.JRDefaultScriptlet

The complete nameof this custom scriptlet class (including the package) has to be specified inthe

scriptletClass attribute of the <jasperReport> element and has tobe available in the classpath,

at report fillingtime, so that the engine could instantiate it on the fly. If no value isspecified for the

scriptletClass attribute, the engine will instantiate the JRDefaultScriptlet class.

When creating aJasperReports scriptlet class, there are several methods that developers should

implement oroverride, like: beforeReportInit(), afterReportInit(), beforePageInit(),

afterPageInit(), beforeGroupInit(), afterGroupInit(), etc. Those methods will be called

by the reportengine at the appropriate time, when filling the report.

For more complexreports, if you need to use very complicate report expressions, for grouping or

displaying data,maybe you should consider transferring this complexity to a separate class towhich

you then makecalls from simplified report expressions. The scriptlet class is perfect fortransferring

this complexityto. This is because the reporting engine supplies you with a reference to thescriptlet

object it createson the fly using the REPORT_SCRIPTLET built-in parameter.

Check thescriptlet sample to see this type offunctionality used.

TheJasperReports Ultimate Guide

Page 44

8 Report Elements

The generatedreports would be empty if you would not put some report elements in the reportdesign.

The reportelements are displayable objects like static texts, text fields, images, linesor rectangles, that

you put in yourreport design sections so that they appear in the final document.

As you can see,the report elements come in two flavors:

_ Text elements: static texts and text fields that display dynamiccontent;

_ Graphic elements: lines, rectangles and images.

We shall see thosetwo element categories and their particularities in the following sections. Fornow

we are going topresent in detail the element properties that both categories share.

When you add areport element to one of your report sections, you have to specify the relativeposition

of this element inthat particular section and its size, along with other general report elementproperties

like color,transparency, stretch behavior, etc.

The propertiesthat are common to all types of report elements are grouped in the <reportElement>

tag that canappear in the declaration of all report elements.

XML Syntax

<!ELEMENTreportElement (printWhenExpression?)>

<!ATTLISTreportElement

positionType(Float | FixRelativeToTop | FixRelativeToBottom)

"FixRelativeToTop"

isPrintRepeatedValues(true | false) "true"

mode (Opaque |Transparent) #IMPLIED

x NMTOKEN#REQUIRED

y NMTOKEN#REQUIRED

width NMTOKEN#REQUIRED

height NMTOKEN#REQUIRED

isRemoveLineWhenBlank(true | false) "false"

isPrintInFirstWholeBand(true | false) "false"

isPrintWhenDetailOverflows(true | false) "false"

printWhenGroupChangesCDATA #IMPLIED

forecolor CDATA#IMPLIED

backcolor CDATA#IMPLIED

<!ELEMENTprintWhenExpression (#PCDATA)>

Absolute Position

The x and y attributes of anyreport element are mandatory and represent x and y coordinates,

measured inpixels, that mark the absolute position of the top-left corner of the specifiedelement within

its parent reportsection.

Relative Position

Some reportelements such as the text fields have special properties that allow them tostretch

downwards in orderto acquire all the information they have to display. Their height is calculatedat

runtime and mayaffect the other neighboring elements present in the same report section,especially

those placedimmediately below them.

The positionType attributespecifies the behavior that the report element should have if the layout of

the report sectionin which it is been place is affected by stretch.

TheJasperReports Ultimate Guide

Page 45

There are 3possible values for the positionType attribute:

_ Floatingposition: The element will float in itsparent section if it is pushed downwards by other

elements fountabove it. It will try to conserve the distance between it and the neighboring

elements placedimmediately above (positionType="Float").

_ Fixed positionrelative to the top of the parent band: Thecurrent report element will simply ignore

what happens tothe other section elements and tries to conserve the y offset measured from thetop

of its parentreport section (positionType="FixRelativeToTop").

_ Fixed positionrelative to the bottom of the parent band: If the height of the parent report section is

affected byelements that stretch, the current element will try to conserve the originaldistance

between its bottommargin and the bottom of the band

(positionType="FixRelativeToBottom").

A report elementcalled e2 will float when another report element called e1 stretches, only

if these threeconditions are met:

_ e2 has postitionType="Float"

_ e1.y + e1.height< e2.y

_ e1.width +e2.width >= max(e1.x + e1.width, e2.x + e2.width) –

min(e1.x, e2.x)

The second and thethird conditions together say that the element e2 must be placed below

the e1.

By default, allelements have a fixed position relative to the top of the band.

To see how elementstretching and element floating work together, check the stretch sample provided.

Element Size

The width and height attributes aremandatory and represent the size of the report element measured

in pixels.Additional element settings that have to do with the element stretchingmechanism will

determine thereporting engine to sometimes ignore the specified element height. But thisattribute

remains mandatorysince even when the height is calculated dynamically, the element will not be

smaller than theoriginal specified height.

Element Color

There are twoattributes that represent colors: forecolor and backcolor. The fore color is the one

used to draw thetext of the text elements and the border of the graphic elements. The backcolor is the

one used to fillthe background of the specified report element, if it is not transparent.

You could specifycolors using the decimal or hexadecimal representation of the integer number

corresponding tothe desired color. The preferred way to specify colors in XML is using the

hexadecimalrepresentation, because it allows controlling the level for each base color ofthe RGB

system.

For example, youcan display some text in red if you set the forecolor attribute of the corresponding

text field likethis:

forecolor="#FF0000"

The equivalentusing the decimal representation would be:

forecolor="16711680"

but theinconvenience is evident.

TheJasperReports Ultimate Guide

Page 46

The default forecolor is black and the default back color is white.

ElementTransparency

Report elementscan be either transparent or opaque, depending on the value you specify for the

attribute mode.

The default valuefor this attribute depends on the type of the report element. Graphic elementslike

rectangles andlines are opaque by default, but the images are transparent. Both static textsand text

fields aretransparent by default, and so are the subreport elements.

Skipping ElementDisplay

The engine candecide at runtime if it really should display a report element, if you use the

<printWhenExpression>that is available for all types of report elements.

If present, thisreport expression should return a java.lang.Booleanobject or null and is evaluated

every time thesection containing the current element is being generated, to see if thisparticular

element shouldappear or not in the report.

If the expressionreturns null, it is equivalent to returning java.lang.Boolean.FALSEand if the

expression ismissing, the report element will get printed every time, that is if othersetting do not

intervene, as weshall see below.

ReprintingElements on Section Overflows

When generating areport section, the engine might be forced to start a new page or column,because

the remainingspace at the bottom of the current page or column was not sufficient for allthe section

elements to fitin, probably because some elements have stretched.

In such cases, youmight want to reprint some of your already displayed elements, on the new pageor

column, torecreate the context in which the page/column break occurred.

To achieve this,you have to set isPrintWhenDetailOverflows="true"for all those report

elements you wantto reappear on the next page or column.

SuppressingRepeating Values Display

First, let's seewhat exactly a "repeating value" is.

It very muchdepends on the type of the report element we are talking about.

For text fieldelements, this is very intuitive. In the following list containing person namestaken from

an usual phonebook, you can see that for some consecutive lines, the value of the"Family Name"

column repeatsitself (those are only dummy phone numbers _).

Family Name FirstName Phone

Johnson Adam256.12.35

Johnson Christine589.54.52

Johnson Peter546.85.95

Johnson Richard125.49.56

Smith John469.85.45

Smith Laura459.86.54

Smith Denise884.51.25

You might want tosuppress the repeating "Family Name" values and print something likethis:

TheJasperReports Ultimate Guide

Page 47

Family Name FirstName Phone

Johnson Adam256.12.35

Christine589.54.52

Peter 546.85.95

Richard 125.49.56

Smith John469.85.45

Laura 459.86.54

Denise 884.51.25

You can do that,if for the text field that displays the family name, you set:

isPrintRepeatedValues="false"

The static textelements behave in the same way. As you would expect, their value alwaysrepeats and

in fact it neverchanges, until the end of the report. This is why we call them static texts.So, if you set

isPrintRepeatedValues="false"for one of your <staticText> elements, youshould expect to

see it displayedonly once, the first time, at the beginning of the report, and never again.

Now, what aboutgraphic elements?

An image isconsidered to be repeating itself if its bytes are exactly the same from oneoccurrence to

the next. Thiscould only happen if you choose to cache your images using the isUsingCache

attributeavailable in the <image> element and if the corresponding <imageExpression> returns the

same value fromone iteration to the next (the same file name, the same URL, etc).

Lines andrectangles are always repeating themselves, because they are static elements,just like the

static texts wehave seen above. So, when deciding to not display repeating values for a lineor a

rectangle, youshould expect to see it displayed only once, at the beginning of the report andthen

ignored until theend of the report.

The isPrintRepeatedValues attribute works only if the corresponding

<printWhenExpression>is missing. If this is not missing, it will alwaysdictate if the

element should beprinted or not, regardless of the repeating values.

If you decide tonot display the repeating values for some of your report elements, you have the

possibility tosoften or refine this behavior, by indicating the exceptional occasions towhich you might

want to have aparticular value redisplayed, during the report generation process.

When the repeatingvalue spans on multiple pages or columns, you have the possibility to redisplaythis

repeating value atleast once for every page or column.

By setting isPrintInFirstWholeBand="true", you make sure that the report element will

reappear in thefirst band of a new page or column that is not an overflow from a previous pageor

column.

Also, if therepeating value you have suppressed spans on multiple groups, you have thepossibility to

make itreappearing at the beginning of a certain report group, is you specify the nameof that particular

group in the printWhenGroupChanges attribute.

Removing BlankSpace

When reportelements are not displayed for some reason: <printWhenExpression> evaluated to

Boolean.FALSE, or repeated value being suppressed, a blank spaceremains where that report element

would have stood.

This blank spacealso appears if a text field displays only blank characters or an empty text.

There is a way toeliminate this unwanted blank space, on the vertical axis, only if someconditions are

met.

TheJasperReports Ultimate Guide

Page 48

For example, ifyou have three successive text fields, one on top of the other like this:

TextField1

TextField2

TextField3

If the second onehas an empty string as its value, or contains a repeated value that you choseto

suppress, theoutput would look like this:

TextField1

TextField3

In order toeliminate the gap between the first text field and the third, you have to set

isRemoveLineWhenBlank="true"for your second text field. You would obtain somethinglike this:

TextField1

TextField3

But there arecertain conditions that have to be met in order for this functionality to work.The blank

space will not beremoved, if your second text field shares some vertical space with other report

elements that areprinted even this second text fields of your does not print.

For example, youmight have some vertical lines on the sides of your report section like this:

| TextField1 |

| |

| TextField3 |

or you might havea rectangle that draws a box around your text fields:

------------------

| TextField1 |

| |

| TextField3 |

------------------

or even other textelements that are placed on the same horizontal with your second text field:

Label1 TextField1

Label2

Label3 TextField3

In all thosesituations, the blank space between the first and the third text field cannotbe remove,

because it isbeing used by other report elements that are printed as you can see.

The blank verticalspace between elements can be removed using the isRemoveWhenBlank

attribute, only ifit is not used by other elements, as explained above.

8.1 Text Elements

There are twokinds of text elements in JasperReports: static texts and text fields.

As their namessuggest it, the first are text elements with a fixed, static content, who doesnot change

during the reportfilling process and are used especially for introducing labels on the finaldocument.

Text fieldshowever, have an associated expression, which is evaluated at runtime toproduce the text

content that willbe displayed.

Both types of textelements share some properties and those are introduced using a <textElement>

element. We arenow going to see them in detail.

TheJasperReports Ultimate Guide

Page 49

XML Syntax

<!ELEMENTtextElement (font?)>

<!ATTLISTtextElement

textAlignment(Left | Center | Right | Justified) "Left"

lineSpacing(Single | 1_1_2 | Double) "Single"

Text Alignment

You can specifyhow the content of a text element should be aligned using the textAlignment

attribute andchoosing one of the 4 possible values: "Left", "Center", "Right" or "Justified".

Text Line Spacing

The amount of spacebetween consecutive lines of text can be set using the lineSpacing attribute:

_ Single: The paragraph text advances normally using an offsetequal to the text line height

(lineSpacing="Single").

_ 1.5 Lines: The offset between two consecutive text lines is of 1 ½lines

(lineSpacing="1_1_2").

_ Double: The space between text lines is double the height of asingle text line

(lineSpacing="Double").

The font settingsfor the text elements are also part of the <textElement> tag, but we aregoing to see

them in detail, inthe following separate section of this book.

8.1.1 Fonts andUnicode Support

Each text elementpresent on your report can have its own font settings. Those settings can bespecified

using the <font> tagavailable in the <textElement> tag.

Since most of thetime, in a report design, there are only a few types of fonts used, that areshared by

different textelements, there's no point forcing XML report design creators to specify thesame font

settings for eachtext element, over and over again. But rather they could reference a reportlevel font

declaration andadjust only some of the font settings, on the spot, if a particular textelement requires it.

Report Fonts

A report font isin fact a collection of font settings declared at report level that can bereused

throughout theentire report design, when setting the font properties of text elements.

TheJasperReports Ultimate Guide

Page 50

XML Syntax

<!ELEMENTreportFont EMPTY>

<!ATTLISTreportFont

name NMTOKEN#REQUIRED

isDefault (true |false) "false"

fontName CDATA"sansserif"

size NMTOKEN"10"

isBold (true |false) "false"

isItalic (true |false) "false"

isUnderline (true| false) "false"

isStrikeThrough(true | false) "false"

pdfFontName CDATA"Helvetica"

pdfEncoding CDATA"CP1252"

isPdfEmbedded(true | false) "false"

Report Font Name

The name attribute of a <reportFont> element is mandatory and must be unique, because it will be

used whenreferencing the corresponding report font throughout the report.

Default ReportFont

You can use isDefault="true" for one of your report font declarations, to mark the report font that

you want to beused by the reporting engine as the default base font, when dealing with textelements

that do notreference a particular report font. This default font will also be used by thetext elements

that do not haveany font settings at all.

All the otherreport font properties are the same as those for a normal <font> elementthat we are

going to seebelow.

XML Syntax

<!ELEMENT fontEMPTY>

<!ATTLIST font

reportFont NMTOKEN#IMPLIED

fontName CDATA#IMPLIED

size NMTOKEN#IMPLIED

isBold (true |false) #IMPLIED

isItalic (true |false) #IMPLIED

isUnderline (true |false) #IMPLIED

isStrikeThrough(true | false) #IMPLIED

pdfFontName CDATA#IMPLIED

pdfEncoding CDATA#IMPLIED

isPdfEmbedded(true | false) #IMPLIED

Referencing aReport Font

When introducingthe font settings for a text element of your report, you have the possibilityto use a

report fontdeclaration as a base, for those font settings you want to obtain.

All the attributesof the <font> element, if present, are used only to override theattributes with the

same name that arepresent in the report font declaration referenced using the reportFont attribute.

TheJasperReports Ultimate Guide

Page 51

For example, if wehave a report font like the following:

<reportFont

name="Arial_Normal"

isDefault="true"

fontName="Arial"

size="8"

pdfFontName="Helvetica"

pdfEncoding="Cp1252"

isPdfEmbedded="false"/>

and we want tocreate a text field that has basically the same font settings like those inthis report font,

but only a greatersize, the only thing we should do is to reference this report font using the

reportFont attribute and specify the desired font size like this:

<textElement>

<fontreportFont="Arial_Normal" size="14"/>

</textElement>

When the reportFont attribute ismissing, the default report font is used as base font.

Font Name

In Java, there aretwo types of fonts: physical fonts and logical fonts. Physical fonts are theactual font

librariesconsisting of, for example, TrueType or PostScript Type 1 fonts. The physicalfonts may be

Arial, Time,Helvetica, Courier, or any number of other fonts, including internationalfonts.

Logical fonts arethe five font types that have been recognized by the Java platform sinceversion 1.0:

Serif, Sans-serif,Monospaced, Dialog, and DialogInput. These logical fonts are not actual fontlibraries

that are installedanywhere on your system. They are merely font-type names recognized by the Java

runtime, whichmust be mapped to some physical font that is installed on your system.

In the fontName attribute of the <font> element orthe <reportFont> element, you have to specify

the name of aphysical font or the name of a logical font. You only have to make sure thefont you

specify reallyexists and is available on your system.

For more detailsabout fonts in Java, check the Java Tutorial or the JDK documentation.

Font Size

The font size ismeasured in points and can be specified using the size attribute.

Font Styles andDecorations

There are 4boolean attributes available in the <font>and <reportFont>elements that control the

font style and/ordecoration. Those are isBold, isItalic, isUnderline and isStrikeThrough

and theirsignificance should be evident to anybody.

PDF Font Name

When exportingreports to PDF format, the JasperReports library uses the iText library.

As their namestates it (Portable Document Format) the PDF files can be viewed on variousplatforms

and you can besure they will always look the same. This is partially because in this formatthere is a

special way ofdealing with fonts.

TheJasperReports Ultimate Guide

Page 52

If you want todesign your reports so that they eventually be exported to PDF, you have tomake sure

you choose the appropriatePDF font settings that correspond to the Java font settings of your text

elements.

The iText libraryknows how to deal with built-in fonts and TTF files. It recognizes thefollowing builtin

font names:

Courier

Courier-Bold

Courier-BoldOblique

Courier-Oblique

Helvetica

Helvetica-Bold

Helvetica-BoldOblique

Helvetica-Oblique

Symbol

Times-Roman

Times-Bold

Times-BoldItalic

Times-Italic

ZapfDingbats

The iText libraryrequires us to specify either a built-in font name from the above list, eitherthe name

of a TTF file thatit can locate on disk, every time we work with fonts. The font name introducedby the

fontName attribute previously explained is of no use whenexporting to PDF. This is why we have

special fontattributes, so that we are able to specify the font settings that the iTextlibrary expects from

us.

The pdfFontName attributecan contain the name of a PDF built-in font from the above list or the

name of a TTF filethat can be located on disk at runtime, when exporting to PDF.

It is for thereport design creator to choose the right value for the pdfFontName attribute

that wouldperfectly corresponds to the Java physical or logical font specified using the

fontName attribute. If those two fonts, one used by the Javaviewers and printers and the

other used in thePDF format, do not represent in fact the same font, or do not at least look

alike, you mightget unexpected results when exporting to PDF format.

Additional PDFfonts can be installed on your system if you choose one of the Acrobat Reader'sfont

packs. Forexample, by installing the Asian font pack from Adobe on your system, you wouldbe able

to use for the pdfFontName attributefont names like:

Language PDF FontName

Simplified ChineseSTSong-Light

TraditionalChinese MHei-Medium

MSung-Light

Japanese HeiseiKakuGo-W5

HeiseiMin-W3

Korean HYGoThic-Medium

HYSMyeongJo-Medium

For more detailsabout how to work with fonts when generating PDF documents, check the iText

librarydocumentation.

PDF Encoding

When creatingreports in different languages and wanting to export them to PDF, you have tomake

sure that youchoose the appropriate character encoding type.

For example, anencoding type widely used in Europe is Cp1252, also known as LATIN1. Other

possible encodingtypes are:

TheJasperReports Ultimate Guide

Page 53

Character SetEncoding

Latin 2: EasternEurope Cp1250

Cyrillic Cp1251

Greek Cp1253

Turkish Cp1254

Windows Baltic Cp1257

Simplified ChineseUniGB-UCS2-H

UniGB-UCS2-V

TraditionalChinese UniCNS-UCS2-H

UniCNS-UCS2-V

Japanese UniJIS-UCS2-H

UniJIS-UCS2-V

UniJIS-UCS2-HW-H

UniJIS-UCS2-HW-V

Korean UniKS-UCS2-H

UniKS-UCS2-V

You can find moredetails about how to work with fonts and character encoding when generating PDF

documents, here,in the iText library documentation.

PDF Embedded Fonts

If you want to usea TTF file when exporting your reports to PDF format and you want to make sure

everybody will beable to view it without problem, you have to make sure that at least one of the

followingconditions are met:

_ they all havethat TTF font installed on their systems;

_ you embed thefont in the PDF document itself.

Its not easy tocomply with the first condition and this is why the preferred way to do it isto embed the

TTF in thegenerated PDF documents that you are distributing.

You can do that bysetting the isPdfEmbedded attribute to "true".

Further detailsabout how to embed fonts in the PDF documents you can find in the iText

documentation. Avery useful example you can find in the unicode sample provided with the project.

8.1.2 Static Texts

Static texts aretext elements with fixed content, which does not change during the reportfilling

process. They areused mostly to introduce static text label in the generated documents.

XML Syntax

<!ELEMENTstaticText (reportElement, textElement?, text?)>

<!ELEMENT text(#PCDATA)>

As you can seefrom the above presented syntax, besides element general properties and textspecific

properties that wehave already explained, a static text definition has in addition only the <text> tag,

which introducesthe fixed text content of the static text element.

8.1.3 Text Fields

Unlike static textelements, which do not change their text content, text fields have anassociated

expression that isevaluated with every iteration in the data source, in order to obtain the textcontent

that has to bedisplayed.

TheJasperReports Ultimate Guide

Page 54

XML Syntax

<!ELEMENTtextField (reportElement, textElement?, textFieldExpression?,

anchorNameExpression?,hyperlinkReferenceExpression?,

hyperlinkAnchorExpression?,hyperlinkPageExpression?)>

<!ATTLISTtextField

isStretchWithOverflow(true | false) "false"

evaluationTime(Now | Report | Page | Column | Group) "Now"

evaluationGroupCDATA #IMPLIED

pattern CDATA#IMPLIED

isBlankWhenNull(true | false) "false"

hyperlinkType(None | Reference | LocalAnchor | LocalPage |

RemoteAnchor |RemotePage) "None"

<!ELEMENTtextFieldExpression (#PCDATA)>

<!ATTLISTtextFieldExpression

class(java.lang.Boolean | java.lang.Byte | java.util.Date |

java.sql.Timestamp| java.lang.Double | java.lang.Float | java.lang.Integer

| java.lang.Long |java.lang.Short | java.math.BigDecimal |

java.lang.String)"java.lang.String"

Variable HeightText Fields

Given the factthat text fields have a dynamic content, most of the time you wont be able toexactly

anticipate theamount of space you have to provide for your text fields so that they candisplay all their

content.

If the space youreserve for your text fields is not sufficient, the text content will be truncatedso that it

fits in theavailable area.

This scenario isnot always acceptable and you can let the reporting engine to calculate itselfat runtime

the amount ofspace required to display the entire content of the text field andautomatically adjust the

size of the reportelement.

You can achievethis by setting the isStretchWithOverflow to "true" for the particular text field

elements you areinterested in. By doing this, you make sure that if the specified height forthe text

field is notsufficient, it will automatically be increased (never decreased) in order to beable to display

the entire textcontent.

When text fieldsare affected by this stretch mechanism, the entire report section to which theybelong

to will be alsostretched.

Evaluating TextFields

Normally, all thereport expressions are evaluated immediately, using the current values of allthe

parameters, fieldsand variables at that particular moment. It is like making a photo of all data,for

every iteration inthe data source, during the report filling process.

This means that atany particular time, you won't have access to values that are going to becalculated

later, in thereport filling process. It perfectly makes sense, since all the variables arecalculated step by

step and reachtheir final value only when the iteration arrives at the end of the data sourcerange they

cover.

For example, areport variable that calculates the sum of a field for each page will notcontain the

expected sum untilthe end of the page is reached. That's because the sum is calculated step bystep,

TheJasperReports Ultimate Guide

Page 55

when iteratingthrough the data source records, and at any particular time, the sum will beonly partial,

since not all therecords of the specified range have been processed.

If this is thecase, how to display the page sum of a this field, on the page header, sincethis value will

be known only whenthe end of the page is reached. At the beginning of the page, when generatingthe

page header, oursum variable would contain zero, or its initial value.

Fortunately,JasperReports has a very interesting feature that lets you decide the exactmoment you

want the textfield expression to be evaluated, avoiding the default behavior which makesthis

expression beevaluated immediately, when generating the current report section.

It is the evaluationTime attributewe are talking about. It can have one of the following values:

_ Immediateevaluation: The text field expression isevaluated when filling the current band

(evaluationTime="Now").

_ End of reportevaluation: The text field expression isevaluated when reaching the end of the

report (evaluationTime="Report").

_ End of pageevaluation: The text field expression isevaluated when reaching the end of the current

page (evaluationTime="Page").

_ End of columnevaluation: The text field expression isevaluated when reaching the end of the

current column (evaluationTime="Column").

_ End of groupevaluation: The text field expression isevaluated when the group specified by the

evaluationGroup attribute changes (evaluationTime="Group").

The default valuefor this attribute is "Now", as already mentioned. In the example presented above,

you could easilyspecify evaluationTime="Page" for the text field placed in the page header

section, so thatit displays the value of the sum variable only when reaching the end of thecurrent page.

The onlyrestriction you should be aware of, when deciding to avoid the immediate

evaluation of thetext field expression, is that in such cases, the text field will never stretch

in order toacquire all its content.

This is becausethe text element height is calculated when the report section is generated and

even the enginewill come back later with the text content of the text field, the element

height will not beadapted, because it will ruin the already created layout.

Suppressing NullValues Display

If the text fieldexpression returns null, your text field will display the "null" text in the generated

document. A simpleway to avoid this is to set the isBlankWhenNull attribute to "true". By doing

this, the textfield will cease to display "null" and will display an empty string.This way nothing will

appear on yourdocument if the text field value is null.

Formatting Output

Of course, whendealing with numeric or date/time values, you could use the Java API to formatthe

output of the textfield expressions yourself. But there is a more convenient way to do it: byusing the

pattern attributeavailable in the <textField> element.

The value youshould supply to this attribute is the same that you would supply if it werefor you to

format the valueusing either the java.text.DecimalFormat class or

java.text.SimpleDateFormatclass, depending on the type of value to format.

In fact, what theengine does is to instantiate the java.text.DecimalFormatclass if the text field

expression returnssubclasses of the java.lang.Number class or to instantiate the

java.text.SimpleDataFormatif the text field expression return java.util.Date or

java.sql.Timestampobjects.

For more detailabout the syntax of this pattern attribute, check the Java API documentation for those

two classes: java.text.DecimalFormat and java.text.SimpleDateFormat.

TheJasperReports Ultimate Guide

Page 56

Text FieldExpression

We have alreadytalked about the text field expression. There is nothing more to say about itexcept

that it isintroduced by the <textFieldExpression> element and can return values from only a

limited range ofclasses listed below:

java.lang.Boolean

java.lang.Byte

java.util.Date

java.sql.Timestamp

java.lang.Double

java.lang.Float

java.lang.Integer

java.lang.Long

java.lang.Short

java.math.BigDecimal

java.lang.String

If the text fieldexpression class is not specified using the class attribute, it is assumed to be

java.lang.String, by default.

8.2 Graphic Elements

The second majorcategory of report elements, besides text elements that we have seen, are thegraphic

elements. In thiscategory we have lines, rectangles and images.

They all have someproperties in common and those are grouped under the attributes of the

<graphicElement>tag.

XML Syntax

<!ELEMENTgraphicElement EMPTY>

<!ATTLISTgraphicElement

stretchType(NoStretch | RelativeToTallestObject |

RelativeToBandHeight)"NoStretch"

pen (None | Thin |1Point | 2Point | 4Point | Dotted) #IMPLIED

fill (Solid)"Solid"

Stretch Behavior

The stretchType attributeof a graphic element can be used to customize the stretch behavior of the

element when onthe same report section there are text fields that stretch themselves becausetheir text

content is toolarge to fit in the original text field height.

When stretchabletext fields are present on a report section, the height of the report sectionitself will be

affected bestretch.

A graphic elementcan respond to the modification of the report section layout in three ways:

_ Won't stretch: The graphic element preserves its original specifiedheight

(strechType="NoStretch").

_ Stretchingrelative to the parent band height: Thegraphic element will adapt its height to match

the new height ofthe report section it placed on, which has been affected by stretch

(stretchType="RelativeToBandHeight").

_ Stretchingrelative to the tallest element in group: You have the possibility to group the elements

of a reportsection in multiple imbricate groups, if you like. The only reason you mighthave for

grouping yourreport elements is to be able to customize their stretch behavior. Detailsabout how

to group elementsare supplied in the section 8.4 Element Groups that will follow. Graphic

TheJasperReports Ultimate Guide

Page 57

elements can bemade to automatically adapt their height to fit the amount of stretch sufferedby

the tallestelement in the group that they are part of

(stretchType="RelativeToTallestObject").

Border Thickness

Unlike textelements, the graphic elements always have a border. You can control the typeand

thickness of itusing the pen attribute. Remember that the color of the border comes from the

forecolor attribute presented when describing the <reportElement> tag, in a previous chapter.

Here are all thepossible types for a graphic element border:

_ No border: The graphic element will not display any border aroundit (pen="None").

_ Thin border: The border around the graphic element will be half apoint thick (pen="Thin").

_ 1 point thickborder: Normal border (pen="1Point").

_ 2 points thickborder: Thick border (pen="2Point").

_ 4 point thickborder: (pen="4Point").

_ Dotted border: The border will be 1 point thick and made of dots (pen="Dotted").

The default borderaround graphic elements depends on their type. Lines and rectangles have anormal

1 point thickborder by default. Images however, do not display any border, by default.

Background FillStyle

The fill attributespecifies the style of the background for the graphic elements, but the onlystyle

supported for themoment is the solid fill style, which is also the default (fill="Solid").

8.2.1 Lines

When displaying aline element, JasperReports draws one of the two diagonals of the rectangle

represented by thex, y, width and height attributesspecified for this particular line element.

XML Syntax

<!ELEMENT line(reportElement, graphicElement?)>

<!ATTLIST line

direction (TopDown| BottomUp) "TopDown"

Line Direction

Which one of thetwo diagonals of the rectangle should be drawn can be decided using thedirection

attribute:

_ The diagonalthat starts in the top-left corner of the rectangle and goes to the bottom leftcorner is

drawn in case youset direction="TopDown".

_ The line willstart in the bottom-left corner and will go to the upper-right if you choose

direction="BottomUp".

You can drawvertical lines by specifying width="0"and horizontal lines setting height="0". For

such lines thedirection is not important.

The defaultdirection for a line is "TopDown".

TheJasperReports Ultimate Guide

Page 58

8.2.2 Rectangles

The rectangles arethe most basic graphic elements. This is why there are no supplementarysettings to

make for thedeclaration of a rectangle element, besides those already seen when talkingabout the

<reportElement>and <graphicElement>tags.

XML Syntax

<!ELEMENTrectangle (reportElement, graphicElement?)>

For more detailedexamples of lines and rectangles, check the shapes sample.

8.2.3 Images

The most complexgraphic elements that you can have on a report are the images.

Just like for thetext field elements, their content is dynamically evaluated at runtime, using areport

expression.

XML Syntax

<!ELEMENT image(reportElement, graphicElement?, imageExpression?,

anchorNameExpression?,hyperlinkReferenceExpression?,

hyperlinkAnchorExpression?,hyperlinkPageExpression?)>

<!ATTLIST image

scaleImage (Clip |FillFrame | RetainShape) "RetainShape"

isUsingCache (true| false) "true"

evaluationTime(Now | Report | Page | Column | Group) "Now"

evaluationGroupCDATA #IMPLIED

hyperlinkType(None | Reference | LocalAnchor | LocalPage |

RemoteAnchor |RemotePage) "None"

<!ELEMENTimageExpression (#PCDATA)>

<!ATTLISTimageExpression

class(java.lang.String | java.io.File | java.net.URL |

java.io.InputStream| java.awt.Image) "java.lang.String"

Scaling Images

Given the factthat images are loaded at runtime, there is no way knowing their exact size,when

creating thereport design. It might be that the dimensions of the image element specifiedat design time

do not correspondto the actual image loaded at runtime.

This is why youhave to decide how you expect the image to behave in order to adapt to theoriginal

image elementdimensions you specified in the report design. There is the scaleImage attribute that

allows you to dothat, by choosing one of its 3 possible values:

_ Clipping theimage: If the actual image is larger than the image elementsize, it will be cut off so

that it keeps itsoriginal resolution, and only the region that fits the specified size will bedisplayed

(scaleImage="Clip").

TheJasperReports Ultimate Guide

Page 59

_ Forcing theimage size: If the dimensions of the actualimage do not fit those specified for the

image element thatdisplays it, the image can be forced to obey them and stretch itself so that itfits

in the designatedoutput area. It will be deformed if necessary (scaleImage="FillFrame").

_ Keeping imageproportions: If the actual image does notfit into the image element, it can be

adapted to thosedimensions without needing to deform it and keep its original proportions

(scaleImage="RetainShape").

Retain Shape

Clip

Fill Frame

- figure 7 -

Caching Images

All image elementshave dynamic content. There are no special elements to introduce static imageson

the reports, likewe have special static text elements.

However, most ofthe time, the images on a report are in fact static and do not necessarily comefrom

the data source orfrom parameters. In the majority of cases, they are loaded from files on diskand

represent logosand other static resources.

If we have todisplay the same image multiple times on a report, if it is about a logoappearing on the

page header forexample, there is no point on loading the image file every time we have todisplay it.

We can instructthe reporting engine to cache this particular image. This way we are makingsure that

the image will beloaded from disk or from its particular location only once and then it willonly be

reused every timeit has to be displayed.

By setting the isUsingCache attributeto "true", the reporting engine will try to recognize

previously loadedimages using their specified source. For example, it will recognize an image ifthe

image source is afile name that it has already loaded, or if it is the same URL.

This cachingfunctionality is available only for image elements that have expressionsreturning

java.lang.String objects as the image source, representing file names,URLs or classpath

resources. That'sbecause the engine uses the image source string as the key to recognize that itis the

same image that ithas already cached.

Evaluating Images

As we have alreadyseen when talking about text fields, you have the possibility to postpone the

evaluation of theimage expression, which by default is performed immediately.

This would allowyou to display in some region of a document, images that are going to be builtor

chose only laterin the report filling process, due to complex algorithms or whatever.

The sameattributes, evaluationTime and evaluationGroup, that we have talked about in the text

fields section areavailable also in the <image> element. The evaluationTimeattribute can have the

following values:

_ Immediateevaluation: The image expression isevaluated when filling the current band

(evaluationTime="Now").

_ End of reportevaluation: The image expression isevaluated when reaching the end of the report

(evaluationTime="Report").

_ End of pageevaluation: The image expression isevaluated when reaching the end of the current

page (evaluationTime="Page").

TheJasperReports Ultimate Guide

Page 60

_ End of columnevaluation: The image expression isevaluated when reaching the end of the current

column (evaluationTime="Column").

_ End of groupevaluation: The image expression isevaluated when the group specified by the

evaluationGroup attribute changes (evaluationTime="Group").

The default valuefor this attribute is "Now".

Image Expression

The value returnedby the image expression is used as the source for the image that is going to be

displayed. Theimage expression is introduced by the <imageExpression>element and can return

values from only alimited range of classes listed below:

java.lang.String

java.io.File

java.net.URL

java.io.InputStream

java.awt.Image

When the imageexpression returns a java.lang.String value, the engine will try to see

whether the valuerepresents an URL from which to load the image. If it is not a valid URL

representation, itwill try to locate a file on disk and load the image from it, assuming that

the valuerepresents a file name. If no file is found, it will finally assume that thestring

value representsthe location of a classpath resource and will try to load the image from

there. Only if allthose fail, an exception will be thrown.

If the imageexpression class is not specified using the class attribute, it is assumed to be

java.lang.String, by default.

The images sample provided with the project contains severalexamples of image elements.

8.2.4 Charts andGraphics

The JasperReportslibrary does not produce charts and graphics itself. This is not one of itsgoals.

However, it caneasily integrate charts and graphics produces by other, more specialized Javalibraries.

The great majorityof available Java libraries that produce charts and graphics can output toimage files

or to in-memoryJava image objects. This is why it shouldn't be hard for anybody to put a chartor a

graphic generatedby one of those libraries into a JasperReports document using a normal image

element that we havepresented in the previous section of this book.

You can see thisworking in the sample called chart, whichcomes with the project.

8.3 Hyperlinks

JasperReportsallows you to create drill-down reports, to introduce tables of contents inyour

documents or toredirect viewers to other external documents using special report elementscalled

hyperlinks.

Hyperlinks arespecial elements that contain a reference to a local destination within thecurrent

document or to anexternal resource to which the viewer of the document will be redirected if heor she

clicks on thatparticular hyperlink element.

Hyperlinks are notthe only actors in this viewer-redirecting scenario. There has to be a way foryou to

specify what arethe destinations in a document. These local destinations are called anchors.

TheJasperReports Ultimate Guide

Page 61

There are nospecial report elements that introduce hyperlinks or anchors in a reportdesign, but rather

some special settingthat make an usual report element to be a hyperlink or/and an anchor.

In JasperReports,only text field elements and image elements can be hyperlinks or anchors. Thisis

because for bothtypes of elements, there are special settings that allow you to specify thehyperlink

reference to whichthe hyperlink will point or the name of the local anchor. Note that aparticular text

field or image canbe both anchor and hyperlink at the same time.

XML Syntax

<!ELEMENTanchorNameExpression (#PCDATA)>

<!ELEMENThyperlinkReferenceExpression (#PCDATA)>

<!ELEMENThyperlinkAnchorExpression (#PCDATA)>

<!ELEMENThyperlinkPageExpression (#PCDATA)>

Hyperlink Type

When presentingthe XML syntax for text field elements and image elements, you probably sawthat

there was anattribute called hyperlinkType, which we didn't explain at that moment. We are going

to do that rightnow and here are the possible values for this attribute along with theirsignificance:

_ No hyperlink: By default, neither the text fields nor the imagesrepresent hyperlinks, even if the

special hyperlinkexpressions are present (hyperlinkType="None").

_ Externalreference: The current hyperlink points toan external resource specified by the

corresponding <hyperlinkReferenceExpression> element, usually an URL

(hyperlinkType="Reference").

_ Local anchor: The current hyperlink points to a local anchorspecified by the corresponding

<hyperlinkAnchorExpression>element (hyperlinkType="LocalAnchor").

_ Local page: The current hyperlink points to a 1 based page indexwithin the current document

specified by thecorresponding <hyperlinkPageExpression> element

(hyperlinkType="LocalPage").

_ Remote anchor: The current hyperlink points to an anchor specified bythe

<hyperlinkAnchorExpression>element, within an external document indicated by the

corresponding <hyperlinkReferenceExpression> element

(hyperlinkType="RemoteAnchor").

_ Remote page: The current hyperlink points to a 1 based page indexspecified by the

<hyperlinkPageExpression>element, within an external document indicated by the

corresponding <hyperlinkReferenceExpression> element

(hyperlinkType="RemotePage").

Anchor Expression

If present in atext field or image element declaration, the <anchorNameExpression> tag will

transform thatparticular text field or image into a local anchor of the resulting document,to which

hyperlinks canpoint. The anchor will bare the name returned after evaluating the anchor name

expression, whichshould always return java.lang.String values.

HyperlinkExpressions

Depending on thecurrent hyperlink type, one or two of the following expressions will beevaluated and

used to build thereference to which the hyperlink element will point:

TheJasperReports Ultimate Guide

Page 62

<hyperlinkReferenceExpression>

<hyperlinkAnchorExpression>

<hyperlinkPageExpression>

What is importantto know is that the first two should always return java.lang.String and the third

should return java.lang.Integer values.

There is a specialsample called hyperlink, provided withthe projects, which shows how this type of

report elementscan be used.

8.4 Element Groups

Report elementsplaced in any report section can be arranged in multiple imbricate groups. Theonly

reason you mighthave for grouping your elements is to be able to customize the stretch behaviorof the

graphic elements,as explained in the section 8.2 Graphic Elements.

The stretchType attribute,available for graphic elements, has among its possible values one called

"RelativeToTallestObject". When choosing this option, the engine will try to identify theobject

from the samegroup with the current graphic element, which suffered the biggest amount ofstretch. It

will then adaptthe height of the current graphic element to the height of this tallest elementof the

group.

But for this towork, you have to group your elements. This is done using the <elementGroup> and

</elementGroup>tags to mark the elements that are part of the samegroup.

XML Syntax

<!ELEMENTelementGroup (line | rectangle | image | staticText | textField |

subreport |elementGroup)*>

Element groups cancontain other nested element groups and there is no limit on the number of the

nested elementgroups.

Check the stretch sample, to see how element grouping works.

TheJasperReports Ultimate Guide

Page 63

9 Subreports

Subreports are animport feature for a report-generating tool. They allow the creation of morecomplex

reports andsimplify the design work. The subreports are very useful when creatingmaster-detail type

of reports or whenthe structure of a single report is not sufficient to describe the complexityof the

desired outputdocument.

A subreport is infact a normal report that is been incorporated as apart of another report. Youcan

imbricate yoursubreports and make a subreport that contains itself other

subreports, thenesting level

not being limited.

On the other hand,a subreport is also a special kind of a report element that helps you introducea

subreport into theparent report.

There's nothingmore to say about subreports, seen as normal reports, because they are compiledand

filled just likenormal reports are and we already have seen all that in previous chapters. Infact, any

report design canbe used as a subreport when incorporated into another report design, withoutthe need

to change anythinginside it.

What we are goingto see now, are the details concerning the <subreport> element that youuse when

introducingsubreports into master reports.

XML Syntax

<!ELEMENTsubreport (reportElement, parametersMapExpression?,

subreportParameter*,(connectionExpression | dataSourceExpression)?,

subreportExpression?)>

<!ATTLISTsubreport

isUsingCache (true| false) "true"

<!ELEMENTparametersMapExpression (#PCDATA)>

<!ELEMENTsubreportParameter (subreportParameterExpression?)>

<!ATTLISTsubreportParameter

name NMTOKEN#REQUIRED

<!ELEMENTsubreportParameterExpression (#PCDATA)>

<!ELEMENTconnectionExpression (#PCDATA)>

<!ELEMENTdataSourceExpression (#PCDATA)>

<!ELEMENTsubreportExpression (#PCDATA)>

<!ATTLISTsubreportExpression

class(java.lang.String | java.io.File | java.net.URL |

java.io.InputStream| dori.jasper.engine.JasperReport) "java.lang.String"

SubreportExpression

Just like normalreport designs, subreport designs are in fact dori.jasper.engine.JasperReport

objects. Those areobtained after compiling a dori.jasper.engine.design.JasperDesignobject

as seen in the 3.2Compiling Report Designs section of thisbook.

TheJasperReports Ultimate Guide

Page 64

We have seen thatthe text field elements have an expression that will be evaluated to obtain thetext

content todisplay. The image elements have an expression representing the source of theimage to

display. In thesame way, subreport elements have an expression that is evaluated at runtime,in order

to obtain thesource of the dori.jasper.engine.JasperReport object to load.

The so-calledsubreport expression is introduced by the <subreportExpression> element and can

return values fromthe following classes:

java.lang.String

java.io.File

java.net.URL

java.io.InputStream

dori.jasper.engine.JasperReport

When the subreportexpression returns a java.lang.String value, the engine will try to

see whether thevalue represents an URL from which to load the subreport design object. If

it is not a validURL representation, it will try to locate a file on disk and load the subreport

design from it,assuming that the value represents a file name. If no file is found, it will

finally assumethat the string value represents the location of a classpath resource and will

try to load thesubreport design from there. Only if all those fail, an exception will be

thrown.

If the imageexpression class is not specified using the class attribute, it is assumed to be

java.lang.String, by default.

Caching Subreports

A subreportelement can load different subreport designs with every evaluation, giving yougreat

flexibility inshaping you documents.

However, most ofthe time, the subreport elements on a report are in fact static and theirsource do not

necessarily changewith every new evaluation of the subreport expression. In the majority ofcases, the

subreport designsare loaded from fixed locations: files on disk or static URLs.

If the samesubreport design is filled multiple times on a report, there is no point onloading the

subreport designobject from the source file every time we have to fill it with data.

We can instructthe reporting engine to cache this particular subreport design object. This waywe are

making sure thatthe subreport design will be loaded from disk or from its particular locationonly once

and then it willonly be reused every time it has to be filled.

By setting the isUsingCache attributeto "true", the reporting engine will try to recognize

previously loadedsubreport design objects, using their specified source. For example, it willrecognize

a subreport objectif its source is a file name that it has already loaded, or if it is the sameURL.

This cachingfunctionality is available only for subreport elements that have expressionsreturning

java.lang.String objects as the subreport design source, representing filenames, URLs or

classpathresources. That's because the engine uses the subreport source string as thekey to recognize

that it is thesame subreport design that it has already cached.

TheJasperReports Ultimate Guide

Page 65

9.1 Subreport Parameters

Since subreportsare normal reports themselves, they are compiled or filled in the same way.This

means that theyalso require a data source from which to get the data when they are filled andthat can

also receiveparameters, for the additional information they have to use when being filled.

There are two waysto supply the parameter values to a subreport and they can be usedsimultaneously,

if desired.

You can supply amap containing the parameter values, like we do when filling a normal reportwith

data, using one ofthe fillReportXXX() methods exposed by the

dori.jasper.engine.JasperFillManagerclass (see the 3.4 Filling Reports chapter to refresh

your memory).

This can beachieved if you use the <parametersMapExpression>element, which introduces the

expression thatwill be evaluated in order to obtain the specified parameter map. Thisexpression should

always return a java.util.Map object inwhich the keys are the parameter names.

In addition to orinstead of supplying the parameter values in a map, you can supply theparameter

valuesindividually, one by one, using a <subreportParameter>element for each parameter you are

interested in. Insuch case, you have to specify the name of the corresponding parameter usingthe

mandatory name attribute and youhave to provide an expression that will be evaluated at runtime to

obtain the valuefor that particular parameter, value that will be supplied to the subreportfilling

routines.

Note that you canuse both ways to provide subreport parameter values, simultaneously. When this

happens, theparameter values specified individually, using the <subreportParameter>element, will

override theparameters values present in the parameter map, that correspond to the samesubreport

parameter. If themap does not contain corresponding parameter values already, the individually

specifiedparameter values will be added to the map.

Attention! Whenyou supply the subreport parameter values, you have to be aware that the

reporting enginewill affect the java.util.Map object it receives, adding the built-in

report parametervalues that correspond to the subreport. This map is also affected by the

individuallyspecified subreport parameter values, as already explained above.

In order to avoidaltering the original java.util.Map object that you send, you can wrap it

in a differentmap, before supplying it to the subreport filling process like this:

newHashMap(myOriginalMap)

This way, youroriginal map object remains unaffected and modifications are made to the

wrapping mapobject.

This is usefulespecially when you want to supply to your subreport the same set of

parameters thatthe master report has received and you use the built-in

REPORT_PARAMETERS_MAPreport parameter of the master report. However, youdon't want

to affect thevalue of this built-in parameter and you will wrap it like this:

<parametersMapExpression>

newHashMap($P{REPORT_PARAMETERS_MAP})

</parametersMapExpression>

TheJasperReports Ultimate Guide

Page 66

9.2 Subreport Data Source

Subreports need adata source in order to generate their content, just like normal report do.

In the 3.4 FillingReports chapter of this book we have seenthat when filling a report you have to

supply either adata source object or a connection object, depending on that particular reporttype. That

is if it has aninternal SQL query and you want to have it executed to obtain the report dataor you

supply the reportdata yourself.

Subreports behavein the same way and expect to receive the same kind of input when they arebeing

filled.

You can supply toyour subreport either a data source using the <dataSourceExpression> element

or a JDBCconnection for the engine to execute the subreport's internal SQL query usingthe

<connectionExpression>element. These two XML elements cannot be both presentat the same

time in asubreport element declaration. This is because you cannot supply both a datasource and a

connection foryour subreport. You have to decide on one of them and stick to it.

The report engineexpects that the data source expression returns a

dori.jasper.engine.JRDataSourceobject and that the connection expression returns a

java.sql.Connnectionobject, whichever is present.

You can see howsubreports work, if you check the subreport sample.

TheJasperReports Ultimate Guide

Page 67

10 Advanced JasperReports

Previous chaptershave presented the core functionality that most people will get to use whenworking

with theJasperReports library.

However, somecomplex requirements of your specific applications might force you to digdeeper into

the JasperReportsfunctionality in order to adapt it to suit you needs.

In the followingsections we are going to take a closer look at those aspects that are likely tointerest

you if you'll wantto make full benefit from the use of the JasperReports library.

10.1 XML Report Designs Loading and Writing

In the 3.2Compiling Report Designs chapter we haveexplained how report designs pass from their

initial XML forminto the compiled form, before being used to generate full-featured documents.

The engine firstparses the XML report design and creates the in-memory representation of it by

instantiating andpreparing a dori.jasper.engine.design.JasperDesignobject. This object is

then subject tovarious validation checks and suffers the compilation process that produces a

corresponding dori.jasper.engine.JasperReport object.

But in certaincases, in your application, you might want to manually load the XML reportdesign into a

dori.jasper.engine.design.JasperDesignobject, without immediately compiling it. Such

scenarios might becommon for applications that programmatically create report designs and use the

XML form to storethem temporary or permanently.

Loading dori.jasper.engine.design.JasperDesign objects from XML report design can be

easily by callingone of the public static load() methods exposed by the

dori.jasper.engine.xml.JRXmlLoaderclass. This way you can load report design object from

XML content storein filed or that is been read from input streams.

The processopposed to the XML report design loading process is the generation of the XMLform for a

given reportdesign object.

As seen above,sometimes report designs are created programmatically, using the JasperReportsAPI.

The report designobjects obtained this way can be serialized, for disk storage or transfer overthe

network, but theyalso can be stored in XML format.

You can obtain theXML representation of a given report design object by using one of the publicstatic

writeReport() methods exposed by the dori.jasper.engine.xml.JRXmlWriter utility class.

10.2 Implementing Data Sources

JasperReportslibrary comes with several default implementations of the

dori.jasper.engine.JRDataSourceinterface. This interface is used to supply the reportdata

when invoking thereport filling process, as explained in the previous chapters of this book.

These defaultimplementations let you generate reports using data from relational databasesretrieved

through JDBC, fromJava Swing tables or from collections and arrays of JavaBeans objects.

But maybe yourapplication data that your are trying to display in your reports has a specialstructure or

is organized in avery particular way preventing you from using any of the defaultimplementations of

the data sourceinterface that come with the library.

In suchsituations, you will have to create custom implementations for the

dori.jasper.engine.JRDataSourceinterface, in order to wrap your special report data,so that

the reportingengine can understand and use it when generating the reports for you.

TheJasperReports Ultimate Guide

Page 68

Creating a customimplementation for the dori.jasper.engine.JRDataSourceinterface is not

very difficultsince you have to implement only two methods.

The first one, thenext() method, is calledby the reporting engine every time it wants the current

pointer to advanceto the next virtual record in the data source.

The other, the getFieldValue() method, iscalled by the reporting engine with every iteration in the

data source toretrieve the value for each report field.

10.3 Customizing Viewers

The JasperReportslibrary comes with built-in viewers that allow you to display the reportsstored in the

library'sproprietary format or to preview your report designs when you create them.

These viewers arerepresented by the following two classes:

_ dori.jasper.view.JasperViewer : You use this class to view generated reports, either as

in-memory objectsor serialized objects on disk or even stored in XML format.

_ dori.jasper.view.JasperDesignViewer : This class can be used to preview report

designs, either inXML form or compiled form.

But these defaultviewers might not suit everybody's needs and therefore you might consider

customizing themso they adapt to certain application requirements.

In order to dothat, you should be aware that these viewers use in fact other, more basicvisual

components thatcome with the JasperReports library.

The report viewermentioned above use the visual component represented by the

dori.jasper.view.JRViewerclass and its companions. It is in fact a special

javax.swing.JPanelcomponent that is capable of displaying generatedreports and that can be

easilyincorporated in other Java Swing based applications or applets.

If thefunctionality of this basic visual component is not sufficient for what youneed, you can adapt it

by subclassing it.If for example you would want to have an extra button on the toolbar of thisviewer,

you might considerextending the component and add that button yourself in the new visualcomponent

you obtain bysubclassing.

This can be seenin the webapp sample, where the "PrinterApplet" displays a customized version of the

report viewer withan extra button in the toolbar.

Another veryimportant issue is that the default report viewer that comes with the librarydoes not know

how to deal withdocument hyperlinks that point to external resources. It only deals with local

references and canredirect the viewer to corresponding local anchor.

However,JasperReports offers you the possibility to handle yourself the clicks made ondocument

hyperlinks thatpoint to external documents and not local anchors.

The only thing youhave to do in order to achieve this, is to implement the

dori.jasper.view.JRHyperlinkListenerinterface and to add to register with the viewer

component aninstance of this listener class, using the addHyperlinkListener() method exposed

by the dori.jasper.view.JRViewer class.

By doing this, youmake sure the viewer will also call your implementation of the gotoHyperlink()

method in whichyou handle yourself the external references.

10.4 Exporting to New Output Formats

The JasperReportslibrary continually evolves and improves. Among the features that are likely tobe

introduced withtime is the ability to export to new documents formats, besides PDF, HTML andXML.

In order to extenddiversify in this direction, without affecting the existing functionality,JasperReports

provides thoseinterested in this subject an interface for them to implement, in case theywant to create

exporter classesthat transform the generated documents into new output formats.

This way, if youneed to export your reports into a special output format that is not yetavailable in the

core library, youmight decide to implement yourself the dori.jasper.engine.JRExporter

interface.

TheJasperReports Ultimate Guide

Page 69

Before deciding toimplement this interface, it is important for you to understand how the

implementation isexpected to function.

All the input datathe exporter might need should be supplied to it using the so-called exporter

parameters, beforethe exporting process is started.

This is becausethe exporting process will be always invoked by calling the exportReport() method

of the dori.jasper.engine.JRExporter interface, and this method does not receive any

parameters itself,when called. The exporter parameters have to be already set using the

setParameter() method on the exporter instance you are working with,before launching the export

task.

You might chooseto bulk set all your exporter parameters using the setParameters() method which

receives a java.util.Map objectcontaining the parameter values. The keys in this map should be

instances of the dori.jasper.engine.JRExporterParameter class, as you would supply when

individuallycalling the setParameter() method for each of your exporter parameters.

Note that nomatter what the type of output your exporter produces, you will be usingparameters to

indicate to theexporter where to place or send this output.

Such parametermight be called OUT parameters.

For example, ifyou want your exporter to send the output it produces to an output stream, youwill

supply the java.io.OutputStream object reference to the exporter using a parameter, probably

identified by the dori.jasper.engine.JRExporterParameter.OUTPUT_STREAM constant.

It is recommendedto use the public constants of the

dori.jasper.engine.JRExporterParameterclass to identify the parameters you set in your

exporters and onlyif you don't find one available for a particular setting you have to make in your

exporter, toextend this class to add new constants. This can be seen for the

dori.jasper.engine.export.JRXmlExporter, where special parameter identifier where created

by subclassing thedori.jasper.engine.JRExporterParameter class in the

dori.jasper.engine.export.JRXmlExporterParameterclass.

You don't have tostart from scratch when implementing the exporter interface, because there is a

convenienceabstract class called dori.jasper.engine.JRAbstractExporterthat at least deals

with parametermanagement for you.

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JasperReport 是一个开源的报表生成工具,它提供了丰富的功能来创建和设计报表。在 JasperReport 中,可以使用分页来控制报表的布局和显示。 要实现分页,首先需要在报表的设计中定义一个分页区域。可以通过在报表模板中添加 Page Header(页眉)、Page Footer(页脚)以及 Detail(详细内容)等部分来定义报表的布局。 在 JasperReport 中,可以使用不同的元素和属性来控制分页行为。以下是一些常用的分页相关元素和属性: 1. Page Break 元素:可以将报表内容分隔到不同的页面上。可以在需要分页的地方添加一个 Page Break 元素,它会将内容分割到下一页。 2. Keep Together 属性:可以设置元素是否保持在同一页上。如果将元素的 Keep Together 属性设置为 true,则 JasperReport 会尽量将该元素保持在同一页上,避免被分割到不同页面。 3. Band Height 属性:可以设置各个 band(区域)的高度。通过适当设置 band 的高度,可以控制每页显示的内容量。 4. Print When Expression:可以使用 Print When Expression 来根据条件控制元素的显示。通过在元素上设置 Print When Expression,可以根据特定条件来决定元素是否显示在报表中,从而达到控制分页的目的。 通过合理地使用以上元素和属性,可以实现 JasperReport 的分页功能。需要根据具体的报表设计和需求来选择合适的方式来控制分页行为。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值