使用poi读取word2007(.docx)中的复杂表格

最近工作需要做一个读取word(.docx)中的表格,并以html形式输出。经过上网查询,使用了poi。

对于2007及之后的word文档,需要导入poi-ooxml-xxx.jar及其依赖包,如下图(图中为使用maven):

对于简单表格,可以使用如下方式来获取每个表格的内容:

XWPFDocument document = new XWPFDocument(new FileInputStream("word.docx"));
// 获取所有表格
List<XWPFTable> tables = document.getTables();
for (XWPFTable table : tables) {
// 获取表格的行
List<XWPFTableRow> rows = table.getRows();
for (XWPFTableRow row : rows) {
// 获取表格的每个单元格
List<XWPFTableCell> tableCells = row.getTableCells();
for (XWPFTableCell cell : tableCells) {
// 获取单元格的内容
String text = cell.getText();
}
}
}

但是对于复杂表格(含合并的单元格),则无法正常处理。

于是继续上网查询,在stackoverflow查到如下生成含有合并的单元格的表格:

public class CreateWordTableMerge {

    static void mergeCellVertically(XWPFTable table, int col, int fromRow, int toRow) {
for(int rowIndex = fromRow; rowIndex <= toRow; rowIndex++){
CTVMerge vmerge = CTVMerge.Factory.newInstance();
if(rowIndex == fromRow){
// The first merged cell is set with RESTART merge value
vmerge.setVal(STMerge.RESTART);
} else {
// Cells which join (merge) the first one, are set with CONTINUE
vmerge.setVal(STMerge.CONTINUE);
}
XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
// Try getting the TcPr. Not simply setting an new one every time.
CTTcPr tcPr = cell.getCTTc().getTcPr();
if (tcPr != null) {
tcPr.setVMerge(vmerge);
} else {
// only set an new TcPr if there is not one already
tcPr = CTTcPr.Factory.newInstance();
tcPr.setVMerge(vmerge);
cell.getCTTc().setTcPr(tcPr);
}
}
} static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
for(int colIndex = fromCol; colIndex <= toCol; colIndex++){
CTHMerge hmerge = CTHMerge.Factory.newInstance();
if(colIndex == fromCol){
// The first merged cell is set with RESTART merge value
hmerge.setVal(STMerge.RESTART);
} else {
// Cells which join (merge) the first one, are set with CONTINUE
hmerge.setVal(STMerge.CONTINUE);
}
XWPFTableCell cell = table.getRow(row).getCell(colIndex);
// Try getting the TcPr. Not simply setting an new one every time.
CTTcPr tcPr = cell.getCTTc().getTcPr();
if (tcPr != null) {
tcPr.setHMerge(hmerge);
} else {
// only set an new TcPr if there is not one already
tcPr = CTTcPr.Factory.newInstance();
tcPr.setHMerge(hmerge);
cell.getCTTc().setTcPr(tcPr);
}
}
} public static void main(String[] args) throws Exception { XWPFDocument document= new XWPFDocument(); XWPFParagraph paragraph = document.createParagraph();
XWPFRun run=paragraph.createRun();
run.setText("The table:"); //create table
XWPFTable table = document.createTable(3,5); for (int row = 0; row < 3; row++) {
for (int col = 0; col < 5; col++) {
table.getRow(row).getCell(col).setText("row " + row + ", col " + col);
}
} //create and set column widths for all columns in all rows
//most examples don't set the type of the CTTblWidth but this
//is necessary for working in all office versions
for (int col = 0; col < 5; col++) {
CTTblWidth tblWidth = CTTblWidth.Factory.newInstance();
tblWidth.setW(BigInteger.valueOf(1000));
tblWidth.setType(STTblWidth.DXA);
for (int row = 0; row < 3; row++) {
CTTcPr tcPr = table.getRow(row).getCell(col).getCTTc().getTcPr();
if (tcPr != null) {
tcPr.setTcW(tblWidth);
} else {
tcPr = CTTcPr.Factory.newInstance();
tcPr.setTcW(tblWidth);
table.getRow(row).getCell(col).getCTTc().setTcPr(tcPr);
}
}
} //using the merge methods
mergeCellVertically(table, 0, 0, 1);
mergeCellHorizontally(table, 1, 2, 3);
mergeCellHorizontally(table, 2, 1, 4); paragraph = document.createParagraph(); FileOutputStream out = new FileOutputStream("create_table.docx");
document.write(out); System.out.println("create_table.docx written successully");
}
}

