由于工作需要,今天简单的看了一下Java生成PDF的相关资料。综合看下来,除了使用报表平台和OOo的附带工具,目前使用较为普遍的有两个途径:iText和Apache的FOP。从实际出发,我们分别看看两者处理带有中文的PDF的具体用法吧。
[iText] (
link)
iText我想大概不少人都有所耳闻,JasperReports默认的PDF支持就来自这个软件包,它处理速度快,支持很多PDF"高级"特性,如:Annotations、AcroForms、数字签名、加密等,支持对已有PDF的处理,通过iTextAsian.jar和iTextAsianCmaps.jar,它对中文的支持也不错。缺点是较为依赖Java代码,需要学习不少的专有API,当输入/输出格式有变化时,需要修改代码(除非手工写一些wrapper),不够灵活。目前的版本是2.1.3。具体代码:
Formatter.java
<!---->
1
import
java.io.FileOutputStream;
2
3
import
com.lowagie.text.Document;
4
import
com.lowagie.text.Font;
5
import
com.lowagie.text.PageSize;
6
import
com.lowagie.text.Paragraph;
7
import
com.lowagie.text.pdf.BaseFont;
8
import
com.lowagie.text.pdf.PdfWriter;
9
10
public
class
Formatter {
11
12
public
static
void
main(String[] args)
throws
Exception {
13
Document document
=
new
Document(PageSize.A4);
14
try
{
15
System.out.print(
"
Generating PDF
"
);
16
PdfWriter.getInstance(document,
new
FileOutputStream(
"
test.pdf
"
));
17
document.open();
18
//
iText自带的中文字体
19
BaseFont bf1
=
BaseFont.createFont(
"
STSong-Light
"
,
"
UniGB-UCS2-H
"
, BaseFont.NOT_EMBEDDED);
20
//
自定义字体
21
BaseFont bf2
=
BaseFont.createFont(
"
wqy-zenhei.ttf
"
, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
22
Font font
=
new
Font(bf2,
12
, Font.NORMAL);
23
Paragraph p
=
new
Paragraph(
"
测试abc中文123
"
, font);
24
document.add(p);
25
System.out.println(
"
Done.
"
);
26
}
finally
{
27
document.close();
28
}
29
}
30
31
}
效果:
中文支持有默认的STSong-Light等字体,但为了优化输出效果,这里使用了文泉驿正黑字体。如果不指定中文字体,默认情况下中文字符不会显示。
[FOP] (
link)
FOP出自Apache,在各大Java网站、论坛出现相对较低,我也是从DocBook这条线摸进来的,DocBook主要提供了一个现成的、符合一般技术书籍要求的数据结构,而展现效果(如PDF),则是通过预定义好的XSL-FO来实现的。XSL-FO是W3C的标准,正式的名称是XSL,是XSL相关的三大组件/语言中的一个,另外两个是XSLT和XPath。Apache的FOP是处理FO的众多proecessor之一,相比iText,支持的输出格式更多,对W3C相关标准支持度高,格式定义可以完全脱离具体的Java代码,十分灵活,且控制力很强。缺点是大数据量时性能较差,默认中文支持不好。目前的版本是0.95。具体代码:
test.xml
<!---->
1
<?
xml version="1.0" encoding="UTF-8"
?>
2
<
source
>
3
<
title
>
4
FOP Sample
5
</
title
>
6
<
paragraph
>
7
测试abc中文123
8
</
paragraph
>
9
</
source
>
test.xsl
<!---->
1
<?
xml version="1.0" encoding="UTF-8"
?>
2
<
xsl:transform
version
="1.0"
3
xmlns:xsl
="http://www.w3.org/1999/XSL/Transform"
4
xmlns:fo
="http://www.w3.org/1999/XSL/Format"
>
5
6
<
xsl:template
match
="/"
>
7
<
fo:root
>
8
<
fo:layout-master-set
>
9
<
fo:simple-page-master
master-name
="A4-portrait"
10
page-height
="29.7cm"
page-width
="21.0cm"
margin
="2cm"
>
11
<
fo:region-body
/>
12
</
fo:simple-page-master
>
13
</
fo:layout-master-set
>
14
<
fo:page-sequence
master-reference
="A4-portrait"
>
15
<
fo:flow
flow-name
="xsl-region-body"
>
16
<
fo:block
font-family
="WenQuanYi Zen Hei"
font-size
="24pt"
>
17
<
xsl:value-of
select
="source/title"
/>
18
</
fo:block
>
19
<
fo:block
font-family
="WenQuanYi Zen Hei"
text-indent
="1cm"
>
20
<
xsl:value-of
select
="source/paragraph"
/>
21
</
fo:block
>
22
</
fo:flow
>
23
</
fo:page-sequence
>
24
</
fo:root
>
25
</
xsl:template
>
26
27
</
xsl:transform
>
fop-config.xml
<!---->
1
<?
xml version="1.0"
?>
2
<
fop
version
="1.0"
>
3
<
base
>
.
</
base
>
4
<
source-resolution
>
72
</
source-resolution
>
5
<
target-resolution
>
72
</
target-resolution
>
6
<
default-page-settings
height
="29.7cm"
width
="21.0cm"
/>
7
<
renderers
>
8
<
renderer
mime
="application/pdf"
>
9
<
filterList
>
10
<
value
>
flate
</
value
>
11
</
filterList
>
12
<
fonts
>
13
<
directory
>
.
</
directory
>
14
<
auto-detect
/>
15
</
fonts
>
16
</
renderer
>
17
</
renderers
>
18
</
fop
>
Formatter.java
<!---->
1
import
java.io.File;
2
import
java.io.FileOutputStream;
3
import
java.io.OutputStream;
4
5
import
javax.xml.transform.Result;
6
import
javax.xml.transform.Source;
7
import
javax.xml.transform.Transformer;
8
import
javax.xml.transform.TransformerFactory;
9
import
javax.xml.transform.sax.SAXResult;
10
import
javax.xml.transform.stream.StreamSource;
11
12
import
org.apache.fop.apps.FOUserAgent;
13
import
org.apache.fop.apps.Fop;
14
import
org.apache.fop.apps.FopFactory;
15
import
org.apache.fop.apps.MimeConstants;
16
17
public
class
Formatter {
18
19
public
static
void
main(String[] args)
throws
Exception {
20
File source
=
new
File(
"
test.xml
"
);
21
File specs
=
new
File(
"
test.xsl
"
);
22
File target
=
new
File(
"
test.pdf
"
);
23
FopFactory fopFactory
=
FopFactory.newInstance();
24
fopFactory.setUserConfig(
"
fop-config.xml
"
);
//
读取自定义配置
25
FOUserAgent foUserAgent
=
fopFactory.newFOUserAgent();
26
OutputStream out
=
new
FileOutputStream(target);
27
out
=
new
java.io.BufferedOutputStream(out);
28
try
{
29
System.out.print(
"
Generating PDF
"
);
30
Fop fop
=
fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
31
TransformerFactory factory
=
TransformerFactory.newInstance();
32
Transformer transformer
=
factory.newTransformer(
new
StreamSource(specs));
33
Source src
=
new
StreamSource(source);
34
Result res
=
new
SAXResult(fop.getDefaultHandler());
35
transformer.transform(src, res);
36
System.out.println(
"
Done.
"
);
37
}
finally
{
38
out.close();
39
}
40
}
41
42
}
效果:
FOP的中文支持(其实是自定义字体支持),在0.94版本之前,十分有限,对每一个需要使用的TrueType字体,都需要生成一个metrics文件,在0.94和之后的版本,则没有这个要求,且可以自动扫描系统字体和指定文件夹中的TTF字体。如果不配置中文字体,默认情况下,中文字符在PDF中将被处理成"#"。
上面的示例代码虽然简单,但展示了FOP真正强大的地方,那就是控制力。这里篇幅有限,不可能全部特性都一一涉及,这个简单的例子至少可以让我们看到从原始的XML格式的数据,通过XSLT按照自定义的规则转换成XSL-FO,最后输出到PDF的过程,每一步都可以在Java代码之外进行严格控制。
以上是我对iText和FOP一些基本特点和用法的整理,它们各有特点,大家可以根据各自需要继续深入研究,FOP和iText相结合也未尝不可。希望能够帮助到有需要的朋友。