【docx4j】docx4j操作docx,实现替换内容、转换pdf、html等操作
主要是想要用此功插件操作docx,主要的操作就是操作段落等信息,另外,也想实现替换docx的内容,实现根据模板动态生成内容的效果,也想用此插件实现docx转换pdf。
word的格式其实可以用xml来表现,docx4j也应该是基于xml来操作docx文档的。xml就比较好理解了。我们都是通过doc树的形式操作docx,只不过对于docx4j来说根节点是一个package,我们可以从根节点获取所有的内容,也可以指定元素的类型从document中查找元素集合,用下标访问指定位置的元素。
docx4j官网下载的包本身缺slf4j的支持包,而且转换pdf的时候fop-2.3的包与docx4j的包冲突,在文章最后会将最终整理过的docx4j及其相关依赖包附上下载链接。
1.docx的下载
到官网下载即可,下载的zip包里面有jar包,也有examples,下面的例子就是出自官网的examples。但是官网下载的lib里面日志记录缺失log4j的包和slf4j-log4j包。
官网下载地址:https://www.docx4java.org/downloads.html
2.简单的使用
0. docx4j.properties 可以指定docx的一些全局属性,包括文字方向,纸张大小等。下面是官网给出的一个配置
# Page size: use a value from org.docx4j.model.structure.PageSizePaper enum
# eg A4, LETTER
docx4j.PageSize=LETTER
# Page size: use a value from org.docx4j.model.structure.MarginsWellKnown enum
docx4j.PageMargins=NORMAL
docx4j.PageOrientationLandscape=false
# Page size: use a value from org.pptx4j.model.SlideSizesWellKnown enum
# eg A4, LETTER
pptx4j.PageSize=LETTER
pptx4j.PageOrientationLandscape=false
# These will be injected into docProps/app.xml
# if App.Write=true
docx4j.App.write=true
docx4j.Application=docx4j
docx4j.AppVersion=2.7
# of the form XX.YYYY where X and Y represent numerical values
# These will be injected into docProps/core.xml
docx4j.dc.write=true
docx4j.dc.creator.value=docx4j
docx4j.dc.lastModifiedBy.value=docx4j
#
#docx4j.McPreprocessor=true
# If you haven't configured log4j yourself
# docx4j will autoconfigure it. Set this to true to disable that
docx4j.Log4j.Configurator.disabled=false
1.创建一个新的docx文档
/**
* 创建一个简单的docx
*/
private static void createDocx() {
// Create the package
WordprocessingMLPackage wordMLPackage;
try {
wordMLPackage = WordprocessingMLPackage.createPackage();
// 另存为新的文件
wordMLPackage.save(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx"));
} catch (InvalidFormatException e) {
log.error("createDocx error:InvalidFormatException", e);
} catch (Docx4JException e) {
log.error("createDocx error: Docx4JException", e);
}
}
调用 WordprocessingMLPackage.createPackage(); 创建一个包,并且调用其save(file)就是生成一个新的文件
补充:还有另一种常用的保存方法是:
Docx4J.save(wordMLPackage, new File("C:/Users/liqiang/Desktop/docx4j/helloworld_2.docx"));
2.向文件中增加段落
/**
* 增加一个段落,增加完成记得保存,否则不生效
*/
public static void addParagraph() {
WordprocessingMLPackage wordprocessingMLPackage;
try {
wordprocessingMLPackage = WordprocessingMLPackage
.load(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx"));
wordprocessingMLPackage.getMainDocumentPart().addParagraphOfText("Hello Word!");
wordprocessingMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title", "Hello Word!");
wordprocessingMLPackage.getMainDocumentPart().addStyledParagraphOfText("Subtitle", " a subtitle!");
wordprocessingMLPackage.save(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx"));
} catch (Docx4JException e) {
log.error("addParagraph to docx error: Docx4JException", e);
}
}
调用 WordprocessingMLPackage.load(file) 加载一个已经存在的docx,最后记得调用其save方法进行保存,否则修改不生效。
最后文件内容:
3.第二种采用工厂类增加段落的方法(工厂类的使用,工厂类也是一种通用的方法)
/**
* 增加一个段落,增加完成记得保存,否则不生效
*/
public static void addParagraph2(String simpleText) { try {
WordprocessingMLPackage wordprocessingMLPackage = WordprocessingMLPackage
.load(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx"));
org.docx4j.wml.ObjectFactory factory = Context.getWmlObjectFactory();
org.docx4j.wml.P para = factory.createP();
if (simpleText != null) {
org.docx4j.wml.Text t = factory.createText();
t.setValue(simpleText);
org.docx4j.wml.R run = factory.createR();
run.getContent().add(t);
para.getContent().add(run);
}
wordprocessingMLPackage.getMainDocumentPart().getContent().add(para);
wordprocessingMLPackage.save(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx"));
} catch (Exception e) {
log.error("addParagraph to docx error: Docx4JException", e);
}
}
先创建一个工厂,(需要导入的包是org.docx4j.wml,导错的的话下面全错)。
R是一个运行块,负责便于将多个属性相同的Object对象统一操作,通过其内部的content成员变量可以添加内容,RPr是运行块的属性(属于类R的一个成员变量),可以对R对象进行操作。R通过被作为其他对象的content内容。所以通过R在A元素中加一个B元素的操作的一般步骤是:(1)创建R;(2)将内容元素B加到R中;(3)将R增加到A元素中;(4)将A元素加到mainDocumentPart内容中。
补充:工厂类的一些通用方法:
4.读取文件的内容
private static void readParagraph() {
try {
WordprocessingMLPackage wordprocessingMLPackage = WordprocessingMLPackage
.load(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx")); String contentType = wordprocessingMLPackage.getContentType();
log.info("contentType -> {}", contentType); MainDocumentPart mainDocumentPart = wordprocessingMLPackage.getMainDocumentPart();
List<Object> content = mainDocumentPart.getContent();
for (Object ob : content) {
log.info("ob -> {}", ob);
}
} catch (Docx4JException e) {
log.error("createDocx error: Docx4JException", e);
}
}
结果:
2018-10-28 13:13:16 [cn.qlq.docx4j.Docx4jTest]-[INFO] contentType -> application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml2018-10-28 13:13:16 [cn.qlq.docx4j.Docx4jTest]-[INFO] ob -> Hello Word!
2018-10-28 13:13:16 [cn.qlq.docx4j.Docx4jTest]-[INFO] ob -> Hello Word!
2018-10-28 13:13:16 [cn.qlq.docx4j.Docx4jTest]-[INFO] ob -> a subtitle!
2018-10-28 13:13:16 [cn.qlq.docx4j.Docx4jTest]-[INFO] ob -> Hello Word!
2018-10-28 13:13:16 [cn.qlq.docx4j.Docx4jTest]-[INFO] ob -> Hello Word!
2018-10-28 13:13:16 [cn.qlq.docx4j.Docx4jTest]-[INFO] ob -> a subtitle!
2018-10-28 13:13:16 [cn.qlq.docx4j.Docx4jTest]-[INFO] ob -> Hello Word!
2018-10-28 13:13:16 [cn.qlq.docx4j.Docx4jTest]-[INFO] ob -> Hello Word!
2018-10-28 13:13:16 [cn.qlq.docx4j.Docx4jTest]-[INFO] ob -> a subtitle!
5.创建表格
(1)创建一个普通的表格
public static void addTable() {
try {
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
ObjectFactory factory = Context.getWmlObjectFactory();
MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart(); // 创建表格元素
Tbl table = factory.createTbl();
addBorders(table); for (int i = 0; i < 3; i++) {
Tr tr = factory.createTr();
for (int j = 0; j < 3; j++) {
Tc tc = factory.createTc();
P p = mainDocumentPart.createParagraphOfText("---row" + i + "---column" + j + "---");
tc.getContent().add(p);
tr.getContent().add(tc); }
table.getContent().add(tr);
} mainDocumentPart.addObject(table);
wordMLPackage.save(new java.io.File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx"));
} catch (Docx4JException e) {
log.error("createDocx error: Docx4JException", e);
} }
查看createParagraphOfText(str)的源码:(1.创建一个text,并设置其值,2.创建一个R并将text增加到R中,3.创建一个P将R加到P中)
public org.docx4j.wml.P createParagraphOfText(String simpleText) { org.docx4j.wml.ObjectFactory factory = Context.getWmlObjectFactory();
org.docx4j.wml.P para = factory.createP(); if (simpleText!=null) {
org.docx4j.wml.Text t = factory.createText();
t.setValue(simpleText); org.docx4j.wml.R run = factory.createR();
run.getContent().add(t); // ContentAccessor para.getContent().add(run); // ContentAccessor
} return para;
}
结果:
上面的表格创建出来了,但是表格的边框也没有,接下来研究更复杂的操作,包括显示边框,合并单元格,设置单元格样式。
(2)显示表格的边框
public static void addTable() {
try {
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
ObjectFactory factory = Context.getWmlObjectFactory();
MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart(); // 0. 创建表格元素
Tbl table = factory.createTbl(); // 1.显示表格的边框
addBorders(table); // 2.添加表格内容(创建行和列)
for (int i = 0; i < 3; i++) {
Tr tr = factory.createTr();
for (int j = 0; j < 3; j++) {
Tc tc = factory.createTc();
P p = mainDocumentPart.createParagraphOfText("---row" + i + "---column" + j + "---");// tc.getContent().add(p);
tr.getContent().add(tc); }
table.getContent().add(tr);
} // 3.加表格加到主要内容中
mainDocumentPart.addObject(table);
wordMLPackage.save(new java.io.File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx"));
} catch (Docx4JException e) {
log.error("createDocx error: Docx4JException", e);
}
} /**
* 设置边框样式
*
* @param table
* 需要设置表格边框的单元格
*/
private static void addBorders(Tbl table) {
table.setTblPr(new TblPr());// 必须设置一个TblPr,否则最后会报空指针异常 CTBorder border = new CTBorder();
border.setColor("auto");
border.setSz(new BigInteger("4"));
border.setSpace(new BigInteger("0"));
border.setVal(STBorder.SINGLE); TblBorders borders = new TblBorders();
borders.setBottom(border);
borders.setLeft(border);
borders.setRight(border);
borders.setTop(border);
borders.setInsideH(border);
borders.setInsideV(border); // 获取其内部的TblPr属性设置属性
table.getTblPr().setTblBorders(borders);
}
结果:
(3)设置表格居中显示,而且内容部分字体加粗,设置列宽等操作
public static void addTable() {
try {
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
ObjectFactory factory = Context.getWmlObjectFactory();
MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart(); // 0. 创建表格元素
Tbl table = factory.createTbl(); // 1.显示表格的边框
addBorders(table); // 2.添加表格内容(创建行和列)
for (int i = 0; i < 3; i++) {
Tr tr = factory.createTr();
for (int j = 0; j < 3; j++) {
Tc tc = factory.createTc(); // P p = mainDocumentPart.createParagraphOfText("---row" + i
// + "---column" + j + "---");
// 第二种创建P并设置样式的方法
P p1 = factory.createP();
R r = factory.createR();
Text text = factory.createText();
text.setValue("---row" + i + "---column" + j + "---"); r.getContent().add(text);
p1.getContent().add(r); // 2.1通过R设置字体加粗等属性
setRStyle(r);
// 2.2设置列宽
if (j == 1) {
setCellWidth(tc, 1250);
} else {
setCellWidth(tc, 2500);
} tc.getContent().add(p1);
tr.getContent().add(tc); }
table.getContent().add(tr);
} // 3.合并单元格 // 3.加表格加到主要内容中
mainDocumentPart.addObject(table);
wordMLPackage.save(new java.io.File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx"));
} catch (Docx4JException e) {
log.error("createDocx error: Docx4JException", e);
}
} /**
* 设置列宽
*
* @param tc
* @param width
*/
private static void setCellWidth(Tc tc, int width) {
TcPr tableCellProperties = new TcPr();
TblWidth tableWidth = new TblWidth();
tableWidth.setW(BigInteger.valueOf(width));
tableCellProperties.setTcW(tableWidth); tc.setTcPr(tableCellProperties);
} /**
* 通过设置R设置表格中属性字体加粗,大小为25
*
* @param
*/
private static void setRStyle(R r) {
// 1.创建一个RPr
RPr rpr = new RPr(); // 2.设置RPr
// 2.1设置字体大小
HpsMeasure size = new HpsMeasure();
size.setVal(new BigInteger("25"));
rpr.setSz(size);
// 2.2设置加粗
BooleanDefaultTrue bold = new BooleanDefaultTrue();
bold.setVal(true);
rpr.setB(bold); // 3.将RPr设置为R的属性
r.setRPr(rpr);
} /**
* 设置边框样式
*
* @param table
* 需要设置表格边框的单元格
*/
private static void addBorders(Tbl table) {
table.setTblPr(new TblPr());// 必须设置一个TblPr,否则最后会报空指针异常 CTBorder border = new CTBorder();
border.setColor("auto");
border.setSz(new BigInteger("4"));
border.setSpace(new BigInteger("0"));
border.setVal(STBorder.SINGLE); TblBorders borders = new TblBorders();
borders.setBottom(border);
borders.setLeft(border);
borders.setRight(border);
borders.setTop(border);
borders.setInsideH(border);
borders.setInsideV(border); // 获取其内部的TblPr属性设置属性
table.getTblPr().setTblBorders(borders);
}
结果:
关于表格合并或者更加复杂的操作参考:https://www.cnblogs.com/cxxjohnson/p/7886275.html
6.读取表格内容:(解析docx4j的树结构---获取指定类型的元素)
表格内容:
代码:(有时候我们调用getContent()获取的元素类型是Tr之类的直接元素,可以强转;有时候不可以直接强转,其类型是JAXBElement,需要进行提取---getAllElementFromObject方法)
package cn.qlq.docx4j; import java.util.ArrayList;
import java.util.List; import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException; import org.docx4j.TraversalUtil;
import org.docx4j.finders.ClassFinder;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.Tc;
import org.docx4j.wml.Tr; /**
* 循环替换表格内容
*
* @author QiaoLiQiang
* @time 2018年10月28日下午8:51:41
*/
public class ReplaceTable { public static void main(String[] args) throws JAXBException {
String template = "C:/Users/liqiang/Desktop/docx4j/helloworld_1.docx";
WordprocessingMLPackage wordMLPackage;
try {
wordMLPackage = WordprocessingMLPackage.load(new java.io.File(template));
MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart(); // 1. ClassFinder 构造类型查询器获取指定元素
ClassFinder find = new ClassFinder(Tbl.class);
new TraversalUtil(documentPart.getContent(), find); Tbl table = (Tbl) find.results.get(0);// 获取到第一个表格元素
List<Object> trs = table.getContent();
System.out.println(trs);
System.out.println("====================="); for (Object obj : trs) {
Tr tr = (Tr) obj;// 获取到tr
List<Object> content = tr.getContent();
System.out.println(content);
List<Object> objList = getAllElementFromObject(tr, Tc.class);// 获取所有的Tc元素
for (Object obj1 : objList) {
Tc tc = (Tc) obj1;
System.out.println(tc.getContent());
}
System.out.println("===============");
}
} catch (Docx4JException e) {
e.printStackTrace();
}
} private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
List<Object> result = new ArrayList<Object>();
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;
}
}
结果:
[org.docx4j.wml.Tr@234f18c8, org.docx4j.wml.Tr@1de40494, org.docx4j.wml.Tr@64e89fe0, org.docx4j.wml.Tr@64585ee1, org.docx4j.wml.Tr@65bd393e, org.docx4j.wml.Tr@69f949a0]
=====================
[javax.xml.bind.JAXBElement@6d50ddba, javax.xml.bind.JAXBElement@580d1667, javax.xml.bind.JAXBElement@4339f15a]
[姓名]
[性别]
[年龄]
===============
[javax.xml.bind.JAXBElement@11146e31, javax.xml.bind.JAXBElement@544e5bb9, javax.xml.bind.JAXBElement@6467f9ec]
[name0]
[sex0]
[age0]
===============
[javax.xml.bind.JAXBElement@66492873, javax.xml.bind.JAXBElement@4cfeca7b, javax.xml.bind.JAXBElement@6b9f78ba]
[name1]
[sex1]
[age1]
===============
[javax.xml.bind.JAXBElement@32af3289, javax.xml.bind.JAXBElement@c1eda5e, javax.xml.bind.JAXBElement@3d925789]
[name2]
[sex2]
[age2]
===============
[javax.xml.bind.JAXBElement@52b102f3, javax.xml.bind.JAXBElement@6338c9ee, javax.xml.bind.JAXBElement@25515b26]
[name3]
[sex3]
[age3]
===============
[javax.xml.bind.JAXBElement@372eee, javax.xml.bind.JAXBElement@26ea0b5e, javax.xml.bind.JAXBElement@4f905c47]
[name4]
[sex4]
[age4]
===============
7.格式化样式的操作:
有时候我们需要格式化一些样式,每个元素内部都有一个XXXpr属性用于操作样式,Pr表示Properties,如下:
3.docx4j高级用法
1.docx转换为html
package cn.qlq.docx4j; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream; import org.docx4j.Docx4J;
import org.docx4j.Docx4jProperties;
import org.docx4j.convert.out.HTMLSettings;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.samples.AbstractSample; public class Docx2Html extends AbstractSample { static { inputfilepath = "C:/Users/liqiang/Desktop/docx4j/helloworld.docx";
save = true;
nestLists = true;
} static boolean save;
static boolean nestLists; public static void main(String[] args) throws Exception {
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage
.load(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx")); HTMLSettings htmlSettings = Docx4J.createHTMLSettings(); htmlSettings.setImageDirPath(inputfilepath + "_files");
htmlSettings.setImageTargetUri(inputfilepath.substring(inputfilepath.lastIndexOf("/") + 1) + "_files");
htmlSettings.setWmlPackage(wordMLPackage); String userCSS = null;
if (nestLists) {
userCSS = "html, body, div, span, h1, h2, h3, h4, h5, h6, p, a, img, table, caption, tbody, tfoot, thead, tr, th, td "
+ "{ margin: 0; padding: 0; border: 0;}" + "body {line-height: 1;} ";
} else {
userCSS = "html, body, div, span, h1, h2, h3, h4, h5, h6, p, a, img, ol, ul, li, table, caption, tbody, tfoot, thead, tr, th, td "
+ "{ margin: 0; padding: 0; border: 0;}" + "body {line-height: 1;} "; }
htmlSettings.setUserCSS(userCSS); OutputStream os;
if (save) {
os = new FileOutputStream(inputfilepath + ".html");
} else {
os = new ByteArrayOutputStream();
} Docx4jProperties.setProperty("docx4j.Convert.Out.HTML.OutputMethodXML", true); Docx4J.toHTML(htmlSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL); if (save) {
System.out.println("Saved: " + inputfilepath + ".html ");
} else {
System.out.println(((ByteArrayOutputStream) os).toString());
} if (wordMLPackage.getMainDocumentPart().getFontTablePart() != null) {
wordMLPackage.getMainDocumentPart().getFontTablePart().deleteEmbeddedFontTempFiles();
}
htmlSettings = null;
wordMLPackage = null;
}
}
封装为一个更简单的工具类的代码如下:(userCSS是生成的html的样式,可以手动设置,使用此参数可以灵活的设置边距字体等信息)
package cn.qlq.docx4j; import java.io.File;
import java.io.FileOutputStream; import org.docx4j.Docx4J;
import org.docx4j.Docx4jProperties;
import org.docx4j.convert.out.HTMLSettings;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.samples.AbstractSample; public class Docx2Html extends AbstractSample { public static void main(String[] args) throws Exception {
String inputfilepath = "C:/Users/liqiang/Desktop/docx4j/helloworld.docx";
boolean nestLists = true; WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage
.load(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx")); HTMLSettings htmlSettings = Docx4J.createHTMLSettings(); htmlSettings.setImageDirPath(inputfilepath + "_files");
htmlSettings.setImageTargetUri(inputfilepath.substring(inputfilepath.lastIndexOf("/") + 1) + "_files");
htmlSettings.setWmlPackage(wordMLPackage); String userCSS = null;
if (nestLists) {
userCSS = "html, body, div, span, h1, h2, h3, h4, h5, h6, p, a, img, table, caption, tbody, tfoot, thead, tr, th, td "
+ "{ margin: 0; padding: 0; border: 0;}" + "body {line-height: 1;} ";
} else {
userCSS = "html, body, div, span, h1, h2, h3, h4, h5, h6, p, a, img, ol, ul, li, table, caption, tbody, tfoot, thead, tr, th, td "
+ "{ margin: 0; padding: 0; border: 0;}" + "body {line-height: 1;} "; }
htmlSettings.setUserCSS(userCSS); Docx4jProperties.setProperty("docx4j.Convert.Out.HTML.OutputMethodXML", true); Docx4J.toHTML(htmlSettings, new FileOutputStream(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.html")),
Docx4J.FLAG_EXPORT_PREFER_XSL); if (wordMLPackage.getMainDocumentPart().getFontTablePart() != null) {
wordMLPackage.getMainDocumentPart().getFontTablePart().deleteEmbeddedFontTempFiles();
}
htmlSettings = null;
wordMLPackage = null;
}
}
2.docx转换为pdf
代码简单,但是依赖的包比较多,依赖了batik解析SVG的项目包,也依赖fop包,而且docx4j-community-6.0.1.zip里面自带的optional\export-fo下面的fop-2.3.jar与docx冲突,所以需要fop-2.1版本才可以转换。所以需要删掉自带的2.3版本,自行下载2.1版本。
public static void main(String[] args) throws Exception {
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage
.load(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx")); Docx4J.toPDF(wordMLPackage, new FileOutputStream(new File("C:/Users/liqiang/Desktop/docx4j/helloworld.pdf"))); }
3.docx中写入图片
package cn.qlq.docx4j; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream; import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.R; public class ImageHandle {
/**
* 像往常一样, 我们创建了一个包(package)来容纳文档. 然后我们创建了一个指向将要添加到文档的图片的文件对象.为了能够对图片做一些操作,
* 我们将它转换 为字节数组. 最后我们将图片添加到包中并保存这个包(package).
*/
public static void main(String[] args) throws Exception {
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
File file = new File("C:/Users/liqiang/Desktop/docx4j/3.jpg");
byte[] bytes = convertImageToByteArray(file);
addImageToPackage(wordMLPackage, bytes);
wordMLPackage.save(new java.io.File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx"));
} /**
* Docx4j拥有一个由字节数组创建图片部件的工具方法, 随后将其添加到给定的包中. 为了能将图片添加 到一个段落中,
* 我们需要将图片转换成内联对象. 这也有一个方法, 方法需要文件名提示, 替换文本, 两个id标识符和一个是嵌入还是链接到的指示作为参数.
* 一个id用于文档中绘图对象不可见的属性, 另一个id用于图片本身不可见的绘制属性. 最后我们将内联 对象添加到段落中并将段落添加到包的主文档部件.
*
* @param wordMLPackage
* 要添加图片的包
* @param bytes
* 图片对应的字节数组
* @throws Exception
* 不幸的createImageInline方法抛出一个异常(没有更多具体的异常类型)
*/
private static void addImageToPackage(WordprocessingMLPackage wordMLPackage, byte[] bytes) throws Exception {
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes); int docPrId = 1;
int cNvPrId = 2;
org.docx4j.dml.wordprocessingDrawing.Inline inline = imagePart.createImageInline("Filename hint",
"Alternative text", docPrId, cNvPrId, false); P paragraph = addInlineImageToParagraph(inline); wordMLPackage.getMainDocumentPart().addObject(paragraph);
} /**
* 创建一个对象工厂并用它创建一个段落和一个可运行块R. 然后将可运行块添加到段落中. 接下来创建一个图画并将其添加到可运行块R中. 最后我们将内联
* 对象添加到图画中并返回段落对象.
*
* @param inline
* 包含图片的内联对象.
* @return 包含图片的段落
*/
private static P addInlineImageToParagraph(org.docx4j.dml.wordprocessingDrawing.Inline inline) {
// 添加内联对象到一个段落中
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);
return paragraph;
} /**
* 将图片从文件对象转换成字节数组.
*
* @param file
* 将要转换的文件
* @return 包含图片字节数据的字节数组
* @throws FileNotFoundException
* @throws IOException
*/
private static byte[] convertImageToByteArray(File file) throws FileNotFoundException, IOException {
InputStream is = new FileInputStream(file);
long length = file.length();
// 不能使用long类型创建数组, 需要用int类型.
if (length > Integer.MAX_VALUE) {
System.out.println("File too large!!");
}
byte[] bytes = new byte[(int) length];
int offset = 0;
int numRead = 0;
while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
// 确认所有的字节都没读取
if (offset < bytes.length) {
System.out.println("Could not completely read file " + file.getName());
}
is.close();
return bytes;
}
}
4.按指定变量替换docx中的内容 ${var}替换
注意:模板的${var}在书写的时候必须从左向右书写(不能直接{},然后在中间写括号),也就是转换成XML之后${var}必须是连续的,否则取不到变量。有时候取不到变量的时候可以抓换为xml然后查看你的变量是否是连续的。
public static void main(String[] args) throws Exception { org.docx4j.wml.ObjectFactory foo = Context.getWmlObjectFactory(); String template = "C:/Users/liqiang/Desktop/docx4j/helloworld.docx"; boolean save = true;
String outputfilepath = "C:/Users/liqiang/Desktop/docx4j/helloworld_1.docx"; WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new java.io.File(template));
MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart(); //需要替换的map
HashMap<String, String> mappings = new HashMap<String, String>();
mappings.put("sex", "男");
mappings.put("age", "25");
mappings.put("username", "qlq"); long start = System.currentTimeMillis(); documentPart.variableReplace(mappings); long end = System.currentTimeMillis();
long total = end - start;
System.out.println("Time: " + total); // Save it
if (save) {
SaveToZipFile saver = new SaveToZipFile(wordMLPackage);
saver.save(outputfilepath);
} else {
System.out.println(XmlUtils.marshaltoString(documentPart.getJaxbElement(), true, true));
}
}
模板:
结果:
补充:在docx进行变量替换的时候其格式也生效,比如我的模板:
最后的结果:
5.替换模板里面的表格(循环替换标签)
模板内容:
代码:
package cn.qlq.docx4j; import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import javax.xml.bind.JAXBException; import org.docx4j.Docx4J;
import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.finders.ClassFinder;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.Tr; /**
* 循环替换表格内容
*
* @author QiaoLiQiang
* @time 2018年10月28日下午8:51:41
*/
public class ReplaceTable { public static void main(String[] args) throws JAXBException {
org.docx4j.wml.ObjectFactory objectFactory = Context.getWmlObjectFactory(); String template = "C:/Users/liqiang/Desktop/docx4j/helloworld.docx"; boolean save = true;
String outputfilepath = "C:/Users/liqiang/Desktop/docx4j/helloworld_1.docx"; WordprocessingMLPackage wordMLPackage;
try {
wordMLPackage = WordprocessingMLPackage.load(new java.io.File(template));
MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart(); // 构造循环列表的数据
ClassFinder find = new ClassFinder(Tbl.class);
new TraversalUtil(wordMLPackage.getMainDocumentPart().getContent(), find);
Tbl table = (Tbl) find.results.get(0);//获取到第一个表格元素
Tr dynamicTr = (Tr) table.getContent().get(1);// 第二行约定为模板,获取到第二行内容
String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);// 获取模板行的xml数据 List<Map<String, Object>> dataList = getDataList();
for (Map<String, Object> dataMap : dataList) {
Tr newTr = (Tr) XmlUtils.unmarshallFromTemplate(dynamicTrXml, dataMap);// 填充模板行数据
table.getContent().add(newTr);
} // 删除模板行的占位行
table.getContent().remove(1); Docx4J.save(wordMLPackage, new File(outputfilepath));
} catch (Docx4JException e) {
e.printStackTrace();
}
} private static List<Map<String, Object>> getDataList() {
List list = new ArrayList();
for (int i = 0; i < 5; i++) {
Map map = new HashMap();
map.put("name", "name" + i);
map.put("sex", "sex" + i);
map.put("age", "age" + i);
list.add(map);
}
return list;
}
}
结果:
6.按书签替换内容(替换变量、表格、图片等格式数据)
这种方式比基于变量的方式灵活,而且操作简单,我们只用在word中插入书签,如下:(word中不能插入同名书签)
代码:
package cn.qlq.docx4j; import java.io.File;
import java.io.FileInputStream;
import java.util.List; import org.apache.commons.io.IOUtils;
import org.docx4j.Docx4J;
import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.finders.RangeFinder;
import org.docx4j.jaxb.Context;
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.Body;
import org.docx4j.wml.CTBookmark;
import org.docx4j.wml.CTMarkupRange;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.Document;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.PPr;
import org.docx4j.wml.ParaRPr;
import org.docx4j.wml.R;
import org.docx4j.wml.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* 通过书签替换变量
*
* @author QiaoLiQiang
* @time 2018年10月28日下午9:35:52
*/
public class BooknameReplaceVar {
private static final Logger log = LoggerFactory.getLogger(BooknameReplaceVar.class);
private static WordprocessingMLPackage wordMLPackage;
private static ObjectFactory factory; public static void main(String[] args) {
bookReplaceVarText();
} private static void bookReplaceVarText() {
String template = "C:/Users/liqiang/Desktop/docx4j/helloworld_1.docx"; try {
wordMLPackage = WordprocessingMLPackage.load(new java.io.File(template));
MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();
factory = Context.getWmlObjectFactory(); Document wmlDoc = (Document) mainDocumentPart.getJaxbElement();
Body body = wmlDoc.getBody();
// 提取正文中所有段落
List<Object> paragraphs = body.getContent();
// 提取书签并创建书签的游标
RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange");
new TraversalUtil(paragraphs, rt);
// 遍历书签
for (CTBookmark bm : rt.getStarts()) {
log.info("标签名称:" + bm.getName());
// 这儿可以对单个书签进行操作,也可以用一个map对所有的书签进行处理
if (bm.getName().equals("name")) {
replaceText(bm, "qlq");
}
if (bm.getName().equals("pic")) {
addImage(wordMLPackage, bm, "C:/Users/liqiang/Desktop/docx4j/3.jpg");
}
}
Docx4J.save(wordMLPackage, new File("C:/Users/liqiang/Desktop/docx4j/helloworld_2.docx")); } catch (Docx4JException e) {
log.error("bookReplaceVarText error:Docx4JException ", e);
} catch (Exception e) {
log.error("bookReplaceVarText error:Docx4JException ", e);
}
} /**
* 在标签处插入内容
*
* @param bm
* @param wPackage
* @param object
* @throws Exception
*/
public static void replaceText(CTBookmark bm, Object object) throws Exception {
if (object == null) {
return;
}
// do we have data for this one?
if (bm.getName() == null)
return;
String value = object.toString();
try {
// Can't just remove the object from the parent,
// since in the parent, it may be wrapped in a JAXBElement
List<Object> theList = null;
ParaRPr rpr = null;
if (bm.getParent() instanceof P) {
PPr pprTemp = ((P) (bm.getParent())).getPPr();
if (pprTemp == null) {
rpr = null;
} else {
rpr = ((P) (bm.getParent())).getPPr().getRPr();
}
theList = ((ContentAccessor) (bm.getParent())).getContent();
} else {
return;
}
int rangeStart = -1;
int rangeEnd = -1;
int i = 0;
for (Object ox : theList) {
Object listEntry = XmlUtils.unwrap(ox);
if (listEntry.equals(bm)) { if (((CTBookmark) listEntry).getName() != null) { rangeStart = i + 1; }
} else if (listEntry instanceof CTMarkupRange) {
if (((CTMarkupRange) listEntry).getId().equals(bm.getId())) {
rangeEnd = i - 1; break;
}
}
i++;
}
int x = i - 1;
// if (rangeStart > 0 && x >= rangeStart) {
// Delete the bookmark range
for (int j = x; j >= rangeStart; j--) {
theList.remove(j);
}
// now add a run
org.docx4j.wml.R run = factory.createR();
org.docx4j.wml.Text t = factory.createText();
// if (rpr != null)
// run.setRPr(paraRPr2RPr(rpr));
t.setValue(value);
run.getContent().add(t);
// t.setValue(value); theList.add(rangeStart, run);
// }
} catch (ClassCastException cce) {
log.error("error", cce);
}
} /**
* 插入图片
*
* @param wPackage
* @param bm
* @param file
*/
public static void addImage(WordprocessingMLPackage wPackage, CTBookmark bm, String file) {
log.info("addImage :->{},{},{}", wPackage, bm,file);
try {
// 这儿可以对单个书签进行操作,也可以用一个map对所有的书签进行处理
// 获取该书签的父级段落
P p = (P) (bm.getParent());
// R对象是匿名的复杂类型,然而我并不知道具体啥意思,估计这个要好好去看看ooxml才知道
R run = factory.createR();
// 读入图片并转化为字节数组,因为docx4j只能字节数组的方式插入图片
byte[] bytes = IOUtils.toByteArray(new FileInputStream(file)); // InputStream is = new FileInputStream;
// byte[] bytes = IOUtils.toByteArray(inputStream);
// byte[] bytes = FileUtil.getByteFormBase64DataByImage("");
// 开始创建一个行内图片
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wPackage, bytes);
// createImageInline函数的前四个参数我都没有找到具体啥意思,,,,
// 最有一个是限制图片的宽度,缩放的依据
Inline inline = imagePart.createImageInline(null, null, 0, 1, false, 0);
// 获取该书签的父级段落
// drawing理解为画布?
Drawing drawing = factory.createDrawing();
drawing.getAnchorOrInline().add(inline);
run.getContent().add(drawing);
p.getContent().add(run);
} catch (Exception e) {
log.error("", e);
}
}
}
结果:上面的文本内容可以被完美的替换掉。如果在一般项目中进行替换书签为字符串变量已经足够了,替换图片还是有点格式问题。接下来还会继续研究书签的使用,尝试更简便的方式替换内容。
通过书签和变量改变的内容的样式会和自定义的样式保持一致。
参考:https://github.com/plutext/docx4j/blob/master/src/samples
jar包下载地址:http://qiaoliqiang.cn/fileDown/docx4j-6.0.1-all.zip
补充:网上一位大哥写的工具类,比较实用:
测试代码;
/**
* 增加一个表格
*/
public static void addTable() {
try {
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
ObjectFactory factory = Context.getWmlObjectFactory();
MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart(); // 0. 创建表格元素
Tbl table = factory.createTbl(); // 1.显示表格的边框
addBorders(table); // 2.添加表格内容(创建行和列)
for (int i = 0; i < 3; i++) {
Tr tr = factory.createTr();
for (int j = 0; j < 3; j++) {
Tc tc = factory.createTc(); // P p = mainDocumentPart.createParagraphOfText("---row" + i
// + "---column" + j + "---");
// 第二种创建P并设置样式的方法
P p1 = factory.createP();
R r = factory.createR();
Text text = factory.createText();
text.setValue("---row" + i + "---column" + j + "---"); r.getContent().add(text);
p1.getContent().add(r); // 2.1通过R设置字体加粗等属性
setRStyle(r);
// 2.2设置列宽
if (j == 1) {
setCellWidth(tc, 1250);
} else {
setCellWidth(tc, 2500);
} tc.getContent().add(p1);
tr.getContent().add(tc); }
table.getContent().add(tr);
} // 3.合并单元格
Docx4jUtil.mergeCellsHorizontal(table, 1, 0, 2); // 3.加表格加到主要内容中
mainDocumentPart.addObject(table);
wordMLPackage.save(new java.io.File("C:/Users/liqiang/Desktop/docx4j/helloworld.docx"));
} catch (Docx4JException e) {
log.error("createDocx error: Docx4JException", e);
}
} /**
* 设置列宽
*
* @param tc
* @param width
*/
private static void setCellWidth(Tc tc, int width) {
TcPr tableCellProperties = new TcPr();
TblWidth tableWidth = new TblWidth();
tableWidth.setW(BigInteger.valueOf(width));
tableCellProperties.setTcW(tableWidth); tc.setTcPr(tableCellProperties);
} /**
* 通过设置R设置表格中属性字体加粗,大小为25
*
* @param
*/
private static void setRStyle(R r) {
// 1.创建一个RPr
RPr rpr = new RPr(); // 2.设置RPr
// 2.1设置字体大小
HpsMeasure size = new HpsMeasure();
size.setVal(new BigInteger("25"));
rpr.setSz(size);
// 2.2设置加粗
BooleanDefaultTrue bold = new BooleanDefaultTrue();
bold.setVal(true);
rpr.setB(bold); // 3.将RPr设置为R的属性
r.setRPr(rpr);
} /**
* 设置边框样式
*
* @param table
* 需要设置表格边框的单元格
*/
private static void addBorders(Tbl table) {
table.setTblPr(new TblPr());// 必须设置一个TblPr,否则最后会报空指针异常 CTBorder border = new CTBorder();
border.setColor("auto");
border.setSz(new BigInteger("4"));
border.setSpace(new BigInteger("0"));
border.setVal(STBorder.SINGLE); TblBorders borders = new TblBorders();
borders.setBottom(border);
borders.setLeft(border);
borders.setRight(border);
borders.setTop(border);
borders.setInsideH(border);
borders.setInsideV(border); // 获取其内部的TblPr属性设置属性
table.getTblPr().setTblBorders(borders);
}
结果:
工具栏:
package cn.qlq.docx4j; import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List; import javax.xml.bind.JAXBElement; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.docx4j.TextUtils;
import org.docx4j.XmlUtils;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.model.properties.table.tr.TrHeight;
import org.docx4j.openpackaging.packages.OpcPackage;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.wml.BooleanDefaultTrue;
import org.docx4j.wml.Br;
import org.docx4j.wml.CTBackground;
import org.docx4j.wml.CTBorder;
import org.docx4j.wml.CTEm;
import org.docx4j.wml.CTHeight;
import org.docx4j.wml.CTLineNumber;
import org.docx4j.wml.CTShd;
import org.docx4j.wml.CTSignedHpsMeasure;
import org.docx4j.wml.CTSignedTwipsMeasure;
import org.docx4j.wml.CTTblCellMar;
import org.docx4j.wml.CTTextScale;
import org.docx4j.wml.CTVerticalAlignRun;
import org.docx4j.wml.CTVerticalJc;
import org.docx4j.wml.Color;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.Highlight;
import org.docx4j.wml.HpsMeasure;
import org.docx4j.wml.Jc;
import org.docx4j.wml.JcEnumeration;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.P.Hyperlink;
import org.docx4j.wml.PPr;
import org.docx4j.wml.PPrBase.Ind;
import org.docx4j.wml.PPrBase.PBdr;
import org.docx4j.wml.PPrBase.Spacing;
import org.docx4j.wml.ParaRPr;
import org.docx4j.wml.R;
import org.docx4j.wml.RFonts;
import org.docx4j.wml.RPr;
import org.docx4j.wml.STBorder;
import org.docx4j.wml.STBrType;
import org.docx4j.wml.STEm;
import org.docx4j.wml.STLineNumberRestart;
import org.docx4j.wml.STLineSpacingRule;
import org.docx4j.wml.STPageOrientation;
import org.docx4j.wml.STShd;
import org.docx4j.wml.STVerticalAlignRun;
import org.docx4j.wml.STVerticalJc;
import org.docx4j.wml.SectPr;
import org.docx4j.wml.SectPr.PgBorders;
import org.docx4j.wml.SectPr.PgMar;
import org.docx4j.wml.SectPr.PgSz;
import org.docx4j.wml.SectPr.Type;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.TblBorders;
import org.docx4j.wml.TblGrid;
import org.docx4j.wml.TblGridCol;
import org.docx4j.wml.TblPr;
import org.docx4j.wml.TblWidth;
import org.docx4j.wml.Tc;
import org.docx4j.wml.TcPr;
import org.docx4j.wml.TcPrInner.GridSpan;
import org.docx4j.wml.TcPrInner.HMerge;
import org.docx4j.wml.TcPrInner.VMerge;
import org.docx4j.wml.Text;
import org.docx4j.wml.TextDirection;
import org.docx4j.wml.Tr;
import org.docx4j.wml.TrPr;
import org.docx4j.wml.U;
import org.docx4j.wml.UnderlineEnumeration; public class Docx4jUtil { /*------------------------------------other--------------------------------------------------- */
/**
* @Description:新增超链接
*/
public static void createHyperlink(WordprocessingMLPackage wordMLPackage, MainDocumentPart mainPart,
ObjectFactory factory, P paragraph, String url, String value, String cnFontName, String enFontName,
String fontSize) throws Exception {
if (StringUtils.isBlank(enFontName)) {
enFontName = "Times New Roman";
}
if (StringUtils.isBlank(cnFontName)) {
cnFontName = "微软雅黑";
}
if (StringUtils.isBlank(fontSize)) {
fontSize = "22";
}
org.docx4j.relationships.ObjectFactory reFactory = new org.docx4j.relationships.ObjectFactory();
org.docx4j.relationships.Relationship rel = reFactory.createRelationship();
rel.setType(Namespaces.HYPERLINK);
rel.setTarget(url);
rel.setTargetMode("External");
mainPart.getRelationshipsPart().addRelationship(rel);
StringBuffer sb = new StringBuffer();
// addRelationship sets the rel's @Id
sb.append("<w:hyperlink r:id=\"");
sb.append(rel.getId());
sb.append("\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" ");
sb.append("xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" >");
sb.append("<w:r><w:rPr><w:rStyle w:val=\"Hyperlink\" />");
sb.append("<w:rFonts w:ascii=\"");
sb.append(enFontName);
sb.append("\" w:hAnsi=\"");
sb.append(enFontName);
sb.append("\" w:eastAsia=\"");
sb.append(cnFontName);
sb.append("\" w:hint=\"eastAsia\"/>");
sb.append("<w:sz w:val=\"");
sb.append(fontSize);
sb.append("\"/><w:szCs w:val=\"");
sb.append(fontSize);
sb.append("\"/></w:rPr><w:t>");
sb.append(value);
sb.append("</w:t></w:r></w:hyperlink>"); Hyperlink link = (Hyperlink) XmlUtils.unmarshalString(sb.toString());
paragraph.getContent().add(link);
} public static String getElementContent(Object obj) throws Exception {
StringWriter stringWriter = new StringWriter();
TextUtils.extractText(obj, stringWriter);
return stringWriter.toString();
} /**
* @Description:得到指定类型的元素
*/
public static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
List<Object> result = new ArrayList<Object>();
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;
} /**
* @Description:保存WordprocessingMLPackage
*/
public static void saveWordPackage(WordprocessingMLPackage wordPackage, File file) throws Exception {
wordPackage.save(file);
} /**
* @Description:新建WordprocessingMLPackage
*/
public static WordprocessingMLPackage createWordprocessingMLPackage() throws Exception {
return WordprocessingMLPackage.createPackage();
} /**
* @Description:加载带密码WordprocessingMLPackage
*/
public static WordprocessingMLPackage loadWordprocessingMLPackageWithPwd(String filePath, String password)
throws Exception {
OpcPackage opcPackage = WordprocessingMLPackage.load(new java.io.File(filePath), password);
WordprocessingMLPackage wordMLPackage = (WordprocessingMLPackage) opcPackage;
return wordMLPackage;
} /**
* @Description:加载WordprocessingMLPackage
*/
public static WordprocessingMLPackage loadWordprocessingMLPackage(String filePath) throws Exception {
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new java.io.File(filePath));
return wordMLPackage;
} /*------------------------------------Word 表格相关--------------------------------------------------- */
/**
* @Description: 跨列合并
*/
public static void mergeCellsHorizontalByGridSpan(Tbl tbl, int row, int fromCell, int toCell) {
if (row < 0 || fromCell < 0 || toCell < 0) {
return;
}
List<Tr> trList = getTblAllTr(tbl);
if (row > trList.size()) {
return;
}
Tr tr = trList.get(row);
List<Tc> tcList = getTrAllCell(tr);
for (int cellIndex = Math.min(tcList.size() - 1, toCell); cellIndex >= fromCell; cellIndex--) {
Tc tc = tcList.get(cellIndex);
TcPr tcPr = getTcPr(tc);
if (cellIndex == fromCell) {
GridSpan gridSpan = tcPr.getGridSpan();
if (gridSpan == null) {
gridSpan = new GridSpan();
tcPr.setGridSpan(gridSpan);
}
gridSpan.setVal(BigInteger.valueOf(Math.min(tcList.size() - 1, toCell) - fromCell + 1));
} else {
tr.getContent().remove(cellIndex);
}
}
} /**
* @Description: 跨列合并
*/
public static void mergeCellsHorizontal(Tbl tbl, int row, int fromCell, int toCell) {
if (row < 0 || fromCell < 0 || toCell < 0) {
return;
}
List<Tr> trList = getTblAllTr(tbl);
if (row > trList.size()) {
return;
}
Tr tr = trList.get(row);
List<Tc> tcList = getTrAllCell(tr);
for (int cellIndex = fromCell, len = Math.min(tcList.size() - 1, toCell); cellIndex <= len; cellIndex++) {
Tc tc = tcList.get(cellIndex);
TcPr tcPr = getTcPr(tc);
HMerge hMerge = tcPr.getHMerge();
if (hMerge == null) {
hMerge = new HMerge();
tcPr.setHMerge(hMerge);
}
if (cellIndex == fromCell) {
hMerge.setVal("restart");
} else {
hMerge.setVal("continue");
}
}
} /**
* @Description: 跨行合并
*/
public static void mergeCellsVertically(Tbl tbl, int col, int fromRow, int toRow) {
if (col < 0 || fromRow < 0 || toRow < 0) {
return;
}
for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
Tc tc = getTc(tbl, rowIndex, col);
if (tc == null) {
break;
}
TcPr tcPr = getTcPr(tc);
VMerge vMerge = tcPr.getVMerge();
if (vMerge == null) {
vMerge = new VMerge();
tcPr.setVMerge(vMerge);
}
if (rowIndex == fromRow) {
vMerge.setVal("restart");
} else {
vMerge.setVal("continue");
}
}
} /**
* @Description:得到指定位置的单元格
*/
public static Tc getTc(Tbl tbl, int row, int cell) {
if (row < 0 || cell < 0) {
return null;
}
List<Tr> trList = getTblAllTr(tbl);
if (row >= trList.size()) {
return null;
}
List<Tc> tcList = getTrAllCell(trList.get(row));
if (cell >= tcList.size()) {
return null;
}
return tcList.get(cell);
} /**
* @Description:得到所有表格
*/
public static List<Tbl> getAllTbl(WordprocessingMLPackage wordMLPackage) {
MainDocumentPart mainDocPart = wordMLPackage.getMainDocumentPart();
List<Object> objList = getAllElementFromObject(mainDocPart, Tbl.class);
if (objList == null) {
return null;
}
List<Tbl> tblList = new ArrayList<Tbl>();
for (Object obj : objList) {
if (obj instanceof Tbl) {
Tbl tbl = (Tbl) obj;
tblList.add(tbl);
}
}
return tblList;
} /**
* @Description:删除指定位置的表格,删除后表格数量减一
*/
public static boolean removeTableByIndex(WordprocessingMLPackage wordMLPackage, int index) throws Exception {
boolean flag = false;
if (index < 0) {
return flag;
}
List<Object> objList = wordMLPackage.getMainDocumentPart().getContent();
if (objList == null) {
return flag;
}
int k = -1;
for (int i = 0, len = objList.size(); i < len; i++) {
Object obj = XmlUtils.unwrap(objList.get(i));
if (obj instanceof Tbl) {
k++;
if (k == index) {
wordMLPackage.getMainDocumentPart().getContent().remove(i);
flag = true;
break;
}
}
}
return flag;
} /**
* @Description: 获取单元格内容,无分割符
*/
public static String getTblContentStr(Tbl tbl) throws Exception {
return getElementContent(tbl);
} /**
* @Description: 获取表格内容
*/
public static List<String> getTblContentList(Tbl tbl) throws Exception {
List<String> resultList = new ArrayList<String>();
List<Tr> trList = getTblAllTr(tbl);
for (Tr tr : trList) {
StringBuffer sb = new StringBuffer();
List<Tc> tcList = getTrAllCell(tr);
for (Tc tc : tcList) {
sb.append(getElementContent(tc) + ",");
}
resultList.add(sb.toString());
}
return resultList;
} public static TblPr getTblPr(Tbl tbl) {
TblPr tblPr = tbl.getTblPr();
if (tblPr == null) {
tblPr = new TblPr();
tbl.setTblPr(tblPr);
}
return tblPr;
} /**
* @Description: 设置表格总宽度
*/
public static void setTableWidth(Tbl tbl, String width) {
if (StringUtils.isNotBlank(width)) {
TblPr tblPr = getTblPr(tbl);
TblWidth tblW = tblPr.getTblW();
if (tblW == null) {
tblW = new TblWidth();
tblPr.setTblW(tblW);
}
tblW.setW(new BigInteger(width));
tblW.setType("dxa");
}
} /**
* @Description:创建表格(默认水平居中,垂直居中)
*/
public static Tbl createTable(WordprocessingMLPackage wordPackage, int rowNum, int colsNum) throws Exception {
colsNum = Math.max(1, colsNum);
rowNum = Math.max(1, rowNum);
int widthTwips = getWritableWidth(wordPackage);
int colWidth = widthTwips / colsNum;
int[] widthArr = new int[colsNum];
for (int i = 0; i < colsNum; i++) {
widthArr[i] = colWidth;
}
return createTable(rowNum, colsNum, widthArr);
} /**
* @Description:创建表格(默认水平居中,垂直居中)
*/
public static Tbl createTable(int rowNum, int colsNum, int[] widthArr) throws Exception {
colsNum = Math.max(1, Math.min(colsNum, widthArr.length));
rowNum = Math.max(1, rowNum);
Tbl tbl = new Tbl();
StringBuffer tblSb = new StringBuffer();
tblSb.append("<w:tblPr ").append(Namespaces.W_NAMESPACE_DECLARATION).append(">");
tblSb.append("<w:tblStyle w:val=\"TableGrid\"/>");
tblSb.append("<w:tblW w:w=\"0\" w:type=\"auto\"/>");
// 上边框
tblSb.append("<w:tblBorders>");
tblSb.append("<w:top w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
// 左边框
tblSb.append("<w:left w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
// 下边框
tblSb.append("<w:bottom w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
// 右边框
tblSb.append("<w:right w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
tblSb.append("<w:insideH w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
tblSb.append("<w:insideV w:val=\"single\" w:sz=\"1\" w:space=\"0\" w:color=\"auto\"/>");
tblSb.append("</w:tblBorders>");
tblSb.append("</w:tblPr>");
TblPr tblPr = null;
tblPr = (TblPr) XmlUtils.unmarshalString(tblSb.toString());
Jc jc = new Jc();
// 单元格居中对齐
jc.setVal(JcEnumeration.CENTER);
tblPr.setJc(jc); tbl.setTblPr(tblPr); // 设定各单元格宽度
TblGrid tblGrid = new TblGrid();
tbl.setTblGrid(tblGrid);
for (int i = 0; i < colsNum; i++) {
TblGridCol gridCol = new TblGridCol();
gridCol.setW(BigInteger.valueOf(widthArr[i]));
tblGrid.getGridCol().add(gridCol);
}
// 新增行
for (int j = 0; j < rowNum; j++) {
Tr tr = new Tr();
tbl.getContent().add(tr);
// 列
for (int i = 0; i < colsNum; i++) {
Tc tc = new Tc();
tr.getContent().add(tc); TcPr tcPr = new TcPr();
TblWidth cellWidth = new TblWidth();
cellWidth.setType("dxa");
cellWidth.setW(BigInteger.valueOf(widthArr[i]));
tcPr.setTcW(cellWidth);
tc.setTcPr(tcPr); // 垂直居中
setTcVAlign(tc, STVerticalJc.CENTER);
P p = new P();
PPr pPr = new PPr();
pPr.setJc(jc);
p.setPPr(pPr);
R run = new R();
p.getContent().add(run);
tc.getContent().add(p);
}
}
return tbl;
} /**
* @Description:表格增加边框 可以设置上下左右四个边框样式以及横竖水平线样式
*/
public static void setTblBorders(TblPr tblPr, CTBorder topBorder, CTBorder rightBorder, CTBorder bottomBorder,
CTBorder leftBorder, CTBorder hBorder, CTBorder vBorder) {
TblBorders borders = tblPr.getTblBorders();
if (borders == null) {
borders = new TblBorders();
tblPr.setTblBorders(borders);
}
if (topBorder != null) {
borders.setTop(topBorder);
}
if (rightBorder != null) {
borders.setRight(rightBorder);
}
if (bottomBorder != null) {
borders.setBottom(bottomBorder);
}
if (leftBorder != null) {
borders.setLeft(leftBorder);
}
if (hBorder != null) {
borders.setInsideH(hBorder);
}
if (vBorder != null) {
borders.setInsideV(vBorder);
}
} /**
* @Description: 设置表格水平对齐方式(仅对表格起作用,单元格不一定水平对齐)
*/
public static void setTblJcAlign(Tbl tbl, JcEnumeration jcType) {
if (jcType != null) {
TblPr tblPr = getTblPr(tbl);
Jc jc = tblPr.getJc();
if (jc == null) {
jc = new Jc();
tblPr.setJc(jc);
}
jc.setVal(jcType);
}
} /**
* @Description: 设置表格水平对齐方式(包括单元格),只对该方法前面产生的单元格起作用
*/
public static void setTblAllJcAlign(Tbl tbl, JcEnumeration jcType) {
if (jcType != null) {
setTblJcAlign(tbl, jcType);
List<Tr> trList = getTblAllTr(tbl);
for (Tr tr : trList) {
List<Tc> tcList = getTrAllCell(tr);
for (Tc tc : tcList) {
setTcJcAlign(tc, jcType);
}
}
}
} /**
* @Description: 设置表格垂直对齐方式(包括单元格),只对该方法前面产生的单元格起作用
*/
public static void setTblAllVAlign(Tbl tbl, STVerticalJc vAlignType) {
if (vAlignType != null) {
List<Tr> trList = getTblAllTr(tbl);
for (Tr tr : trList) {
List<Tc> tcList = getTrAllCell(tr);
for (Tc tc : tcList) {
setTcVAlign(tc, vAlignType);
}
}
}
} /**
* @Description: 设置单元格Margin
*/
public static void setTableCellMargin(Tbl tbl, String top, String right, String bottom, String left) {
TblPr tblPr = getTblPr(tbl);
CTTblCellMar cellMar = tblPr.getTblCellMar();
if (cellMar == null) {
cellMar = new CTTblCellMar();
tblPr.setTblCellMar(cellMar);
}
if (StringUtils.isNotBlank(top)) {
TblWidth topW = new TblWidth();
topW.setW(new BigInteger(top));
topW.setType("dxa");
cellMar.setTop(topW);
}
if (StringUtils.isNotBlank(right)) {
TblWidth rightW = new TblWidth();
rightW.setW(new BigInteger(right));
rightW.setType("dxa");
cellMar.setRight(rightW);
}
if (StringUtils.isNotBlank(bottom)) {
TblWidth btW = new TblWidth();
btW.setW(new BigInteger(bottom));
btW.setType("dxa");
cellMar.setBottom(btW);
}
if (StringUtils.isNotBlank(left)) {
TblWidth leftW = new TblWidth();
leftW.setW(new BigInteger(left));
leftW.setType("dxa");
cellMar.setLeft(leftW);
}
} /**
* @Description: 得到表格所有的行
*/
public static List<Tr> getTblAllTr(Tbl tbl) {
List<Object> objList = getAllElementFromObject(tbl, Tr.class);
List<Tr> trList = new ArrayList<Tr>();
if (objList == null) {
return trList;
}
for (Object obj : objList) {
if (obj instanceof Tr) {
Tr tr = (Tr) obj;
trList.add(tr);
}
}
return trList; } /**
* @Description:设置tr高度
*/
public static void setTrHeight(Tr tr, String heigth) {
TrPr trPr = getTrPr(tr);
CTHeight ctHeight = new CTHeight();
ctHeight.setVal(new BigInteger(heigth));
TrHeight trHeight = new TrHeight(ctHeight);
trHeight.set(trPr);
} /**
* @Description: 在表格指定位置新增一行,默认居中
*/
public static void addTrByIndex(Tbl tbl, int index) {
addTrByIndex(tbl, index, STVerticalJc.CENTER, JcEnumeration.CENTER);
} /**
* @Description: 在表格指定位置新增一行(默认按表格定义的列数添加)
*/
public static void addTrByIndex(Tbl tbl, int index, STVerticalJc vAlign, JcEnumeration hAlign) {
TblGrid tblGrid = tbl.getTblGrid();
Tr tr = new Tr();
if (tblGrid != null) {
List<TblGridCol> gridList = tblGrid.getGridCol();
for (TblGridCol tblGridCol : gridList) {
Tc tc = new Tc();
setTcWidth(tc, tblGridCol.getW().toString());
if (vAlign != null) {
// 垂直居中
setTcVAlign(tc, vAlign);
}
P p = new P();
if (hAlign != null) {
PPr pPr = new PPr();
Jc jc = new Jc();
// 单元格居中对齐
jc.setVal(hAlign);
pPr.setJc(jc);
p.setPPr(pPr);
}
R run = new R();
p.getContent().add(run);
tc.getContent().add(p);
tr.getContent().add(tc);
}
} else {
// 大部分情况都不会走到这一步
Tr firstTr = getTblAllTr(tbl).get(0);
int cellSize = getTcCellSizeWithMergeNum(firstTr);
for (int i = 0; i < cellSize; i++) {
Tc tc = new Tc();
if (vAlign != null) {
// 垂直居中
setTcVAlign(tc, vAlign);
}
P p = new P();
if (hAlign != null) {
PPr pPr = new PPr();
Jc jc = new Jc();
// 单元格居中对齐
jc.setVal(hAlign);
pPr.setJc(jc);
p.setPPr(pPr);
}
R run = new R();
p.getContent().add(run);
tc.getContent().add(p);
tr.getContent().add(tc);
}
}
if (index >= 0 && index < tbl.getContent().size()) {
tbl.getContent().add(index, tr);
} else {
tbl.getContent().add(tr);
}
} /**
* @Description: 得到行的列数
*/
public static int getTcCellSizeWithMergeNum(Tr tr) {
int cellSize = 1;
List<Tc> tcList = getTrAllCell(tr);
if (tcList == null || tcList.size() == 0) {
return cellSize;
}
cellSize = tcList.size();
for (Tc tc : tcList) {
TcPr tcPr = getTcPr(tc);
GridSpan gridSpan = tcPr.getGridSpan();
if (gridSpan != null) {
cellSize += gridSpan.getVal().intValue() - 1;
}
}
return cellSize;
} /**
* @Description: 删除指定行 删除后行数减一
*/
public static boolean removeTrByIndex(Tbl tbl, int index) {
boolean flag = false;
if (index < 0) {
return flag;
}
List<Object> objList = tbl.getContent();
if (objList == null) {
return flag;
}
int k = -1;
for (int i = 0, len = objList.size(); i < len; i++) {
Object obj = XmlUtils.unwrap(objList.get(i));
if (obj instanceof Tr) {
k++;
if (k == index) {
tbl.getContent().remove(i);
flag = true;
break;
}
}
}
return flag;
} public static TrPr getTrPr(Tr tr) {
TrPr trPr = tr.getTrPr();
if (trPr == null) {
trPr = new TrPr();
tr.setTrPr(trPr);
}
return trPr;
} /**
* @Description:隐藏行(只对表格中间的部分起作用,不包括首尾行)
*/
public static void setTrHidden(Tr tr, boolean hidden) {
List<Tc> tcList = getTrAllCell(tr);
for (Tc tc : tcList) {
setTcHidden(tc, hidden);
}
} /**
* @Description: 设置单元格宽度
*/
public static void setTcWidth(Tc tc, String width) {
if (StringUtils.isNotBlank(width)) {
TcPr tcPr = getTcPr(tc);
TblWidth tcW = tcPr.getTcW();
if (tcW == null) {
tcW = new TblWidth();
tcPr.setTcW(tcW);
}
tcW.setW(new BigInteger(width));
tcW.setType("dxa");
}
} /**
* @Description: 隐藏单元格内容
*/
public static void setTcHidden(Tc tc, boolean hidden) {
List<P> pList = getTcAllP(tc);
for (P p : pList) {
PPr ppr = getPPr(p);
List<Object> objRList = getAllElementFromObject(p, R.class);
if (objRList == null) {
continue;
}
for (Object objR : objRList) {
if (objR instanceof R) {
R r = (R) objR;
RPr rpr = getRPr(r);
setRPrVanishStyle(rpr, hidden);
}
}
setParaVanish(ppr, hidden);
}
} public static List<P> getTcAllP(Tc tc) {
List<Object> objList = getAllElementFromObject(tc, P.class);
List<P> pList = new ArrayList<P>();
if (objList == null) {
return pList;
}
for (Object obj : objList) {
if (obj instanceof P) {
P p = (P) obj;
pList.add(p);
}
}
return pList;
} public static TcPr getTcPr(Tc tc) {
TcPr tcPr = tc.getTcPr();
if (tcPr == null) {
tcPr = new TcPr();
tc.setTcPr(tcPr);
}
return tcPr;
} /**
* @Description: 设置单元格垂直对齐方式
*/
public static void setTcVAlign(Tc tc, STVerticalJc vAlignType) {
if (vAlignType != null) {
TcPr tcPr = getTcPr(tc);
CTVerticalJc vAlign = new CTVerticalJc();
vAlign.setVal(vAlignType);
tcPr.setVAlign(vAlign);
}
} /**
* @Description: 设置单元格水平对齐方式
*/
public static void setTcJcAlign(Tc tc, JcEnumeration jcType) {
if (jcType != null) {
List<P> pList = getTcAllP(tc);
for (P p : pList) {
setParaJcAlign(p, jcType);
}
}
} public static RPr getRPr(R r) {
RPr rpr = r.getRPr();
if (rpr == null) {
rpr = new RPr();
r.setRPr(rpr);
}
return rpr;
} /**
* @Description: 获取所有的单元格
*/
public static List<Tc> getTrAllCell(Tr tr) {
List<Object> objList = getAllElementFromObject(tr, Tc.class);
List<Tc> tcList = new ArrayList<Tc>();
if (objList == null) {
return tcList;
}
for (Object tcObj : objList) {
if (tcObj instanceof Tc) {
Tc objTc = (Tc) tcObj;
tcList.add(objTc);
}
}
return tcList;
} /**
* @Description: 获取单元格内容
*/
public static String getTcContent(Tc tc) throws Exception {
return getElementContent(tc);
} /**
* @Description:设置单元格内容,content为null则清除单元格内容
*/
public static void setTcContent(Tc tc, RPr rpr, String content) {
List<Object> pList = tc.getContent();
P p = null;
if (pList != null && pList.size() > 0) {
if (pList.get(0) instanceof P) {
p = (P) pList.get(0);
}
} else {
p = new P();
tc.getContent().add(p);
}
R run = null;
List<Object> rList = p.getContent();
if (rList != null && rList.size() > 0) {
for (int i = 0, len = rList.size(); i < len; i++) {
// 清除内容(所有的r
p.getContent().remove(0);
}
}
run = new R();
p.getContent().add(run);
if (content != null) {
String[] contentArr = content.split("\n");
Text text = new Text();
text.setSpace("preserve");
text.setValue(contentArr[0]);
run.setRPr(rpr);
run.getContent().add(text); for (int i = 1, len = contentArr.length; i < len; i++) {
Br br = new Br();
run.getContent().add(br);// 换行
text = new Text();
text.setSpace("preserve");
text.setValue(contentArr[i]);
run.setRPr(rpr);
run.getContent().add(text);
}
}
} /**
* @Description:设置单元格内容,content为null则清除单元格内容
*/
public static void removeTcContent(Tc tc) {
List<Object> pList = tc.getContent();
P p = null;
if (pList != null && pList.size() > 0) {
if (pList.get(0) instanceof P) {
p = (P) pList.get(0);
}
} else {
return;
}
List<Object> rList = p.getContent();
if (rList != null && rList.size() > 0) {
for (int i = 0, len = rList.size(); i < len; i++) {
// 清除内容(所有的r
p.getContent().remove(0);
}
}
} /**
* @Description:删除指定位置的表格
* @deprecated
*/
public static void deleteTableByIndex2(WordprocessingMLPackage wordMLPackage, int index) throws Exception {
if (index < 0) {
return;
}
final String xpath = "(//w:tbl)[" + index + "]";
final List<Object> jaxbNodes = wordMLPackage.getMainDocumentPart().getJAXBNodesViaXPath(xpath, true);
if (jaxbNodes != null && jaxbNodes.size() > 0) {
wordMLPackage.getMainDocumentPart().getContent().remove(jaxbNodes.get(0));
}
} /**
* @Description:获取NodeList
* @deprecated
*/
public static List<Object> getObjectByXpath(WordprocessingMLPackage wordMLPackage, String xpath) throws Exception {
final List<Object> jaxbNodes = wordMLPackage.getMainDocumentPart().getJAXBNodesViaXPath(xpath, true);
return jaxbNodes;
} /*------------------------------------Word 段落相关--------------------------------------------------- */
/**
* @Description: 只删除单独的段落,不包括表格内或其他内的段落
*/
public static boolean removeParaByIndex(WordprocessingMLPackage wordMLPackage, int index) {
boolean flag = false;
if (index < 0) {
return flag;
}
List<Object> objList = wordMLPackage.getMainDocumentPart().getContent();
if (objList == null) {
return flag;
}
int k = -1;
for (int i = 0, len = objList.size(); i < len; i++) {
if (objList.get(i) instanceof P) {
k++;
if (k == index) {
wordMLPackage.getMainDocumentPart().getContent().remove(i);
flag = true;
break;
}
}
}
return flag;
} /**
* @Description: 设置段落水平对齐方式
*/
public static void setParaJcAlign(P paragraph, JcEnumeration hAlign) {
if (hAlign != null) {
PPr pprop = paragraph.getPPr();
if (pprop == null) {
pprop = new PPr();
paragraph.setPPr(pprop);
}
Jc align = new Jc();
align.setVal(hAlign);
pprop.setJc(align);
}
} /**
* @Description: 设置段落内容
*/
public static void setParaRContent(P p, RPr runProperties, String content) {
R run = null;
List<Object> rList = p.getContent();
if (rList != null && rList.size() > 0) {
for (int i = 0, len = rList.size(); i < len; i++) {
// 清除内容(所有的r
p.getContent().remove(0);
}
}
run = new R();
p.getContent().add(run);
if (content != null) {
String[] contentArr = content.split("\n");
Text text = new Text();
text.setSpace("preserve");
text.setValue(contentArr[0]);
run.setRPr(runProperties);
run.getContent().add(text); for (int i = 1, len = contentArr.length; i < len; i++) {
Br br = new Br();
run.getContent().add(br);// 换行
text = new Text();
text.setSpace("preserve");
text.setValue(contentArr[i]);
run.setRPr(runProperties);
run.getContent().add(text);
}
}
} /**
* @Description: 添加段落内容
*/
public static void appendParaRContent(P p, RPr runProperties, String content) {
if (content != null) {
R run = new R();
p.getContent().add(run);
String[] contentArr = content.split("\n");
Text text = new Text();
text.setSpace("preserve");
text.setValue(contentArr[0]);
run.setRPr(runProperties);
run.getContent().add(text); for (int i = 1, len = contentArr.length; i < len; i++) {
Br br = new Br();
run.getContent().add(br);// 换行
text = new Text();
text.setSpace("preserve");
text.setValue(contentArr[i]);
run.setRPr(runProperties);
run.getContent().add(text);
}
}
} /**
* @Description: 添加图片到段落
*/
public static void addImageToPara(WordprocessingMLPackage wordMLPackage, ObjectFactory factory, P paragraph,
String filePath, String content, RPr rpr, String altText, int id1, int id2) throws Exception {
R run = factory.createR();
if (content != null) {
Text text = factory.createText();
text.setValue(content);
text.setSpace("preserve");
run.setRPr(rpr);
run.getContent().add(text);
} InputStream is = new FileInputStream(filePath);
byte[] bytes = IOUtils.toByteArray(is);
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);
Inline inline = imagePart.createImageInline(filePath, altText, id1, id2, false);
Drawing drawing = factory.createDrawing();
drawing.getAnchorOrInline().add(inline);
run.getContent().add(drawing);
paragraph.getContent().add(run);
} /**
* @Description: 段落添加Br 页面Break(分页符)
*/
public static void addPageBreak(P para, STBrType sTBrType) {
Br breakObj = new Br();
breakObj.setType(sTBrType);
para.getContent().add(breakObj);
} /**
* @Description: 设置段落是否禁止行号(禁止用于当前行号)
*/
public static void setParagraphSuppressLineNum(P p) {
PPr ppr = getPPr(p);
BooleanDefaultTrue line = ppr.getSuppressLineNumbers();
if (line == null) {
line = new BooleanDefaultTrue();
}
line.setVal(true);
ppr.setSuppressLineNumbers(line);
} /**
* @Description: 设置段落底纹(对整段文字起作用)
*/
public static void setParagraphShdStyle(P p, STShd shdType, String shdColor) {
PPr ppr = getPPr(p);
CTShd ctShd = ppr.getShd();
if (ctShd == null) {
ctShd = new CTShd();
}
if (StringUtils.isNotBlank(shdColor)) {
ctShd.setColor(shdColor);
}
if (shdType != null) {
ctShd.setVal(shdType);
}
ppr.setShd(ctShd);
} /**
* @param isSpace
* 是否设置段前段后值
* @param before
* 段前磅数
* @param after
* 段后磅数
* @param beforeLines
* 段前行数
* @param afterLines
* 段后行数
* @param isLine
* 是否设置行距
* @param lineValue
* 行距值
* @param sTLineSpacingRule
* 自动auto 固定exact 最小 atLeast 1磅=20 1行=100 单倍行距=240
*/
public static void setParagraphSpacing(P p, boolean isSpace, String before, String after, String beforeLines,
String afterLines, boolean isLine, String lineValue, STLineSpacingRule sTLineSpacingRule) {
PPr pPr = getPPr(p);
Spacing spacing = pPr.getSpacing();
if (spacing == null) {
spacing = new Spacing();
pPr.setSpacing(spacing);
}
if (isSpace) {
if (StringUtils.isNotBlank(before)) {
// 段前磅数
spacing.setBefore(new BigInteger(before));
}
if (StringUtils.isNotBlank(after)) {
// 段后磅数
spacing.setAfter(new BigInteger(after));
}
if (StringUtils.isNotBlank(beforeLines)) {
// 段前行数
spacing.setBeforeLines(new BigInteger(beforeLines));
}
if (StringUtils.isNotBlank(afterLines)) {
// 段后行数
spacing.setAfterLines(new BigInteger(afterLines));
}
}
if (isLine) {
if (StringUtils.isNotBlank(lineValue)) {
spacing.setLine(new BigInteger(lineValue));
}
if (sTLineSpacingRule != null) {
spacing.setLineRule(sTLineSpacingRule);
}
}
} /**
* @Description: 设置段落缩进信息 1厘米≈567
*/
public static void setParagraphIndInfo(P p, String firstLine, String firstLineChar, String hanging,
String hangingChar, String right, String rigthChar, String left, String leftChar) {
PPr ppr = getPPr(p);
Ind ind = ppr.getInd();
if (ind == null) {
ind = new Ind();
ppr.setInd(ind);
}
if (StringUtils.isNotBlank(firstLine)) {
ind.setFirstLine(new BigInteger(firstLine));
}
if (StringUtils.isNotBlank(firstLineChar)) {
ind.setFirstLineChars(new BigInteger(firstLineChar));
}
if (StringUtils.isNotBlank(hanging)) {
ind.setHanging(new BigInteger(hanging));
}
if (StringUtils.isNotBlank(hangingChar)) {
ind.setHangingChars(new BigInteger(hangingChar));
}
if (StringUtils.isNotBlank(left)) {
ind.setLeft(new BigInteger(left));
}
if (StringUtils.isNotBlank(leftChar)) {
ind.setLeftChars(new BigInteger(leftChar));
}
if (StringUtils.isNotBlank(right)) {
ind.setRight(new BigInteger(right));
}
if (StringUtils.isNotBlank(rigthChar)) {
ind.setRightChars(new BigInteger(rigthChar));
}
} public static PPr getPPr(P p) {
PPr ppr = p.getPPr();
if (ppr == null) {
ppr = new PPr();
p.setPPr(ppr);
}
return ppr;
} public static ParaRPr getParaRPr(PPr ppr) {
ParaRPr parRpr = ppr.getRPr();
if (parRpr == null) {
parRpr = new ParaRPr();
ppr.setRPr(parRpr);
}
return parRpr; } public static void setParaVanish(PPr ppr, boolean isVanish) {
ParaRPr parRpr = getParaRPr(ppr);
BooleanDefaultTrue vanish = parRpr.getVanish();
if (vanish != null) {
vanish.setVal(isVanish);
} else {
vanish = new BooleanDefaultTrue();
parRpr.setVanish(vanish);
vanish.setVal(isVanish);
}
} /**
* @Description: 设置段落边框样式
*/
public static void setParagraghBorders(P p, CTBorder topBorder, CTBorder bottomBorder, CTBorder leftBorder,
CTBorder rightBorder) {
PPr ppr = getPPr(p);
PBdr pBdr = new PBdr();
if (topBorder != null) {
pBdr.setTop(topBorder);
}
if (bottomBorder != null) {
pBdr.setBottom(bottomBorder);
}
if (leftBorder != null) {
pBdr.setLeft(leftBorder);
}
if (rightBorder != null) {
pBdr.setRight(rightBorder);
}
ppr.setPBdr(pBdr);
} /**
* @Description: 设置字体信息
*/
public static void setFontStyle(RPr runProperties, String cnFontFamily, String enFontFamily, String fontSize,
String color) {
setFontFamily(runProperties, cnFontFamily, enFontFamily);
setFontSize(runProperties, fontSize);
setFontColor(runProperties, color);
} /**
* @Description: 设置字体大小
*/
public static void setFontSize(RPr runProperties, String fontSize) {
if (StringUtils.isNotBlank(fontSize)) {
HpsMeasure size = new HpsMeasure();
size.setVal(new BigInteger(fontSize));
runProperties.setSz(size);
runProperties.setSzCs(size);
}
} /**
* @Description: 设置字体
*/
public static void setFontFamily(RPr runProperties, String cnFontFamily, String enFontFamily) {
if (StringUtils.isNotBlank(cnFontFamily) || StringUtils.isNotBlank(enFontFamily)) {
RFonts rf = runProperties.getRFonts();
if (rf == null) {
rf = new RFonts();
runProperties.setRFonts(rf);
}
if (cnFontFamily != null) {
rf.setEastAsia(cnFontFamily);
}
if (enFontFamily != null) {
rf.setAscii(enFontFamily);
}
}
} /**
* @Description: 设置字体颜色
*/
public static void setFontColor(RPr runProperties, String color) {
if (color != null) {
Color c = new Color();
c.setVal(color);
runProperties.setColor(c);
}
} /**
* @Description: 设置字符边框
*/
public static void addRPrBorderStyle(RPr runProperties, String size, STBorder bordType, String space,
String color) {
CTBorder value = new CTBorder();
if (StringUtils.isNotBlank(color)) {
value.setColor(color);
}
if (StringUtils.isNotBlank(size)) {
value.setSz(new BigInteger(size));
}
if (StringUtils.isNotBlank(space)) {
value.setSpace(new BigInteger(space));
}
if (bordType != null) {
value.setVal(bordType);
}
runProperties.setBdr(value);
} /**
* @Description:着重号
*/
public static void addRPrEmStyle(RPr runProperties, STEm emType) {
if (emType != null) {
CTEm em = new CTEm();
em.setVal(emType);
runProperties.setEm(em);
}
} /**
* @Description: 空心
*/
public static void addRPrOutlineStyle(RPr runProperties) {
BooleanDefaultTrue outline = new BooleanDefaultTrue();
outline.setVal(true);
runProperties.setOutline(outline);
} /**
* @Description: 设置上标下标
*/
public static void addRPrcaleStyle(RPr runProperties, STVerticalAlignRun vAlign) {
if (vAlign != null) {
CTVerticalAlignRun value = new CTVerticalAlignRun();
value.setVal(vAlign);
runProperties.setVertAlign(value);
}
} /**
* @Description: 设置字符间距缩进
*/
public static void addRPrScaleStyle(RPr runProperties, int indent) {
CTTextScale value = new CTTextScale();
value.setVal(indent);
runProperties.setW(value);
} /**
* @Description: 设置字符间距信息
*/
public static void addRPrtSpacingStyle(RPr runProperties, int spacing) {
CTSignedTwipsMeasure value = new CTSignedTwipsMeasure();
value.setVal(BigInteger.valueOf(spacing));
runProperties.setSpacing(value);
} /**
* @Description: 设置文本位置
*/
public static void addRPrtPositionStyle(RPr runProperties, int position) {
CTSignedHpsMeasure ctPosition = new CTSignedHpsMeasure();
ctPosition.setVal(BigInteger.valueOf(position));
runProperties.setPosition(ctPosition);
} /**
* @Description: 阴文
*/
public static void addRPrImprintStyle(RPr runProperties) {
BooleanDefaultTrue imprint = new BooleanDefaultTrue();
imprint.setVal(true);
runProperties.setImprint(imprint);
} /**
* @Description: 阳文
*/
public static void addRPrEmbossStyle(RPr runProperties) {
BooleanDefaultTrue emboss = new BooleanDefaultTrue();
emboss.setVal(true);
runProperties.setEmboss(emboss);
} /**
* @Description: 设置隐藏
*/
public static void setRPrVanishStyle(RPr runProperties, boolean isVanish) {
BooleanDefaultTrue vanish = runProperties.getVanish();
if (vanish != null) {
vanish.setVal(isVanish);
} else {
vanish = new BooleanDefaultTrue();
vanish.setVal(isVanish);
runProperties.setVanish(vanish);
}
} /**
* @Description: 设置阴影
*/
public static void addRPrShadowStyle(RPr runProperties) {
BooleanDefaultTrue shadow = new BooleanDefaultTrue();
shadow.setVal(true);
runProperties.setShadow(shadow);
} /**
* @Description: 设置底纹
*/
public static void addRPrShdStyle(RPr runProperties, STShd shdtype) {
if (shdtype != null) {
CTShd shd = new CTShd();
shd.setVal(shdtype);
runProperties.setShd(shd);
}
} /**
* @Description: 设置突出显示文本
*/
public static void addRPrHightLightStyle(RPr runProperties, String hightlight) {
if (StringUtils.isNotBlank(hightlight)) {
Highlight highlight = new Highlight();
highlight.setVal(hightlight);
runProperties.setHighlight(highlight);
}
} /**
* @Description: 设置删除线样式
*/
public static void addRPrStrikeStyle(RPr runProperties, boolean isStrike, boolean isDStrike) {
// 删除线
if (isStrike) {
BooleanDefaultTrue strike = new BooleanDefaultTrue();
strike.setVal(true);
runProperties.setStrike(strike);
}
// 双删除线
if (isDStrike) {
BooleanDefaultTrue dStrike = new BooleanDefaultTrue();
dStrike.setVal(true);
runProperties.setDstrike(dStrike);
}
} /**
* @Description: 加粗
*/
public static void addRPrBoldStyle(RPr runProperties) {
BooleanDefaultTrue b = new BooleanDefaultTrue();
b.setVal(true);
runProperties.setB(b);
} /**
* @Description: 倾斜
*/
public static void addRPrItalicStyle(RPr runProperties) {
BooleanDefaultTrue b = new BooleanDefaultTrue();
b.setVal(true);
runProperties.setI(b);
} /**
* @Description: 添加下划线
*/
public static void addRPrUnderlineStyle(RPr runProperties, UnderlineEnumeration enumType) {
U val = new U();
val.setVal(enumType);
runProperties.setU(val);
} /*------------------------------------Word 相关--------------------------------------------------- */
/**
* @Description: 设置分节符 nextPage:下一页 continuous:连续 evenPage:偶数页 oddPage:奇数页
*/
public static void setDocSectionBreak(WordprocessingMLPackage wordPackage, String sectValType) {
if (StringUtils.isNotBlank(sectValType)) {
SectPr sectPr = getDocSectPr(wordPackage);
Type sectType = sectPr.getType();
if (sectType == null) {
sectType = new Type();
sectPr.setType(sectType);
}
sectType.setVal(sectValType);
}
} /**
* @Description: 设置页面背景色
*/
public static void setDocumentBackGround(WordprocessingMLPackage wordPackage, ObjectFactory factory, String color)
throws Exception {
MainDocumentPart mdp = wordPackage.getMainDocumentPart();
CTBackground bkground = mdp.getContents().getBackground();
if (StringUtils.isNotBlank(color)) {
if (bkground == null) {
bkground = factory.createCTBackground();
bkground.setColor(color);
}
mdp.getContents().setBackground(bkground);
}
} /**
* @Description: 设置页面边框
*/
public static void setDocumentBorders(WordprocessingMLPackage wordPackage, ObjectFactory factory, CTBorder top,
CTBorder right, CTBorder bottom, CTBorder left) {
SectPr sectPr = getDocSectPr(wordPackage);
PgBorders pgBorders = sectPr.getPgBorders();
if (pgBorders == null) {
pgBorders = factory.createSectPrPgBorders();
sectPr.setPgBorders(pgBorders);
}
if (top != null) {
pgBorders.setTop(top);
}
if (right != null) {
pgBorders.setRight(right);
}
if (bottom != null) {
pgBorders.setBottom(bottom);
}
if (left != null) {
pgBorders.setLeft(left);
}
} /**
* @Description: 设置页面大小及纸张方向 landscape横向
*/
public static void setDocumentSize(WordprocessingMLPackage wordPackage, ObjectFactory factory, String width,
String height, STPageOrientation stValue) {
SectPr sectPr = getDocSectPr(wordPackage);
PgSz pgSz = sectPr.getPgSz();
if (pgSz == null) {
pgSz = factory.createSectPrPgSz();
sectPr.setPgSz(pgSz);
}
if (StringUtils.isNotBlank(width)) {
pgSz.setW(new BigInteger(width));
}
if (StringUtils.isNotBlank(height)) {
pgSz.setH(new BigInteger(height));
}
if (stValue != null) {
pgSz.setOrient(stValue);
}
} public static SectPr getDocSectPr(WordprocessingMLPackage wordPackage) {
SectPr sectPr = wordPackage.getDocumentModel().getSections().get(0).getSectPr();
return sectPr;
} /**
* @Description:设置页边距
*/
public static void setDocMarginSpace(WordprocessingMLPackage wordPackage, ObjectFactory factory, String top,
String left, String bottom, String right) {
SectPr sectPr = getDocSectPr(wordPackage);
PgMar pg = sectPr.getPgMar();
if (pg == null) {
pg = factory.createSectPrPgMar();
sectPr.setPgMar(pg);
}
if (StringUtils.isNotBlank(top)) {
pg.setTop(new BigInteger(top));
}
if (StringUtils.isNotBlank(bottom)) {
pg.setBottom(new BigInteger(bottom));
}
if (StringUtils.isNotBlank(left)) {
pg.setLeft(new BigInteger(left));
}
if (StringUtils.isNotBlank(right)) {
pg.setRight(new BigInteger(right));
}
} /**
* @Description: 设置行号
* @param distance
* :距正文距离 1厘米=567
* @param start
* :起始编号(0开始)
* @param countBy
* :行号间隔
* @param restartType
* :STLineNumberRestart.CONTINUOUS(continuous连续编号)<br/>
* STLineNumberRestart.NEW_PAGE(每页重新编号)<br/>
* STLineNumberRestart.NEW_SECTION(每节重新编号)
*/
public static void setDocInNumType(WordprocessingMLPackage wordPackage, String countBy, String distance,
String start, STLineNumberRestart restartType) {
SectPr sectPr = getDocSectPr(wordPackage);
CTLineNumber lnNumType = sectPr.getLnNumType();
if (lnNumType == null) {
lnNumType = new CTLineNumber();
sectPr.setLnNumType(lnNumType);
}
if (StringUtils.isNotBlank(countBy)) {
lnNumType.setCountBy(new BigInteger(countBy));
}
if (StringUtils.isNotBlank(distance)) {
lnNumType.setDistance(new BigInteger(distance));
}
if (StringUtils.isNotBlank(start)) {
lnNumType.setStart(new BigInteger(start));
}
if (restartType != null) {
lnNumType.setRestart(restartType);
}
} /**
* @Description:设置文字方向 tbRl 垂直
*/
public static void setDocTextDirection(WordprocessingMLPackage wordPackage, String textDirection) {
if (StringUtils.isNotBlank(textDirection)) {
SectPr sectPr = getDocSectPr(wordPackage);
TextDirection textDir = sectPr.getTextDirection();
if (textDir == null) {
textDir = new TextDirection();
sectPr.setTextDirection(textDir);
}
textDir.setVal(textDirection);
}
} /**
* @Description:设置word 垂直对齐方式(Word默认方式都是"顶端对齐")
*/
public static void setDocVAlign(WordprocessingMLPackage wordPackage, STVerticalJc valignType) {
if (valignType != null) {
SectPr sectPr = getDocSectPr(wordPackage);
CTVerticalJc valign = sectPr.getVAlign();
if (valign == null) {
valign = new CTVerticalJc();
sectPr.setVAlign(valign);
}
valign.setVal(valignType);
}
} /**
* @Description:获取文档的可用宽度
*/
public static int getWritableWidth(WordprocessingMLPackage wordPackage) throws Exception {
return wordPackage.getDocumentModel().getSections().get(0).getPageDimensions().getWritableWidthTwips();
} }
【docx4j】docx4j操作docx,实现替换内容、转换pdf、html等操作的更多相关文章
- 将页面内容转换Pdf\Word\Excel格式
项目中用到了将邮件内容转换为Pdf.Word.Excel格式,做为邮件附件发送. 查了一些解决方案,走了一些弯路.以此代码记录下. 转换PDF需要下载NReco.PdfGenerator.dll 以下 ...
- jq选择器(jq 与 js 互相转换),jq操作css样式 / 文本内容, jq操作类名,jq操作全局属性,jq获取盒子信息,jq获取位置信息
jq选择器(jq 与 js 互相转换) // 获取所有的页面元素jq对象 $('css3选择器语法'); var $box = $(".box:nth-child(1)"); 获取 ...
- python3.4学习笔记(十五) 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)
python3.4学习笔记(十五) 字符串操作(string替换.删除.截取.复制.连接.比较.查找.包含.大小写转换.分割等) python print 不换行(在后面加上,end=''),prin ...
- 转 Python 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)
转自: http://www.cnblogs.com/huangcong/archive/2011/08/29/2158268.html 黄聪:Python 字符串操作(string替换.删除.截取. ...
- python操作docx文档(转)
python操作docx文档 关于python操作docx格式文档,我用到了两个python包,一个便是python-docx包,另一个便是python-docx-template;,同时我也用到了很 ...
- tp5 使用phpword 替换word模板并利用com组件转换pdf
tp5 使用phpword 替换word模板并利用com组件转换pdf 一.首先composer安装PHPword,就不多说了 二.然后是把模板中要替换的部分用变量代替 三.把原始的模板文件放入项 ...
- qrcode.js插件将你的内容转换成二维码格式
---qrcode.js插件将你的内容转换成二维码格式--- 我之前一直想知道二维码是怎么生成,所以就了解了一下, 最后还是不知道它的原理, 但是,我知道怎么生成. 现在就让我带你制作一个你喜爱的二维 ...
- JQuery(三)——操作HTML和CSS内容
前边我们学习过JS通过DOM来操作HTML(详看DOM(一)——HTML DOM ),这篇博客我们来看一下JQuery是如何方便的对HTML以及CSS进行各种操作呢?顺便两者之间相互比较一下,看其差别 ...
- Python3 将configparser从ini文件中读取的内容转换成字典格式
因为写脚本的用到了,所以研究了下怎么将configparser从ini文件中读取的内容转换成字典格式. 整理一下,希望能对大家有帮助. 从http://stackoverflow.com/questi ...
随机推荐
- npm指向淘宝源
临时 npm --registry https://registry.npm.taobao.org install express1 持久 npm config set registry https: ...
- Problem B. Harvest of Apples HDU - 6333(莫队)
Problem Description There are n apples on a tree, numbered from 1 to n.Count the number of ways to p ...
- bzoj 2212 : [Poi2011]Tree Rotations (线段树合并)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2212 思路:用线段树合并求出交换左右儿子之前之后逆序对的数量,如果数量变小则交换. 实现 ...
- linux screen 命令 :离线运行程序
screen工具是linux下虚拟终端的一个常用工具.在 发现这个工具之前,笔者经常在远程ssh中运行需要长时间处理数据的命令,比如远程编译安装软件,如果在编译的过程中网络断开,那这个编译进程就会停止 ...
- 【转】用宏定义代替printf函数
问题提出 有时候我们想用宏定义来决定是编译debug版本的代码还是release的代码,dubug版本的代码会通过printf打印调试信息,release版本的代码则不会.我们总不能对每一条print ...
- 【转】 Keil C51重定向printf到串口
概述 进行C/C++开发的时候我们都会需要打印调试信息,打印调试信息时我们习惯使用printf函数,但是在Keil C51环境下,由于我们的程序是下载到单片机里,使用printf函数时不能直接打印到串 ...
- 洛谷 P4408 逃学的小孩 解题报告
P4408 [NOI2003]逃学的小孩 题目描述 Chris家的电话铃响起了,里面传出了Chris的老师焦急的声音:"喂,是Chris的家长吗?你们的孩子又没来上课,不想参加考试了吗?&q ...
- bzoj3277 串 (后缀数组+二分答案+ST表)
常见操作:先把所有串都连到一起,但中间加上一个特殊的符号(不能在原串中/出现过)作为分割 由于全部的子串就等于所有后缀的所有前缀,那我们对于每一个后缀,去求一个最长的前缀,来满足这个前缀在至少K个原串 ...
- Gym 100971J-Robots at Warehouse
题目链接:http://codeforces.com/gym/100971/problem/J Vitaly works at the warehouse. The warehouse can be ...
- Java -- JDBC_利用反射及 JDBC 元数据编写通用的查询方法
先利用 SQL 进行查询,得到结果集: 利用反射创建实体类的对象:创建对象: 获取结果集的列的别名: 再获取结果集的每一列的值, 结合 3 得到一个 Map,键:列的别名,值:列的值: 再利用反射为 ...