运行一下确实可以实现,不过仍是一头雾水,对于其中的cTTc,tcPr,vMerge等属性仍是不知道是什么。

直到后来知道了Office Open XML (OOXML) ,可以将.docx文件后缀改为.zip,即可以使用解压软件打开,进入后有一个word文件夹,里面的document.xml即为word正文内容。

对于word中的上图行合并表格,对应的xml如下:

<w:tbl>
<w:tblPr>
<w:tblStyle w:val="a3"/>
<w:tblW w:w="0" w:type="auto"/>
<w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1" w:lastColumn="0" w:noHBand="0" w:noVBand="1"/>
</w:tblPr>
<w:tblGrid>
<w:gridCol w:w="2765"/>
<w:gridCol w:w="2765"/>
</w:tblGrid>
<w:tr w:rsidR="00151AA4" w:rsidTr="000249EF">
<w:tc>
<w:tcPr>
<w:tcW w:w="2765" w:type="dxa"/>
<w:vMerge w:val="restart"/>
</w:tcPr>
<w:p w:rsidR="00151AA4" w:rsidRDefault="00151AA4" w:rsidP="00915802">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>0,0</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:tcW w:w="2765" w:type="dxa"/>
</w:tcPr>
<w:p w:rsidR="00151AA4" w:rsidRDefault="00151AA4">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>0,1</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr w:rsidR="00151AA4" w:rsidTr="000249EF">
<w:tc>
<w:tcPr>
<w:tcW w:w="2765" w:type="dxa"/>
<w:vMerge/>
</w:tcPr>
<w:p w:rsidR="00151AA4" w:rsidRDefault="00151AA4"/>
</w:tc>
<w:tc>
<w:tcPr>
<w:tcW w:w="2765" w:type="dxa"/>
</w:tcPr>
<w:p w:rsidR="00151AA4" w:rsidRDefault="00151AA4">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>1,1</w:t>
</w:r>
<w:bookmarkStart w:id="0" w:name="_GoBack"/>
<w:bookmarkEnd w:id="0"/>
</w:p>
</w:tc>
</w:tr>
</w:tbl>

看到这里,相信大家会理解了前面的tc,tcPr,vMerge等属性了吧。

其中w:tr表示的是表格的一行,tcPr代表的是一个单元格的属性。

具体可以参考:http://www.datypic.com/sc/ooxml/e-w_tbl-1.html

下面在给大家展示一下列合并的情况,大家也可以用来验证一下:

对应的xml:

<w:tbl>
<w:tblPr>
<w:tblStyle w:val="a3"/>
<w:tblW w:w="0" w:type="auto"/>
<w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1" w:lastColumn="0" w:noHBand="0" w:noVBand="1"/>
</w:tblPr>
<w:tblGrid>
<w:gridCol w:w="2765"/>
<w:gridCol w:w="2765"/>
</w:tblGrid>
<w:tr w:rsidR="006C0A9A" w:rsidTr="006C099A">
<w:tc>
<w:tcPr>
<w:tcW w:w="5530" w:type="dxa"/>
<w:gridSpan w:val="2"/>
</w:tcPr>
<w:p w:rsidR="006C0A9A" w:rsidRDefault="006C0A9A">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>0,0</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr w:rsidR="006C0A9A" w:rsidTr="000249EF">
<w:tc>
<w:tcPr>
<w:tcW w:w="2765" w:type="dxa"/>
</w:tcPr>
<w:p w:rsidR="006C0A9A" w:rsidRDefault="006C0A9A">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>1,0</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:tcW w:w="2765" w:type="dxa"/>
</w:tcPr>
<w:p w:rsidR="006C0A9A" w:rsidRDefault="006C0A9A">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>1,1</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
</w:tbl>

通过观察可以总结如下(使用poi提供的方法):

行合并情况:
CTTcPr tcpr = tables.get(0).getRow(2).getCell(0).getCTTc().getTcPr(); // 此属性每个单元格都有,为每个单元格的属性:tableCell.cellProperty
如果是行合并的第一行单元格,则: tcpr.getVMerge().getVal().toString() == "restart"
如果是行合并的其他行单元格,则: tcpr.getVMerge().getVal() == null
如果不是行合并的单元格,则: tcpr.getVMerge() == null

列合并情况:
CTTcPr tcpr = tables.get(0).getRow(2).getCell(0).getCTTc().getTcPr();
如果是列合并的第一列单元格,则:tcpr.getGridSpan().getVal()可以获取到这列单元格所占的行数
其他单元格:tcpr.getGridSpan() == null

这里有一个获取表格内容转为html的demo供大家参考。

