你想把XML转成PDF?喔,使用FOP

12/23/2007来源:J2EE/J2ME教程人气:9377

作者: Vikram Goyal       10/16/2002
出处: http://www.onjava.com/pub/a/onjava/2002/10/16/fop.Html


格式对象处理器 (FOP) 是一个源码开放的Java API,可以把xml数据转换成PDF格式,同样的,也可以转成其他相关的格式,象TXT, SVG, AWT, MIF, 和 PS. 该软件是在Apache XML项目下开发的,可以免费使用。
这篇文章展示了如何开始使用FOP。FOP的主要优点是它转换XML数据到PDF格式报告的性能,使用的了格式化树结构。我们提供的绝大多数的例子将会集中展示这个特别的转换,但也会把XML数据转成Java AWT格式.
这篇文章的面向于精通XML和XSLT的开发者的。要获取更多的关于XML的信息,可以到XML.com。
安装
FOP 可以从FOP 发布目录上面下载。下载文件是一个gzip 文件包,它包括两个发行包。其中fop-0.20.4-src 发行包包含源代码,使用它可以自己用Ant 进行编译。fop-0.20.4-bin 发行包包含了运行部分, 里面没有源代码和Java文档。
把源码包解压到你选择的一个目录下。你会得到程序解压生成的fop-0.20.4主目录 和一系列子目录:build, conf, docs, hyph, lib 和 src.
? build 包含最新的FOP运行文件fop.jar, 这个文件应该被放在应用程序的CLASSPATH 中。
? conf 包含了确定的配置文件,这个我们将在后面进行讨论。
? docs 包含了各种各样的例子,文档,和一些图形图片。
? hyph 包含了不同语言的连字符信息。
? lib 包含了所有的外部.jar 文件,这些文件对于FOP自己的运行是必需的。他们包括Avalon, Batik, Xalan, 和 Xerces。 由于这篇文章
? 是着重介绍FOP的, 我们不会深入讨论这些API。只需要知道FOP使用这些API就够了。在配置的时候我们需要把这些库放到应用程序的CLASSPATH 中。
? src 包含了所有源代码。
FOP 简介
 
图形1. FOP 架构.
FOP 是一个被3W组织在XSL 说明书 中指定的用于理解格式化对象的工具。说明书的第一部分说明了XSLT的转化处理。我们感兴趣的是第二部分, 里面说明了我们所说的格式化对象(FO)的处理。 说明书中的这一部分定义了独立输出的格式化对象, 它由一系列的文档风格以及布局的词表组成。举个例子来说, 其中的一个格式化对象是fo:simple-page-master, 它用来说明一个页面模板,它包含一些相关的属性 (边界,页头, 等等)。就这样,象FOP这样的工具可以读吃这些信息并且把这些信息转化成想要的格式输出(PDF/TXT)。很重要的一点是相同的风格信息可以产生不同的输出。
一篇FO 文档简单的说就是一篇XML文档。 它的命名空间由W3C 站点 定义。它 可以包含任何在这个命名空间中元素。你能够手工的创建这样的文档并且为每一个元素指定确切的值,每一个元素应该被输出。然而,更为通用的方法是写一个XSLT的样式表,来管理你的XML数据文件, 根据你的样式标的规则来进行转换, 从而产生最终的FO文档。动态生成的数据能够跟一个存在的样式表绑定来产生FO 文档。


虽然fop的主要意图是处理fo文档, 但是他也能够通过一个样式表完成已存在数据文件(XML)的转换。我们假定在一XSL文件的表中有你的以XML个是存放的商务数据以及样式表信息。如果你把这两者提供给FOP, FOP 将会把这些信息转换成一篇临时的FO文档并且最终生成你期望的输出。


一个简单的例子
例子文件
下载这篇文章中的例子文件 。这个  .zip 文件包含了下列文件:
krusty.fo, krusty.pdf, krusty.xml, and krusty.xsl.
了解了原理以后,让我们实际运行一下FOP。 打开一个命令窗口并且定位到你的FOP安装目录下。FOP 的根目录下面包含了两个运行体:一部分是可运行在Unix系统上的shell脚本,另一部分是用于windows环境的一系列文件,通过这些文件FOP能够通过命令行来运行。根据你的系统情况来运行相关的脚本。FOP将会报没有输入参数的提示,从而输出一系列的运行例子。好的—这意味着你现在可以开始使用FOP了。
让我们从创建一个简单的FO 文件开始。如果你想要看到最终结果, 可以点击这里krusty.fo.
就像我前面所讲到过的,一个FO文件是一个简单的类似表的Xml文件。因此开开你的文本编辑器并输入第一行:

