最近的工作中有一个需求,需要处理word文档,有一些内容需要根据不同用户进行替换修改,使用的是word文档,替换后的内容还需要转换为pdf进行签章确认,并进行防篡改处理。

所以记录一下处理步骤,首先可以从百度文库上下载一份用于测试使用的询证函

比如这篇文章,我已经下载下来了,然后打开文档,对需要进行替换的部分使用变量占位符处理

然后将word另存为xml格式

另存的时候,最好把文件名改为英文名,防止freemark读取模板文件路径时,中文路径乱码问题

然后再将xml后缀改为ftl格式,因为freemarker模板文件的格式是ftl格式,并将模板文件放在本地目录下:

  可以看到改了后缀为ftl,打开文件仍然是xml格式

接下来创建实体类,实体类属性名要与刚才在word文档中设置的占位符变量名相同,用于替换占位符为具体的数据,至此,准备工作基本上已经完成

接下来开始处理模板文件:

所需要的jar包依赖

   <dependency><groupId>com.aspose</groupId><artifactId>aspose-words</artifactId><version>15.8.0</version></dependency><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.30</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.4</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3。3.9</version></dependency>  <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.10</version></dependency>

创建实体类:

import lombok.Data;/*** @author xiaomifeng1010* @version 1.0* @date: 2021/11/27 11:56* @Description*/
@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;/*** @author xiaomifeng1010* @version 1.0* @date: 2021/11/27 10:20* @Description*/
@Slf4j
public class ConvertWordToPDFUtil {private static Configuration cfg;/*** word模板文件所在目录路径*/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;}}/*** 获取license 去除水印** @return*/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;}/***使用freemarker处理word模板* @return*/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;}/*** word 转 pdf** @param inputPath word文件path* @param outPath   pdf文件path* @return*/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文件

<?xml version="1.0" encoding="UTF-8" ?>
<License><Data><Products><Product>Aspose.Total for Java</Product><Product>Aspose.Words for Java</Product></Products><EditionType>Enterprise</EditionType><SubscriptionExpiry>20991231</SubscriptionExpiry><LicenseExpiry>20991231</LicenseExpiry><SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber></Data><Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature>
</License>

然后写一个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;/*** @author xiaomifeng1010* @version 1.0* @date: 2021/11/27 12:53* @Description*/
@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:
<STRING_LITERAL>
<RAW_STRING>
"false"
"true"
<INTEGER>
<DECIMAL>
"."
"+"
"-"
"!"
"["
"("
"{"
<ID>

此时需要打开保存的xml文件

可以看到原本设置的${enterpriseName}被拆分开成了好几部分

而正常的应该是这样的

应该是整体在一起的,所以呢需要进行手动修改xml文件,是正哥EL表达式是整体在一起的

这还没完,注意下边还有一个 <w:proofErr w:type="gramStart"/>标签,包括刚才删除的那一部分,也有<w:proofErr w:type="spellStart"/>注意都有一个proofErr的属性,说明这个标签是提示错误的,这部分里边的内容有错误

所以,列信息那几个字也得处理一下,把列信息三个字移到下边,然后删除这个标签

下边的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后带水印)

