一、简介

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

      -- word 中 属性值替换

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

      -- word 转 pdf

二、环境

<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>

三、工具类

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 javax.xml.bind.JAXBException;
import java.io.*;
import java.util.*;/*** Time:   2021/12/20 13:22* Author: Dily* Remark: */
public class Docx4jUtils {/*** 创建一个空白 Docx 文档** @param filePath 文件路径*/public void createDocx(String filePath) throws Docx4JException {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();wordMLPackage.save(new File(filePath));}/*** 加载 Docx 文件** @param filePath 文件地址* @return WordProcessingMLPackage操作包*/public WordprocessingMLPackage loadDocx(String filePath) throws Docx4JException {return WordprocessingMLPackage.load(new File(filePath));}/*** 创建一个空白 Docx 文档** @param outputStream 流*/public void createDocx(OutputStream outputStream) throws Docx4JException {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();wordMLPackage.save(outputStream);}/*** 添加带样式的文本/段落** @param wordMLPackage docx 操作包* @param styled        样式* @param conText       文本* @return WordProcessingMLPackage 操作包*/public WordprocessingMLPackage addContext(WordprocessingMLPackage wordMLPackage, String styled, String conText) {wordMLPackage.getMainDocumentPart().addStyledParagraphOfText(styled, conText);return wordMLPackage;}/*** 添加文本/段落** @param wordMLPackage docx 操作包* @param conText       正文* @return WordProcessingMLPackage 操作包*/public WordprocessingMLPackage addContext(WordprocessingMLPackage wordMLPackage, String conText) {wordMLPackage.getMainDocumentPart().addParagraphOfText(conText);return wordMLPackage;}/*** 添加图片** @param wordMLPackage 操作包* @param imagePath     图片地址* @return WordProcessingMLPackage 操作包*/public WordprocessingMLPackage addImage(WordprocessingMLPackage wordMLPackage, 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 wordMLPackage;}/*** 添加空表格** @param wordMLPackage 操作包* @param row           行数* @param col           列数* @return WordProcessingMLPackage 操作包*/public WordprocessingMLPackage addTable(WordprocessingMLPackage wordMLPackage, int row, int col) {Tbl table = TblFactory.createTable(row, col, 20000 / col);wordMLPackage.getMainDocumentPart().addObject(table);return wordMLPackage;}/*** 添加带数据表格, 数据必须是整齐的* 表头为数据的 key, 表头在第一行** @param wordMLPackage 操作包* @param list          数据* @return WordProcessingMLPackage 操作包*/public WordprocessingMLPackage addTableWithDataAndTopHeader(WordprocessingMLPackage wordMLPackage, 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 wordMLPackage;}/*** 添加带数据表格, 数据必须是整齐的* 表头为数据的 key, 表头在第一列** @param wordMLPackage 操作包* @param list          数据* @return WordProcessingMLPackage 操作包*/public WordprocessingMLPackage addTableWithDataAndLeftHeader(WordprocessingMLPackage wordMLPackage, 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 wordMLPackage;}/*** 保存到流** @param wordMLPackage 操作包* @param outputStream  流* @throws Docx4JException 异常*/public void save(WordprocessingMLPackage wordMLPackage, OutputStream outputStream) throws Docx4JException {wordMLPackage.save(outputStream);}/*** 保存到文件,文件必须为Docx** @param wordMLPackage 操作包* @param docxPath      文件地址* @throws Docx4JException 异常*/public void save(WordprocessingMLPackage wordMLPackage, String docxPath) throws Docx4JException {wordMLPackage.save(new File(docxPath));}/*** 获取文件中所有内容** @param obj      JAXBElement子类* @param toSearch 搜索的类型* @return 符合搜索类型的所有对象*/public List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {List<Object> result = new ArrayList<>();if (obj instanceof JAXBElement)obj = ((JAXBElement<?>) obj).getValue();if (obj.getClass().equals(toSearch))result.add(obj);else if (obj instanceof ContentAccessor) {List<?> children = ((ContentAccessor) obj).getContent();for (Object child : children) {result.addAll(getAllElementFromObject(child, toSearch));}}return result;}/*** 加载模板并替换数据** @param filePath 模板文件路径* @param data     数据属性map* @return 输出文件路径* @throws Exception 异常*/public String replaceData(String filePath, Map<String, String> data, String out) throws Exception {// 生成文件String target = UUID.randomUUID().toString();String outPath = out + target + ".docx";//加载模板文件并创建Word processingMLPackage对象InputStream templateInputStream = new FileInputStream(filePath);WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(templateInputStream);MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();VariablePrepare.prepare(wordMLPackage);// 替换属性documentPart.variableReplace(data);OutputStream os = new FileOutputStream(outPath);ByteArrayOutputStream outputStream = new ByteArrayOutputStream();wordMLPackage.save(outputStream);outputStream.writeTo(os);os.close();outputStream.close();templateInputStream.close();return outPath;}/*** 替换模板Docx中数据和表格数据动态添加** @param filePath      文件路径* @param data          全局替换属性Map* @param tableDataList 列表属性* @return 输出文件路径* @throws Exception 异常*/public String replaceData(String filePath, Map<String, String> data, List<Map<String, Object>> tableDataList, String out) throws Exception {String target = UUID.randomUUID().toString();// 输出文件名String outPath = out + target + ".docx";// 加载模板文件并创建Word processingMLPackage对象InputStream templateInputStream = new FileInputStream(filePath);WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(templateInputStream);// 构造循环列表的数据ClassFinder find = new ClassFinder(Tbl.class);new TraversalUtil(wordMLPackage.getMainDocumentPart().getContent(), find);// 获取第二个表格属性if (find.results.size() > 0) {Tbl table = (Tbl) find.results.get(0);// 第二行约定为模板Tr dynamicTr = (Tr) table.getContent().get(1);// 获取模板行的xml数据String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);// 循环填充模板表格行数据for (Map<String, Object> dataMap : tableDataList) {Tr newTr = (Tr) XmlUtils.unmarshallFromTemplate(dynamicTrXml, dataMap);table.getContent().add(newTr);}// 删除模板行的占位行table.getContent().remove(1);}// 设置全局的变量替换wordMLPackage.getMainDocumentPart().variableReplace(data);Docx4J.save(wordMLPackage, new File(outPath));return outPath;}/*** 替换模板Docx中数据和表格数据动态添加** @param filePath      文件路径* @param data          全局替换属性Map* @param tableDataList 列表属性*/public void replaceData(String filePath, Map<String, String> data, List<Map<String, Object>> tableDataList, OutputStream out) throws Exception {// 加载模板文件并创建Word processingMLPackage对象InputStream templateInputStream = new FileInputStream(filePath);WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(templateInputStream);// 构造循环列表的数据ClassFinder find = new ClassFinder(Tbl.class);new TraversalUtil(wordMLPackage.getMainDocumentPart().getContent(), find);// 获取第一个表格属性if (find.results.size() > 0) {Tbl table = (Tbl) find.results.get(0);// 第二行约定为模板Tr dynamicTr = (Tr) table.getContent().get(1);// 获取模板行的xml数据String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);// 循环填充模板表格行数据tableDataList.forEach(e -> {try {table.getContent().add((Tr) XmlUtils.unmarshallFromTemplate(dynamicTrXml, e));} catch (JAXBException jaxbException) {jaxbException.printStackTrace();}});// 删除模板行的占位行table.getContent().remove(1);}// 设置全局的变量替换wordMLPackage.getMainDocumentPart().variableReplace(data);Docx4J.save(wordMLPackage, out);templateInputStream.close();}/*** 加载模板并替换数据** @param filePath 模板文件路径* @param data     数据属性map*/public void replaceData(String filePath, Map<String, String> data, OutputStream out) throws Exception {//加载模板文件并创建Word processingMLPackage对象InputStream templateInputStream = new FileInputStream(filePath);WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(templateInputStream);MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();VariablePrepare.prepare(wordMLPackage);// 替换属性documentPart.variableReplace(data);wordMLPackage.save(out);templateInputStream.close();}/*** word 转 pdf** @param wordPath word文档地址* @return pdf地址*/public 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 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 "";}
}

四、水印工具类

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

五、docx模板及替换结果

Word模板文档

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

Java docx4j 操作word 1.0相关推荐

  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 poi操作word 2003 2007

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

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

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

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

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

