Read Sean

Read me, read Sean.
posts - 508, comments - 655, trackbacks - 9, articles - 4

iText vs FOP - Java动态生成PDF的两个选择

Posted on 2008-10-05 23:28 laogao 阅读(13717) 评论(11)  编辑  收藏 所属分类: On Java

由于工作需要,今天简单的看了一下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 }

效果:
itext.png

中文支持有默认的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.png

FOP的中文支持(其实是自定义字体支持),在0.94版本之前,十分有限,对每一个需要使用的TrueType字体,都需要生成一个metrics文件,在0.94和之后的版本,则没有这个要求,且可以自动扫描系统字体和指定文件夹中的TTF字体。如果不配置中文字体,默认情况下,中文字符在PDF中将被处理成"#"。

上面的示例代码虽然简单,但展示了FOP真正强大的地方,那就是控制力。这里篇幅有限,不可能全部特性都一一涉及,这个简单的例子至少可以让我们看到从原始的XML格式的数据,通过XSLT按照自定义的规则转换成XSL-FO,最后输出到PDF的过程,每一步都可以在Java代码之外进行严格控制。

以上是我对iText和FOP一些基本特点和用法的整理,它们各有特点,大家可以根据各自需要继续深入研究,FOP和iText相结合也未尝不可。希望能够帮助到有需要的朋友。

Feedback

# re: iText vs FOP - Java动态生成PDF的两个选择  回复  更多评论   

2008-10-06 08:43 by 123
FOP没有使用过,上个周就用iText做了一个PDF报表,还不错,很强大。
不过,郁闷的是,对网页转成PDF格式,都不怎么样。
估计是网页格式里用了层的原因吧。

# re: iText vs FOP - Java动态生成PDF的两个选择[未登录]  回复  更多评论   

2008-10-06 23:14 by shenguanghua
itext我觉得最难的是排版,希望大哥能重点讲些排版的例子

# re: iText vs FOP - Java动态生成PDF的两个选择  回复  更多评论   

2008-10-07 14:14 by innate
IText在Html支持上,表格的宽度控制有bug,我用的是2.1.2不知道更新的有没有解决

# re: iText vs FOP - Java动态生成PDF的两个选择  回复  更多评论   

2008-10-07 14:16 by innate
IText中文支持也不是很好,曾经在中文问题上郁闷了很长时间。
我用IText一般都是结合他的Html进行的,排版上问题不大,但是有些地方还是不好看。

# re: iText vs FOP - Java动态生成PDF的两个选择  回复  更多评论   

2008-10-08 00:50 by 大胃
iText对中文的支持至少一方面自带了中文字体,另外也可以通过normal体计算出粗体和斜体,不像FOP完全依赖字体文件。

# re: iText vs FOP - Java动态生成PDF的两个选择  回复  更多评论   

2008-11-04 11:15 by ALGO
很多java的PDF都是以iText为底层的,iText的文档中也提到,他们不打算做一个完整的html2pdf converter,并推荐ICE Browser SDK和另外一个什么来着,ice我试过,效果相对好很多,对普通html就能支持,不过可惜这个库是要卖钱的。

# re: iText vs FOP - Java动态生成PDF的两个选择  回复  更多评论   

2008-11-04 12:05 by 大胃
从对Java友好这个角度,iText确实不错,但正因为这个特点,脱离了手写的Java代码,操作起来也就不是那么方便了。不像FOP,虽然不是100%标准实现,但思路还是清晰的,基本按照XSL-FO定义就能够做出比较漂亮的排版,不需要写任何Java代码。文中的例子只是示意,其实FOP通过命令行就能玩转,就算通过Java代码去调用,这段Java也是写一次就好,不管你格式多复杂。

对于项目预算有限,或者由于其他原因不能或不希望采用商业解决方案的情况,iText和FOP都是不错的选择,看具体项目/工程需要吧。对于纯输出,以我实际使用看,FOP效果已经很好了,大不了多写点XML,多画点<fo:table/>,死不了。

# re: iText vs FOP - Java动态生成PDF的两个选择  回复  更多评论   

2008-11-04 13:35 by ALGO
我正有一个项目需要做这种html2pdf的转换,咨询过ICE Browser, 但是他们说2010年后不再提供支持,看来生意做得也不好。此外有什么好的推荐吗?

# re: iText vs FOP - Java动态生成PDF的两个选择  回复  更多评论   

2008-11-04 14:44 by 大胃
现在很多商用软件受到同类型开源软件的冲击都比较大,有些被迫开源,有些则慢慢淡出视线。

曾经看过AntennaHouse的FO实现,好像还不错,你可以看看:
http://www.antennahouse.com/

其实Apache的FOP已经很好了,主要是花时间熟悉FO语法。

# re: iText vs FOP - Java动态生成PDF的两个选择  回复  更多评论   

2015-04-14 16:23 by fop
代码拷贝运行报错啊

# re: iText vs FOP - Java动态生成PDF的两个选择  回复  更多评论   

2015-04-14 16:25 by fop
用0.95报错的大概意思是还不支持。。。。。
本地字库的字体embet不进去。。。。

只有注册用户登录后才能发表评论。


网站导航: