使用 XSLFO 和 FOP 进行 PDF 生成
一篇关于如何使用 XSLFO 和 FOP 以编程方式创建 PDF 文档的文章。
引言
本文档描述了如何使用 XSLFO 和 Apache FOP 创建 PDF 文档。
背景
在开发基于 JSP 的 Web 应用程序时,我遇到了一个需求,即在单击按钮时创建 PDF 文档并导出它。 有很多教程可用于导出到 Excel、Word 等。我决定使用 XSLFO 和 FOP 来创建 PDF 文档。
XSLFO 是 XSL 格式化对象,可用于格式化 XML 数据。 更多信息请访问 此处 和 此处。
Apache FOP(格式化对象处理器)是一个 Java 应用程序,它读取格式化对象树并将生成的页面渲染到指定的输出(此处,在本例中,为 PDF)。 更多信息请访问 此处。
工作原理

我们有一个包含数据的 XML,以及一个 XSLT,它通过从第一个 XML 中获取数据来创建包含格式化对象的 XML。 将此生成的 XML 反序列化为 Java 对象。 FOP 使用这些 Java 对象创建 PDF 文件。
Hello World XSLFO
这是一个示例 XML。 此 XML 包含姓名和带有联系方式的朋友列表。
<?xml version="1.0" encoding="iso-8859-1"?>
<root>
<name>shyam</name>
<friend>
<name>Abc</name>
<phNo>90909090909</phNo>
</friend>
<friend>
<name>Xyz</name>
<phNo>32323232323</phNo>
</friend>
</root>
将其保存为 Hello.xml。
这是示例 xslfo 样式表。 首先,它从上面的 XML 中获取一个姓名。 然后,此 xslt 渲染一个包含上述 XML 中可用朋友详细信息的表。
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.1"
xmlns:xsl=http://www.w3.org/1999/XSL/Transform
xmlns:fo="http://www.w3.org/1999/XSL/Format"
exclude-result-prefixes="fo">
<xsl:template match="root">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="my-page">
<fo:region-body margin="1in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="my-page">
<fo:flow flow-name="xsl-region-body">
<fo:block>Hello, <xsl:value-of select="name" />!</fo:block>
<fo:block>
<fo:table>
<fo:table-body>
<fo:table-row>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
No.
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
Name
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
Phone Number
</fo:block>
</fo:table-cell>
</fo:table-row>
<xsl:for-each select="./friend">
<fo:table-row>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>
<xsl:value-of select="position()" />
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>
<xsl:value-of select="name" />
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>
<xsl:value-of select="phNo" />
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
将其保存为 HelloWorld.xsl。
Java 代码
这是创建 PDF 的代码
// the XSL FO file
File xsltfile = new File("HelloWorld.xsl");
// the XML file from which we take the name
StreamSource source = new StreamSource(new File("Hello.xml"));
// creation of transform source
StreamSource transformSource = new StreamSource(xsltfile);
// create an instance of fop factory
FopFactory fopFactory = FopFactory.newInstance();
// a user agent is needed for transformation
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
// to store output
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Transformer xslfoTransformer;
try
{
xslfoTransformer = getTransformer(transformSource);
// Construct fop with desired output format
Fop fop;
try
{
fop = fopFactory.newFop
(MimeConstants.MIME_PDF, foUserAgent, outStream);
// Resulting SAX events (the generated FO)
// must be piped through to FOP
Result res = new SAXResult(fop.getDefaultHandler());
// Start XSLT transformation and FOP processing
try
{
// everything will happen here..
xslfoTransformer.transform(source, res);
// if you want to get the PDF bytes, use the following code
//return outStream.toByteArray();
// if you want to save PDF file use the following code
/*File pdffile = new File("Result.pdf");
OutputStream out = new java.io.FileOutputStream(pdffile);
out = new java.io.BufferedOutputStream(out);
FileOutputStream str = new FileOutputStream(pdffile);
str.write(outStream.toByteArray());
str.close();
out.close();*/
// to write the content to out put stream
byte[] pdfBytes = outStream.toByteArray();
response.setContentLength(pdfBytes.length);
response.setContentType("application/pdf");
response.addHeader("Content-Disposition",
"attachment;filename=pdffile.pdf");
response.getOutputStream().write(pdfBytes);
response.getOutputStream().flush();
}
catch (TransformerException e) {
throw e;
}
}
catch (FOPException e) {
throw e;
}
}
catch (TransformerConfigurationException e)
{
throw e;
}
catch (TransformerFactoryConfigurationError e)
{
throw e;
}
“getTransformer
”函数。 我使用了 xslt2 处理器来进行高级 XSLT 处理。 简单的 XSLT 处理器在这里就足够了。
private Transformer getTransformer(StreamSource streamSource)
{
// setup the xslt transformer
net.sf.saxon.TransformerFactoryImpl impl =
new net.sf.saxon.TransformerFactoryImpl();
try {
return impl.newTransformer(streamSource);
} catch (TransformerConfigurationException e) {
e.printStackTrace();
}
return null;
}
以下是上述代码中使用的主要命名空间列表
import java.io.File;
import javax.xml.transform.stream.StreamSource;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import java.io.ByteArrayOutputStream;
import javax.xml.transform.Transformer;
import javax.xml.transform.Result;
工作原理
首先发生正常的 XML-XSLT 转换,从而创建 XSLFO XML。 Hello.xml 通过 HelloWorld.xsl 转换创建以下 XML。
<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="my-page">
<fo:region-body margin="1in" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="my-page">
<fo:flow flow-name="xsl-region-body">
<fo:block>Hello, shyam!</fo:block>
<fo:block>
<fo:table>
<fo:table-body>
<fo:table-row>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
No.
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
Name
</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black"
text-align="center" font-weight="bold">
<fo:block>
Phone Number
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>1</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>Abc</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>90909090909</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>2</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>Xyz</fo:block>
</fo:table-cell>
<fo:table-cell border="solid 1px black" text-align="center">
<fo:block>32323232323</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
此 XML 是序列化的 FO 对象树。 此 XML 被反序列化为 FO 对象,并通过这些对象,FOP 绘制 PDF 文件。 所有这些过程都发生在 transform;
函数中,我们无需担心!!!
关注点
FOP 是一个为我们提供创建 PDF 的绝佳工具。 我希望这段代码能帮助您以编程方式创建 PDF。 谢谢。
历史
- 初始版本于 2009 年 6 月 24 日