使用poi读取word2007(.docx)中的复杂表格的更多相关文章

  1. SpringMVC 实现POI读取Excle文件中数据导入数据库(上传)、导出数据库中数据到Excle文件中(下载)

    读取Excale表返回一个集合: package com.shiliu.game.utils; import java.io.File; import java.io.FileInputStream; ...

  2. python读取excel文件中所有sheet表格

    sales: store: """(1)用load_workbook函数打开excel文件,返回一个工作簿对象 (2)用工作簿对象获取所有的sheet (3)第一个for ...

  3. poi读取docx中的文字和图片(自己应用)

    poi读取docx中的文字和图片(自己应用) package com.fry.poiDemo.dao; import java.io.File; import java.io.FileInputStr ...

  4. java使用poi读取doc和docx文件(maven自动导入依赖包)

    java使用poi读取doc和docx文件(maven自动导入依赖包) 于是在网上搜寻了一阵之后才发现原来doc文档和excel一样不能用普通的io流的方法来读取,而是也需要用poi,于是进行了一番尝 ...

  5. iText、poi操作word2007(读取,生成)

    关于生成word文件以及插入文字.表格.图片等功能,我使用了poi和itext,因为poi插入图片的jar包我在网上查并不是太完全,也可能我没找到如何使用,所以插入图片我用的是itext iText所 ...

  6. poi提取docx中的文字和图片

    package com.fry.poiDemo.dao; import java.io.File; import java.io.FileInputStream; import java.io.Fil ...

  7. java用poi读取Excel表格中的数据

    Java读写Excel的包是Apache POI(项目地址:http://poi.apache.org/),因此需要先获取POI的jar包,本实验使用的是POI 3.9稳定版.Apache POI 代 ...

  8. poi读取word2003(.doc文档)中的表格

    poi读取word2003(.doc文档)中的表格 Jakarta POI 是apache的子项目,目标是处理ole2对象.它提供了一组操纵Windows文档的Java API.在网上见到好多通过po ...

  9. 随笔记录①—利用poi读取Word中的标题和内容

    使用时间:4小时 使用poi方法将word中的内容提取出来,并输出到控制台或者存储到数据库poi.jar下载地址:https://www.apache.org/dyn/closer.lua/poi/r ...

随机推荐

  1. ZfNet解卷积:可视化CNN模型( PythonCode可视化Cifar10)

    原文链接:caffe Model的可视化 snapshot: 6000       一个在线可视化小工具:http://blog.csdn.net/10km/article/details/52713 ...

  2. 微信小程序音频长度获取的问题

    小程序推荐使用wx.createInnerAudioContext()创建的innerAudioContext,我们也通过这个接口创建音频.音频的长度可以通过属性获取: 但是,给innerAudioC ...

  3. BZOJ 3489: A simple rmq problem KDtree

    Code: #include<bits/stdc++.h> #define maxn 200000 #define inf 100000000 #define mid ((l+r)> ...

  4. mybatis批量操作(foreach)

    foreach可以在SQL语句中通过拼接的方式进行集合迭代.foreach元素的属性主要有collection,item,index,separator,open,close. item属性:表示循环 ...

  5. UVA1001 Say Cheese(Dijkstra或Floyd)

    题目链接:UVA1001 题意:在一个巨大奶酪中的A要以最短的时间与B相遇.在奶酪中走一米的距离花费的时间是10s,而奶酪中有许多洞,穿过这些洞的时间是0s.给出A.B以及各个洞的坐标,求最短的时间. ...

  6. Django REST framework 内置访问频率控制

    对匿名用户采用 IP 控制访问频率,对登录用户采用 用户名 控制访问频率. from rest_framework.throttling import SimpleRateThrottle class ...

  7. JS练习:表格全选与全不选

    代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title ...

  8. JavaScript学习笔记之对象

    目录 1.自定义对象 2.Array 3.Boolean 4.Date 5.Math 6.Number 7.String 8.RegExp 9.Function 10.Event 在 JavaScri ...

  9. spring配置Converter、Formatter日期转换器

    最近有点恶补spring的嫌疑,然后学了一点知识点纪录在此. 往往在项目中我们会遇到前端页面输入一个日期类型的字符串传递到后端后我们需要去做转换.甚至在传递的过程中就会报错. Spring有一个一劳永 ...

  10. RBAC(Role-Based Access Control)

    http://hi.baidu.com/akini/blog/item/eddbd61b90f6d4fbae513371.html RBAC 求助编辑百科名片 基 于角色的访问控制(Role-Base ...