12.2 BIRT报表的打印和导出
birt报表在打印和导出的时候,如果出现中文字符,有可能会出现文字丢失空白,或者pdf缺少相应字体,或者直接乱码的问题,这个现象非常普遍和复杂。这是一个和环境密切相关的配置,由于中文字体涉及到很多的地方配置,主要包括五个方面,报表设计时候的字体配置,runtime的page css的配置,runtime的fontconfig的配置,linux环境的字体配置,adobe reader的字体库。故而很难达成一致的解决方案,不过本节试图给出一个推荐方案。
首先是在报表设计时,整个报表的字体要设置为宋体,主要包括表,行,单元格
当然这样去设置一个复杂的报表会很麻烦,我们可以采用css的方式,应用如下的css:
- <SPAN style="FONT-SIZE: 14px">.reportTitle{
- text-align:center;
- font-family:宋体;
- font-size:16px;
- padding-top:15pt;
- padding-bottom:17pt;
- font-weight:bold;}
- .searchCondition{
- font-family:宋体;
- font-size:12px;
- font-weight:normal;
- margin-bottom:3pt;
- text-align:center;
- vertical-align:middle;}
- .dataCell{
- border-color:#000000;
- border-style:solid;
- border-width:1px;
- padding-top:3px;
- padding-bottom:3px;
- text-align:center;
- font-family:宋体;
- font-size:10pt;
- vertical-align:middle;
- font-weight:normal;}
- .columnName{
- border-color:#000000;
- border-style:solid;
- border-width:1px;
- background-color:#F2F2F2;
- padding-top:3px;
- padding-bottom:3px;
- text-align:center;
- font-family:宋体;
- font-size:10pt;
- vertical-align:middle;
- font-weight:bold;}</SPAN>
- <span style="font-size:14px;">.reportTitle{
- text-align:center;
- font-family:宋体;
- font-size:16px;
- padding-top:15pt;
- padding-bottom:17pt;
- font-weight:bold;}
- .searchCondition{
- font-family:宋体;
- font-size:12px;
- font-weight:normal;
- margin-bottom:3pt;
- text-align:center;
- vertical-align:middle;}
- .dataCell{
- border-color:#000000;
- border-style:solid;
- border-width:1px;
- padding-top:3px;
- padding-bottom:3px;
- text-align:center;
- font-family:宋体;
- font-size:10pt;
- vertical-align:middle;
- font-weight:normal;}
- .columnName{
- border-color:#000000;
- border-style:solid;
- border-width:1px;
- background-color:#F2F2F2;
- padding-top:3px;
- padding-bottom:3px;
- text-align:center;
- font-family:宋体;
- font-size:10pt;
- vertical-align:middle;
- font-weight:bold;}</span>
reportTitle 是指的是报表标题的显示格式
searchCondition是指的是报表查询条件显示格式
(注:searchCondition 是放在表格中的一个Crid控件)
dataCell 是指的是查询数据的格式
columnName是指的是表格横标题显示的格式
一般这样问题就能解决了。但有些linux的环境缺失字体,则需要进行如下的一些配置。
如果runtime的版本低于3.7.0,也就是存在WebRoot\WEB-INF\report-engine\platform\plugins\路径
操作系统环境的配置为:
1、如果运行tomcat的linux帐号权限可以访问系统的fonts目录,则在linux下安装部分中文字体。
2、在linux建立一个目录,将此目录权限设为任意用户可读,将windows下的字体文件copy到该目录下,然后修改前边所说的fontsconfig.xml目录,增加一行 <path path="xxxxxxx" />,指向字体目录,即可解决问题。
服务器平台插件的配置如下:如果报表设置的字体不是宋体,而是Serif
(1) 需要在服务器端安装CSS样式中定义的字体。具体操作如下:
(A) 在birt工程路径WebRoot/WEB-INF/report-engine/platform/plugins/(org.eclipse.birt.report.engine.fonts_2.3.0.v20080606)下存在4个xml文件,分别是:fontsConfig.xml/ fontsConfig_linux.xml / fontsConfig_pdf.xml / fontsConfig_win32.xml。由于导出PDF时出现少字的情况,需要修改fontsConfig_pdf.xml配置文件
- <SPAN style="FONT-SIZE: 14px"><font-paths>
- <path path="/usr/share/fonts/chinese/TrueType/simsun.ttc" />
- </font-paths></SPAN>
- <span style="font-size:14px;"><font-paths>
- <path path="/usr/share/fonts/chinese/TrueType/simsun.ttc" />
- </font-paths></span>
(B) 在linux上安装宋体文件。步骤如下:
首先:将Windows下的宋体文件simsun.ttc,拷贝到/usr/share/fonts/chinese/TrueType/simsum
其 次 :编辑/etc/X11/fs/config 加入路径如下
- <SPAN style="FONT-SIZE: 14px"> catalogue = /usr/share/X11/fonts/misc:unscaled,
- /usr/share/X11/fonts/75dpi:unscaled,
- /usr/share/X11/fonts/100dpi:unscaled,
- /usr/share/X11/fonts/Type1,
- /usr/share/X11/fonts/TTF,
- /usr/share/fonts/default/Type1,
- ,
- /usr/share/fonts/chinese/misc:unscaled,
- /usr/share/fonts/chinese/misc,
- /usr/share/fonts/chinese/TrueType/simsun,
- /usr/share/fonts/chinese/TrueType</SPAN>
- <span style="font-size:14px;"> catalogue = /usr/share/X11/fonts/misc:unscaled,
- /usr/share/X11/fonts/75dpi:unscaled,
- /usr/share/X11/fonts/100dpi:unscaled,
- /usr/share/X11/fonts/Type1,
- /usr/share/X11/fonts/TTF,
- /usr/share/fonts/default/Type1,
- ,
- /usr/share/fonts/chinese/misc:unscaled,
- /usr/share/fonts/chinese/misc,
- /usr/share/fonts/chinese/TrueType/simsun,
- /usr/share/fonts/chinese/TrueType</span>
注:如果服务器中已经安装了CSS样式中的字体而且路径没有问题的话,则不需要配置上述操作。
回到3.7.0以后的版本,理论上只要按照如下的方式修改org.eclipse.birt.runtime_3.7.0.v20110615-1818.jar包中的fontsConfig_pdf.xml配置,也是能解决问题。
- <SPAN style="FONT-SIZE: 14px"><Code>
- <font>
- <font-aliases>
- <mapping name="serif" font-family="SimSun" />
- <mapping name="sans-serif" font-family="SimSun" />
- <mapping name="monospace" font-family="SimSun" />
- </font-aliases>
- <font-encodings>
- <encoding font-family="SimSun" encoding="utf-8" />
- <encoding font-family="Times-Roman" encoding="Cp1252" />
- <encoding font-family="Helvetica" encoding="Cp1252" />
- <encoding font-family="Courier" encoding="Cp1252" />
- <encoding font-family="Zapfdingbats" encoding="Cp1252" />
- <encoding font-family="Symbol" encoding="Cp1252" />
- <encoding font-family="STSong-Light" encoding="UniGB-UCS2-H" />
- <encoding font-family="STSongStd-Light" encoding="UniGB-UCS2-H" />
- <encoding font-family="MHei-Medium" encoding="UniCNS-UCS2-H" />
- <encoding font-family="MSung-Light" encoding="UniCNS-UCS2-H" />
- <encoding font-family="MSungStd-Light" encoding="UniCNS-UCS2-H" />
- <encoding font-family="HeiseiMin-W3" encoding="UniJIS-UCS2-H" />
- <encoding font-family="HeiseiKakuGo-W5" encoding="UniJIS-UCS2-H" />
- <encoding font-family="KozMinPro-Regular" encoding="UniJIS-UCS2-H" />
- <encoding font-family="HYGoThic-Medium" encoding="UniKS-UCS2-H" />
- <encoding font-family="HYSMyeongJo-Medium" encoding="UniKS-UCS2-H" />
- <encoding font-family="HYSMyeongJoStd" encoding="UniKS-UCS2-H" />
- </font-encodings>
- <composite-font name="all-fonts">
- <font font-family="Times-Roman" catalog="Western" />
- <font font-family="SimSun" catalog="Chinese" />
- <font font-family="HeiseiKakuGo-W5" catalog="Japanese" />
- <font font-family="HYGoThic-Medium" catalog="Korean" />
- </composite-font>
- </font>
- </Code> </SPAN>
- <span style="font-size:14px;"><Code>
- <font>
- <font-aliases>
- <mapping name="serif" font-family="SimSun" />
- <mapping name="sans-serif" font-family="SimSun" />
- <mapping name="monospace" font-family="SimSun" />
- </font-aliases>
- <font-encodings>
- <encoding font-family="SimSun" encoding="utf-8" />
- <encoding font-family="Times-Roman" encoding="Cp1252" />
- <encoding font-family="Helvetica" encoding="Cp1252" />
- <encoding font-family="Courier" encoding="Cp1252" />
- <encoding font-family="Zapfdingbats" encoding="Cp1252" />
- <encoding font-family="Symbol" encoding="Cp1252" />
- <encoding font-family="STSong-Light" encoding="UniGB-UCS2-H" />
- <encoding font-family="STSongStd-Light" encoding="UniGB-UCS2-H" />
- <encoding font-family="MHei-Medium" encoding="UniCNS-UCS2-H" />
- <encoding font-family="MSung-Light" encoding="UniCNS-UCS2-H" />
- <encoding font-family="MSungStd-Light" encoding="UniCNS-UCS2-H" />
- <encoding font-family="HeiseiMin-W3" encoding="UniJIS-UCS2-H" />
- <encoding font-family="HeiseiKakuGo-W5" encoding="UniJIS-UCS2-H" />
- <encoding font-family="KozMinPro-Regular" encoding="UniJIS-UCS2-H" />
- <encoding font-family="HYGoThic-Medium" encoding="UniKS-UCS2-H" />
- <encoding font-family="HYSMyeongJo-Medium" encoding="UniKS-UCS2-H" />
- <encoding font-family="HYSMyeongJoStd" encoding="UniKS-UCS2-H" />
- </font-encodings>
- <composite-font name="all-fonts">
- <font font-family="Times-Roman" catalog="Western" />
- <font font-family="SimSun" catalog="Chinese" />
- <font font-family="HeiseiKakuGo-W5" catalog="Japanese" />
- <font font-family="HYGoThic-Medium" catalog="Korean" />
- </composite-font>
- </font>
- </Code> </span>
但实际上org.eclipse.birt.runtime_3.7.0.v20110615-1818.jar是经过数字指纹加密的,如果把fontsConfig_pdf.xml添加进去后,会导致字体包失效。这个时候weblogic会寻找自己类库中的别的字体包,如果weblogic的字体包比较完整,那么也能导出或者打印正常的pdf.
官方的文档是这样说明的:
至于打印时超出纸张边界,这个是需要在master page中进行设置
默认是A4 纸张:210mm*297mm
A3 纸张:297mm*420mm
根据报表实际长宽大小进行设置:在Master page 中的General中进行设置。
打印时出现pdf汉字丢失或者乱码时的另一个解决方案是用html格式打印,因为目前浏览器支持的字符集是相当全面的。
另外还有一种方式可以自定义导出的格式与方法,不过难度稍高,就是扩展"org.eclipse.birt.report.engine.dataExtraction"。
用户完全可以基于已经实现的导出代码,实现自已的导出格式,如导出为XML格式,甚至可以保存到数据库。
下面我将给出一个大致的实现步骤:
1) 在eclipse下,新建plugin project。
2) 修改plugin.xml,添加extenion point定义。举例如下:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
<extension point="org.eclipse.birt.report.engine.dataExtraction">
<dataExtraction
id = "org.eclipse.birt.report.engine.dataextraction.xml"
name = "XML"
format = "xml"
mimeType = "text/xml"
class = "org.eclipse.birt.report.engine.dataextraction.xml.DataExtractionImpl"/>
</extension>
</plugin>
注意,id, name, format,mimeType, class都是必须的。
3) 实现IDataExtractionExtension接口
该接口主要有三个方法:
public void initilize( IReportContext context, IDataExtractionOption option ) throws BirtException
public void output( IExtractionResults results ) throws BirtException
public void release( )
initialize方法会传入报表相关的IReportContext对象,如果需要获取报表相应的信息,可以通过该对象。
IDataExtractionOption对象是从BIRT viewer传入的一些参数。
如:
(Map) this.option.getOption( "birt.viewer.parameters" )--得到viewer相关的参数(包含用户在url上传入的自定义参数)
(Locale)this.option.getOption("birt.viewer.locale")--得到locale信息
(String[])this.option.getOption("birt.viewer.export.columns")--得到选中的columns。
具体的实现逻辑可以在output方法里实现,应该非常容易理解。
下面给出output的方法的实现
- public void output( IExtractionResults results )
- throws BirtException{
- String[ ] columnNames = (String[ ]) selectedColumnNames;
- try {
- outDocument = createNewDocument ( );
- } catch (Exception e) {
- e.printStackTrace();
- }
- //create root element of output XML document
- Element rootElement = outDocument.createElement( "Report" );
- rootElement.appendChild( outDocument.createComment("Extract report data in XML format" ));
- outDocument.appendChild( rootElement );
- Element pRow = outDocument.createElement( "Properties" );
- rootElement.appendChild( outDocument.createComment("Lists the report properties, such as name, locale,
- encoding, user parameters" ));
- rootElement.appendChild( pRow );
- Element nodeReportName = outDocument.createElement( "ReportName" );
- nodeReportName.appendChild(outDocument.createTextNode( reportName ));
- pRow.appendChild( nodeReportName );
- Element nodeLocale =outDocument.createElement( "Locale" );
- nodeLocale.appendChild(outDocument.createTextNode(locale.getDisplayLanguage( ) ));
- pRow.appendChild( nodeLocale );
- Element nodeEncoding =outDocument.createElement( "Encoding" );
- nodeEncoding.appendChild( outDocument.createTextNode( encoding ));
- pRow.appendChild( nodeEncoding );
- Element nodeParameters = outDocument.createElement( "Parameters" );
- nodeParameters.appendChild(outDocument.createTextNode( userParameters ));
- pRow.appendChild(nodeParameters);
- try {
- // if selected columns are null or empty,
- returns all columns
- if ( columnNames == null || columnNames.length <= 0 ){
- int count =results.getResultMetaData( ).getColumnCount( );
- columnNames = new String[ count ];
- for ( int i = 0; i < count; i++ ){
- String colName =results.getResultMetaData( ).getColumnName(i);
- columnNames[i] = colName;
- }
- }
- IDataIterator iData = null;
- if ( results != null ){
- iData = results.nextResultIterator( );
- if ( iData != null && columnNames.length > 0 ){
- String[ ] values =new String[ columnNames.length ];
- while ( iData.next( ) ){
- Element row =outDocument.createElement( "Row" );
- rootElement.appendChild( row );
- for (int i = 0; i < columnNames.length;i++){
- String columnName =results.getResultMetaData( ).getColumnName( i );
- values[i] = getStringValue(iData.getValue( columnNames[i] ),isLocaleNeutral, locale );
- Element node =outDocument.createElement(columnName);
- node.appendChild(outDocument.createTextNode(values[ i ] ));
- row.appendChild( node );
- }
- }
- }
- }
- if ( encoding != null && encoding.trim( ).length( ) > 0 ){
- outputStream.write(serialize( outDocument ).getBytes(encoding.trim( )));
- }
- else{
- outputStream.write( serialize( outDocument ).getBytes( ));
- }
- }
- catch ( Exception e ){
- }
- }
- public void output( IExtractionResults results )
- throws BirtException{
- String[ ] columnNames = (String[ ]) selectedColumnNames;
- try {
- outDocument = createNewDocument ( );
- } catch (Exception e) {
- e.printStackTrace();
- }
- //create root element of output XML document
- Element rootElement = outDocument.createElement( "Report" );
- rootElement.appendChild( outDocument.createComment("Extract report data in XML format" ));
- outDocument.appendChild( rootElement );
- Element pRow = outDocument.createElement( "Properties" );
- rootElement.appendChild( outDocument.createComment("Lists the report properties, such as name, locale,
- encoding, user parameters" ));
- rootElement.appendChild( pRow );
- Element nodeReportName = outDocument.createElement( "ReportName" );
- nodeReportName.appendChild(outDocument.createTextNode( reportName ));
- pRow.appendChild( nodeReportName );
- Element nodeLocale =outDocument.createElement( "Locale" );
- nodeLocale.appendChild(outDocument.createTextNode(locale.getDisplayLanguage( ) ));
- pRow.appendChild( nodeLocale );
- Element nodeEncoding =outDocument.createElement( "Encoding" );
- nodeEncoding.appendChild( outDocument.createTextNode( encoding ));
- pRow.appendChild( nodeEncoding );
- Element nodeParameters = outDocument.createElement( "Parameters" );
- nodeParameters.appendChild(outDocument.createTextNode( userParameters ));
- pRow.appendChild(nodeParameters);
- try {
- // if selected columns are null or empty,
- returns all columns
- if ( columnNames == null || columnNames.length <= 0 ){
- int count =results.getResultMetaData( ).getColumnCount( );
- columnNames = new String[ count ];
- for ( int i = 0; i < count; i++ ){
- String colName =results.getResultMetaData( ).getColumnName(i);
- columnNames[i] = colName;
- }
- }
- IDataIterator iData = null;
- if ( results != null ){
- iData = results.nextResultIterator( );
- if ( iData != null && columnNames.length > 0 ){
- String[ ] values =new String[ columnNames.length ];
- while ( iData.next( ) ){
- Element row =outDocument.createElement( "Row" );
- rootElement.appendChild( row );
- for (int i = 0; i < columnNames.length;i++){
- String columnName =results.getResultMetaData( ).getColumnName( i );
- values[i] = getStringValue(iData.getValue( columnNames[i] ),isLocaleNeutral, locale );
- Element node =outDocument.createElement(columnName);
- node.appendChild(outDocument.createTextNode(values[ i ] ));
- row.appendChild( node );
- }
- }
- }
- }
- if ( encoding != null && encoding.trim( ).length( ) > 0 ){
- outputStream.write(serialize( outDocument ).getBytes(encoding.trim( )));
- }
- else{
- outputStream.write( serialize( outDocument ).getBytes( ));
- }
- }
- catch ( Exception e ){
- }
- }
4) 最后一步当然是发布了,非常简单,如果是3.7.0以前的版本,则打包成plugin,然后放到WEB-INF/platform/plugins下就可以了,注意BIRT已经有了一个org.eclipse.birt.report.engine.dataextraction.csv.jar的plugin,和它放一起就行了,3.7.0以后的版本直接放在WEB-INF/lib下即可。
5)打开页面,在export dialog里就会看到多了一个XML的导出选项了。
具体怎么实现导出成XML格式,还要实现其它接口的方法,在后面的BIRT报表的扩展中会详细的说明。