Aspose-words结合Freemarker实现word邮件合并功能,批量处理word模板文件相关推荐

  1. C# 实现多种Word邮件合并功能

    C# 实现多种Word邮件合并功能 在处理Word文档的工作中经常会遇见这样一种情况:文件的主要内容和格式大体相同,只是需要填充的数据不同.如果一个一个填写数据的话会非常麻烦,而且很耗时间.这时候我们 ...

  2. java实现word邮件合并,Aspose.Words控件操作实例—邮件合并功能概述

    本文中会介绍Aspose.Words的邮件合并的功能,邮件合并是时常需要使用的功能,比如在学校教务系统中,需要将学生证数据信息导出成Word文文件,如果以系统已写好的格式输出,会缺乏灵活性,但若使用A ...

  3. Word邮件合并功能详解:合并后生成多个word文档,删除空白页

    Word邮件合并功能详解:合并后生成多个word文档,删除空白页 最近在实习,干了很多打杂得工作,所以office软件用的很多很多,瞬间觉得自己可以去裸考计算机二级了哈哈哈哈哈哈.今天因为工作用到了邮 ...

  4. python 批量替换一对多sheet邮件合并_:巧用Word的邮件合并功能批量处理文件_读览天下...

    Word中的邮件合并功能特别适合处理大批量的文件.在秘书工作中,诸如制作信封.席卡等格式相同的大批量文件,均可用邮件合并功能实现,以减少工作量.本文试以制作3份请柬为例,讲解Word2007中该功能的 ...

  5. word邮件合并功能的用法

    为您找到了一些邮件合并的实例: 一个实例是使用邮件合并来制作和发送电子邮件邀请函.您需要准备一个Excel表格,存储收件人的姓名和邮箱地址等信息,然后在Word中创建一个邀请函模板,插入合并域,连接数 ...

  6. Word 邮件合并功能(Excel表中指定列数据插入到Word模板指定位置,批量生成Word)

    [邮件]选项卡[开始邮件合并]下拉按钮,选择[邮件合并分布向导] 在"邮件合并"任务窗格中的"选择文档类型"中选择"信函",单击" ...

  7. word删除分节符后之前的格式乱了_办公室高级技能之Word邮件合并拆分

    当我们需要批量生成名片,合同,成绩单等有相同内容的文件时,使用word邮件合并功能非常方便.如果你还需要把word文件发给不同的人就需要再拆分一下,用复制粘贴的方法拆分word,数量一多不仅效率低,还 ...

  8. WORD中“邮件合并”功能和应用

    应用 Word邮件合并功能在制作信函.信封或者是准考证.成绩通知单.毕业证.工资条.问卷等方面有着丰富的应用. 应用领域:批量打印信封,信件,请柬,工资条,个人简历,成绩单,获奖证书,明信片等等,由电 ...

  9. python邮件合并的基本操作步骤_Python如何实现反向邮件合并功能

    摘要:邮件合并是Office的一项功能,可便捷地批量生成文档,却无法从大量Word文档中汇总信息.结合实际应用,阐述基于Python语言对Word文档进行批量导入.读取其中的表格与段落.汇总信息至Ex ...

最新文章

  1. linux php 编译 pdo,Linux 下 PHP 扩展 PDO 编译安装
  2. JS Date格式化为yyyy-MM-dd类字符串
  3. 谷歌要求华为不启用鸿蒙,谷歌:华为我不让你用我的服务!华为:我还是照样用!...
  4. 也谈创业企业CEO该拿多少工资
  5. ubuntu14.04(server amd64)免密码sudo
  6. 在ASP.NET的复合组件中实现冒泡处理机制
  7. 服务器操作系统使用相关要求,服务器操作系统使用相关要求
  8. 播放器:七牛播放器小记
  9. 矩阵的Cholesky 分解
  10. skiplist及Java实现
  11. 淘宝联盟API对接过程记录(1)
  12. aws 亚马逊_Amazon AWS Rekognition教程
  13. linux 查看内网IP和外网IP
  14. [dataTables使用的坑]requested unknown parameter 'XXX' for row xx, column xx
  15. 2021年茶艺师(初级)考试技巧及茶艺师(初级)试题及解析
  16. plc梯形图的c语言写法,PLC梯形图讲解.ppt
  17. 目前A股市场情绪未定,假期前多看少动?后市大概率分化轮动!
  18. The Devil Wears Prada-10
  19. 冷门项目玩法思路,小竞争大利润
  20. 黄俊滔:没有人能够打败趋势,将会有大规模公司倒闭

热门文章

  1. 通过wget在Linux上下载Java JDK会显示在许可证页面上
  2. 什么是Java中的守护程序线程?
  3. 大O,您如何计算/近似?
  4. servlet实现mvc
  5. windows 电脑属性调用程序
  6. Linux:sudo命令实例讲解
  7. 心理阴影面积 (5 分)
  8. C# 字符串string的基本操作
  9. C# DateTime类
  10. iOS:重识Transform和frame