一、简介

本工具类复制即可使用,内附测试代码,包含以下操作:

      -- word 中 属性值替换

      -- word 中 列表动态插入数据

      -- word 转 pdf

    -- 版本更新:

          1、链式调用

          2、方法解耦

二、环境

<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.2</version>
</dependency>
<dependency><groupId>freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.8</version>
</dependency>
<dependency><groupId>org.docx4j</groupId><artifactId>docx4j</artifactId><version>6.1.2</version>
</dependency>
<dependency><groupId>org.docx4j</groupId><artifactId>docx4j-export-fo</artifactId><version>8.1.7</version> //版本号不能高于8.1.7
</dependency><!-- 条形码 -->
<dependency><groupId>net.sf.barcode4j</groupId><artifactId>barcode4j-light</artifactId><version>2.0</version>
</dependency>
<dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.activation/activation -->
<dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
<dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId><version>2.3.5</version>
</dependency>

三、工具类

package com.dily.scaffold.common.utils;import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import org.docx4j.Docx4J;
import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.convert.out.FOSettings;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.finders.ClassFinder;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.jaxb.Context;
import org.docx4j.model.datastorage.migration.VariablePrepare;
import org.docx4j.model.table.TblFactory;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import org.springframework.core.io.ClassPathResource;import javax.xml.bind.JAXBElement;
import java.io.*;
import java.util.*;/*** Time:   2021/12/20 13:22* Author: Dily* Remark:* Word文档工具*/
public class Docx4jUtils {public static WordOption getWordOption() {return new WordOption();}/*** 获取文件中所有内容** @param context  word 正文: getWordMLPackage().getMainDocumentPart().getContent()* @param toSearch 搜索的类型* @return 符合搜索类型的所有对象*/public static List<Object> getAllElementFromObject(List<Object> context, Class<?> toSearch) {List<Object> result = new ArrayList<>();for (Object obj : context) {if (obj instanceof JAXBElement)obj = ((JAXBElement<?>) obj).getValue();if (obj.getClass().equals(toSearch))result.add(obj);else if (obj instanceof ContentAccessor) {List<Object> children = ((ContentAccessor) obj).getContent();getAllElementFromObject(children, toSearch);}}return result;}/*** word 转 pdf** @param wordPath word文档地址* @return pdf地址*/public static String Docx2Pdf(String wordPath) {OutputStream os = null;InputStream is = null;//输出pdf文件路径和名称String pdfNoMarkPath = wordPath.substring(0, wordPath.indexOf('.')) + ".pdf";try {is = new FileInputStream(wordPath);WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(is);Mapper fontMapper = new IdentityPlusMapper();fontMapper.put("等线", PhysicalFonts.get("SimSun"));fontMapper.put("等线 Light", PhysicalFonts.get("SimSun"));fontMapper.put("隶书", PhysicalFonts.get("LiSu"));fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));fontMapper.put("黑体", PhysicalFonts.get("SimHei"));fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));fontMapper.put("宋体", PhysicalFonts.get("SimSun"));fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));fontMapper.put("华文琥珀", PhysicalFonts.get("STHupo"));fontMapper.put("华文隶书", PhysicalFonts.get("STLiti"));fontMapper.put("华文新魏", PhysicalFonts.get("STXinwei"));fontMapper.put("华文彩云", PhysicalFonts.get("STCaiyun"));fontMapper.put("方正姚体", PhysicalFonts.get("FZYaoti"));fontMapper.put("方正舒体", PhysicalFonts.get("FZShuTi"));fontMapper.put("华文细黑", PhysicalFonts.get("STXihei"));fontMapper.put("新細明體", PhysicalFonts.get("SimSun"));PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun"));            //解决宋体(正文)和宋体(标题)的乱码问题PhysicalFonts.put("新細明體", PhysicalFonts.get("SimSun"));fontMapper.put("SimSun", PhysicalFonts.get("SimSun"));             //宋体&新宋体mlPackage.setFontMapper(fontMapper);os = new FileOutputStream(pdfNoMarkPath);//docx4j  docx转pdfFOSettings foSettings = Docx4J.createFOSettings();foSettings.setWmlPackage(mlPackage);Docx4J.toFO(foSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL);is.close();//关闭输入流os.close();//关闭输出流return pdfNoMarkPath;} catch (Exception e) {e.printStackTrace();try {if (is != null) {is.close();}if (os != null) {os.close();}} catch (Exception ex) {ex.printStackTrace();}} finally {File file = new File(wordPath);if (file.isFile() && file.exists()) {file.delete();}}return "";}/*** 添加水印图片** @param inPdfPath 无水印pdf路径* @param markPath  水印图片地址* @return 生成的带水印的pdf路径*/public static String addTextMark(String inPdfPath, String markPath) {PdfStamper stamp = null;PdfReader reader = null;try {//输出pdf带水印文件路径和名称String outPdfMarkPath = inPdfPath.substring(0, inPdfPath.indexOf('.')) + "水印.pdf";//添加水印reader = new PdfReader(inPdfPath, "PDF".getBytes());stamp = new PdfStamper(reader, new FileOutputStream(outPdfMarkPath));PdfContentByte under;int pageSize = reader.getNumberOfPages();// 原pdf文件的总页数//水印图片Image image;if (markPath.contains(":"))image = Image.getInstance(markPath);else {ClassPathResource resource = new ClassPathResource(markPath);image = Image.getInstance(resource.getFile().getPath());}for (int i = 1; i <= pageSize; i++) {under = stamp.getOverContent(i);// 水印在之前文本下for (int j = 0; j < 4; j++) {image.setAbsolutePosition(0, j * 250 + 100);//水印位置under.addImage(image);image.setAbsolutePosition(200, j * 250 + 100);//水印位置under.addImage(image);image.setAbsolutePosition(400, j * 250 + 100);//水印位置under.addImage(image);}}stamp.close();// 关闭reader.close();//关闭return outPdfMarkPath;} catch (Exception e) {e.printStackTrace();try {if (stamp != null) {stamp.close();}if (reader != null) {reader.close();//关闭}} catch (Exception ex) {ex.printStackTrace();}} finally {//删除生成的无水印pdfFile file = new File(inPdfPath);if (file.exists() && file.isFile()) {file.delete();}}return "";}/*** word 单步操作类*/public static class WordOption {private static WordprocessingMLPackage wordMLPackage;public WordprocessingMLPackage getWordMLPackage() {return wordMLPackage;}/*** 创建一个空白 Docx 文档*/public WordOption createDocx() throws Docx4JException {wordMLPackage = WordprocessingMLPackage.createPackage();return this;}/*** 加载 Docx 文件** @param filePath 文件地址* @return WordProcessingMLPackage操作包*/public WordOption loadDocx(String filePath) throws Docx4JException {wordMLPackage = WordprocessingMLPackage.load(new File(filePath));return this;}/*** 添加带样式的文本/段落** @param styled  样式* @param conText 文本*/public WordOption addContext(String styled, String conText) {wordMLPackage.getMainDocumentPart().addStyledParagraphOfText(styled, conText);return this;}/*** 添加文本/段落** @param conText 正文*/public WordOption addContext(String conText) {wordMLPackage.getMainDocumentPart().addParagraphOfText(conText);return this;}/*** 添加图片** @param imagePath 图片地址*/public WordOption addImage(String imagePath) throws Exception {BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, new File(imagePath));Inline inline = imagePart.createImageInline("Filename hint", "Alternative text", 1, 2, 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);return this;}/*** 添加空表格** @param row 行数* @param col 列数*/public WordOption addTable(int row, int col) {Tbl table = TblFactory.createTable(row, col, 20000 / col);wordMLPackage.getMainDocumentPart().addObject(table);return this;}/*** 添加带数据表格, 数据必须是整齐的* 表头为数据的 key, 表头在第一行** @param list 数据*/public WordOption addTableWithDataAndTopHeader(List<Map<String, String>> list) {Set<String> keySet = new HashSet<>(list.get(0).keySet());ObjectFactory factory = Context.getWmlObjectFactory();Tbl table = TblFactory.createTable(0, 0, 20000 / list.get(0).size());// 表头Tr tableHeader = factory.createTr();keySet.forEach(e -> {Tc tableCell = factory.createTc();tableCell.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(e));tableHeader.getContent().add(tableCell);});table.getContent().add(tableHeader);// 数据list.forEach(e -> {Tr tableRow = factory.createTr();keySet.forEach(item -> {Tc tableCell = factory.createTc();tableCell.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(e.get(item)));tableRow.getContent().add(tableCell);});table.getContent().add(tableRow);});wordMLPackage.getMainDocumentPart().addObject(table);return this;}/*** 添加带数据表格, 数据必须是整齐的* 表头为数据的 key, 表头在第一列** @param list 数据*/public WordOption addTableWithDataAndLeftHeader(List<Map<String, String>> list) {Set<String> keySet = new HashSet<>(list.get(0).keySet());ObjectFactory factory = Context.getWmlObjectFactory();Tbl table = TblFactory.createTable(0, 0, 20000 / list.get(0).size());keySet.forEach(e -> {Tr tableRow = factory.createTr();Tc tableHeader = factory.createTc();tableHeader.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(e));tableRow.getContent().add(tableHeader);list.forEach(item -> {Tc tableCell = factory.createTc();tableCell.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(item.get(e)));tableRow.getContent().add(tableCell);});table.getContent().add(tableRow);});wordMLPackage.getMainDocumentPart().addObject(table);return this;}/*** 替换模板Docx中数据和表格数据动态添加* 必须存在占位符** @param data               全局替换属性Map* @param tableDataList      列表属性* @param tableTemplateIndex 表格模版位置* @param tableIndex         表格位置* @throws Exception 异常*/public WordOption replaceData(Map<String, String> data, List<Map<String, Object>> tableDataList, int tableTemplateIndex, int tableIndex) throws Exception {// 构造循环列表的数据ClassFinder find = new ClassFinder(Tbl.class);new TraversalUtil(wordMLPackage.getMainDocumentPart().getContent(), find);// 获取第二个表格属性if (find.results.size() > 0) {Tbl table = (Tbl) find.results.get(tableIndex);// 第二行约定为模板Tr dynamicTr = (Tr) table.getContent().get(tableTemplateIndex);// 获取模板行的xml数据String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);// 循环填充模板表格行数据int addIndex = tableTemplateIndex;for (Map<String, Object> dataMap : tableDataList) {addIndex++;Tr newTr = (Tr) XmlUtils.unmarshallFromTemplate(dynamicTrXml, dataMap);table.getContent().add(addIndex, newTr);}// 删除模板行的占位行table.getContent().remove(tableTemplateIndex);}// 设置全局的变量替换wordMLPackage.getMainDocumentPart().variableReplace(data);return this;}/*** 加载模板并替换数据* 必须存在占位符** @param data 数据属性map* @return 输出文件路径* @throws Exception 异常*/public WordOption replaceData(Map<String, String> data) throws Exception {MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();VariablePrepare.prepare(wordMLPackage);// 替换属性documentPart.variableReplace(data);ByteArrayOutputStream outputStream = new ByteArrayOutputStream();wordMLPackage.save(outputStream);return this;}public void save(String path) throws Docx4JException {wordMLPackage.save(new File(path));}public void save(OutputStream outputStream) throws Docx4JException {wordMLPackage.save(outputStream);}}}

四、水印工具类

Java 生成水印https://mp.csdn.net/mp_blog/creation/editor/121976565https://mp.csdn.net/mp_blog/creation/editor/121976565https://mp.csdn.net/mp_blog/creation/editor/121976565

五、docx模板及替换结果

Word模板文档

替换后的PDF文档(未加水印)

Java docx4j 操作word 2.1相关推荐

