最近需要用docx4j来对docx进行一些操作,用到的技术是docx4j,这个技术在国内其实用的不是很多,看了一些博主的文章,有些感悟,做了一些总结,如果有疑问或错误之处欢迎交流。

创建包:

WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();  

保存包:

wordMLPackage.save(new java.io.File("C://xxx.docx")); 

得到主段落,并且输出/带样式输出:

MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
wordMLPackage.getMainDocumentPart().addParagraphOfText("Hello Word!");
wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title", "Hello Word!"); wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Subtitle"," a subtitle!");

创建表格并添加内容:

ObjectFactory factory=Context.getWmlObjectFactory();
Tbl table = factory.createTbl();
Tr tableRow = factory.createTr();
Tc tableCell = factory.createTc();
tableCell.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText("Field 1"));
tableRow.getContent().add(tableCell);
table.getContent().add(tableRow);
wordMLPackage.getMainDocumentPart().addObject(table);

先创建一个工厂,(需要导入的包是org.docx4j.wml,导错的的话下面全错)创建表格,在创建行和单元格(tableCell),在单元格里添加你想要的内容,因为返回值是Object,只能通过这种方式传入数据,最后层层退回去,用add添加,最后在主段落添加。

编辑表格样式:

table.setTblPr(new TblPr());
CTBorder border = new CTBorder();
border.setColor("auto");
border.setSz(new BigInteger("4"));
TblBorders borders = new TblBorders();
borders.setBottom(border);
borders.setLeft(border);
borders.setInsideV(border);
table.getTblPr().setTblBorders(borders);
先创建table样式对象,在用CTBorder对象规定样式规范,用TblBorders对象将样式规范应用进去。

创建 段落/运行块/运行块属性/文本 对象:

ObjectFactory factory=Context.getWmlObjectFactory();
P paragraph = factory.createP();
Text text = factory.createText();
text.setValue(content);
R run = factory.createR();
run.getContent().add(text);
paragraph.getContent().add(run);
RPr runProperties = factory.createRPr();
run.setRPr(runProperties);
tableCell.getContent().add(paragraph);

P是一个段落,Text是文本的值对象,R是一个运行块,负责便于将多个属性相同的Text对象统一操作,RPr是运行块的属性,可以对R对象进行操作。简单的说几个对象之间的关系可以这么理解:Tc tableCell > P paragraph > R run > Text text。其中,run.setRPr(RPr runProperties)可以设置块中属性。个人认为用开始的方法输入内容,在某种程度上是和上述代码做了一样的工作,效果相同。 
加粗字体和调整字体大小:

HpsMeasure size = new HpsMeasure();
size.setVal(new BigInteger("40"));
runProperties.setSz(size);
runProperties.setSzCs(size);
BooleanDefaultTrue b = new BooleanDefaultTrue();
b.setVal(true);
runProperties.setB(b);

思路是先创建各自的对象,设置对象的值为自己想要的情况,再用RPr的对象来set相应的属性。其中注意setVal中的值最后会被现实一半,所以只有字体20大小。

纵向合并单元格:

Tc tableCell = factory.createTc();
TcPr tableCellProperties = new TcPr();
VMerge merge = new VMerge();
merge.setVal("restart");
tableCellProperties.setVMerge(merge);
tableCell.setTcPr(tableCellProperties);
tableCell.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(content));
row.getContent().add(tableCell);

先创建单元格属性,再创建VMerge对象,如果设置merge则为向上合并,如果将merge属性设为restart则重新开始新的单元格。

设置单元格宽度:

TcPr tableCellProperties = new TcPr();
TblWidth tableWidth = new TblWidth();
tableWidth.setW(BigInteger.valueOf("50"));
tableCellProperties.setTcW(tableWidth);
tableCell.setTcPr(tableCellProperties);

先创建单元格属性对象,创建Tblwidth对象并且设置宽度,用单元格属性对象通过方法调用Tblwidth对象。

添加图片:

File file = new File("c:\\a.jpg");
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, file);
int docPrId = 1;
int cNvPrId = 2;
Inline inline = imagePart.createImageInline("Filename hint","Alternative text", docPrId, cNvPrId, false);
ObjectFactory factory = new ObjectFactory();
P paragraph = factory.createP();
R run = factory.createR();
paragraph.getContent().add(run);
Drawing drawing = factory.createDrawing();
run.getContent().add(drawing);
drawing.getAnchorOrInline().add(inline);
wordMLPackage.getMainDocumentPart().addObject(paragraph);

打开文件,通过imagePart将图片读进去,现在图片被转换成二进制,为了能在文件中内联中显示出图片,调用函数将图片存在inline中。之后paragraph,run,drawing,用drawing读inline,方法同上。

加载读入docx文件:

WordprocessingMLPackage template = WordprocessingMLPackage.load(new File("c:\\a.docx"));

获取文档中所有内容(方法):

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

通过对类型的判断,将obj的内容分类读到List中,最后将内容按照列表的顺序贮存,如果obj是JAXB的一个实例就将他转型获取值,如果是和第二个参数的类型相同就添加,如果是ContentAccessor的一个对象,就将对象中的内容存到另外的一个列表中,再次调用自己将全部元素添加到原来的List中,返回一个List。

05-04 00:13