itext,java,使用html2pdf的一些注意,以及多字体匹配的坑

【首先强烈推荐大家去看itext官方文档,里边有很多问题的回答   https://kb.itextpdf.com/itext/installation-guidelines】

一、前提

1,做一个能动态改变样式的pdf,并且将文本内容填充进去,那么使用PdfRender就做不到了,e签宝的模板接口也做不到动态改变字体的颜色等。百度查到可以使用itext的html2pdf,可是却没想到在使用过程中有那么多坑,而且很多教程都不贴html,所说html规范严格也没说到底咋严格,最终还是跟源码也解决问题。

2,最终需要的pdf样式如下:

一些信息我马赛克了,可以看到标题是优设标题黑,正文是微软雅黑,部分字体要根据所填内容不同变换颜色

二、代码

代码在这里:https://www.cnblogs.com/hsql/p/17980241

用到的字体放在百度网盘了:

链接:https://pan.baidu.com/s/1O7sqPnWOHTd1ftruB-pYnQ
提取码:azxq

 此文档参考的官方文档为:itext官网文档   官方文档2

三、一些注意事项

1,加载html模板的方式

① 加载本地模板(不是放在程序resources下,而是服务器的一个位置)

 ② 加载url模板

2,html中的css格式不生效

①首先html的格式一定要标准,标签结尾一定要有,比如<div>开头,必须有</div>

②检查引入的itext是否是最新版本,我一开始引入的是5.0.2,结果css的渐变背景(linear-gradient)标签就没有生效,查看maven仓库发现最新的到8.0.2了,html2pdf升到5.0.2,freeMaker到2.3.31 

③建议使用Visual Studio Code格式化一下html,方便检查格式

3,html填充内容的velocity语法

 如果用过idea的easyCode插件那一定对velocity不陌生,关于velocity的语法可以参考以下文档:Java中Velocity ,freeMaker使用的稍微有点区别:模板语言参考 - FreeMarker 中文官方参考手册 (foofun.cn)

① 一般填充使用 ${}的方式,如

② if else

一般在easyCode模板中是这样使用的

 但在这里的html中,应该这样使用

 例如:

 或者

 其余用法请参考上面文档

4,字体不生效 font-family的问题

首先不用系统自带的字体,会浪费资源

也不使用网上下载的乱七八糟的漂亮字体(有些会乱码),所以这里我将使用标准字体,系统字体都设置为false(注意,即使三个都是false,但是在pdf搜索不到可用字体时,也会使用内置的14种字体比如Times)

官网对使用字体的解释:第 6 章:在 pdfHTML 中使用字体

有两种方式加载字体, 第一种方式,在html中指定@font-face 从网络下载字体,第二种方式,在后台指定字体,两种方式都需要在html样式中指定“font-family”(注:font-family后一定要加单引号);

① html中指定font-face,如下图

 此时后台就可以不加字体或加一个默认的字体。(注意,此时两条线的两端连接处设置的名称一定要一致,下面会说匹配字体的事儿,这里设置 @font-face中的font-family的名字等于给此字体设置了一个别名alias)

(我在测试时发现使用这个html生成的pdf有些文字字体不对,没有使用指定的,我一直以为是font-family没有生效,跟源码才发现是这个woff不支持某些字符,所以一定要先验证这字体好不好用啊,真坑,下面有验证方法。)

② 后台指定字体

 由于使用前端下载字体的方式特别慢,所以我选择在后台指定,html中去掉@font-face, 只写font-family与后台加载的字体对应,比如

 那么html中的font-family和后台加载的字体是如何对应的呢?  由于之前生成的字体都是混乱的,网上也找不到答案,贴点儿边的就说ttf的名字要是英文的,最后无可奈何跟了一下源码,才发现在layout包的字体选择器fontSelector中,是这样判断的

 所以与ttf是否是英文也没关系,html中的font-family要与后台加载ttf的 familyNameLowerCase 相同,html中也不用写@font-face了,那么如何知道ttf的familyNameLowerCase呢?如下:

或者这样创建字体(设置一个别名,要与html中的font-family对应):

匹配familyNameLowerCase的过程其实是将所有的字体排序的过程,将匹配到的放在第一位,接下来还会继续循环排好序的字体集合逐字校验是否支持,如下:

 所以这里要保证,加载的字体一定要支持设置了font-family标签的文字

如何验证是否支持某字呢?使用fontTools如下:

 校验字体是否支持某个字

 可以看到截图中的woff文件,支持“姓”,却不支持“长”。

总结:如果html中没有指定font-family,那么最终会使用后台addFont的第一个字体;如果指定了font-family,但是名称与后台字体的familyNameLowerCase或alias不同,那么最终会使用后台addFont的第一个字体(或者使用系统自带的默认字体或乱码);

但是要注意,后台加载的ttf越多,生成pdf的时间越慢,所以最好不要加载系统字体,即设置:

FontProvider fontProvider = new DefaultFontProvider(false, false, false)

由于fontSelector会将font-family跟每一个ttf作对比,所以如果想更快的生成pdf,应调整ttf的加载顺序,将pdf中使用次数较多的字体放在第一个,这样能确保第一次循环就匹配到;

出现字体混乱,第一时间验证字体文件是否支持。

 5,生成的pdf出现偏移、缺失、分页

一开始设置的pdf纸张大小是A4,与html中定义的大小不同,那就会出现一部分被截断,或分页的情况(我的需求要不分页),所以要根据html的大小进行调试,直到合适

6,生成的pdf文件太大

有可能是嵌入字体的问题,由于我这里使用的是自定义字体(中文),并且pdf内部会嵌入字体否则在其他电脑可能看不到,对于这个问题官方解释是这样的:为什么即使我指定不嵌入,iText 也会嵌入字体?

由于中文字符一般使用Identity-H的编码,所以这时如果设置为 FORCE_NOT_EMBEDDED 会报错:Cannot create Type0 font with true type font program without embedding it.

不设置 EmbeddingStrategy 的话,默认是  PREFER_EMBEDDED (趋向于嵌入),所以我这里设置为 PREFER_NOT_EMBEDDED

生成的pdf大小会小很多,我猜测(趋向于不嵌入)仅仅嵌入了使用字体的子字符集(即pdf中的文字),搜索了官网回答,对于嵌入字体的解释,官方回答如下: 如何仅部分嵌入字体?

查看文档属性也确实如此

也就是说,使用了Identity-H,就永远不会嵌入完整字体,但是我测试的使用PREFER_NOT_EMBEDDED和PREFER_EMBEDDED生成的文件大小相同,但是PREFER_EMBEDDED生成的时间却慢很多,这里可以判定使用PREFER_NOT_EMBEDDED最佳。

 然后我又在网上找了另一种微软雅黑的字体,测试发现用这个字体生成的pdf大小比原来的小一倍,当使用思源宋体的时候pdf更小,所以在不嵌入整个字体的情况下,与引用的三方字体也有关系。

另外:在css中设置加粗、倾斜是不生效的,如果想使用加粗字体,应该使用单独的加粗ttf文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值