一、原理
首先doc与docx是现实不一样的,因此在读取的时候需要分开写。在Word2007(.docx)文档中的解析中,先是将文档当做.zip的压缩文件解压处理,其中包含一些.xml文件,其中就包含了文档中的内容以及样式参数。跨行跨列的xml信息获取方式不同。
1.1 跨行getVMerge
以下是我举得一个例子,是一个单元格的信息,其中w:val="restart" 表示此单元格为跨行信息。一般单元格不存在跨行时无此xml信息,对象为null。存在此xml信息并且存在w:val="restart"信息时为跨行首行,若存在此xml信息但并没有w:val="restart"信息时为跨行非首行。根据此信息加上自己的处理即可
<xml-fragment w:val="restart"
xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas"
xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
xmlns:w10="urn:schemas-microsoft-com:office:word"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"
xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex"
xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"
xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk"
xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"
xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"
/>
1.2 跨列getGridSpan
对于跨列相对容易,w:val="2"这个值就说跨两列。不存在垮列时则无此xml信息。
<xml-fragment w:val="2" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"/>
二、实现
以下代码只是实例,实际不可能只有docx还会涉及doc、PDF、WPS等多种文档。写好接口做好不同实现就好。我的方法并不是最好的,只是根据想法先实现了出来,测试了不同复杂表格均可以表现出来。逻辑也都注释了,一目了然,代码直接用就可以。POI的jar就不多说了。
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import java.io.FileInputStream;
import java.util.List;
/*
* 使用POI库对word进行处理的工具类
*/
public class DocxPOIUtils {
public static void main(String[] args) throws Exception {
String sourceFile = "D:\\JetBrains\\Cloud\\测试表格.docx";
getAllTable2(sourceFile);
}
public static void getAllTable2(String sourceFile){
try{
XWPFDocument docx = new XWPFDocument(new FileInputStream(sourceFile));
List<XWPFTable> tables = docx.getTables();
List<XWPFTableRow> rows;
List<XWPFTableCell> cells;
for (XWPFTable table : tables) {
String str = "";
str += "<table border='1px solid black' cellspacing='0'>";
// 获取表格对应的行
rows = table.getRows();
for (int i = 0; i < rows.size(); i++) {
System.out.println("row---"+i);
str += "<tr>";
// 获取行对应的单元格
cells = rows.get(i).getTableCells();
for (int j = 0; j < cells.size(); j++) {
System.out.println("cell---"+j);
CTVMerge rowspan = cells.get(j).getCTTc().getTcPr().getVMerge();//跨行
//------------------
int d = 1;//跨行行数//计算是从跨行下一行开始统计的,故加上首行
if (rowspan != null) {//rowspan存在未有跨行信息
//跨行的第一行为restart,其他行为null
STMerge.Enum enum1 = rowspan.getVal();
if (enum1 != null && "restart".equals(enum1.toString())){//发现跨行信息为第一行时进行计算行数,非跨行首行则绕过不处理,已经计算在内
for (int k = i+1; k < rows.size(); k++) {//从当前单元格的跨行首行开始遍历,避免不必要的计算
if (rows.get(k).getTableCells().size() > j){
CTVMerge mer = rows.get(k).getTableCells().get(j).getCTTc().getTcPr().getVMerge();
if (mer != null) {//只要不为null就是存在连续的跨行
STMerge.Enum enum2 = mer.getVal();
if (enum2 != null && "restart".equals(enum2.toString())) {//若再次遇到restart,为下一次跨行,终止此次
break;
}else {//只要不是restart就加计算
d++;
}
}else {//连续中断,出现null则为此单元格跨行结束
break;
}
}else {
d++;
}
}
}
}
//------------------
CTDecimalNumber colspan = cells.get(j).getCTTc().getTcPr().getGridSpan();//跨列
if (colspan != null && rowspan != null) {
STMerge.Enum enum1 = rowspan.getVal();
if (enum1 != null) {
str += "<td colspan='"+colspan.getVal()+"' rowspan='"+d+"'>";
}
}else if (colspan == null && rowspan != null) {
STMerge.Enum enum1 = rowspan.getVal();
if (enum1 != null) {
str += "<td rowspan='"+d+"'>";
}
}else if (colspan != null && rowspan == null) {
str += "<td colspan='"+colspan.getVal()+"'>";
}else if (colspan == null && rowspan == null) {
str += "<td>";
}
str += cells.get(j).getText().trim().replaceAll("[\r\n]", "").toLowerCase();
str += "</td>";
}
str += "</tr>";
}
str += "</table>";
System.out.println(str);
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
产出效果为标签字符串。有个问题就是标签样式,我并没有获取详细的样式
<table border='1px solid black' cellspacing='0'><tr><td colspan='3' rowspan='2'>跨行跨列</td><td>a</td><td colspan='20'>a课题</td></tr><tr></td><td colspan='7'></td><td colspan='5'>不规则位置跨列</td><td colspan='9'></td></tr><tr><td colspan='3' rowspan='2'>紧随跨行跨列</td><td colspan='3'></td><td colspan='3'>性别</td><td></td><td colspan='4'>民族</td><td colspan='2'></td><td colspan='5'>中间跨列1</td><td colspan='3'>年 月</td></tr><tr></td><td colspan='3'></td><td colspan='4'>职务</td><td colspan='6'></td><td colspan='5'>中间跨列2</td><td colspan='3'></td></tr><tr><td colspan='3'>跨列4</td><td colspan='3'></td><td colspan='4'>学位</td><td colspan='6'></td><td colspan='5'>中间跨列3</td><td colspan='3'></td></tr><tr><td colspan='3'>跨列5</td><td colspan='10'>不规则位置跨列</td><td colspan='6'>联系电话</td><td colspan='5'></td></tr><tr><td colspan='3'>跨列6</td><td colspan='17'></td><td colspan='2'>邮编</td><td colspan='2'></td></tr><tr><td colspan='3'>跨列7</td><td colspan='4'></td><td colspan='4'>职务</td><td colspan='2'></td><td colspan='5'>工作单位</td><td colspan='6'></td></tr><tr><td colspan='3'>跨列8</td><td colspan='4'></td><td colspan='4'>职务</td><td colspan='2'></td><td colspan='5'>工作单位</td><td colspan='6'></td></tr><tr><td colspan='2'>跨列9</td><td></td><td></td><td colspan='16'>a.专著b.译著c.工具书</td><td colspan='3'>字数</td><td>万字</td></tr><tr><td colspan='5'>跨列</td><td colspan='7'></td><td colspan='7'>时间</td><td colspan='5'>年 月 日</td></tr><tr><td rowspan='3'>同列隔行跨行</td><td colspan='2'>标题1</td><td>标题2</td><td colspan='4'>标题3</td><td colspan='3'>标题4</td><td colspan='3'>标题5</td><td colspan='3'>标题6</td><td colspan='3'>标题7</td><td colspan='4'>标题8</td></tr><tr></td><td colspan='2'></td><td></td><td colspan='4'></td><td colspan='3'></td><td colspan='3'></td><td colspan='3'></td><td colspan='3'></td><td colspan='4'></td></tr><tr></td><td colspan='2'></td><td></td><td colspan='4'></td><td colspan='3'></td><td colspan='3'></td><td colspan='3'></td><td colspan='3'></td><td colspan='4'></td></tr></table>
**三、效果展示
3.1Word内表格截图**
3.2 HTML效果