  1. docx4j操作word文档之生成页码、合并多个文档

    docx4j操作word文档: 动态生成表格行数并填充数据 单元格内填充图片 合并多个word文档(包含页码,纸张方向等等) 1.动态生成表格行数并填充数据 首先创建模板文件.docx,如图: 代码如 ...

  2. java poi 操作word遇到的问题

    java poi 操作word文本,图表,遇到的问题 直接上问题 模板字段匹配问题 图表问题 图表导出 问题:模板找不到对应图表 问题:数据填充后效果不达目标 图表中为零的数值去掉(!!!模板层面解决 ...

  3. 怎么用Java来操作Word和Excel?

    Java操作word文档 Java 操作word,对word文档进行读写时,主要用 Apache写的POI这个工具类,网上有POI对用的API和文档,在自己实际的项目中可以查找对应的API进行对应的操 ...

  4. java openxml 操作 word,(三)、JAVA基于OPENXML的word文档插入、合并、替换操作系列之html转word...

    (三).JAVA基于OPENXML的word文档插入.合并.替换操作系列之html转word 系列笔记传送门 富文本转word文档 准备待转换内容 内容清理与格式化 转换成word文档 输出结果展示 ...

  5. java openxml 操作 word,openxml word转成xml

    word2007无法打开Office Open XML 文档 由于种种原因,如程序安装错误.注册表被修改,或系统被病毒侵害等. 有时候会造成WORD 2007无法打开.DOC文档的问题,常规的处理办法 ...

  6. java web 操作word文档_Java Web项目中使用Freemarker生成Word文档

    Web项目中生成Word文档的操作屡见不鲜.基于Java的解决方式也是非常多的,包含使用Jacob.Apache POI.Java2Word.iText等各种方式,事实上在从Office 2003開始 ...

  7. java poi操作word 2003 2007

    实现java 替换 word中指定内容,实现word2003 版本 很轻松的 完成了,可是到2007版本时缺头疼了.因为在网上查找的很多相关例子都是需要删除哪一行的信息在setText 添加进去这样不 ...

  8. java poi操作word模版 导出word文档(附工具类)

    模板格式 合同编号:{ contractNumber},合同序号:{ contractSequence} 买家: { buyerName} 卖家:{ sellerName} 业务员: { salesN ...

  9. Java Poi-tl操作Word文档,插入文本和图片

    前言:poi-tl(poi template language)是Word模板引擎,基于Microsoft Word模板和数据生成新的文档.详细使用见:官方文档 1.pom.xml引入依赖 <d ...

  10. Java poi 操作word替换模版中固定参数(页眉、段落、表格)

    近期碰到一个稍微头疼的需求,将word模版中的参数替换为实际值,其中包括段落.列表(行数不够时自动递增).页眉:本文以docx文档为例,其中代码有其他地方参考,如有冒犯,还请海涵: 模版: 实现效果: ...

最新文章

  1. 从“连锁”到“新联锁”,尚美生活如何引领酒店行业变革?
  2. python numpy ones.like()(返回形状与类型与给定数组相同的数组)
  3. asp listview 单元格文字颜色_如何替换CAD表格中特定区域单元格中的文字?
  4. 一个简单而又灵活的数据库操作类
  5. leetcode力扣454. 四数相加 II
  6. 分窗 Gram-Schmidt 高光谱降维的 水稻纹枯病检测
  7. [POI2007]ODW-Weights(贪心)
  8. 华硕天选3笔记本电脑WiFi功能消失
  9. 腾讯求职经历(后附大量面试题)
  10. 桂林理工大学 程序设计实践课程 实习报告
  11. Python爬虫 之 异步爬虫
  12. Android实现图书借阅管理系统的开发
  13. 【自动控制原理】【计算机控制技术】通俗易懂地理解Z变换
  14. 大学生入学必备:计算机科学与技术专业相关信息,一文让你全了解!
  15. 第十一章 “万金油”的String,为什么不好用了
  16. 低波动溢价:是否还能持续?
  17. 根旅游签证中,保险的功能涉及了那些事项?
  18. MobaXterm专业版(含授权)
  19. 蚂蚁开放联盟链已全面公测,预计2月全面上线 | 产业区块链周报
  20. 如何破解Excel VBA的工程密码

热门文章

  1. 如何让WPS像word一样使用快捷键打开MathType
  2. 测试用例经典练习之微信发红包测试用例
  3. 时间序列分析实验报告总结_时间序列分析实验报告
  4. [裴礼文数学分析中的典型问题与方法习题参考解答]5.1.26
  5. WEB应用程序的开发和研究
  6. 插头插座新旧标准对比和安规测试设备
  7. HyperLynx(三)传输线类型及相关设置
  8. Apachebench压力测试
  9. 《软件工程之美》打卡第六周
  10. Linux FTP服务搭建(完整步骤)