itext、flying saucer、html、PDF 、CSS、中文等相关问题

因为项目中有个业务需要导出PDF,大致的需求原型是:客户基本上每天都会手动生成一个PDF,即将资料在WORD里编辑好,弄好样式之类的,然后将WORD转成PDF。于是就有了需求“一键导出PDF”。心想,既然是带样式的PDF,那我把原型做成HTML,再看看有没有什么工具可以将HTML直接转换成PDF好了(因为已经有一个功能是导出HTML格式的源码,所以为了省事,故而有此想法)。

于是就找了找相关的资料,最开始是想在前端做(客户端是Flex做的),发现,Flex自带的生成PDF的API太弱,于是转向后端(Java端)。其实,谷歌一下,你就知道,Java下面用于生成PDF的框架不少,其中最有名的(大家都那么说)就是iText了,于是就试了试iText,发现,iText对于CSS的解析不是很好(很多样式都认不出来)故而继续寻求谷歌。继而flying saucer便映入眼帘了。网上介绍flying saucer的文章不少,对它的夸奖都是称赞其对HTML以及CSS的良好支持。毫无疑问,flying saucer投入我的怀抱了。值得一说的是,flying saucer是站在iText的肩膀上的。即:flying saucer是对iText的扩展,弥补了后者在CSS解析方面的不足。

好了,废话不多说,直接说说我在使用flying saucer将HTML转成PDF所遇到的各种问题吧。
第一:中文显示问题以及图片相对路径。这两个问题网上很多文章都有提过到。我的现象的是,中文不显示,即空白。只能显示英文、数字、符号等,没有乱码。我的解决办法是,添加中文字体,并在CSS中给body指定中文字体。
代码如下:
[code lang=”java”]
// 解决中文支持问题
// String path = ClassLoader.getSystemResource("simsun.ttc").getPath();
//之所以不用这个方法,是因为,该方法在linux下不好用,取不到simsun.ttc,可能的原因就是路径问题了。
String path = this.getClass().getResource("/simsun.ttf").getPath();
ITextFontResolver fontResolver = renderer.getFontResolver();
fontResolver.addFont(path, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
//解决图片的相对路径问题
path = path.substring(0, path.lastIndexOf("/"));
renderer.getSharedContext().setBaseURL("file:" + path + "/img");//HTML中,图片的相对路径形似“img/XXX.png”[/code]

第二:中文不换行。
对于该问题,网上普遍流行的文章说的都是替换flying saucer的一个JAR包(老实说,我不是太赞同修改源码替换JAR包的)。虽然说这个框架是老外做的,对中文支持不是很好。但是,我就想,能不能通过CSS来控制让其换行呢。于是我的解决办法:

[css]p{word-wrap:break-word;}[/css]
加上上面的这个样式就好用了,也可能是版本的问题,可能网上那些文章作者是使用的时候,flying saucer的版本还比较旧,用此办法行不通吧。我所使用的版本是9.0.4

第三:发布在linux服务器上后运行时发生的问题。
在Window下开发远程测试没问题后,我决定把应用发布到Linux上跑一跑,毕竟,客户那边服务器就是在Linux下跑的。
好吧,这不跑不要紧,一跑就出问题了。首先,就是问题一中的代码注释所写路径问题。至于具体原因我还没有来得及详究。即:ClassLoader.getSystemResource()与Class.getResource()的区别。修改好路径后,问题再次出现:

[code]“com.itextpdf.text.DocumentException: Table ‘name’ does not exist”[/code]
二话不说,直接谷歌一下:以下是某老外原话:
“Found the problem. Maven was processing my font files and messing them up so iText couldn’t process them.

I excluded them from being process in the pom using

[code]

<build>

&lt;resources&gt;

    &lt;resource&gt;

        &lt;directory&gt;src/main/staticresources&lt;/directory&gt;

        &lt;filtering&gt;false&lt;/filtering&gt;

    &lt;/resource&gt;

</resources>

</build>

[/code]

And now it works.”
原来如此,是Maven搞的鬼。于是修改项目的pom文件如下:
[code]
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>/*.xml</include>
<include>
/.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>**/
.xml</exclude>
<exclude>*/.properties</exclude>
</excludes>
</resource>
</resources>
[/code]
即,原来的配置文件(.xml和.properties)照常过滤,而其他的都不过滤。至于为什么要写两个一样目录的resource,Maven官网有关Filtering的解释:
Warning: Do not filter files with binary content like images! This will most likely result in corrupt output. If you have both text files and binary files as resources, you need to declare two mutually exclusive resource sets. The first resource set defines the files to be filtered and the other resource set defines the files to copy unaltered as illustrated below:

[code]<project>

<build>

...

&lt;resources&gt;

  &lt;resource&gt;

    &lt;directory&gt;src/main/resources&lt;/directory&gt;

    &lt;filtering&gt;true&lt;/filtering&gt;

    &lt;includes&gt;

      &lt;include&gt;**/*.xml&lt;/include&gt;

    &lt;/includes&gt;

  &lt;/resource&gt;

  &lt;resource&gt;

    &lt;directory&gt;src/main/resources&lt;/directory&gt;

    &lt;filtering&gt;false&lt;/filtering&gt;

    &lt;excludes&gt;

      &lt;exclude&gt;**/*.xml&lt;/exclude&gt;

    &lt;/excludes&gt;

  &lt;/resource&gt;

  ...

&lt;/resources&gt;

...

</build>

</project>[/code]