如题,通过docx4j这个框架解析项目下的docx模板,然后在数据库查询数据,填充到Map,然后替换模板中的占位符${name},生成一个新的docx文档,并生成pdf文件。

1.jar包依赖

<dependency><groupId>org.docx4j</groupId><artifactId>docx4j</artifactId><version>6.0.1</version></dependency><dependency><groupId>org.docx4j</groupId><artifactId>docx4j-export-fo</artifactId><version>6.0.1</version></dependency>

代码如下

package com.sgcc.dlsc.omemberregister.controller.membermanagement;import cn.hutool.core.date.DateUtil;
import com.sgcc.comm.util.ResultUtil;
import com.sgcc.dlsc.omemberregister.enums.VoltageEnum;
import com.sgcc.dlsc.omemberregister.model.po.*;
import com.sgcc.dlsc.omemberregister.service.membersmanagement.*;
import com.sgcc.dlsc.pxcommondocument.util.WordUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.docx4j.Docx4J;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.wml.STPageOrientation;
import org.docx4j.wml.SectPr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;/*** @author * @Date 2021/9/15* @Description*/
@RestController
@RequestMapping("mmAgreement")
public class MmAgreementController {private static final Logger logger = LoggerFactory.getLogger(MmAgreementController.class);@Autowiredprivate BaPhzhbAgencyAgreementService baPhzhbAgencyAgreementService;@Autowiredprivate BaPhzhbPlanMonthEngreyService baPhzhbPlanMonthEngreyService;@Autowiredprivate BaPhzhbCurvetradeService phzhbCurvetradeService;@Autowiredprivate BaPhzhbTimepartService baPhzhbTimepartService;@Autowiredprivate BaPhzhbTimepartDetailService baPhzhbTimepartDetailService;@Autowiredprivate StringRedisTemplate stringRedisTemplate;private static final String GENERATE_AGREEMENT_REDIS_KEY = "agreement_to_pdf:";private static final String TEMP_FILE_PATH = System.getProperty("os.name").toLowerCase().contains("windows") ? System.getProperty("user.dir") + File.separator : "/home/admin/tempfile/";@PostMapping("/generateAgreementPdf")public ResultUtil generateAgreementPdf(@RequestBody BaPhzhbAgencyAgreement baPhzhbAgencyAgreement) {if (Objects.isNull(baPhzhbAgencyAgreement) || StringUtils.isBlank(baPhzhbAgencyAgreement.getGuid())) {return ResultUtil.result(-1, "参数为空");}//幂等处理String redisKey = GENERATE_AGREEMENT_REDIS_KEY + baPhzhbAgencyAgreement.getGuid();String value = stringRedisTemplate.opsForValue().get(redisKey);if (StringUtils.isNotBlank(value)) {return ResultUtil.result(-1, "请勿重复请求");}//添加当前幂等,10s可以处理一次stringRedisTemplate.opsForValue().set(redisKey, "", 10, TimeUnit.SECONDS);BaPhzhbAgencyAgreement agreement = baPhzhbAgencyAgreementService.findBaPhzhbAgencyAgreementById(baPhzhbAgencyAgreement.getGuid());if (Objects.isNull(agreement)) {return ResultUtil.result(-1, "协议数据不存在");}final Map<String, String> dataMap = new HashMap<>();//合同编号dataMap.put("CONTRACT_BH", agreement.getContractBh());//甲方名称dataMap.put("PARTY_A_NAME", agreement.getPartyAName());//乙方名称dataMap.put("PARTY_B_NAME", agreement.getPartyAName());//乙方客户编号dataMap.put("PARTY_B_CONS", agreement.getPartyBCons());//签订日期dataMap.put("SIGN_DATE", DateUtil.format(agreement.getSignDate(), "yyyy-MM-dd"));//签订地点dataMap.put("SIGN_ADDRESS", agreement.getSignAddress());//甲方(售电公司) 同上 PARTY_A_NAME//乙方 (电力用户) 同上 PARTY_B_NAME//行业dataMap.put("PARTY_B_INDUSTRY", agreement.getPartyBIndustry());//合同开始日期dataMap.put("CONTRACT_BEGIN", DateUtil.format(agreement.getContractBegin(), "yyyy-MM-dd"));//合同结束日期dataMap.put("CONTRACT_END", DateUtil.format(agreement.getContractEnd(), "yyyy-MM-dd"));//乙方购入电量dataMap.put("PURCHASED_ENERGY", agreement.getPurchasedEnergy().toPlainString());//大工业电量dataMap.put("GREATE_INDUSTRY", agreement.getGreateIndustry().toPlainString());//一般工业电量dataMap.put("GENERAL_BUSINESS", agreement.getGeneralBusiness().toPlainString());//价格模式dataMap.put("SHARE_TYPE", agreement.getShareType());String shareType = agreement.getShareType();dataMap.put("SHARE_PROPORTION_D", "");dataMap.put("SHARE_PROPORTION_A", "");dataMap.put("SHARE_PROPORTION_B", "");dataMap.put("SHARE_PROPORTION_X", "");dataMap.put("SHARE_PROPORTION_Y", "");dataMap.put("SHARE_PROPORTION_Z", "");if ("1".equals(shareType)) {//固定电价dataMap.put("SHARE_PROPORTION_D", agreement.getShareProportionD().toPlainString());} else if ("2".equals(shareType)) {dataMap.put("SHARE_PROPORTION_A", agreement.getShareProportionA().toPlainString());dataMap.put("SHARE_PROPORTION_B", agreement.getShareProportionB().toPlainString());} else if ("3".equals(shareType)) {dataMap.put("SHARE_PROPORTION_X", agreement.getShareProportionD().toPlainString());dataMap.put("SHARE_PROPORTION_Y", agreement.getShareProportionA().toPlainString());dataMap.put("SHARE_PROPORTION_Z", agreement.getShareProportionB().toPlainString());}/*//固定电价dataMap.put("SHARE_PROPORTION_D", agreement.getShareProportionD().toPlainString());//甲方分配比例dataMap.put("SHARE_PROPORTION_A", agreement.getShareProportionA().toPlainString());//乙方分成比例dataMap.put("SHARE_PROPORTION_B", agreement.getShareProportionB().toPlainString());*///偏差范围dataMap.put("BORNE_PROPORTION_E", agreement.getBorneProportionE().toPlainString());dataMap.put("BORNE_PROPORTION_F", agreement.getBorneProportionF().toPlainString());dataMap.put("BORNE_PROPORTION_D", agreement.getBorneProportionD().toPlainString());dataMap.put("BORNE_PROPORTION_C", agreement.getBorneProportionC().toPlainString());//甲方违约赔偿标准dataMap.put("STATEMENT_OF_BREACH_A", agreement.getStatementOfBreachA());//乙方赔偿标准dataMap.put("STATEMENT_OF_BREACH_B", agreement.getStatementOfBreachB());//多少份dataMap.put("CONTRACT_COPY_NUM", agreement.getContractCopyNum().toString());//甲方多少份dataMap.put("CONTRACT_A_COPY_NUM", agreement.getContractACopyNum().toString());//乙方份数dataMap.put("CONTRACT_B_COPY_NUM", agreement.getContractBCopyNum().toString());//查询电力分布数据List<BaPhzhbPlanMonthEngrey> planMonthEngreyList = baPhzhbPlanMonthEngreyService.findBaPhzhbPlanMonthEngreyByRelationId(agreement.getRelationId());this.getElectricData(dataMap, planMonthEngreyList);//查询时段电量电价List<BaPhzhbCurvetrade> baPhzhbCurvetrades = phzhbCurvetradeService.selectBaPhzhbCurvetradeByRelationId(agreement.getRelationId());this.getCurvetradeData(dataMap, baPhzhbCurvetrades);ClassPathResource classPathResource = new ClassPathResource("template/hubei-protocol-template2021.docx");try (InputStream inputStream = classPathResource.getInputStream()) {String uuid = UUID.randomUUID().toString();File pathFile = new File(TEMP_FILE_PATH);if (!pathFile.exists()) {pathFile.mkdirs();}String outFile = TEMP_FILE_PATH + uuid + ".docx";logger.info("generateAgreementPdf outFile = {}", outFile);
//            File file = new File(outFile);
//            String parent = classPathResource.getFile().getParent();
//            String outFile = parent + File.separator + uuid + ".docx";FileOutputStream os = new FileOutputStream(outFile);WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputStream);SectPr sectPr = wordMLPackage.getDocumentModel().getSections().get(0).getSectPr();SectPr.PgSz pgSz = sectPr.getPgSz();pgSz.setOrient(STPageOrientation.LANDSCAPE);WordUtil.replaceVariable(wordMLPackage, dataMap);Docx4J.save(wordMLPackage, os);//保存后的word转换成pdfInputStream is = new FileInputStream(outFile);
//            String outPdfPath = parent + File.separator + uuid + ".pdf";String outPdfPath = TEMP_FILE_PATH + uuid + ".pdf";OutputStream osPdf = new FileOutputStream(outPdfPath);/*toPdf(is, osPdf);*/WordUtil.toPDF(is, osPdf);return ResultUtil.result(0, uuid);} catch (IOException e) {e.printStackTrace();} catch (Docx4JException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();} finally {//删除掉redis幂等keystringRedisTemplate.delete(redisKey);}return ResultUtil.result(-1, "生成协议pdf失败,请重试或联系管理员");}@PostMapping("/downloadAgreementPdf")public void downloadAgreementPdf(@RequestBody BaPhzhbAgencyAgreement baPhzhbAgencyAgreement, HttpServletResponse response) {if (Objects.isNull(baPhzhbAgencyAgreement) || StringUtils.isBlank(baPhzhbAgencyAgreement.getGuid())) {return;}//暂时将uuid封装在guId字段
//        ClassPathResource resource = new ClassPathResource("template/" + baPhzhbAgencyAgreement.getGuid() + ".pdf");File file = new File(TEMP_FILE_PATH + baPhzhbAgencyAgreement.getGuid() + ".pdf");if (!file.exists()) {return;}OutputStream outputStream = null;InputStream inputStream = null;try {outputStream = response.getOutputStream();inputStream = new FileInputStream(file);String pdfName = "代理协议.pdf" ;response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);setAttachmentResponseHeader(response, pdfName);int len = 0;byte[] bytes = new byte[1024];while ((len = inputStream.read(bytes)) > 0) {outputStream.write(bytes, 0, len);}//下载完毕后删除掉pdf文件和docx文件
//            resource.getFile().delete();
//            ClassPathResource docxResource = new ClassPathResource("template/" + baPhzhbAgencyAgreement.getGuid() + ".docx");
//            if (docxResource.exists()) {                docxResource.getFile().delete();
            }
//            file.delete();//删除docx
//            File docFile = new File(TEMP_FILE_PATH + baPhzhbAgencyAgreement.getGuid() + ".docx");
//            if (docFile.exists() && docFile.isFile()) {//                docFile.delete();
//            }} catch (Exception e) {e.printStackTrace();} finally {if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}}/* private void toPdf(InputStream inputStream, OutputStream outputStream) throws Exception {
//        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputStream);
//        Mapper fontMapper = new IdentityPlusMapper();
//        wordMLPackage.setFontMapper(fontMapper);
//        fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
//        fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
//        fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));
//        fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
//        fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
//        fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
//        fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
//        fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
//        fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
//        fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
//        fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
//        fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
//        fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
//        fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
//        FOSettings foSettings = Docx4J.createFOSettings();
//
//        List<SectionWrapper> sections = wordMLPackage.getDocumentModel().getSections();
//        for (int i = 0; i < sections.size(); i++) {
//            SectPr.PgSz pgSz = sections.get(i).getSectPr().getPgSz();
//            pgSz.setW(BigInteger.valueOf(23814L));
//            pgSz.setH(BigInteger.valueOf(16840L));
//            pgSz.setOrient(STPageOrientation.LANDSCAPE);
//            pgSz.setCode(BigInteger.valueOf(8L));
//        }SectPr sectPr = sectionWrapper.getSectPr();SectPr.PgSz pgSz = sectPr.getPgSz();pgSz.setOrient(STPageOrientation.LANDSCAPE);sectPr.setPgSz(pgSz);sectionWrapper.setSectPr(sectPr);
//        foSettings.setWmlPackage(wordMLPackage);
//        Docx4J.toFO(foSettings, outputStream, 1);
//    }*/private void getCurvetradeData(Map<String, String> dataMap, List<BaPhzhbCurvetrade> baPhzhbCurvetrades) {if (CollectionUtils.isNotEmpty(baPhzhbCurvetrades)) {//查询时段数据int tpSize = this.getTpData(dataMap, baPhzhbCurvetrades.get(0).getTpId());if (tpSize > 0) {//有时段//电量的开始位置int electricEnergyStartIndex = 10;int electricPriceStartIndex = 20;BigDecimal energySum = BigDecimal.ZERO;BigDecimal regularPriceSum = BigDecimal.ZERO;int shareProportionAStartIndex = 30;int shareProportionBStartIndex = 40;BigDecimal shareProportionASum = BigDecimal.ZERO;BigDecimal shareProportionBSum = BigDecimal.ZERO;int thirdEnergyStartIndex = 50;int thirdShareProportionAStartIndex = 60;int thirdShareProportionBStartIndex = 70;BigDecimal thirdEnergySum = BigDecimal.ZERO;BigDecimal thirdShareProportionASum = BigDecimal.ZERO;BigDecimal thirdShareProportionBSum = BigDecimal.ZERO;//结算类型(1.固定价格模式,2.价差分成模式,3.固定价格+价差分成模式Integer shareType = baPhzhbCurvetrades.get(0).getShareType();for (int i = 0; i < baPhzhbCurvetrades.size(); i++) {//处理电量BaPhzhbCurvetrade baPhzhbCurvetrade = baPhzhbCurvetrades.get(i);energySum = energySum.add(baPhzhbCurvetrade.getEnergy());dataMap.put("t" + electricEnergyStartIndex, baPhzhbCurvetrade.getEnergy().setScale(3, RoundingMode.HALF_UP).toPlainString());electricEnergyStartIndex++;//处理if (shareType == 1) {//// 20 - 29dataMap.put("t" + electricPriceStartIndex, baPhzhbCurvetrade.getPrice().setScale(3, RoundingMode.HALF_UP).toPlainString());regularPriceSum = regularPriceSum.add(baPhzhbCurvetrade.getPrice());electricPriceStartIndex++;} else if (shareType == 2) {//dataMap.put("t" + shareProportionAStartIndex, baPhzhbCurvetrade.getShareProportionA().setScale(3, RoundingMode.HALF_UP).toPlainString());dataMap.put("t" + shareProportionBStartIndex, baPhzhbCurvetrade.getShareProportionB().setScale(3, RoundingMode.HALF_UP).toPlainString());shareProportionASum = shareProportionASum.add(baPhzhbCurvetrade.getShareProportionA());shareProportionBSum = shareProportionBSum.add(baPhzhbCurvetrade.getShareProportionB());shareProportionAStartIndex++;shareProportionBStartIndex++;} else {//dataMap.put("t" + thirdEnergyStartIndex, baPhzhbCurvetrade.getPrice().setScale(3, RoundingMode.HALF_UP).toPlainString());dataMap.put("t" + thirdShareProportionAStartIndex, baPhzhbCurvetrade.getShareProportionA().setScale(3, RoundingMode.HALF_UP).toPlainString());dataMap.put("t" + thirdShareProportionBStartIndex, baPhzhbCurvetrade.getShareProportionB().setScale(3, RoundingMode.HALF_UP).toPlainString());thirdEnergySum = thirdEnergySum.add(baPhzhbCurvetrade.getPrice());thirdShareProportionASum = thirdShareProportionASum.add(baPhzhbCurvetrade.getShareProportionA());thirdShareProportionBSum = thirdShareProportionBSum.add(baPhzhbCurvetrade.getShareProportionB());thirdEnergyStartIndex++;thirdShareProportionAStartIndex++;thirdShareProportionBStartIndex++;}}//电量之和dataMap.put("t19", energySum.setScale(3, RoundingMode.HALF_UP).toPlainString());//固定电价if (regularPriceSum.compareTo(BigDecimal.ZERO) != 0) {dataMap.put("t29", regularPriceSum.setScale(3, RoundingMode.HALF_UP).toPlainString());}if (shareProportionASum.compareTo(BigDecimal.ZERO) != 0) {dataMap.put("t39", shareProportionASum.setScale(3, RoundingMode.HALF_UP).toPlainString());}if (shareProportionBSum.compareTo(BigDecimal.ZERO) != 0) {dataMap.put("t49", shareProportionBSum.setScale(3, RoundingMode.HALF_UP).toPlainString());}if (thirdEnergySum.compareTo(BigDecimal.ZERO) != 0) {dataMap.put("t59", thirdEnergySum.setScale(3, RoundingMode.HALF_UP).toPlainString());}if (thirdShareProportionASum.compareTo(BigDecimal.ZERO) != 0) {dataMap.put("t69", thirdShareProportionASum.setScale(3, RoundingMode.HALF_UP).toPlainString());}if (thirdShareProportionBSum.compareTo(BigDecimal.ZERO) != 0) {dataMap.put("t79", thirdShareProportionBSum.setScale(3, RoundingMode.HALF_UP).toPlainString());}}}String key = "";for (int i = 1; i <= 79; i++) {key = "t" + i;if (dataMap.get(key) == null) {dataMap.put(key, "");}}}private int getTpData(Map<String, String> dataMap, String tpId) {if (StringUtils.isBlank(tpId)) {return 0;}BaPhzhbTimepart baPhzhbTimepart = baPhzhbTimepartService.selectBaPhzhbTimepartByTpId(tpId);if (Objects.nonNull(baPhzhbTimepart) && Objects.nonNull(baPhzhbTimepart.getIsEnable()) && baPhzhbTimepart.getIsEnable() == 1) {//启用状态下查询具体时段List<BaPhzhbTimepartDetail> baPhzhbTimepartDetails = baPhzhbTimepartDetailService.selectBaPhzhbTimepartDetailByTpId(tpId);for (int i = 0; i < baPhzhbTimepartDetails.size(); i++) {BaPhzhbTimepartDetail baPhzhbTimepartDetail = baPhzhbTimepartDetails.get(i);dataMap.put("t" + (i + 1), baPhzhbTimepartDetail.getTpDetail());}return baPhzhbTimepartDetails.size();}return 0;}/*** 下载文件名重新编码** @param response 响应对象* @param realFileName 真实文件名* @return*/public void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {String percentEncodedFileName = percentEncode(realFileName);StringBuilder contentDispositionValue = new StringBuilder();contentDispositionValue.append("attachment; filename=").append(percentEncodedFileName).append(";").append("filename*=").append("utf-8''").append(percentEncodedFileName);response.setHeader("Content-disposition", contentDispositionValue.toString());}/*** 百分号编码工具方法** @param s 需要百分号编码的字符串* @return 百分号编码后的字符串*/private String percentEncode(String s) throws UnsupportedEncodingException {String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());return encode.replaceAll("\\+", "%20");}/*** * @param dataMap* @param planMonthEngreyList*/private void getElectricData(Map<String, String> dataMap, List<BaPhzhbPlanMonthEngrey> planMonthEngreyList) {if (CollectionUtils.isEmpty(planMonthEngreyList)) {return;}Class<BaPhzhbPlanMonthEngrey> baPhzhbPlanMonthEngreyClass = BaPhzhbPlanMonthEngrey.class;Map<String, List<BaPhzhbPlanMonthEngrey>> listMap = planMonthEngreyList.stream().collect(Collectors.groupingBy(BaPhzhbPlanMonthEngrey::getIndustryType));try {for (Map.Entry<String, List<BaPhzhbPlanMonthEngrey>> entry : listMap.entrySet()) {String key = entry.getKey();List<BaPhzhbPlanMonthEngrey> value = entry.getValue();if (CollectionUtils.isEmpty(value)) {continue;}String prefix = "1".equals(key) ? "m" : "k";for (BaPhzhbPlanMonthEngrey baPhzhbPlanMonthEngrey : value) {VoltageEnum instance = VoltageEnum.getInstance(baPhzhbPlanMonthEngrey.getVoltage());int i = 0;BigDecimal plus = BigDecimal.valueOf(0);for (int j = instance.getStartIndex(); j <= instance.getEndIndex(); j++) {i++;String dataKey = prefix + j;if (j == instance.getEndIndex()) {//最后一行,设置总和dataMap.put(dataKey, plus.setScale(3, RoundingMode.HALF_UP).toPlainString());continue;}//非最后一行,获取当前列的数据Field declaredField = baPhzhbPlanMonthEngreyClass.getDeclaredField("m" + i);declaredField.setAccessible(true);Object o = declaredField.get(baPhzhbPlanMonthEngrey);if (o != null) {BigDecimal decimal = (BigDecimal)o;dataMap.put(dataKey, decimal.setScale(3, RoundingMode.HALF_UP).toPlainString());//计算总和plus = plus.add(decimal);} else {dataMap.put(dataKey, "");}}}}} catch (Exception e) {e.printStackTrace();return;}String[] col = {"m","k"};for (String str : col) {for (int i = 1; i <= 156; i++) {String key = str + i;if (dataMap.get(key) == null) {dataMap.put(key, "");}}}}
}

