Java docx4j 操作word 1.0
一、简介
本工具类复制即可使用,内附测试代码,包含以下操作:
-- 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模板及替换结果
Java docx4j 操作word 1.0相关推荐
- docx4j操作word文档之生成页码、合并多个文档
docx4j操作word文档: 动态生成表格行数并填充数据 单元格内填充图片 合并多个word文档(包含页码,纸张方向等等) 1.动态生成表格行数并填充数据 首先创建模板文件.docx,如图: 代码如 ...
- java poi 操作word遇到的问题
java poi 操作word文本,图表,遇到的问题 直接上问题 模板字段匹配问题 图表问题 图表导出 问题:模板找不到对应图表 问题:数据填充后效果不达目标 图表中为零的数值去掉(!!!模板层面解决 ...
- 怎么用Java来操作Word和Excel?
Java操作word文档 Java 操作word,对word文档进行读写时,主要用 Apache写的POI这个工具类,网上有POI对用的API和文档,在自己实际的项目中可以查找对应的API进行对应的操 ...
- java openxml 操作 word,(三)、JAVA基于OPENXML的word文档插入、合并、替换操作系列之html转word...
(三).JAVA基于OPENXML的word文档插入.合并.替换操作系列之html转word 系列笔记传送门 富文本转word文档 准备待转换内容 内容清理与格式化 转换成word文档 输出结果展示 ...
- java openxml 操作 word,openxml word转成xml
word2007无法打开Office Open XML 文档 由于种种原因,如程序安装错误.注册表被修改,或系统被病毒侵害等. 有时候会造成WORD 2007无法打开.DOC文档的问题,常规的处理办法 ...
- java poi操作word 2003 2007
实现java 替换 word中指定内容,实现word2003 版本 很轻松的 完成了,可是到2007版本时缺头疼了.因为在网上查找的很多相关例子都是需要删除哪一行的信息在setText 添加进去这样不 ...
- java poi操作word模版 导出word文档(附工具类)
模板格式 合同编号:{ contractNumber},合同序号:{ contractSequence} 买家: { buyerName} 卖家:{ sellerName} 业务员: { salesN ...
- java web 操作word文档_Java Web项目中使用Freemarker生成Word文档
Web项目中生成Word文档的操作屡见不鲜.基于Java的解决方式也是非常多的,包含使用Jacob.Apache POI.Java2Word.iText等各种方式,事实上在从Office 2003開始 ...
- Java SE学习——word作业(0~40)
word作业 1. 2. 3. 4. public class Demo04 {public static void main(String[] args) {double time = 45.5 / ...
- Java poi 操作word替换模版中固定参数(页眉、段落、表格)
近期碰到一个稍微头疼的需求,将word模版中的参数替换为实际值,其中包括段落.列表(行数不够时自动递增).页眉:本文以docx文档为例,其中代码有其他地方参考,如有冒犯,还请海涵: 模版: 实现效果: ...
最新文章
- 2019年深度学习的十大预测
- mysql数据库主从同步
- java多线程生产者与消费者问题_Java多线程详解之四:生产者消费者问题
- php回调函数原理和实例
- python最新版本-官方宣布不再维护Python2,并每年发布一个新版本
- 计算机网络实验(华为eNSP模拟器)——第十二章 VLAN集中管理协议(VCMP)
- java中的後綴表達式_求Java堆栈,将中缀算术表达式转换成后缀表达式。
- win32 控件的创建和消息响应
- oracle导入导出表
- 在windows系统安装nginx
- Android更新主线程UI的两种方式handler与runOnUiThread()
- mdt抓取镜像后只显示回收站_又涨了!废纸价格贵过废铜烂铁,回收站缩减废旧物品收购规模...
- 视频教程-VB程序设计教程:从入门到精通-计算机等级考试
- 地理图例大全整理初中
- 基于zookeeper的统一命名服务及实现
- Android Studio中关于消除“Permission is only granted to system apps”错误的方法
- 笔记本重装系统如何找回之前自己自带的office
- python request 报错 #No JSON object could be decoded
- 谷歌等大型科技公司对你了解多深,你想知道吗?
- 华为完成首个5G测试;央行搭建区块链平台;苹果将于今夜凌晨举行发布会;蔚来汽车明日在美上市; | 雷锋网9月12日消息...
热门文章
- Linux应用层24点小游戏,C++ Builder构建算二十四点小游戏
- 用Python推送书籍到Kindle
- 天线的特性及微带天线的设计
- Windows10系统JDK下载和安装
- 一个很好用的JS在线格式化工具
- 硬盘安装助手安装苹果Mac系统镜像Change partition type to AF: not a HFS partition的解决方法
- 笔记学习:关于如何使用ESPwifi模块与51单片机通信
- 计算机装系统找不到硬盘分区,u盘装系统找不到硬盘分区的处理方法
- QT网络编程TCP/UDP开发流程 制作网络调试助手
- Apex里面的retainAll