最近的工作中有一个需求,需要处理word文档,有一些内容需要根据不同用户进行替换修改,使用的是word文档,替换后的内容还需要转换为pdf进行签章确认,并进行防篡改处理。
所以记录一下处理步骤,首先可以从百度文库上下载一份用于测试使用的询证函
比如这篇文章,我已经下载下来了,然后打开文档,对需要进行替换的部分使用变量占位符处理
然后将word另存为xml格式
另存的时候,最好把文件名改为英文名,防止freemark读取模板文件路径时,中文路径乱码问题
然后再将xml后缀改为ftl格式,因为freemarker模板文件的格式是ftl格式,并将模板文件放在本地目录下:
可以看到改了后缀为ftl,打开文件仍然是xml格式
接下来创建实体类,实体类属性名要与刚才在word文档中设置的占位符变量名相同,用于替换占位符为具体的数据,至此,准备工作基本上已经完成
接下来开始处理模板文件:
所需要的jar包依赖
com.aspose aspose-words15.8.0 org.freemarker freemarker2.3.30 org.projectlombok lombok1.18.4 org.apache.commons commons-lang33。3.9 cn.hutool hutool-all5.7.10
创建实体类:
import lombok.Data; @Data public class AssetsReconciliation { private String enterpriseName; private String agencyName; private String address; private String amount; private String postcode; private String phone; private String fax; private String linkman; private String year; private String month; private String day; private String date; private String assets; private String liabilities; private String content; private String sign; private String operator; }
创建处理工具类:
import cn.hutool.core.util.CharsetUtil; import cn.hutool.setting.dialect.Props; import cn.hutool.system.OsInfo; import com.aspose.cells.License; import com.aspose.words.document; import com.aspose.words.FontSettings; import com.aspose.words.SaveFormat; import com.xiaomifeng1010.tmc.newenergy.word.bo.AssetsReconciliation; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateExceptionHandler; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.io.*; import java.nio.charset.StandardCharsets; @Slf4j public class ConvertWordToPDFUtil { private static Configuration cfg; private static Props props=new Props("pdf.properties"); private static String templateFileDirPath=props.getProperty("template.path"); private static String docFilePath=props.getProperty("word.path"); private static boolean initSuccess = true; static{ // 初始化freemarker配置 try { cfg=new Configuration(Configuration.VERSION_2_3_22); // 加载模板所在目录(从pdf.properties文件中可以看出,模板文件是在本地存放的) // 当然也可以把ftl模板文件放在resources目录下,这样的话,就需要获取classpath下的目录 cfg.setDirectoryForTemplateLoading(new File(templateFileDirPath)); cfg.setDefaultEncoding(CharsetUtil.UTF_8); cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); } catch (IOException e) { log.error("freemarker初始化配置出错",e); initSuccess=false; } } public static boolean getLicense() { boolean result = false; try { InputStream is = ConvertWordToPDFUtil.class.getClassLoader().getResourceAsStream("\license.xml"); License aposeLic = new License(); aposeLic.setLicense(is); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } public static String handleWordTemplate(AssetsReconciliation assetsReconciliation){ String docpath=docFilePath+System.currentTimeMillis()+".docx"; try (Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docpath), StandardCharsets.UTF_8))) { String fileName="template_test.ftl"; // 获取模板 Template template = cfg.getTemplate(fileName, CharsetUtil.UTF_8); // 往模板中填充内容,并保存为docx文件 template.process(assetsReconciliation,out); } catch (Exception e) { log.error("模板填充内容失败!", e); return StringUtils.EMPTY; } return docpath; } public static Boolean word2pdf(String inputPath, String outPath) { OsInfo osInfo = new OsInfo(); // linux平台下需要安装字体样式,从window下拷贝过去安装即可 if (osInfo.isLinux()) { FontSettings.setFontsFolder(File.separator + "usr" + File.separator + "share" + File.separator + "fonts", true); } else if (osInfo.isMac()) { FontSettings.setFontsFolder("/Library/Fonts/Microsoft", true); } try (FileOutputStream os = new FileOutputStream(outPath)) { if (getLicense()) { long start = System.currentTimeMillis(); document doc = new document(inputPath); doc.save(os, SaveFormat.PDF); long end = System.currentTimeMillis(); log.info("转换成功, 花费 " + (end - start) / 1000.0 + " seconds!"); return true; } return false; } catch (Exception e) { log.error("转换报错!", e); return false; } } }
pdf.properties放在项目的resources目录下:
以及license.xml文件
Aspose.Total for Java Aspose.Words for Java Enterprise 20991231 20991231 8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7 sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=
然后写一个controller类请求方法测试一下
import cn.hutool.core.date.DateUtil; import com.xiaomifeng1010.tmc.newenergy.word.bo.AssetsReconciliation; import com.xiaomifeng1010.tmc.newenergy.word.util.ConvertWordToPDFUtil; import com.github.xiaoymin.knife4j.annotations.ApiSort; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Api(value = "处理word文档",tags = {"处理word文档"}) @ApiSort(26) @RestController @RequestMapping("/word") public class WordTestController { @ApiOperation("测试填充word模板并转换为pdf") @GetMapping("/convertTest") public String testConvertExcelToPDF(){ AssetsReconciliation assetsReconciliation = new AssetsReconciliation(); assetsReconciliation.setAddress("广东省广州市白云区人和镇999路233号"); assetsReconciliation.setAmount("¥2,456,987.05"); assetsReconciliation.setAssets("¥1,345,675.45"); assetsReconciliation.setAgencyName("非凡科技有限公司"); assetsReconciliation.setContent("无异议"); assetsReconciliation.setDate(DateUtil.now()); assetsReconciliation.setEnterpriseName("天涯科技有限公司"); assetsReconciliation.setFax("010-3458-450"); assetsReconciliation.setLiabilities("¥45,769.56"); assetsReconciliation.setYear("2021"); assetsReconciliation.setMonth("11"); assetsReconciliation.setDay("27"); assetsReconciliation.setlinkman("小明"); assetsReconciliation.setOperator("ablert"); assetsReconciliation.setPhone("13576890543"); assetsReconciliation.setPostcode("443900"); assetsReconciliation.setSign("假装有印章"); String docPath = ConvertWordToPDFUtil.handleWordTemplate(assetsReconciliation); if (StringUtils.isNotEmpty(docPath)) { String pdfFileName="C:\Users\MSI\Desktop\template\"+ RandomStringUtils.randomAlphanumeric(6) + ".pdf"; ConvertWordToPDFUtil.word2pdf(docPath,pdfFileName); return "填充word模板成功,并转为pdf成功"; } return "处理失败!"; } }
然后再knife4j接口文档界面发起请求
填充内容后的word文档:
注意:使用freemarker处理之后的word文档用microsoft word是打不开的,不知道为什么,但是可以用金山WPS 文字软件打开,效果如上
然后再看一下转换为pdf后的效果:
样式和word文档样式是一样的,这就是为什么不建议使用apache poi去处理word文档,再用itext生成pdf的原因。poi可以读取到word文档的内容,但是读取出来后,会读成为字符串了,样式都丢失了,再用itext去保存为pdf的时候,因为样式丢失,整体布局是错乱的。
注意事项:我在用freemark处理word转换之后的ftl模板时遇到了一些麻烦问题,现在也总结一下
doc格式的文档,在一些需要填充数据的地方,写上了EL表达式${变量名},但是在另存为xml格式的时候,一些EL表达式变量,会被拆分开,这时候,freemarker在解析模板的时候就会报错:
Encountered "<", but was expecting one of:"false" "true" "." "+" "-" "!" "[" "(" "{"
此时需要打开保存的xml文件
可以看到原本设置的${enterpriseName}被拆分开成了好几部分
而正常的应该是这样的
应该是整体在一起的,所以呢需要进行手动修改xml文件,是正哥EL表达式是整体在一起的
这还没完,注意下边还有一个
所以,列信息那几个字也得处理一下,把列信息三个字移到下边,然后删除这个标签
下边的agencyName出错与enterpriseName出错一致,处理一样
这样的话,语法问题就解决了,freemark就可以正常解析EL表达式了。还有一个问题就是在创建实体类的时候,最开始的时候,实体类创建的时候少了一个属性amount,但是模板文件中是有这个EL表达式的
但是创建实体类时候,没加上这个属性,填充模板内容的时候也报错了
Caused by: freemarker.core.InvalidReferenceException: The following has evaluated to null or missing: ==> amount in
所以在测试时候,设置AssetsReconciliation时候,就没有设置amount的值,在填充模板时候,${amount}就为null,就报上边这个异常,所以后来在实体类上加上了这个属性,并赋值,就正常了
或者修改模板文件,添加判空的判断,给个默认值
在ftl文件中修改 ${(amount)!' '} 或者${amount!" "} 如果为空,就以默认值(“!”后的字符)显示;
还有就是如果同时使用apose-words处理word,有用apose-cells处理excel的话,需要同时分开用两个工具类或配置文件去分别获取对应的license,不能共用一个工具类去获取license,不然总会有一个工具处理后带水印(处理word转pdf之后,或者excel转pdf后带水印)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)