最近在使用freemaker做一个word模板,里面包含大量表格、截图、超链接等数据、历时一周多,遇到很多坑,现在想想都后怕,现在简单总结一下,希望给以后的小伙伴提供帮助,少走弯路!
坑一:word打不开
可能原因:
1、往xml文件中添加数据占位时,不细心导致文件中出现多余的{、}、;、#等字符,导致xml校验错误,会导致生成的word打不开。
2、word中有超链接,链接中包含多个参数时,会用&进行连接,然而在xml中&属于特殊字符,如若不处理,导致xml校验错误,会导致生成的word打不开;处理方式有两种如下
第一种:使用CDATA包含此链接,使得xml不解析,比如
第二种:将特殊字符转义,&对应的转义为& 其他特殊字符转义可自行去网上查询,转义后的链接就变成www.test.com?param=1&num=2,这种xml是可以解析的。
3、xml校验没有问题(xml校验器https://www.runoob.com/xml/xml-validator.html),此时生成的docx文档,使用wps是可以打开的,但是使用office确打不开,解决方案是直接生成doc格式的文档,这样wps跟office都能打开了,具体原因不详,可能跟freemarker对docx跟doc支持不一样。
坑二:批量添加图片
由于第一次往word文档中加图片,而且是动态批量添加,没有头绪,网上查询了资料也都没有太好的方法,后面想着直接动态循环生成rId,没成想直接成功了,非常开心,但也产生一个新问题,就是图片变形了(下面在详细说),实现方式如下:
往word中加图片,首先在模板中需要插入一个图片进行占位,转换成xml后,关于图片的地方有三点,我自己理解的,描述的可能不太对,见谅!
1、图片的源定义
2、图片的base64码,用变量替换
${img_gqctt!"--"}
3、图片展示
1跟2是通过image99.jpeg关联,1跟3是通过rId99关联
具体实现方式:
<#list img_glrcyry as glrcyry>
#list>
为什么要+200,是为了防止id重复,我这文档中包含大量的图片
<#list img_glrcyry as glrcyry>
${glrcyry.img_glrcyry}
#list>
<#list img_glrcyry as glrcyry>
#list>
这样就能将图片批量动态的添加到word模板中了。
坑三:图片变形问题
因为事先用了一个图片进行占位,所有在模板中,图片的大小固定,如果新插入的图片跟占位大小不一样,就会导致图片变形,压缩或者拉伸。下面看一下图片位置里面代码有两个核心的地方:
第一个是图片在word中的区域,长跟宽的大小,第二个是图片本身的通过像素值转换的长跟宽(我自己理解的,可能描述的不太对),网上有说图片像素值转换成这个大小的计算逻辑,是 像素值*914400/100,然后去替换长跟宽,后面我经过反复试验验证,发现不是很准确,这样会导致图片变形。
我的思路:图片变形无非就是长宽比例不一致导致,所以我们根据要插入的图片的宽跟高,计算宽高比,来改变模板里这个宽高值,保证图片在模板里跟原图是同样的宽高比,这样图片就不会变形。
因为一个图片在word中只能占一页,而实际上有的图片过长,有的图片过宽,所以我的思路是,如果图片过长,就固定长度,压缩宽度,如果图片过宽,就固定宽度,压缩长度。我自己在word中用一个图片拉满,占满一页,宽度大概是5275000,长度大概是8780000,我就以这个为基准,以图片时间宽高计算要在模板中展示的实际宽高,部分代码如下:
BufferedImage bufferedImage = ImgBase64Util.base64String2BufferedImage(imgData);
double wordProportion = (double) 5275000/8780000;
double realProportion = (double) bufferedImage.getWidth()/bufferedImage.getHeight();
// 过宽,以宽度为基准
if(realProportion >= wordProportion){
root.put("img_gqctt_width", "5275000");
long realHeight = 5275000L * bufferedImage.getHeight() / bufferedImage.getWidth();
root.put("img_gqctt_height", String.valueOf(realHeight));
}else{
root.put("img_gqctt_height", "8780000");
long realWidth = 8780000L * bufferedImage.getWidth() / bufferedImage.getHeight();
root.put("img_gqctt_width", String.valueOf(realWidth));
}
这样到时候直接替换模板中的宽高,如果是批量添加图片,需要计算出每个图片的在模板中的实际宽高
至此就能保证图片在word中不变形了!
一周多的时间,中间遇到各种问题,曾一度要奔溃,好在后来慢慢问题全部解决,收获颇丰!继续加油!共勉!
写在最后:freemarker对高版本的docx支持不是很好,建议模板生成xml时,选择word 2003 XML文档,不建议选择word XML文档,最后生成word文档时,直接生成doc格式的,能避免很多坑!