所有的FO文件必须有这个外层元素作为根
这个 根 元素的后面跟着元素,它表明了我们文档的页面布局。

page-height="29.7cm"
page-width="21cm"
margin-top="1cm"
margin-bottom="2cm"
margin-left="2.5cm"
margin-right="2.5cm">





就像你看到的, 主要的布局设置包括不同的简单的可以放在里面的页面布局定义。在我们的例子中,已经定义了一个单独的简单页面管理者,在里面,我们可以看到简单页面的属性,我们给它起的名字simple (这个名字以后我们会用到), 这个页面的高度是29.7 厘米, 宽度是21 厘米, 顶端空白为1 厘米, 等等。我们可以定义许多我们想要的简单页面并且赋给他们不同的名字,这些名字我们会在后面饮用这些页面的时候用到。
现在我们已经定义了页面队列术语和大小与它们将来看上去模样的对应,进而我们需要定义实际的目录持有者。下面是我们使用 标记的位置:

注意在定义页面顺序的时候,我们引用到了简单的页面管理者, 之所以称为简单, 是因为我们在前面已经定义了。这意味着我们的内容将被简单页面管理者限定在一个页面之内。 实际的内容被放在 元素中。在元素中的 元素,开始一个段落并定义这个段落的每个属性。就此出来说我们设开头文字"Krusty the Clown,"的字体为sans serif 字体, 背景色为蓝色,文字排序方式为居中。简单的, 对于下一个块,我们设定字体大小为12 磅。文字排序方式根据具体情况而定。



font-family="sans-serif"
line-height="24pt"
space-after.optimum="15pt"
background-color="blue"
color="white"
text-align="center"
padding-top="3pt">
Krusty the Clown


font-family="sans-serif"
line-height="15pt"
space-after.optimum="3pt"
text-align="justify">
这个备忘录解释了为什末Krusty the Clown 是我们做好的客户。我们需要从现在开始关心他的需求,从而确保他的宠物猴子有足够的香蕉可以吃。




最后, 关闭所有打开的标记并且保存为文件krusty.fo,这个文件存放在FOP根目录下。
现在是看FOP运作的时候了。在FOP的根目录下,敲入如下的命令:
fop krusty.fo krusty.pdf


fop 将会运行并且在相同的目录下将我们的krusty.fo 文件转化为一个krusty.pdf 文档。通过双击打开文件,检查最终的输出是否真的符合我们的要求。 对FO 文件进行一点改变,然后看看在输出中有什末影响。 从改变内容中的文字开始,继而试着改变风格,边界, 颜色, 字体, 等等, 然后看所有的变化是怎样的。
XSL + XML
通过运行简单的例子, 你将会注意到手工的产生一个FO文档是很难的。直接的修改和改变FO文件实在是令人讨厌的,我们正在失去内容独立性的优越性。因此通常地, 你将会换用一个XSLT样式表来把你的XML数据转化成为一篇XSL-FO 文件。你不需要在外部或者为这个转换明确地做什末事情来转换到FOP。你可以为FOP指定样式表和XML文件,这样FOP就可以自己进行转化了。让我们看看下面的例子,它会告诉我们这些是怎样实现的。
我们将从前面的例子中把数据提取出来放到一个XML 文件 中。因此我们的XML 数据文件看书去就会使这个样子的:




Krusty the Clown


这个备忘录解释了为什末Krusty the Clown 是我们做好的客户。我们需要从现在开始关心他的需求,从而确保他的宠物猴子有足够的香蕉可以吃。




把这个文件保存在FOP的根目录下。
我们现在需要生成一个样式表,然后用它来转化这个数据文件为FO文件。要想察看最后的结果,可以下载最后的XSL 文件 (这个文件也在FOP.zip 例子文件中)。

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
像期望的那样, XSL文档的开头是XML声明, 紧跟着是命名空间的声明。

