前段时间做了一个项目需求,需要将网页中间显示的一块内容转成PDF文档供用户下载。完成这个需求的过程中使用到了itext和freemarker,中间遇到了不少问题,这里分享一下,希望可以帮助到遇到同样问题的朋友。
首先,先总结一下遇到过得一些问题吧。
1. PDF中中文不显示
2. 长英文字符串不换行
3. 添加的字体文件编译、打包之后损坏,字体文件变大,导致字体不可用
4. 将<XXX>识别为HTML标签,导致HTML显示不全,生成PDF失败
5. 关于freemarker
以上的话,就是目前能想到的一些问题,如果后续有想到的话再补充。在进行以上问题的解答之前,先说一下用到的jar包吧。本次供使用了两个jar包,一个是freemarker的jar包,用于数据渲染,生成HTML;另一个是html2pdf的jar包,用于将生成的HTML转成PDF文件。
以下是添加jar包的截图:
1. 关于中文不显示的问题
有两种解决办法,一种是添加支持中文显示的字体文件,另一种是添加中文的字体包itext-asian。由于使用字体包生成的PDF显示效果不是很好,所以本次方案中并没有采用这种方案,但是后面会在附录中对这种方案的使用进行一个演示,下面重点来说一下添加字体文件的方式。
重点看一下红色框框里的内容,是与字体设置相关的,fontDir就是我们存放字体的目录,我是将字体文件放在resources下面的fontDir文件夹下面的。
2. 长英文字符串不换行
先给大家看一下没解决之前的展示吧,如下图所示:
一开始百度搜索换行问题显示的时候,网上给出的都是关于HTML中的换行操作,如word-break: break-all; word-wrap: break-word。我也照着做了,但是,这也仅仅能解决生成的HTML页面显示中长英文字符串换行的问题,根本无法解决生成的PDF中换行的问题。没办法,只能自己琢磨看看是不是有什么可用的函数。然后,我注意到,在Document的类里面提供了一个函数似乎有控制换行的功能,也就是setSpiltCharacter();从函数名可以看到他时可以控制分割的字符。但是具体怎么使用呢?
这里教大家一招,也许在以后的开发工作中也许会使用到。首先,这个函数里面要传入的是一个ISpiltCharacters的接口或其实现类的对象。然后,我们可以看到,这个接口有两个实现类,如下:
首先,我们看一下在DefaultSplitCharacters实现类里面是怎么操作的,如下:
然后,再看一下NoSplitCharacters实现类里面是怎么操作的,如下:
使用这两个函数具体是什么效果我就不对比演示了。从函数的命名上我们大概也能明白其中的道理。所以为了使长英文字符串能够换行,我们必须实现isSplitCharacter()这个函数,允许换行的字符返回true。为了使英文字符、符号处换行,就设置Unicode编码小于128的返回true,下面是我实现的:
3. 添加的字体文件编译、打包后损坏,字体文件变大
这个主要是因为maven在编译文件的时候,会对文件进行过滤,会将${xxx}的值替换为配置文件中设置的值。为了使字体文件编译后的数据不被替换,需要将字体文件的过滤属性设置为false下:
但是,在这里设置只能保证编译之后的字体文件不被破坏,但是打包的话,字体文件还是变大了。在百度上也搜索到很多解决方案,但是语句位置不对,并没有起作用,这里也是浪费了很多时间。后来还是通过官方文档,才把问题解决,这里还是希望大家以后遇到问题的时候,还是要看一下官方文档,当然,百度也是有必要的,有助于理解。这里附上maven配置的官方文档http://maven.apache.org/plugins/maven-war-plugin/examples/adding-filtering-webresources.html.
废话不多说,为了使打包后的字体文件不被破坏,需要加入以下代码:
4. 将<XXX>识别为HTML标签,导致HTML显示不全,生成PDF失败
这个问题是这样的:字符串里有这样的一串字符“xxxxx Map<String,String> xxxxxxxxxxxxxxxx”;由ftl模板与数据融合生成HTML的时候,会把<String,String>当做HTML标签处理(其实主要是“<>”的原因);因此,必须将字符串中这些类似HTML的标签忽略掉,不做处理操作很简单,只要熟悉一下freemarker语法就可以了。操作很简单,
这个语句的意思就是,如果header.c_explain存在的话,就不处理其中的HTML标签。
5. 关于freemarker
这里的话不想介绍太多很多关于freemarker的语法,需要的话可以再去学学。这里需要说的是,在引入变量的时候,最好是对该值是否存在进行一下判断,避免由于变量不存在导致解析出错,程序崩了。如取值为null时,则会报异常 freemarker.core.InvalidReferenceException,解决办法可以是:
(1)${name!""},表示如果name为null时,就将该值设为空字符串
(2)${name?if_exists?html} 表示name为null时不输出,不为null时才输出