2006 年 11 月发布
Oracle XML Publisher 是基于 XSL-FO 的 Oracle 报表工具,它包含一个 GUI 和一组核心 API 组件。以下核心 API 可以与 Oracle JDeveloper 10.1.3 集成,以生成 PDF、Excel、HTML 或 RTF 报表:
在本文中,您将了解如何使用 FO Processor Engine、XSL-FO Utility 和 PDF Document Merger。您还可以大概了解能够从数据库生成 XML 文档的 Data Engine API。该 API 使用 XML 模板指定对数据库的 SQL 查询以及所生成的 XML 文档的结构。
某些 Oracle XML Publisher API 需要 RTF 和 PDF 模板作为输入。要创建 RTF 模板,您需要 Oracle XML Publisher Desktop,这是一个 Microsoft Word 插件。要创建 PDF 模板,只需在 Word 文档中定义一个布局,然后使用 Adobe Acrobat Distiller 将该文档转换为 PDF。尽管本文没有讨论需要 RTF 和 PDF 模板的 API,但使用它们生成报表的过程类似于使用 Oracle XML Publisher API 生成报表的过程。
您将在以下部分中为项目添加其他 XML 和 XSLT 文档。图 1 展示了 Oracle XML Publisher 项目的目录结构:
图 2 展示了 Oracle XML Publisher 项目中的 Jar 文件/库:
下载并安装 Oracle 数据库 10g,然后创建一个包含示例模式的数据库实例。然后,在 OE 模式中使用下面所示的 SQL 脚本创建示例数据库表。
CREATE TABLE OE.Catalog(CatalogId VARCHAR(25) PRIMARY KEY, Journal VARCHAR(25), Publisher VARCHAR(25), Edition VARCHAR(25), Title Varchar(45), Author Varchar(25)); INSERT INTO OE.Catalog VALUES('catalog1', 'Oracle Magazine', 'Oracle Publishing', 'March-April 2006', 'Commanding ASM', 'Arup Nanda'); INSERT INTO OE.Catalog VALUES('catalog2', 'Oracle Magazine', 'Oracle Publishing', 'May-June 2006', 'Tuning Your View Objects', 'Steve Muench'); INSERT INTO OE.Catalog VALUES('catalog3', 'Oracle Magazine', 'Oracle Publishing', 'May-June 2006', 'Managing Oracle Portal', 'Aradhana Puri');
FO Processor Engine API 用于从 XML 文件和 XSLT 文件生成 PDF、Excel、HTML 或 RTF 报表。在本部分中,我们将创建一个 PDF 文档。PDF 报表的输入 XML 文档如下所示。
<?xml version="1.0" encoding="UTF-8"?> <!--A Oracle Magazine Catalog--> <catalog title="Oracle Magazine" publisher="Oracle Publishing"> <magazine date="March-April 2006"> <article> <title>Using Bind Variables</title> <author>Steve Muench</author> </article> <article> <title>Commanding ASM</title> <author>Arup Nanda</author> </article> </magazine> <magazine date="May-June 2006"> <article> <title>Tuning Your View Objects</title> <author>Steve Muench</author> </article> <article> <title>Managing Oracle Portal</title> <author>Aradhana Puri</author> </article> </magazine> </catalog>
要生成 PDF 报表,您需要一个 XSL-FO 文件。XSL 规范中说明了格式化对象。这里,您将定义一个 XSLT 样式表,用于将 XML 文件转换为 XSL-FO 文件。随后,我们将使用 FO Processor Engine 将该文件转换为报表。
XSL-FO 文件指定如何在报表中格式化数据,包括布局、字体和表。XSL-FO 文件中的元素位于带‘fo’前缀的命名空间中,该命名空间通过命名空间声明 xmlns:fo=http://www.w3.org/1999/XSL/Format 来指定。XSL-FO 文件的 DTD 可以从 fo.zip 中获得。
您可以通过 XML 模式验证 XSL-FO 文件,方法是将 XSL-FO 文件的 DTD 转换为 XML 模式。Oracle JDeveloper 可以注册 XSL-FO 文件的 XML 模式,并生成或验证 XSL-FO 文件的实例。验证 XSL-FO 文件指的是检查 XSL-FO 文件的格式。表 1 列出了 XSL-FO 文件中的某些元素。
表 1. XSL-FO 文档元素
元素 | 说明 |
fo:root |
XSL-FO 文档中的根元素 |
fo:layout-master-set |
指定一组页面模板 |
fo:simple-page-master |
页面布局 |
fo:page-sequence |
指定页面模板的顺序 |
fo:flow |
页面内容 |
fo:block |
块内容 |
fo:list-block |
指定列表 |
fo:list-item |
列表项 |
fo:table |
表 |
fo:table-column |
表列 |
fo:table-header |
表头 |
fo:table-body |
表主体 |
fo:table-row |
表行 |
fo:table-cell |
表单元格 |
示例 XSLT 样式表 (catalog.xsl) 用于从输入 XML 文件 (catalog.xml) 生成 XSL-FO 文件。
catalog.xsl
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:output method="xml" version="1.0" omit-xml-declaration="no" indent="yes" /> <!-- ========================= --> <!-- root element:catalog --> <!-- ========================= --> <xsl:template match="/catalog"> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="simpleA4" page-height="29.7cm" page-width="21cm" margin-top="2cm" margin-bottom="2cm" margin-left="2cm" margin-right="2cm"> <fo:region-body /> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="simpleA4"> <fo:flow flow-name="xsl-region-body"> <fo:block font-size="16pt" font-weight="bold" space-after="5mm"> Catalog:<xsl:value-of select="@title" /> </fo:block> <fo:block font-size="16pt" font-weight="bold" space-after="5mm"> Publisher:<xsl:value-of select="@publisher" /> </fo:block> <fo:block font-size="10pt"> <fo:table table-layout="fixed"> <fo:table-column column-width="4cm" /> <fo:table-column column-width="4cm" /> <fo:table-column column-width="5cm" /> <fo:table-header> <fo:table-row font-weight="bold"><fo:table-cell> <fo:block> <xsl:text>Date</xsl:text> </fo:block> </fo:table-cell> <fo:table-cell> <fo:block> <xsl:text>Title</xsl:text> </fo:block> </fo:table-cell> <fo:table-cell> <fo:block> <xsl:text>Author</xsl:text> </fo:block> </fo:table-cell> </fo:table-row> </fo:table-header> <fo:table-body> <xsl:apply-templates select="magazine" /> </fo:table-body> </fo:table> </fo:block> </fo:flow> </fo:page-sequence> </fo:root> </xsl:template> <xsl:template match="magazine"> <xsl:for-each select="article"> <fo:table-row> <fo:table-cell> <fo:block> <xsl:value-of select="../@date"/> </fo:block> </fo:table-cell> <fo:table-cell> <fo:block> <xsl:value-of select="title"/> </fo:block> </fo:table-cell> <fo:table-cell> <fo:block> <xsl:value-of select="author"/> </fo:block> </fo:table-cell> </fo:table-row> </xsl:for-each> </xsl:template> </xsl:stylesheet>
接下来,您将开发一个 Java 应用程序,以使用 Oracle XML Publisher API 生成报表。在 XMLPublisher.java 类中,导入 Oracle XML Publisher 类:
import oracle.apps.xdo.template.FOProcessor; import oracle.apps.xdo.template.fo.util.FOUtility; import oracle.apps.xdo.common.pdf.util.PDFDocMerger; import oracle.apps.xdo.dataengine.DataProcessor; import oracle.apps.xdo.XDOException;
创建 FOProcessor 对象。FOProcessor 类是使用 FO Processing Engine 的主要类。
FOProcessor processor = new FOProcessor();
设置输入 XML 文件:
processor.setData("catalog.xml");
设置 XSLT 模板:
processor.setTemplate("catalog.xsl");
设置 PDF 文档的输出文件和输出格式:
processor.setOutput("catalog.pdf"); processor.setOutputFormat(FOProcessor.FORMAT_PDF);
表 2 列出了支持的输出格式。
表 2. FO Processor 输出格式
输出格式 | 说明 |
FORMAT_AWT |
AWT 格式 |
FORMAT_EXCEL |
Excel 电子表格格式 |
FORMAT_HTML |
HTML 格式 |
FORMAT_PDF |
PDF 格式 |
FORMAT_RTF |
RTF 格式 |
FORMAT_UIX |
UIX 格式 |
如果需要 HTML 输出,应指定 HTML 输出文件和 HTML 输出格式:
processor.setOutput("catalog.html"); processor.setOutputFormat(FOProcessor.FORMAT_HTML);
运行 FO Processor Engine:
processor.generate();
您将在示例代码 zip 中找到 PDF 报表 (catalog.pdf),该报表是通过 FO Processor Engine 生成的。
在前面部分中,您通过 XML 文件和 XSLT 文件生成了 Oracle XML Publisher 报表。输入还可以是一组 XML 文件和 XSLT 文件。XSL-FO Utility 可以通过一组输入 XML 和 XSLT 文件生成 XSL-FO 文件。
例如,我们通过以下两个输入 XML 文件(catalog2.xml 和 catalog3.xml)来创建 XSL-FO 文件。
catalog2.xml
<?xml version="1.0" encoding="UTF-8"?> <!--A Oracle Magazine Catalog--> <catalog title="Oracle Magazine" publisher="Oracle Publishing"> <magazine date="July-August 2006"> <article> <title>Archiving Data Using XML</title> <author>Arup Nanda</author> </article> </magazine> </catalog>
catalog3.xml
<?xml version="1.0" encoding="UTF-8"?> <!--A Oracle Magazine Catalog--> <catalog title="Oracle Magazine" publisher="Oracle Publishing"> <magazine date="July-August 2006"> <article> <title>XML in Databases</title> <author>Ari Kaplan</author> </article> </magazine> <magazine date="Sept-Oct 2006"> <article> <title>Harnessing the Active Data Model</title> <author>Steve Muench</author> </article> </magazine> </catalog>
我们将使用 XSLT 文件 (catalog.xsl) 为输入 XML 文件生成 XSL-FO 文件,然后合并这些 XSL-FO 文件以生成单个 XSL-FO 文件。
为要通过输入 XML 和 XSLT 文件生成的 XSL-FO 文件创建 InputStream 对象数组:
InputStream[] input = new InputStream[2];
通过 XSL-FO Utility 的主要类 FOUtility,根据输入 XML 和 XSLT 文件创建 XSL-FO 文件。使用静态方法 createFO(java.lang.String xmlFile,java.lang.String xslFile) 创建 XSL-FO 文件:
InputStream firstFOStream = FOUtility.createFO("catalog2.xml", "catalog.xsl");InputStream secondFOStream = FOUtility.createFO("catalog3.xml", "catalog.xsl");
在 InputStream 数组中设置 XSL-FO InputStream 对象:
Array.set(input, 0, firstFOStream); Array.set(input, 1, secondFOStream);
使用静态方法 mergeFOs() 合并 XSL-FO InputStream 对象:
InputStream mergedFOStream = FOUtility.mergeFOs(input, null);
创建 FOProcessor 对象,以便通过合并的 XSL-FO 文件生成 PDF 报表:
FOProcessor processor = new FOProcessor();
在 FOProcessor 对象上设置合并的 XSL-FO InputStream:
processor.setData(mergedFOStream);
如果将 XSL-FO 文件指定为 FOProcessor 的数据源,则不需要设置 XSLT 文档。将 XSLT 模板设置为 null:
processor.setTemplate((String)null);
设置输出 PDF 文件和输出格式,并生成 PDF 报表:
processor.setOutput("catalog2.pdf"); processor.setOutputFormat(FOProcessor.FORMAT_PDF); processor.generate();
您将在示例代码中找到 PDF 报表 (catalog2.pdf),该报表是通过合并的 XSL-FO InputStream 生成的。
有时,您必须合并 PDF 文档。PDF Document Merger 用于合并 PDF 文档,还可以为合并的文档添加页码。在本部分中,我们将合并在前面部分中生成的 catalog.pdf 和 catalog2.pdf。
首先,根据 catalog.pdf 和 catalog2.pdf 创建一个 InputStream 数组:
FileInputStream[] inputStreams = new FileInputStream[2]; inputStreams[0] = new FileInputStream("catalog.pdf"); inputStreams[1] = new FileInputStream("catalog2.pdf");
为合并的 PDF 文档创建 FileOutputStream:
FileOutputStream outputStream = new FileOutputStream("catalog3.pdf");
合并 PDF 文档与 PDFDocMerger 类:
PDFDocMerger pdfMerger = new PDFDocMerger(inputStreams, outputStream);
要添加页码,应指定页码坐标和页码字体:
pdfMerger.setPageNumberCoordinates(300, 20); pdfMerger.setPageNumberFontInfo("Courier", 10);
使用 setPageNumberValue(int initialValue, int startPageIndex) 方法设置页码值。initialValue 指定页码的初始值。startPageIndex 指定从哪个页码开始编号:
pdfMerger.setPageNumberValue(1, 1);
运行 PDF Document Merger:
pdfMerger.process();
您将在示例代码中找到合并后的 PDF 文档 (catalog3.pdf)。
Data Engine API 可以使用数据库数据创建 XML 文档。DataProcessor 类用于从数据模板生成 XML 文档。该模板是一个 XML 文件,可以指定输入参数、从数据库检索数据的 SQL 查询以及要生成的 XML 文档的数据结构。与 XML SQL Utility(也是从数据库生成 XML 文档)相比,Data Engine API 的一个优势是可让您在 XML 文档中生成元素的层次结构。表 3 列出了一些在数据模板中常用的元素。
表 3. 数据模板元素
元素 | 说明 | 属性 |
dataTemplate(必需) |
根元素 |
name(必需) |
parameters |
指定 SQL 查询的输入参数,由 <parameter> 元素组成。参数值可以在运行时设置。 |
name(必需) |
dataQuery(必需) |
指定从数据库检索数据的 SQL 查询,由 <sqlstatement> 元素组成。 |
- |
sqlstatement(必需) |
指定 SQL 语句。 |
name(必需) |
dataStructure(对于多个查询是必需的) |
定义输出 XML 的结构,由 <group> 和 <element> 元素组成。如果没有为单个查询指定,则输出 XML 由与 SQL 查询中的列相对应的元素组成。 |
- |
group |
指定一组元素和子组。元素的层次结构可以通过子组元素指定。 |
name(必需) |
element(必需) |
指定输出 XML 文档中的元素。 |
name(必需) |
该示例数据模板 (catalogDataTemplate.xml) 用于从数据库表生成 XML 文档:
catalogDataTemplate.xml
<?xml version="1.0" encoding="WINDOWS-1252" ?> <dataTemplate name="catalogDataTemplate" description="Magazine Catalog" defaultPackage="" Version="1.0"> <parameters> <parameter name="id" dataType="character" /> </parameters> <dataQuery> <sqlStatement name="Q1"> <![CDATA[ SELECT CatalogId, Journal, Publisher, Edition, Title, Author from OE.CATALOG WHERE CatalogId=:id </sqlStatement> </dataQuery> <dataStructure> <group name="G_Catalog" source="Q1"> <element name="CATALOGID" value="CatalogId" /> <element name="JOURNAL" value="Journal" /> <element name="PUBLISHER" value="Publisher" /> <element name="EDITION" value="Edition" /> <element name="TITLE" value="Title"/> <element name="AUTHOR" value="Author" /> </group> </dataStructure> </dataTemplate>
示例数据模板指定的参数、id 均为 character 类型。SQL 查询中的绑定变量用于设置 CatalogId 列的值(通过 id 参数)。CatalogId 列的值在运行时设置。示例查询中的所有列都是 VARCHAR 类型。可以在 SQL 查询中指定以下列类型:VARCHAR2、CHAR、NUMBER、DATE、TIMESTAMP、BLOB、CLOB 和 XMLType。
接下来,我们将通过 Data Engine API 使用数据库数据生成 XML 文档。首先,创建一个 DataProcessor 对象:
DataProcessor dataProcessor = new DataProcessor();
在 DataProcessor 对象上设置数据模板:
dataProcessor.setDataTemplate("catalogDataTemplate.xml");
指定参数的值,并在 DataProcessor 对象上设置参数:
Hashtable parameters = new Hashtable(); parameters.put("id","catalog1"); dataProcessor.setParameters(parameters);
创建 Oracle 数据库的 JDBC 连接,并在 DataProcessor 对象上设置该连接:
Class.forName("oracle.jdbc.OracleDriver"); String url="jdbc:oracle:thin:@localhost:1521:ORCL"; java.sql.Connection jdbcConnection = DriverManager.getConnection(url, "OE", "password"); dataProcessor.setConnection(jdbcConnection);
设置输出 XML 文档并运行数据处理器:
dataProcessor.setOutput("catalogData.xml"); dataProcessor.processData();
数据库将生成 XML 文档 (catalogData.xml),如下所示:
<?xml version="1.0" encoding="UTF-8"?> <catalogDataTemplate> <id>catalog1</id> <LIST_G_CATALOG> <G_CATALOG> <CATALOGID>catalog1</CATALOGID> <JOURNAL>Oracle Magazine</JOURNAL> <PUBLISHER>Oracle Publishing</PUBLISHER> <EDITION>March-April 2006</EDITION> <TITLE>Commanding ASM</TITLE> <AUTHOR>Arup Nanda</AUTHOR> </G_CATALOG> </LIST_G_CATALOG> </catalogDataTemplate>
以下是 XMLPublisher.java 类:
package xmlpublisher; import com.sun.java.util.collections.Hashtable; import oracle.apps.xdo.XDOException; import oracle.apps.xdo.common.pdf.util.PDFDocMerger; import oracle.apps.xdo.dataengine.DataProcessor; import oracle.apps.xdo.template.FOProcessor; import oracle.apps.xdo.template.fo.util.FOUtility; import oracle.xml.parser.v2.NSResolver; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.lang.reflect.Array; import java.sql.DriverManager; import java.sql.SQLException; public class XMLPublisher { public XMLPublisher() { } public void foProcessorEngine() { try { FOProcessor processor = new FOProcessor(); processor.setData("catalog.xml"); processor.setTemplate("catalog.xsl"); processor.setOutput("catalog.pdf"); processor.setOutputFormat(FOProcessor.FORMAT_PDF); processor.generate(); } catch (XDOException e) { System.out.println("XDOException " + e.getMessage()); } } public void xslFoUtility() { try { InputStream[] input = new InputStream[2]; InputStream firstFOStream = FOUtility.createFO("catalog2.xml", "catalog.xsl"); InputStream secondFOStream = FOUtility.createFO("catalog3.xml", "catalog.xsl"); Array.set(input, 0, firstFOStream); Array.set(input, 1, secondFOStream); InputStream mergedFOStream = FOUtility.mergeFOs(input, null); if (mergedFOStream == null) { System.out.println("Merge failed."); } FOProcessor processor = new FOProcessor(); processor.setData(mergedFOStream); processor.setTemplate((String) null); processor.setOutput("catalog2.pdf"); processor.setOutputFormat(FOProcessor.FORMAT_PDF); processor.generate(); } catch (XDOException e) { System.out.println("XDOException" + e.getMessage()); } } public void pdfDocumentMerger() { try { FileInputStream[] inputStreams = new FileInputStream[2]; inputStreams[0] = new FileInputStream("catalog.pdf"); inputStreams[1] = new FileInputStream("catalog2.pdf"); FileOutputStream outputStream = new FileOutputStream("catalog3.pdf"); PDFDocMerger pdfMerger = new PDFDocMerger(inputStreams, outputStream); pdfMerger.setPageNumberCoordinates(300, 20); pdfMerger.setPageNumberFontInfo("Courier", 10); pdfMerger.setPageNumberValue(1, 1); pdfMerger.process(); pdfMerger = null; } catch (XDOException e) { System.out.println("XDOException" + e.getMessage()); } catch (FileNotFoundException e) { System.out.println("FileNotFoundException " + e.getMessage()); } } public void dataEngine() { try { Class.forName("oracle.jdbc.OracleDriver"); String url = "jdbc:oracle:thin:@localhost:1521:ORCL"; java.sql.Connection jdbcConnection = DriverManager.getConnection(url, "OE", "password"); DataProcessor dataProcessor = new DataProcessor(); dataProcessor.setDataTemplate("catalogDataTemplate.xml"); Hashtable parameters = new Hashtable(); parameters.put("id", "catalog1"); dataProcessor.setParameters(parameters); dataProcessor.setConnection(jdbcConnection); dataProcessor.setOutput("catalogData.xml"); dataProcessor.processData(); } catch (SQLException e) { System.out.println("SQLException " + e.getMessage()); } catch (ClassNotFoundException e) { System.out.println("ClassNotFoundException " + e.getMessage()); } catch (XDOException e) { System.out.println("XDOException" + e.getMessage()); } } public static void main(String[] argv) { XMLPublisher xmlPublisher = new XMLPublisher(); xmlPublisher.foProcessorEngine(); xmlPublisher.xslFoUtility(); xmlPublisher.pdfDocumentMerger(); xmlPublisher.dataEngine(); } }
接下来,运行 Oracle XML Publisher 应用程序。右键单击 XMLPublisher.java 类并选择 Run,如图 3 所示。这将生成 PDF 和 XML 报表:
如果 FO Processor Engine 部分中的输出格式设为 HTML,则将生成输出 HTML 报表,如图 4 所示:
本文未讨论的 XMLPublisher API 可能也集成在类似过程中。
恭喜!您已经将 Oracle XML Publisher API 与 Oracle JDeveloper 集成在一起,可以生成 PDF、Excel、HTML 和 XML 报表了。