如果你以前曾经用过XSLT, 你会注意到现在我们试图做的是对我们的XML中的标记进行匹配,替换并使用它们来转化为另一个XML文件,也就是我们的FO文件。因此上面的文本行寻找和匹配根 标记并使用它后面的内容来替换它。这个内容, 下面是它的概要, 基本来说,它是我们简单布局管理者的定义,正如我们上面对FO文件所描述的那样。

page-height="29.7cm"
page-width="21cm"
margin-top="1cm"
margin-bottom="2cm"
margin-left="2.5cm"
margin-right="2.5cm">











用实际的格式化信息替换数据标记从而形成我们的XSL 文件。







font-family="sans-serif"
line-height="24pt"
space-after.optimum="15pt"
background-color="blue"
color="white"
text-align="center"
padding-top="3pt">





font-family="sans-serif"
line-height="15pt"
space-after.optimum="3pt"
text-align="justify">




正如你所看到的,  在FO标记术语中的模板匹配项被相应的格式化信息替换了
。样式表包括样式表的结束标记。把这个文件保存为krusty.xsl文件,并存放在 FOP根目录下。
下面通过运行来看看我们努力的结果, 在FOP的根目录下输入下面的命令行:
fop -xml krusty.xml -xsl krusty.xsl -pdf krusty.pdf
因而, 你正在指定输入的XSL 和 XML 文件以及输出的PDF文件。当你打开这个PDF文件的时候, 你将会看到结果跟你运行FOP处理FO文件是一样的。
第二种途径的优点应该很清楚的了。我们的输入XMl数据在不同的环境中是不同的。今天我们正为Krusty the Clown准备报告, 明天它可能是Bart the Kid。我们的数据文件将会改变,但是样式表不会改变。
转化为AWT
我不需要做多少事情就会输出不同的格式。FOP 在内部会自动处理。我们要输出Java 的 AWT, 我们所有要做的就是:
fop -xml krusty.xml -xsl krusty.xsl -awt
That is, 指定输出为AWT 而不是PDF。 FOP 会创建一个AWT 阅读器给你, as 如图 2所示。
 
图形2. AWT 阅读器。
嵌入的FOP
不管是作为WEB程序一部分还是桌面程序的一部分,在你自己的程序中使用嵌入FOP的步骤是简单的。
1. org.apache.fop.apps.Driver的示例。
Driver driver = new Driver();
2. 设定你想要的转换类型。
driver.setRenderer(Driver.RENDER_PDF);
3. 设置一个跟踪对象。
driver.setLogger(log);
4. 设定输入资源。
driver.setInputSource(new FileInputSource(file));
5. 设定一个要转换的输出流。
driver.setOutputStream(new FileOutputStream(out));
6. 最后,运行并输出结果。
driver.run();
如果你指定了一个FO文件作为输入,上面的处理就是有效的。如果你在前面指定的是XSL 和 XML 文件作为输入, 你需要按照下面的做法来修改第四步和第六步:
4. 声明一个设定XSLTInputHandler类型作为一个输入处理句柄。
InputHandler inputHandler = new XSLTInputHandler(xmlfile, xslfile);
6. 获得处理者和转换者的解析器。
driver.render(inputHandler.getParser(), inputHandler.getInputSource());
上面我们对嵌入应用程序中的FOP,作了一个系统的阐述。在docs\examples\embedding 目录下还有一个完整的例子来说明嵌入到servlet中的FOP的应用的。
资源
? FOP 站点 是一个获取所有FOP文档的好地方; 然而,这些信息并不总是最新的。
? XSL 说明书 被W3C维护,是个获取所有格式化信息的好地方。
? O'Reilly'的 XML.com 可以很好的获取到通常的XML 和 XSL 信息。
? 最后, 在XML圣经摘录上面有很有用的FOP介绍。
其他的XML 资源
? 什末是 XSL-FO?
?  从XML打印: 一个对XSL-FO的介绍
? XSL 格式化对象的使用
? XSL 格式化对象,第二部分
? O'Reilly的 XSL-FO - Making XML 被打印技术看好
? 一个FOP的Web Service 实现
结论
FOP是一个开发源码的处理格式化对象的工具。它转换这些对象到我们需要的不同的媒体上。你可以单独运行FOP,也可以在你的应用程序中调用它。
Vikram Goyal 是一个地道的Java爱好者,有6年以上的相关经验。


(出处:http://www.knowsky.com)