  9. Java SE学习——word作业(0~40)

    word作业 1. 2. 3. 4. public class Demo04 {public static void main(String[] args) {double time = 45.5 / ...

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

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

最新文章

  1. 2019年深度学习的十大预测
  2. mysql数据库主从同步
  3. java多线程生产者与消费者问题_Java多线程详解之四:生产者消费者问题
  4. php回调函数原理和实例
  5. python最新版本-官方宣布不再维护Python2,并每年发布一个新版本
  6. 计算机网络实验(华为eNSP模拟器)——第十二章 VLAN集中管理协议(VCMP)
  7. java中的後綴表達式_求Java堆栈,将中缀算术表达式转换成后缀表达式。
  8. win32 控件的创建和消息响应
  9. oracle导入导出表
  10. 在windows系统安装nginx
  11. Android更新主线程UI的两种方式handler与runOnUiThread()
  12. mdt抓取镜像后只显示回收站_又涨了!废纸价格贵过废铜烂铁,回收站缩减废旧物品收购规模...
  13. 视频教程-VB程序设计教程:从入门到精通-计算机等级考试
  14. 地理图例大全整理初中
  15. 基于zookeeper的统一命名服务及实现
  16. Android Studio中关于消除“Permission is only granted to system apps”错误的方法
  17. 笔记本重装系统如何找回之前自己自带的office
  18. python request 报错 #No JSON object could be decoded
  19. 谷歌等大型科技公司对你了解多深,你想知道吗?
  20. 华为完成首个5G测试;央行搭建区块链平台;苹果将于今夜凌晨举行发布会;蔚来汽车明日在美上市; | 雷锋网9月12日消息...

热门文章

  1. Linux应用层24点小游戏,C++ Builder构建算二十四点小游戏
  2. 用Python推送书籍到Kindle
  3. 天线的特性及微带天线的设计
  4. Windows10系统JDK下载和安装
  5. 一个很好用的JS在线格式化工具
  6. 硬盘安装助手安装苹果Mac系统镜像Change partition type to AF: not a HFS partition的解决方法
  7. 笔记学习:关于如何使用ESPwifi模块与51单片机通信
  8. 计算机装系统找不到硬盘分区,u盘装系统找不到硬盘分区的处理方法
  9. QT网络编程TCP/UDP开发流程 制作网络调试助手
  10. Apex里面的retainAll