另外WordUtil也贴一下吧

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//package com.sgcc.dlsc.pxcommondocument.util;import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.docx4j.Docx4J;
import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.convert.out.FOSettings;
import org.docx4j.convert.out.HTMLSettings;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.finders.ClassFinder;
import org.docx4j.finders.RangeFinder;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.Body;
import org.docx4j.wml.CTBookmark;
import org.docx4j.wml.Document;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.Text;
import org.docx4j.wml.Tr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.FileCopyUtils;public class WordUtil {private static final Logger log = LoggerFactory.getLogger(WordUtil.class);public WordUtil() {}public static void replaceVariable(String templatePath, Map<String, String> map, String outPath) throws Exception {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File(templatePath));MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();Docx4jUtils.cleanDocumentPart(documentPart);documentPart.variableReplace(map);Docx4J.save(wordMLPackage, new File(outPath));}public static void replaceVariable(InputStream inputStream, Map<String, String> map, OutputStream outputStream) throws Exception {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputStream);MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();Docx4jUtils.cleanDocumentPart(documentPart);documentPart.variableReplace(map);Docx4J.save(wordMLPackage, outputStream);}public static void replaceVariable(WordprocessingMLPackage wordMLPackage, Map<String, String> map) throws Exception {MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();Docx4jUtils.cleanDocumentPart(documentPart);documentPart.variableReplace(map);}public static void replaceTable(String templatePath, int tableNum, int rowNum, List<Map<String, Object>> dataList, String outPath) throws Exception {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File(templatePath));MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();Docx4jUtils.cleanDocumentPart(mainDocumentPart);ClassFinder find = new ClassFinder(Tbl.class);new TraversalUtil(mainDocumentPart.getContent(), find);Tbl table = (Tbl)find.results.get(tableNum - 1);Tr dynamicTr = (Tr)table.getContent().get(rowNum - 1);String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);Iterator var11 = dataList.iterator();while(var11.hasNext()) {Map<String, Object> dataMap = (Map)var11.next();Tr newTr = (Tr)XmlUtils.unmarshallFromTemplate(dynamicTrXml, dataMap);table.getContent().add(newTr);}table.getContent().remove(rowNum - 1);Docx4J.save(wordMLPackage, new File(outPath));}public static void replaceTable(InputStream inputStream, int tableNum, int rowNum, List<Map<String, Object>> dataList, OutputStream outputStream) throws Exception {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputStream);MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();Docx4jUtils.cleanDocumentPart(mainDocumentPart);ClassFinder find = new ClassFinder(Tbl.class);new TraversalUtil(mainDocumentPart.getContent(), find);Tbl table = (Tbl)find.results.get(tableNum - 1);Tr dynamicTr = (Tr)table.getContent().get(rowNum - 1);String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);Iterator var11 = dataList.iterator();while(var11.hasNext()) {Map<String, Object> dataMap = (Map)var11.next();Tr newTr = (Tr)XmlUtils.unmarshallFromTemplate(dynamicTrXml, dataMap);table.getContent().add(newTr);}table.getContent().remove(rowNum - 1);Docx4J.save(wordMLPackage, outputStream);}public static void replaceTable(WordprocessingMLPackage wordMLPackage, int tableNum, int rowNum, List<Map<String, Object>> dataList) throws Exception {MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();Docx4jUtils.cleanDocumentPart(mainDocumentPart);ClassFinder find = new ClassFinder(Tbl.class);new TraversalUtil(mainDocumentPart.getContent(), find);Tbl table = (Tbl)find.results.get(tableNum - 1);Tr dynamicTr = (Tr)table.getContent().get(rowNum - 1);String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);Iterator var9 = dataList.iterator();while(var9.hasNext()) {Map<String, Object> dataMap = (Map)var9.next();Tr newTr = (Tr)XmlUtils.unmarshallFromTemplate(dynamicTrXml, dataMap);table.getContent().add(newTr);}table.getContent().remove(rowNum - 1);}public static void replacePicture(WordprocessingMLPackage wordMLPackage, String bookmarkName, InputStream inputStream) throws Exception {try {MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();Document wmlDoc = (Document)mainDocumentPart.getJaxbElement();Body body = wmlDoc.getBody();List<Object> paragraphs = body.getContent();RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange");new TraversalUtil(paragraphs, rt);Iterator var8 = rt.getStarts().iterator();while(var8.hasNext()) {CTBookmark bm = (CTBookmark)var8.next();if (bm.getName().equals(bookmarkName)) {byte[] bytes = IOUtils.toByteArray(inputStream);BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);Inline inline = imagePart.createImageInline("", "", 0, 1, true);P p = (P)((P)bm.getParent());ObjectFactory factory = new ObjectFactory();R run = factory.createR();Drawing drawing = factory.createDrawing();drawing.getAnchorOrInline().add(inline);run.getContent().add(drawing);p.getContent().add(run);}}} catch (Exception var17) {}}public static void replaceForTemplate(String templatePath, Map<String, String> map, int tableNum, List<Map<String, Object>> dataList, String outPath) throws Exception {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File(templatePath));MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();Docx4jUtils.cleanDocumentPart(mainDocumentPart);ClassFinder find = new ClassFinder(Tbl.class);new TraversalUtil(mainDocumentPart.getContent(), find);Tbl table = (Tbl)find.results.get(tableNum - 1);Tr dynamicTr = (Tr)table.getContent().get(1);String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);Iterator var11 = dataList.iterator();while(var11.hasNext()) {Map<String, Object> dataMap = (Map)var11.next();Tr newTr = (Tr)XmlUtils.unmarshallFromTemplate(dynamicTrXml, dataMap);table.getContent().add(newTr);}table.getContent().remove(1);wordMLPackage.getMainDocumentPart().variableReplace(map);Docx4J.save(wordMLPackage, new File(outPath));}public static void replaceForTemplate(InputStream inputStream, Map<String, String> map, int tableNum, List<Map<String, Object>> dataList, OutputStream outputStream) throws Exception {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputStream);MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();Docx4jUtils.cleanDocumentPart(mainDocumentPart);ClassFinder find = new ClassFinder(Tbl.class);new TraversalUtil(mainDocumentPart.getContent(), find);Tbl table = (Tbl)find.results.get(tableNum - 1);Tr dynamicTr = (Tr)table.getContent().get(1);String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);Iterator var11 = dataList.iterator();while(var11.hasNext()) {Map<String, Object> dataMap = (Map)var11.next();Tr newTr = (Tr)XmlUtils.unmarshallFromTemplate(dynamicTrXml, dataMap);table.getContent().add(newTr);}table.getContent().remove(1);wordMLPackage.getMainDocumentPart().variableReplace(map);Docx4J.save(wordMLPackage, outputStream);}public static void toPDF(InputStream inputStream, OutputStream outputStream) throws Exception {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputStream);Mapper fontMapper = new IdentityPlusMapper();wordMLPackage.setFontMapper(fontMapper);fontMapper.put("隶书", PhysicalFonts.get("LiSu"));fontMapper.put("宋体", PhysicalFonts.get("SimSun"));fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));fontMapper.put("黑体", PhysicalFonts.get("SimHei"));fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));FOSettings foSettings = Docx4J.createFOSettings();foSettings.setWmlPackage(wordMLPackage);Docx4J.toFO(foSettings, outputStream, 1);}public static void toPDF(WordprocessingMLPackage wordMLPackage, OutputStream outputStream) throws Exception {Mapper fontMapper = new IdentityPlusMapper();wordMLPackage.setFontMapper(fontMapper);fontMapper.put("隶书", PhysicalFonts.get("LiSu"));fontMapper.put("宋体", PhysicalFonts.get("SimSun"));fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));fontMapper.put("黑体", PhysicalFonts.get("SimHei"));fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));FOSettings foSettings = Docx4J.createFOSettings();foSettings.setWmlPackage(wordMLPackage);Docx4J.toFO(foSettings, outputStream, 1);}public static void toHTML(InputStream inputStream, OutputStream outputStream) throws Exception {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputStream);HTMLSettings htmlSettings = Docx4J.createHTMLSettings();String folder = "optimages";htmlSettings.setImageDirPath(folder);htmlSettings.setImageTargetUri("images");htmlSettings.setWmlPackage(wordMLPackage);Docx4J.toHTML(htmlSettings, outputStream, 0);}public static void toHTML(WordprocessingMLPackage wordMLPackage, OutputStream outputStream) throws Exception {HTMLSettings htmlSettings = Docx4J.createHTMLSettings();String folder = "optimages";htmlSettings.setImageDirPath(folder);htmlSettings.setImageTargetUri("images");htmlSettings.setWmlPackage(wordMLPackage);Docx4J.toHTML(htmlSettings, outputStream, 0);}private static void addImageInlineToPackage(WordprocessingMLPackage wordMLPackage, byte[] bytes) throws Exception {BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);int docPrId = 1;int cNvPrId = 2;Inline inline = imagePart.createImageInline("Filename hint", "Alternative text", docPrId, cNvPrId, false);ObjectFactory factory = new ObjectFactory();P paragraph = factory.createP();R run = factory.createR();paragraph.getContent().add(run);Drawing drawing = factory.createDrawing();run.getContent().add(drawing);drawing.getAnchorOrInline().add(inline);wordMLPackage.getMainDocumentPart().addObject(paragraph);}private static void addImageInlineAndTextToPackage(WordprocessingMLPackage wordMLPackage, byte[] bytes, String text) throws Exception {BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);int docPrId = 1;int cNvPrId = 2;Inline inline = imagePart.createImageInline("Filename hint", "Alternative text", docPrId, cNvPrId, false);ObjectFactory factory = new ObjectFactory();P paragraph = factory.createP();R run = factory.createR();paragraph.getContent().add(run);if (text != null) {Text txt = factory.createText();txt.setValue(text);txt.setSpace("preserve");run.getContent().add(txt);}Drawing drawing = factory.createDrawing();run.getContent().add(drawing);drawing.getAnchorOrInline().add(inline);wordMLPackage.getMainDocumentPart().addObject(paragraph);}public static void main(String[] args) throws Exception {File file = new File("d:" + File.separator + "opt" + File.separator + "template" + File.separator + "请假条_template.docx");InputStream is = new FileInputStream(file);WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(is);File png = new File("d:" + File.separator + "opt" + File.separator + "template" + File.separator + "badge.png");byte[] bytes = FileCopyUtils.copyToByteArray(new FileInputStream(png));addImageInlineToPackage(wordMLPackage, bytes);addImageInlineAndTextToPackage(wordMLPackage, bytes, "这是图3,name.png,看图片的嵌入方式");wordMLPackage.save(new File("d:/opt/测试" + System.currentTimeMillis() + ".docx"));}
}

说说在发布到linux上遇到的问题:
以上代码在windows上测试都ok,但是发布到linux的docker容器就出现了一些问题,首先是

  1. FileNotFoundException,这个原因是因为容器不存在/home/admin。。。这个目录导致的,因此使用File类mkdirs()进行创建解决了。
  2. 下载的文件显示损坏,打不开。在下载的时候加入这段代码解决response.setContentLength((int) file.length());
  3. 生成的pdf文件发现中文有问题,这一个目前还在看问题,怀疑是容器里的docx4j生成pdf的字体库问题,只是怀疑,还在排查中。

2021-10-08日解决了pdf生成后的字体问题。
步骤:
1.把下载好的字体文件放到resource目录下

2.在代码中引入该字体,并在导出pdf时设置该字体文件

public void toPDF(InputStream inputStream, OutputStream outputStream) throws Exception {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputStream);addFonts(wordMLPackage);FOSettings foSettings = Docx4J.createFOSettings();foSettings.setWmlPackage(wordMLPackage);Docx4J.toFO(foSettings, outputStream, 1);}public void addFonts(WordprocessingMLPackage wordMLPackage) {Mapper fontMapper = new IdentityPlusMapper();try {wordMLPackage.setFontMapper(fontMapper);ClassPathResource resource = new ClassPathResource("/fonts/simsun.ttc");URL simsunUrl = resource.getURL();String fontFamily = "simsun";PhysicalFonts.addPhysicalFonts(fontFamily, simsunUrl);PhysicalFont simsunFont = PhysicalFonts.get(fontFamily);fontMapper.put(fontFamily, simsunFont);RFonts rfonts = Context.getWmlObjectFactory().createRFonts(); //設置文件默認字體rfonts.setAsciiTheme(null);rfonts.setAscii(fontFamily);wordMLPackage.getMainDocumentPart().getPropertyResolver().getDocumentDefaultRPr().setRFonts(rfonts);} catch (Exception e) {e.printStackTrace();return;}}

3.尝试导出pdf,发现pdf的中文能正常显示了

使用docx4j解析word模板,替换占位符生成新的docx,并生成pdf相关推荐

  1. 【java spring boot使用easypoi实现word文档占位符替换文字和图片、Excel图片贴到对应单元格】

    最近接到一个任务,写两个导出工具:1.word文档导出,将数据和图片放入到word中,将多个word合并为一个导出.2.Excel文档导出,将二维码信息和图片按照模板放入到Excel中,按9个一页排版 ...

  2. aspose.word 20 java 替换占位符为文字或图片

    以前写过用asposeword 替换文字和图片,但是后来遇到问题,有些word替换替换图片后会导致打开时弹出错误:此文件中检测到错误,单word可以通过进行一下修复来打开文件. 考虑可能是版本有bug ...

  3. java word模板替换多行_Java动态替换word模板的最佳实践

    poi-tl 基于word的模板渲染(替换)组件 对docx格式的文档增加模板语法,增加渲染模板的方便性,目前支持对段落.页眉.页脚.表格的文本.图片.表单渲染. 对于word模板替换,我们不仅要考虑 ...

  4. java 解析word模板为xml, 动态填充数据到xml,最后输出word文档

    java 解析word模板为xml, 动态填充数据到xml,最后输出word文档 在项目中, 一开始是使用java Apache poi 根据word模板生成word报表, 后面发现框架有个低版本的p ...

  5. Mybatis日志参数快速替换占位符工具

    Mybatis log printf工具网页地址: http://www.feedme.ltd/log.html Mybatis执行的sql的打印格式为: 2020-08-04 09:16:44 -D ...

  6. JAVA 字符串替换占位符

    JAVA 字符串替换占位符 1 MessageFormat.format() code public static void main(String[] args) {String a= " ...

  7. 跨平台POI处理word模板替换、转pdf

    2019独角兽企业重金招聘Python工程师标准>>> 实现跨平台Java处理word模板替换,包括标签替换文字.标签替换图片.设置文字样式.图片大小,word转pdf(借助Open ...

  8. 使用XWPFRun替换WORD中的占位符${xxx}

    1.要填充的文件模板,里面需要填充的内容使用${xxxx}. 2.以map的形式,编辑要填充的数据内容 public class OtherDesignReportServiceImpl implem ...

  9. java word模板替换多行_java poi word模板替换段落的换行显示

    在poi操作word模板进行数据替换时,会存在替换的段落需要换行的情况,由于传入的都是字符串,之前在网上搜索过一些处理方法都没有效果,如给字符串添加:"\r","\r\n ...

  10. C#读取Word模板替换相应的字符串(标签)生成新的Word

    在平常工作中,生成word的方式主要是C#读取html的模板文件处理之后保存为.doc文件,这样的好处是方便,快捷,能满足大部分的需求.不过有些特殊的需求并不能满足,如要生成的Word为一个表格,只是 ...

最新文章

  1. Facebook的一些基本操作(网页版)
  2. 【mysql学习-2】
  3. 构造函数必须是public吗_c++ 构造函数,析构函数必须要给成公有的吗?
  4. Java语言基础学习笔记——基础语法
  5. Go语言基础之4--流程控制
  6. 手机端使用ghelper_Anki手机端使用指南(一)
  7. matlab的fft点数,matlab fft的点数问题
  8. linux设置家目录,usermod更改用户家目录
  9. debian修改键盘布局
  10. 增加网站的档次!网页设计师可在网站中加入暗色调
  11. 记录微信小程序createIntersectionObserver()方法的使用
  12. IDEA中怎么调出右下角的版本控制Git
  13. Java若伊是啥_java运行环境和运行机制
  14. Unity DOTS入门教程(Unity2019)
  15. 电子书之当当在线图书
  16. Flash分模块开发,Flash分模块加载,以及使用Flex经行游戏开发!
  17. PrintDocument DrawString C# 换行问题
  18. 统计学~方差分析【第十周】
  19. Flow-3D的fortran语言简单科普,足够二次开发的入门
  20. 关于input中的checked属性

热门文章

  1. 老司机的HCIE战报
  2. 需求文档你怎么写?为什么这么写?如何写一份好的需求文档?
  3. 20140419-MCSA 2012 Server R2 Command
  4. 使用代码控制小米智能插座
  5. selenium反爬
  6. UOS 虚拟打印到 PDF 文件
  7. Java取交集方法retainAll()
  8. 网易云信浏览器WebRTC视频聊天集成
  9. MATLAB —— 散点图绘制
  10. 1+5T LineageOS 谷歌相机 相关处理和链接